diff options
Diffstat (limited to 'src/platform')
| -rw-r--r-- | src/platform/opengl/opengl_device.cpp | 34 | ||||
| -rw-r--r-- | src/platform/vulkan/vulkan_common.h | 4 | ||||
| -rw-r--r-- | src/platform/vulkan/vulkan_device.cpp | 98 |
3 files changed, 135 insertions, 1 deletions
diff --git a/src/platform/opengl/opengl_device.cpp b/src/platform/opengl/opengl_device.cpp index 82fdae1..7ef0ba1 100644 --- a/src/platform/opengl/opengl_device.cpp +++ b/src/platform/opengl/opengl_device.cpp @@ -7,6 +7,7 @@ #include <glad/glad.h> #include <GLFW/glfw3.h> +#include <algorithm> #include <imgui.h> #include <imgui_impl_glfw.h> #include <imgui_impl_opengl3.h> @@ -18,7 +19,7 @@ namespace Donut::RHI auto gl_topology(Topology t) -> GLenum { return t == Topology::Lines ? GL_LINES : GL_TRIANGLES; } auto gl_compare(CompareOp o) -> GLenum { return o == CompareOp::Always ? GL_ALWAYS : o == CompareOp::LessEqual ? GL_LEQUAL : GL_LESS; } auto gl_filter(Filter f) -> GLint { return f == Filter::Nearest ? GL_NEAREST : GL_LINEAR; } - auto gl_internal(Format f) -> GLint { return f == Format::RGBA16F ? GL_RGBA16F : f == Format::D32 ? GL_DEPTH_COMPONENT32F : GL_RGBA8; } + auto gl_internal(Format f) -> GLint { return f == Format::RGBA16F ? GL_RGBA16F : f == Format::RGBA32F ? GL_RGBA32F : f == Format::D32 ? GL_DEPTH_COMPONENT32F : GL_RGBA8; } // ---- Buffer ------------------------------------------------------- class GLBuffer : public Buffer @@ -255,6 +256,37 @@ namespace Donut::RHI } auto end_frame() -> void override { glfwSwapBuffers(static_cast<GLFWwindow*>(m_window)); } + auto run_offscreen(const std::function<void(CommandList&)>& record) -> void override + { + glBindVertexArray(m_vao); // GL is immediate: record executes now + record(*m_cmds); + glFinish(); + } + auto read_render_target(RenderTarget* target, std::vector<uint8_t>& out) -> void override + { + auto* rt = static_cast<GLRenderTarget*>(target); + int w = rt->m_w, h = rt->m_h; + out.resize((size_t)w * h * 4); + glBindFramebuffer(GL_FRAMEBUFFER, rt->m_fbo); + glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, out.data()); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + const size_t row = (size_t)w * 4; // GL is bottom-up -> flip to top-left + for (int y = 0; y < h / 2; ++y) + std::swap_ranges(out.begin() + y * row, out.begin() + (y + 1) * row, out.begin() + (h - 1 - y) * row); + } + auto read_render_target_float(RenderTarget* target, std::vector<float>& out) -> void override + { + auto* rt = static_cast<GLRenderTarget*>(target); + int w = rt->m_w, h = rt->m_h; + out.resize((size_t)w * h * 4); + glBindFramebuffer(GL_FRAMEBUFFER, rt->m_fbo); + glReadPixels(0, 0, w, h, GL_RGBA, GL_FLOAT, out.data()); // GL converts to float32 + glBindFramebuffer(GL_FRAMEBUFFER, 0); + const size_t row = (size_t)w * 4; // GL is bottom-up -> flip to top-left + for (int y = 0; y < h / 2; ++y) + std::swap_ranges(out.begin() + y * row, out.begin() + (y + 1) * row, out.begin() + (h - 1 - y) * row); + } + auto init_imgui() -> void override { IMGUI_CHECKVERSION(); ImGui::CreateContext(); diff --git a/src/platform/vulkan/vulkan_common.h b/src/platform/vulkan/vulkan_common.h index 5ae257e..8f02f17 100644 --- a/src/platform/vulkan/vulkan_common.h +++ b/src/platform/vulkan/vulkan_common.h @@ -41,6 +41,7 @@ namespace Donut::RHI { switch (f) { case Format::RGBA16F: return VK_FORMAT_R16G16B16A16_SFLOAT; + case Format::RGBA32F: return VK_FORMAT_R32G32B32A32_SFLOAT; case Format::D32: return VK_FORMAT_D32_SFLOAT; default: return VK_FORMAT_R8G8B8A8_UNORM; } @@ -277,6 +278,9 @@ namespace Donut::RHI auto begin_frame(const glm::vec4& clear) -> CommandList* override; auto end_frame() -> void override; + auto run_offscreen(const std::function<void(CommandList&)>& record) -> void override; + auto read_render_target(RenderTarget* target, std::vector<uint8_t>& out) -> void override; + auto read_render_target_float(RenderTarget* target, std::vector<float>& out) -> void override; auto init_imgui() -> void override; auto imgui_new_frame() -> void override; diff --git a/src/platform/vulkan/vulkan_device.cpp b/src/platform/vulkan/vulkan_device.cpp index 5d80cff..ff2e296 100644 --- a/src/platform/vulkan/vulkan_device.cpp +++ b/src/platform/vulkan/vulkan_device.cpp @@ -117,6 +117,104 @@ namespace Donut::RHI m_current_frame = (m_current_frame + 1) % MAX_FRAMES_IN_FLIGHT; } + auto VulkanDevice::run_offscreen(const std::function<void(CommandList&)>& record) -> void + { + vkDeviceWaitIdle(m_device); + vkResetDescriptorPool(m_device, m_frame_pools[0], 0); // per-draw sets for this pass + + VkCommandBufferAllocateInfo cbai{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; + cbai.commandPool = m_command_pool; cbai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; cbai.commandBufferCount = 1; + VkCommandBuffer cmd; vkAllocateCommandBuffers(m_device, &cbai, &cmd); + VkCommandBufferBeginInfo bi{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; bi.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + vkBeginCommandBuffer(cmd, &bi); + + m_cmds.m_device = m_device; m_cmds.m_cmd = cmd; + m_cmds.m_swapchain_rp = m_swapchain_rp; m_cmds.m_swapchain_fb = VK_NULL_HANDLE; m_cmds.m_extent = m_extent; + m_cmds.m_frame_pool = m_frame_pools[0]; m_cmds.m_pipe = nullptr; + record(m_cmds); // records its own off-screen render pass(es) + + vkEndCommandBuffer(cmd); + VkSubmitInfo si{ VK_STRUCTURE_TYPE_SUBMIT_INFO }; si.commandBufferCount = 1; si.pCommandBuffers = &cmd; + vkQueueSubmit(m_graphics_queue, 1, &si, VK_NULL_HANDLE); + vkQueueWaitIdle(m_graphics_queue); + vkFreeCommandBuffers(m_device, m_command_pool, 1, &cmd); + } + + auto VulkanDevice::read_render_target(RenderTarget* target, std::vector<uint8_t>& out) -> void + { + auto* rt = static_cast<VkRenderTargetR*>(target); + uint32_t w = (uint32_t)rt->m_w, h = (uint32_t)rt->m_h; + VkDeviceSize sz = (VkDeviceSize)w * h * 4; + out.resize(sz); + + VkBuffer buf; VkDeviceMemory mem; + create_buffer_raw(sz, VK_BUFFER_USAGE_TRANSFER_DST_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, buf, mem); + + VkCommandBufferAllocateInfo cbai{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; + cbai.commandPool = m_command_pool; cbai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; cbai.commandBufferCount = 1; + VkCommandBuffer cmd; vkAllocateCommandBuffers(m_device, &cbai, &cmd); + VkCommandBufferBeginInfo bi{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; bi.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + vkBeginCommandBuffer(cmd, &bi); + // The target ended its render pass in SHADER_READ_ONLY; move it to TRANSFER_SRC to copy. + VkImageMemoryBarrier b{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; + b.oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; b.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + b.image = rt->m_image; b.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; + b.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; b.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &b); + VkBufferImageCopy copy{}; copy.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 }; copy.imageExtent = { w, h, 1 }; + vkCmdCopyImageToBuffer(cmd, rt->m_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, buf, 1, ©); + VkImageMemoryBarrier b2 = b; b2.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; b2.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + b2.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; b2.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &b2); + vkEndCommandBuffer(cmd); + VkSubmitInfo si{ VK_STRUCTURE_TYPE_SUBMIT_INFO }; si.commandBufferCount = 1; si.pCommandBuffers = &cmd; + vkQueueSubmit(m_graphics_queue, 1, &si, VK_NULL_HANDLE); vkQueueWaitIdle(m_graphics_queue); + + void* mp = nullptr; vkMapMemory(m_device, mem, 0, sz, 0, &mp); + std::memcpy(out.data(), mp, sz); // RGBA8_UNORM, top-down (top-left origin) + vkUnmapMemory(m_device, mem); + vkFreeCommandBuffers(m_device, m_command_pool, 1, &cmd); + vkDestroyBuffer(m_device, buf, nullptr); vkFreeMemory(m_device, mem, nullptr); + } + + auto VulkanDevice::read_render_target_float(RenderTarget* target, std::vector<float>& out) -> void + { + auto* rt = static_cast<VkRenderTargetR*>(target); + uint32_t w = (uint32_t)rt->m_w, h = (uint32_t)rt->m_h; + out.resize((size_t)w * h * 4); + VkDeviceSize sz = (VkDeviceSize)out.size() * sizeof(float); + + VkBuffer buf; VkDeviceMemory mem; + create_buffer_raw(sz, VK_BUFFER_USAGE_TRANSFER_DST_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, buf, mem); + + VkCommandBufferAllocateInfo cbai{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; + cbai.commandPool = m_command_pool; cbai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; cbai.commandBufferCount = 1; + VkCommandBuffer cmd; vkAllocateCommandBuffers(m_device, &cbai, &cmd); + VkCommandBufferBeginInfo bi{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; bi.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + vkBeginCommandBuffer(cmd, &bi); + VkImageMemoryBarrier b{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; + b.oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; b.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + b.image = rt->m_image; b.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; + b.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; b.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &b); + VkBufferImageCopy copy{}; copy.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 }; copy.imageExtent = { w, h, 1 }; + vkCmdCopyImageToBuffer(cmd, rt->m_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, buf, 1, ©); + VkImageMemoryBarrier b2 = b; b2.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; b2.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + b2.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; b2.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &b2); + vkEndCommandBuffer(cmd); + VkSubmitInfo si{ VK_STRUCTURE_TYPE_SUBMIT_INFO }; si.commandBufferCount = 1; si.pCommandBuffers = &cmd; + vkQueueSubmit(m_graphics_queue, 1, &si, VK_NULL_HANDLE); vkQueueWaitIdle(m_graphics_queue); + + void* mp = nullptr; vkMapMemory(m_device, mem, 0, sz, 0, &mp); + std::memcpy(out.data(), mp, sz); // R32G32B32A32_SFLOAT, top-down + vkUnmapMemory(m_device, mem); + vkFreeCommandBuffers(m_device, m_command_pool, 1, &cmd); + vkDestroyBuffer(m_device, buf, nullptr); vkFreeMemory(m_device, mem, nullptr); + } + auto VulkanDevice::init_imgui() -> void { VkDescriptorPoolSize pool_size{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 }; |
