From 441a29433023fb2c687be786107f764ad4ca9cb4 Mon Sep 17 00:00:00 2001 From: hachem Date: Sun, 23 Aug 2026 17:41:31 +0200 Subject: fix: generalize render targets and window seams --- src/core/application.cpp | 3 +- src/platform/opengl/opengl_device.cpp | 14 +-- src/platform/vulkan/vulkan_device.cpp | 166 +++++++++++++++++++++------------- src/rendering/black_hole_renderer.cpp | 8 +- src/rendering/rhi.h | 22 ++++- src/rendering/scene_renderer.cpp | 6 +- 6 files changed, 137 insertions(+), 82 deletions(-) (limited to 'src') diff --git a/src/core/application.cpp b/src/core/application.cpp index 215ce9c..8b411c3 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -65,7 +65,8 @@ namespace Donut else m_device = RHI::create_opengl_device(); - if (!m_device->init(m_window->get_native_window(), w, h)) + RHI::NativeWindow native{ m_window->get_native_window(), w, h }; + if (!m_device->init(native)) { DONUT_ERROR("RHI device initialization failed"); return; diff --git a/src/platform/opengl/opengl_device.cpp b/src/platform/opengl/opengl_device.cpp index fbdbf91..ea48078 100644 --- a/src/platform/opengl/opengl_device.cpp +++ b/src/platform/opengl/opengl_device.cpp @@ -199,17 +199,17 @@ namespace Donut::RHI class GLDevice : public Device { public: - auto init(void* window, int width, int height) -> bool override + auto init(const NativeWindow& window) -> bool override { - m_window = window; - glfwMakeContextCurrent(static_cast(window)); + m_window = window.glfw_handle; + glfwMakeContextCurrent(static_cast(m_window)); if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { DONUT_ERROR("GL RHI: GLAD load failed"); return false; } - m_width = width; m_height = height; + m_width = window.width; m_height = window.height; const char* name = (const char*)glGetString(GL_RENDERER); m_name = name ? name : "OpenGL"; glGenVertexArrays(1, &m_vao); glBindVertexArray(m_vao); m_cmds = create_scope(m_vao); - DONUT_INFO("GL RHI device: {} ({}x{})", m_name, width, height); + DONUT_INFO("GL RHI device: {} ({}x{})", m_name, m_width, m_height); return true; } auto shutdown() -> void override @@ -229,8 +229,8 @@ namespace Donut::RHI HDRIManager::get().set_current_hdri(path); return create_ref(HDRIManager::get().get_current_hdri()); } - auto create_render_target(int w, int h, Format color, bool depth, Filter fl, int) -> Ref override - { return create_ref(w, h, color, depth, fl); } + auto create_render_target(int w, int h, Format color, Format depth, Filter fl, int) -> Ref override + { return create_ref(w, h, color, depth != Format::None, fl); } auto create_pipeline(const PipelineDesc& d) -> Ref override { return create_ref(d); } diff --git a/src/platform/vulkan/vulkan_device.cpp b/src/platform/vulkan/vulkan_device.cpp index c55e158..7cad210 100644 --- a/src/platform/vulkan/vulkan_device.cpp +++ b/src/platform/vulkan/vulkan_device.cpp @@ -19,6 +19,7 @@ #include #include #include +#include namespace Donut::RHI { @@ -105,11 +106,14 @@ namespace Donut::RHI public: ~VkRenderTargetR() override { - if (m_fb) vkDestroyFramebuffer(m_device, m_fb, nullptr); - if (m_sampler) vkDestroySampler(m_device, m_sampler, nullptr); - if (m_view) vkDestroyImageView(m_device, m_view, nullptr); - if (m_image) vkDestroyImage(m_device, m_image, nullptr); - if (m_mem) vkFreeMemory(m_device, m_mem, nullptr); + if (m_fb) vkDestroyFramebuffer(m_device, m_fb, nullptr); + if (m_sampler) vkDestroySampler(m_device, m_sampler, nullptr); + if (m_view) vkDestroyImageView(m_device, m_view, nullptr); + if (m_image) vkDestroyImage(m_device, m_image, nullptr); + if (m_mem) vkFreeMemory(m_device, m_mem, nullptr); + if (m_depth_view) vkDestroyImageView(m_device, m_depth_view, nullptr); + if (m_depth_image) vkDestroyImage(m_device, m_depth_image, nullptr); + if (m_depth_mem) vkFreeMemory(m_device, m_depth_mem, nullptr); } auto width() const -> int override { return m_w; } auto height() const -> int override { return m_h; } @@ -122,6 +126,11 @@ namespace Donut::RHI VkImageView m_view = VK_NULL_HANDLE; VkSampler m_sampler = VK_NULL_HANDLE; VkFramebuffer m_fb = VK_NULL_HANDLE; + VkRenderPass m_pass = VK_NULL_HANDLE; + bool m_has_depth = false; + VkImage m_depth_image = VK_NULL_HANDLE; + VkDeviceMemory m_depth_mem = VK_NULL_HANDLE; + VkImageView m_depth_view = VK_NULL_HANDLE; VkTextureR m_color; }; @@ -153,9 +162,9 @@ namespace Donut::RHI if (target) { auto* rt = static_cast(target); - rpbi.renderPass = m_offscreen_rp; rpbi.framebuffer = rt->m_fb; + rpbi.renderPass = rt->m_pass; rpbi.framebuffer = rt->m_fb; rpbi.renderArea = { { 0, 0 }, { (uint32_t)rt->m_w, (uint32_t)rt->m_h } }; - rpbi.clearValueCount = 1; rpbi.pClearValues = cvs; + rpbi.clearValueCount = rt->m_has_depth ? 2 : 1; rpbi.pClearValues = cvs; } else { @@ -231,7 +240,6 @@ namespace Donut::RHI VkDevice m_device = VK_NULL_HANDLE; VkCommandBuffer m_cmd = VK_NULL_HANDLE; VkRenderPass m_swapchain_rp = VK_NULL_HANDLE; - VkRenderPass m_offscreen_rp = VK_NULL_HANDLE; VkFramebuffer m_swapchain_fb = VK_NULL_HANDLE; VkExtent2D m_extent{}; VkDescriptorPool m_frame_pool = VK_NULL_HANDLE; @@ -244,7 +252,7 @@ namespace Donut::RHI class VulkanDevice : public Device { public: - auto init(void* glfwWindow, int width, int height) -> bool override; + auto init(const NativeWindow& window) -> bool override; auto shutdown() -> void override; auto resize(int width, int height) -> void override { m_framebuffer_resized = true; m_width = width; m_height = height; } auto wait_idle() -> void override { if (m_device) vkDeviceWaitIdle(m_device); } @@ -252,7 +260,7 @@ namespace Donut::RHI auto create_buffer(BufferType type, size_t size, const void* data) -> Ref override; auto create_texture(int w, int h, Format format, Filter filter, const void* data) -> Ref override; auto create_cubemap_from_hdri(const std::string& path) -> Ref override; - auto create_render_target(int w, int h, Format color, bool with_depth, Filter filter, int mips) -> Ref override; + auto create_render_target(int w, int h, Format color, Format depth, Filter filter, int mips) -> Ref override; auto create_pipeline(const PipelineDesc& desc) -> Ref override; auto begin_frame(const glm::vec4& clear) -> CommandList* override; @@ -273,8 +281,7 @@ namespace Donut::RHI auto pick_physical_and_device() -> bool; auto create_swapchain() -> bool; auto create_image_views() -> bool; - auto create_swapchain_render_pass() -> bool; - auto create_offscreen_render_pass() -> bool; + auto get_render_pass(VkFormat color, VkFormat depth, bool present) -> VkRenderPass; auto create_depth_and_framebuffers() -> bool; auto create_command_and_sync() -> bool; auto recreate_swapchain() -> bool; @@ -299,7 +306,7 @@ namespace Donut::RHI std::vector m_images; std::vector m_image_views; VkRenderPass m_swapchain_rp = VK_NULL_HANDLE; - VkRenderPass m_offscreen_rp = VK_NULL_HANDLE; + std::unordered_map m_pass_cache; std::vector m_framebuffers; VkImage m_depth_image = VK_NULL_HANDLE; VkDeviceMemory m_depth_mem = VK_NULL_HANDLE; VkImageView m_depth_view = VK_NULL_HANDLE; @@ -499,55 +506,61 @@ namespace Donut::RHI return true; } - auto VulkanDevice::create_swapchain_render_pass() -> bool + auto VulkanDevice::get_render_pass(VkFormat color, VkFormat depth, bool present) -> VkRenderPass { + uint64_t key = (uint64_t)(uint32_t)color + | ((uint64_t)(uint32_t)depth << 24) + | ((uint64_t)(present ? 1 : 0) << 48); + auto it = m_pass_cache.find(key); + if (it != m_pass_cache.end()) return it->second; + + const bool has_depth = depth != VK_FORMAT_UNDEFINED; VkAttachmentDescription atts[2]{}; - atts[0].format = m_swapchain_format; atts[0].samples = VK_SAMPLE_COUNT_1_BIT; + atts[0].format = color; atts[0].samples = VK_SAMPLE_COUNT_1_BIT; atts[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; atts[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; atts[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; atts[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - atts[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; atts[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - atts[1].format = VK_FORMAT_D32_SFLOAT; atts[1].samples = VK_SAMPLE_COUNT_1_BIT; + atts[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + atts[0].finalLayout = present ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + atts[1].format = depth; atts[1].samples = VK_SAMPLE_COUNT_1_BIT; atts[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; atts[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; atts[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; atts[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; atts[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; atts[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; VkAttachmentReference color_ref{ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; VkAttachmentReference depth_ref{ 1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL }; VkSubpassDescription subpass{}; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &color_ref; subpass.pDepthStencilAttachment = &depth_ref; - VkSubpassDependency dep{}; - dep.srcSubpass = VK_SUBPASS_EXTERNAL; dep.dstSubpass = 0; - dep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; - dep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; - dep.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - VkRenderPassCreateInfo rpci{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO }; - rpci.attachmentCount = 2; rpci.pAttachments = atts; rpci.subpassCount = 1; rpci.pSubpasses = &subpass; - rpci.dependencyCount = 1; rpci.pDependencies = &dep; - VKD_CHECK(vkCreateRenderPass(m_device, &rpci, nullptr, &m_swapchain_rp)); - return true; - } + subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &color_ref; + if (has_depth) subpass.pDepthStencilAttachment = &depth_ref; - auto VulkanDevice::create_offscreen_render_pass() -> bool - { - VkAttachmentDescription color{}; - color.format = VK_FORMAT_R8G8B8A8_UNORM; color.samples = VK_SAMPLE_COUNT_1_BIT; - color.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; color.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - color.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; color.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - color.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; color.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - VkAttachmentReference ref{ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; - VkSubpassDescription subpass{}; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &ref; VkSubpassDependency deps[2]{}; - deps[0].srcSubpass = VK_SUBPASS_EXTERNAL; deps[0].dstSubpass = 0; - deps[0].srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; deps[0].srcAccessMask = VK_ACCESS_SHADER_READ_BIT; - deps[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; deps[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - deps[1].srcSubpass = 0; deps[1].dstSubpass = VK_SUBPASS_EXTERNAL; - deps[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; deps[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - deps[1].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; deps[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + uint32_t dep_count; + if (present) + { + deps[0].srcSubpass = VK_SUBPASS_EXTERNAL; deps[0].dstSubpass = 0; + deps[0].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; + deps[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; + deps[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + dep_count = 1; + } + else + { + deps[0].srcSubpass = VK_SUBPASS_EXTERNAL; deps[0].dstSubpass = 0; + deps[0].srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; deps[0].srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + deps[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; deps[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + deps[1].srcSubpass = 0; deps[1].dstSubpass = VK_SUBPASS_EXTERNAL; + deps[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; deps[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + deps[1].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; deps[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + dep_count = 2; + } + VkRenderPassCreateInfo rpci{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO }; - rpci.attachmentCount = 1; rpci.pAttachments = &color; rpci.subpassCount = 1; rpci.pSubpasses = &subpass; - rpci.dependencyCount = 2; rpci.pDependencies = deps; - VKD_CHECK(vkCreateRenderPass(m_device, &rpci, nullptr, &m_offscreen_rp)); - return true; + rpci.attachmentCount = has_depth ? 2 : 1; rpci.pAttachments = atts; + rpci.subpassCount = 1; rpci.pSubpasses = &subpass; + rpci.dependencyCount = dep_count; rpci.pDependencies = deps; + VkRenderPass rp = VK_NULL_HANDLE; + if (vkCreateRenderPass(m_device, &rpci, nullptr, &rp) != VK_SUCCESS) + { DONUT_ERROR("Vulkan RHI: render pass creation failed"); return VK_NULL_HANDLE; } + m_pass_cache[key] = rp; + return rp; } auto VulkanDevice::create_depth_and_framebuffers() -> bool @@ -615,15 +628,15 @@ namespace Donut::RHI return true; } - auto VulkanDevice::init(void* glfwWindow, int width, int height) -> bool + auto VulkanDevice::init(const NativeWindow& window) -> bool { - m_window = (GLFWwindow*)glfwWindow; m_width = width; m_height = height; + m_window = (GLFWwindow*)window.glfw_handle; m_width = window.width; m_height = window.height; if (!create_instance()) return false; if (!pick_physical_and_device()) return false; if (!create_swapchain()) return false; if (!create_image_views()) return false; - if (!create_swapchain_render_pass()) return false; - if (!create_offscreen_render_pass()) return false; + m_swapchain_rp = get_render_pass(m_swapchain_format, VK_FORMAT_D32_SFLOAT, true); + if (!m_swapchain_rp) return false; if (!create_depth_and_framebuffers())return false; if (!create_command_and_sync()) return false; DONUT_INFO("Vulkan RHI device ready: {} swapchain images, {}x{}", (int)m_images.size(), m_extent.width, m_extent.height); @@ -723,13 +736,13 @@ namespace Donut::RHI return tex; } - auto VulkanDevice::create_render_target(int w, int h, Format color, bool, Filter filter, int) -> Ref + auto VulkanDevice::create_render_target(int w, int h, Format color, Format depth, Filter filter, int) -> Ref { auto rt = create_ref(); rt->m_device = m_device; rt->m_w = w; rt->m_h = h; - VkFormat fmt = vk_format(color); + VkFormat cfmt = (color == Format::Swapchain) ? m_swapchain_format : vk_format(color); VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; - ici.imageType = VK_IMAGE_TYPE_2D; ici.format = fmt; ici.extent = { (uint32_t)w, (uint32_t)h, 1 }; + ici.imageType = VK_IMAGE_TYPE_2D; ici.format = cfmt; ici.extent = { (uint32_t)w, (uint32_t)h, 1 }; ici.mipLevels = 1; ici.arrayLayers = 1; ici.samples = VK_SAMPLE_COUNT_1_BIT; ici.tiling = VK_IMAGE_TILING_OPTIMAL; ici.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; vkCreateImage(m_device, &ici, nullptr, &rt->m_image); @@ -739,15 +752,39 @@ namespace Donut::RHI vkAllocateMemory(m_device, &ai, nullptr, &rt->m_mem); vkBindImageMemory(m_device, rt->m_image, rt->m_mem, 0); VkImageViewCreateInfo vci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; - vci.image = rt->m_image; vci.viewType = VK_IMAGE_VIEW_TYPE_2D; vci.format = fmt; + vci.image = rt->m_image; vci.viewType = VK_IMAGE_VIEW_TYPE_2D; vci.format = cfmt; vci.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; vkCreateImageView(m_device, &vci, nullptr, &rt->m_view); VkSamplerCreateInfo smci{ VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; smci.magFilter = vk_filter(filter); smci.minFilter = vk_filter(filter); smci.addressModeU = smci.addressModeV = smci.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; vkCreateSampler(m_device, &smci, nullptr, &rt->m_sampler); + + rt->m_has_depth = depth != Format::None; + VkFormat dfmt = VK_FORMAT_UNDEFINED; + if (rt->m_has_depth) + { + dfmt = vk_format(depth); + VkImageCreateInfo dici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; + dici.imageType = VK_IMAGE_TYPE_2D; dici.format = dfmt; dici.extent = { (uint32_t)w, (uint32_t)h, 1 }; + dici.mipLevels = 1; dici.arrayLayers = 1; dici.samples = VK_SAMPLE_COUNT_1_BIT; + dici.tiling = VK_IMAGE_TILING_OPTIMAL; dici.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + vkCreateImage(m_device, &dici, nullptr, &rt->m_depth_image); + VkMemoryRequirements dreq{}; vkGetImageMemoryRequirements(m_device, rt->m_depth_image, &dreq); + VkMemoryAllocateInfo dai{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + dai.allocationSize = dreq.size; dai.memoryTypeIndex = find_memory_type(dreq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + vkAllocateMemory(m_device, &dai, nullptr, &rt->m_depth_mem); + vkBindImageMemory(m_device, rt->m_depth_image, rt->m_depth_mem, 0); + VkImageViewCreateInfo dvci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; + dvci.image = rt->m_depth_image; dvci.viewType = VK_IMAGE_VIEW_TYPE_2D; dvci.format = dfmt; + dvci.subresourceRange = { VK_IMAGE_ASPECT_DEPTH_BIT, 0, 1, 0, 1 }; + vkCreateImageView(m_device, &dvci, nullptr, &rt->m_depth_view); + } + + rt->m_pass = get_render_pass(cfmt, dfmt, false); + VkImageView atts[2] = { rt->m_view, rt->m_depth_view }; VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; - fbci.renderPass = m_offscreen_rp; fbci.attachmentCount = 1; fbci.pAttachments = &rt->m_view; + fbci.renderPass = rt->m_pass; fbci.attachmentCount = rt->m_has_depth ? 2u : 1u; fbci.pAttachments = atts; fbci.width = w; fbci.height = h; fbci.layers = 1; vkCreateFramebuffer(m_device, &fbci, nullptr, &rt->m_fb); @@ -1094,9 +1131,12 @@ namespace Donut::RHI gpci.stageCount = 2; gpci.pStages = stages; gpci.pVertexInputState = &vin; gpci.pInputAssemblyState = &ia; gpci.pViewportState = &vps; gpci.pRasterizationState = &rs; gpci.pMultisampleState = &ms; gpci.pColorBlendState = &cb; gpci.pDynamicState = &dsci; - if (desc.has_depth) gpci.pDepthStencilState = &ds; + VkFormat pcolor = (desc.target.color == Format::Swapchain) ? m_swapchain_format : vk_format(desc.target.color); + VkFormat pdepth = (desc.target.depth == Format::None) ? VK_FORMAT_UNDEFINED : vk_format(desc.target.depth); + bool present = desc.target.color == Format::Swapchain; + if (pdepth != VK_FORMAT_UNDEFINED) gpci.pDepthStencilState = &ds; gpci.layout = p->m_layout; - gpci.renderPass = desc.has_depth ? m_swapchain_rp : m_offscreen_rp; + gpci.renderPass = get_render_pass(pcolor, pdepth, present); gpci.subpass = 0; VkResult pr = vkCreateGraphicsPipelines(m_device, VK_NULL_HANDLE, 1, &gpci, nullptr, &p->m_pipeline); vkDestroyShaderModule(m_device, vmod, nullptr); vkDestroyShaderModule(m_device, fmod, nullptr); @@ -1124,7 +1164,7 @@ namespace Donut::RHI 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_offscreen_rp = m_offscreen_rp; + m_cmds.m_swapchain_rp = m_swapchain_rp; m_cmds.m_swapchain_fb = m_framebuffers[m_image_index]; m_cmds.m_extent = m_extent; m_cmds.m_frame_pool = m_frame_pools[m_current_frame]; m_cmds.m_pipe = nullptr; return &m_cmds; @@ -1207,8 +1247,8 @@ namespace Donut::RHI for (auto f : m_in_flight) vkDestroyFence(m_device, f, nullptr); m_render_finished.clear(); m_image_available.clear(); m_in_flight.clear(); if (m_command_pool) vkDestroyCommandPool(m_device, m_command_pool, nullptr); - if (m_offscreen_rp) vkDestroyRenderPass(m_device, m_offscreen_rp, nullptr); - if (m_swapchain_rp) vkDestroyRenderPass(m_device, m_swapchain_rp, nullptr); + for (auto& [key, rp] : m_pass_cache) vkDestroyRenderPass(m_device, rp, nullptr); + m_pass_cache.clear(); m_swapchain_rp = VK_NULL_HANDLE; cleanup_swapchain(); vkDestroyDevice(m_device, nullptr); m_device = VK_NULL_HANDLE; if (m_surface) vkDestroySurfaceKHR(m_instance, m_surface, nullptr); diff --git a/src/rendering/black_hole_renderer.cpp b/src/rendering/black_hole_renderer.cpp index 2534b1d..c7b58c5 100644 --- a/src/rendering/black_hole_renderer.cpp +++ b/src/rendering/black_hole_renderer.cpp @@ -27,8 +27,8 @@ namespace Donut { m_device = &device; - m_geo_lo = device.create_render_target(GEO_LO_W, GEO_LO_H, Format::RGBA8, false, Filter::Linear); - m_geo_hi = device.create_render_target(GEO_HI_W, GEO_HI_H, Format::RGBA8, false, Filter::Linear); + m_geo_lo = device.create_render_target(GEO_LO_W, GEO_LO_H, Format::RGBA8, Format::None, Filter::Linear); + m_geo_hi = device.create_render_target(GEO_HI_W, GEO_HI_H, Format::RGBA8, Format::None, Filter::Linear); const float quad[] = { -1.f, 1.f, 0.f, 1.f, -1.f, -1.f, 0.f, 0.f, 1.f, -1.f, 1.f, 0.f, @@ -59,7 +59,7 @@ namespace Donut { ResourceKind::Texture, 4, "u_HDRIEnvironment" }, }; d.topology = Topology::Triangles; - d.has_depth = false; + d.target = { Format::RGBA8, Format::None }; m_geo_pipeline = device.create_pipeline(d); } { @@ -68,7 +68,7 @@ namespace Donut d.vertex_layout = { 16, { { 0, 2, 0 }, { 1, 2, 8 } } }; d.resources = { { ResourceKind::Texture, 0, "u_ScreenTexture" } }; d.topology = Topology::Triangles; - d.has_depth = true; + d.target = { Format::Swapchain, Format::D32 }; m_present_pipeline = device.create_pipeline(d); } diff --git a/src/rendering/rhi.h b/src/rendering/rhi.h index 1a61420..7684aa0 100644 --- a/src/rendering/rhi.h +++ b/src/rendering/rhi.h @@ -11,11 +11,26 @@ namespace Donut::RHI { enum class Format { + None, + Swapchain, RGBA8, RGBA16F, D32, }; + struct RenderTargetFormat + { + Format color = Format::RGBA8; + Format depth = Format::None; + }; + + struct NativeWindow + { + void* glfw_handle = nullptr; + int width = 0; + int height = 0; + }; + enum class BufferType { Vertex, Index, Uniform }; enum class Topology { Triangles, Lines }; enum class CullMode { None, Back, Front }; @@ -53,8 +68,7 @@ namespace Donut::RHI bool depth_test = false; bool depth_write = false; CompareOp depth_op = CompareOp::Less; - Format color_format = Format::RGBA8; - bool has_depth = false; + RenderTargetFormat target; }; class CommandList @@ -83,7 +97,7 @@ namespace Donut::RHI public: virtual ~Device() = default; - virtual auto init(void* glfwWindow, int width, int height) -> bool = 0; + virtual auto init(const NativeWindow& window) -> bool = 0; virtual auto shutdown() -> void = 0; virtual auto resize(int width, int height) -> void = 0; virtual auto wait_idle() -> void = 0; @@ -91,7 +105,7 @@ namespace Donut::RHI virtual auto create_buffer(BufferType type, size_t size, const void* data = nullptr) -> Ref = 0; virtual auto create_texture(int width, int height, Format format, Filter filter, const void* data = nullptr) -> Ref = 0; virtual auto create_cubemap_from_hdri(const std::string& equirect_path) -> Ref = 0; - virtual auto create_render_target(int width, int height, Format color, bool with_depth, + virtual auto create_render_target(int width, int height, Format color, Format depth = Format::None, Filter sample_filter = Filter::Linear, int mip_levels = 1) -> Ref = 0; virtual auto create_pipeline(const PipelineDesc& desc) -> Ref = 0; diff --git a/src/rendering/scene_renderer.cpp b/src/rendering/scene_renderer.cpp index 076f6ba..c9da332 100644 --- a/src/rendering/scene_renderer.cpp +++ b/src/rendering/scene_renderer.cpp @@ -65,7 +65,7 @@ namespace Donut d.topology = Topology::Lines; d.blend = BlendMode::AlphaBlend; d.depth_test = true; d.depth_write = false; d.depth_op = CompareOp::LessEqual; - d.has_depth = true; + d.target = { Format::Swapchain, Format::D32 }; m_grid_pipeline = device.create_pipeline(d); } @@ -101,7 +101,7 @@ namespace Donut { ResourceKind::Texture, 1, "u_HDRIEnvironment" } }; d.topology = Topology::Triangles; d.depth_test = true; d.depth_write = true; d.depth_op = CompareOp::Less; - d.has_depth = true; + d.target = { Format::Swapchain, Format::D32 }; m_sphere_pipeline = device.create_pipeline(d); } @@ -124,7 +124,7 @@ namespace Donut { ResourceKind::Texture, 1, "u_Skybox" } }; d.topology = Topology::Triangles; d.depth_test = false; d.depth_write = false; - d.has_depth = true; + d.target = { Format::Swapchain, Format::D32 }; m_skybox_pipeline = device.create_pipeline(d); } -- cgit v1.3