aboutsummaryrefslogtreecommitdiff
path: root/src/platform/vulkan
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2026-08-24 03:57:40 +0200
committerhachem <im@hachem.wtf>2026-08-24 03:57:40 +0200
commit59b7c407550d63e997ed1e7bed286be5c332c285 (patch)
tree95f168df75d33326dde486b401c1abf62608ff7c /src/platform/vulkan
parent7ae22084c74cbe0c127e0ea1c96ec22a1f47d221 (diff)
[feat]: work on render path exporting
Diffstat (limited to 'src/platform/vulkan')
-rw-r--r--src/platform/vulkan/vulkan_common.h4
-rw-r--r--src/platform/vulkan/vulkan_device.cpp98
2 files changed, 102 insertions, 0 deletions
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, &copy);
+ 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, &copy);
+ 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 };