From 40f2df1a813af699263c7a32aeea1f2bcdfa92d1 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 10 May 2024 15:39:56 +0200 Subject: [PATCH] Vulkan: Render graph clear attachments Adds support for clear attachments to the render graph. Clearing attachments require that the command buffer is being set for rendering (vkCmdBeginRendering). **What works** - Clear attachments are working with dynamic rendering and the render graph - `GPUVulkanTest.framebuffer_clear_color_single_attachment` - `GPUVulkanTest.framebuffer_clear_color_multiple_attachments` - `GPUVulkanTest.framebuffer_clear_multiple_color_multiple_attachments` - `GPUVulkanTest.framebuffer_scissor_test` - `GPUVulkanTest.framebuffer_clear_depth` **What still needs to be addressed** When a command buffer is rendering it cannot do any pipeline barriers. For clearing attachments this isn't needed, but when we address drawing we might need to register drawing resource dependencies to the dependency list of the begin rendering node. This will be solved when drawing will be implemented. [#121648] The begin rendering node is large as it has to store data for framebuffers with 8 color attachments as well. This might become an overhead and we could solve this by splicing the data of larger nodes into 2 lists. This will be addressed after we are able to draw a screen so we can collect data on this topic. [#121649] Pull Request: https://projects.blender.org/blender/blender/pulls/121073 --- source/blender/gpu/CMakeLists.txt | 4 + .../nodes/vk_begin_rendering_node.hh | 109 ++++++++++++++ .../nodes/vk_clear_attachments_node.hh | 66 +++++++++ .../nodes/vk_end_rendering_node.hh | 64 +++++++++ .../vulkan/render_graph/nodes/vk_node_info.hh | 5 +- .../tests/vk_render_graph_test_render.cc | 136 ++++++++++++++++++ .../tests/vk_render_graph_test_types.hh | 14 +- .../vulkan/render_graph/vk_render_graph.hh | 12 ++ .../render_graph/vk_render_graph_node.hh | 33 +++++ source/blender/gpu/vulkan/vk_context.cc | 22 ++- source/blender/gpu/vulkan/vk_context.hh | 9 ++ source/blender/gpu/vulkan/vk_framebuffer.cc | 131 +++++++++++++---- source/blender/gpu/vulkan/vk_framebuffer.hh | 44 ++++-- source/blender/gpu/vulkan/vk_texture.cc | 2 + source/blender/gpu/vulkan/vk_to_string.cc | 9 +- source/blender/gpu/vulkan/vk_to_string.hh | 1 + 16 files changed, 618 insertions(+), 43 deletions(-) create mode 100644 source/blender/gpu/vulkan/render_graph/nodes/vk_begin_rendering_node.hh create mode 100644 source/blender/gpu/vulkan/render_graph/nodes/vk_clear_attachments_node.hh create mode 100644 source/blender/gpu/vulkan/render_graph/nodes/vk_end_rendering_node.hh create mode 100644 source/blender/gpu/vulkan/render_graph/tests/vk_render_graph_test_render.cc diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 687ac3989f0..a2ba66fcc70 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -271,6 +271,8 @@ set(VULKAN_SRC vulkan/vk_push_constants.hh vulkan/vk_query.hh vulkan/render_graph/nodes/vk_blit_image_node.hh + vulkan/render_graph/nodes/vk_begin_rendering_node.hh + vulkan/render_graph/nodes/vk_clear_attachments_node.hh vulkan/render_graph/nodes/vk_clear_color_image_node.hh vulkan/render_graph/nodes/vk_clear_depth_stencil_image_node.hh vulkan/render_graph/nodes/vk_copy_buffer_node.hh @@ -278,6 +280,7 @@ set(VULKAN_SRC vulkan/render_graph/nodes/vk_copy_image_node.hh vulkan/render_graph/nodes/vk_copy_image_to_buffer_node.hh vulkan/render_graph/nodes/vk_dispatch_node.hh + vulkan/render_graph/nodes/vk_end_rendering_node.hh vulkan/render_graph/nodes/vk_fill_buffer_node.hh vulkan/render_graph/nodes/vk_node_info.hh vulkan/render_graph/nodes/vk_pipeline_data.hh @@ -944,6 +947,7 @@ if(WITH_GTESTS) vulkan/tests/vk_memory_layout_test.cc vulkan/render_graph/tests/vk_render_graph_test_compute.cc vulkan/render_graph/tests/vk_render_graph_test_present.cc + vulkan/render_graph/tests/vk_render_graph_test_render.cc vulkan/render_graph/tests/vk_render_graph_test_transfer.cc ) endif() diff --git a/source/blender/gpu/vulkan/render_graph/nodes/vk_begin_rendering_node.hh b/source/blender/gpu/vulkan/render_graph/nodes/vk_begin_rendering_node.hh new file mode 100644 index 00000000000..ada12095cfc --- /dev/null +++ b/source/blender/gpu/vulkan/render_graph/nodes/vk_begin_rendering_node.hh @@ -0,0 +1,109 @@ +/* SPDX-FileCopyrightText: 2024 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +#include "render_graph/vk_resource_access_info.hh" +#include "vk_node_info.hh" + +namespace blender::gpu::render_graph { + +/** + * Information stored inside the render graph node. See `VKRenderGraphNode`. + */ +struct VKBeginRenderingData { + VkRenderingAttachmentInfo color_attachments[8]; + VkRenderingAttachmentInfo depth_attachment; + VkRenderingAttachmentInfo stencil_attachment; + VkRenderingInfoKHR vk_rendering_info; +}; + +struct VKBeginRenderingCreateInfo { + VKBeginRenderingData node_data; + const VKResourceAccessInfo &resources; + VKBeginRenderingCreateInfo(const VKResourceAccessInfo &resources) : resources(resources) + { + /* Using memset as MSVC didn't clear the color_attachments array. */ + memset(&node_data, 0, sizeof(node_data)); + } +}; + +/** + * Begin rendering node + * + * - Contains logic to copy relevant data to the VKRenderGraphNode. + * - Determine read/write resource dependencies. + * - Add commands to a command builder. + */ +class VKBeginRenderingNode : public VKNodeInfo { + public: + /** + * Update the node data with the data inside create_info. + * + * Has been implemented as a template to ensure all node specific data + * (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The + * actual node data (`VKRenderGraphNode` includes all header files.) + */ + template void set_node_data(Node &node, const CreateInfo &create_info) + { + BLI_assert_msg(ELEM(create_info.node_data.vk_rendering_info.pColorAttachments, + nullptr, + create_info.node_data.color_attachments), + "When create_info.node_data.vk_rendering_info.pColorAttachments points to " + "something, it should point to create_info.node_data.color_attachments."); + BLI_assert_msg(ELEM(create_info.node_data.vk_rendering_info.pDepthAttachment, + nullptr, + &create_info.node_data.depth_attachment), + "When create_info.node_data.vk_rendering_info.pDepthAttachment points to " + "something, it should point to create_info.node_data.depth_attachment."); + BLI_assert_msg(ELEM(create_info.node_data.vk_rendering_info.pStencilAttachment, + nullptr, + &create_info.node_data.stencil_attachment), + "When create_info.node_data.vk_rendering_info.pStencilAttachment points to " + "something, it should point to create_info.node_data.stencil_attachment."); + node.begin_rendering = create_info.node_data; + /* Localize pointers when set.*/ + if (node.begin_rendering.vk_rendering_info.pColorAttachments) { + node.begin_rendering.vk_rendering_info.pColorAttachments = + node.begin_rendering.color_attachments; + } + if (node.begin_rendering.vk_rendering_info.pDepthAttachment) { + node.begin_rendering.vk_rendering_info.pDepthAttachment = + &node.begin_rendering.depth_attachment; + } + if (node.begin_rendering.vk_rendering_info.pStencilAttachment) { + node.begin_rendering.vk_rendering_info.pStencilAttachment = + &node.begin_rendering.stencil_attachment; + } + } + + /** + * Extract read/write resource dependencies from `create_info` and add them to `node_links`. + */ + void build_links(VKResourceStateTracker &resources, + VKRenderGraphNodeLinks &node_links, + const CreateInfo &create_info) override + { + create_info.resources.build_links(resources, node_links); + } + + /** + * Build the commands and add them to the command_buffer. + */ + void build_commands(VKCommandBufferInterface &command_buffer, + const Data &data, + VKBoundPipelines & /*r_bound_pipelines*/) override + { + command_buffer.begin_rendering(&data.vk_rendering_info); + } +}; +} // namespace blender::gpu::render_graph diff --git a/source/blender/gpu/vulkan/render_graph/nodes/vk_clear_attachments_node.hh b/source/blender/gpu/vulkan/render_graph/nodes/vk_clear_attachments_node.hh new file mode 100644 index 00000000000..109e860b9e7 --- /dev/null +++ b/source/blender/gpu/vulkan/render_graph/nodes/vk_clear_attachments_node.hh @@ -0,0 +1,66 @@ +/* SPDX-FileCopyrightText: 2024 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +#include "render_graph/vk_resource_access_info.hh" +#include "vk_node_info.hh" + +namespace blender::gpu::render_graph { + +/** + * Information stored inside the render graph node. See `VKRenderGraphNode`. + */ +struct VKClearAttachmentsData { + uint32_t attachment_count; + VkClearAttachment attachments[8]; + VkClearRect vk_clear_rect; +}; + +class VKClearAttachmentsNode : public VKNodeInfo { + public: + /** + * Update the node data with the data inside create_info. + * + * Has been implemented as a template to ensure all node specific data + * (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The + * actual node data (`VKRenderGraphNode` includes all header files.) + */ + template static void set_node_data(Node &node, const CreateInfo &create_info) + { + node.clear_attachments = create_info; + } + + /** + * Extract read/write resource dependencies from `create_info` and add them to `node_links`. + */ + void build_links(VKResourceStateTracker &resources, + VKRenderGraphNodeLinks &node_links, + const CreateInfo &create_info) override + { + UNUSED_VARS(resources, node_links, create_info); + } + + /** + * Build the commands and add them to the command_buffer. + */ + void build_commands(VKCommandBufferInterface &command_buffer, + const Data &data, + VKBoundPipelines & /*r_bound_pipelines*/) override + { + command_buffer.clear_attachments( + data.attachment_count, data.attachments, 1, &data.vk_clear_rect); + } +}; +} // namespace blender::gpu::render_graph diff --git a/source/blender/gpu/vulkan/render_graph/nodes/vk_end_rendering_node.hh b/source/blender/gpu/vulkan/render_graph/nodes/vk_end_rendering_node.hh new file mode 100644 index 00000000000..5ffe94dba64 --- /dev/null +++ b/source/blender/gpu/vulkan/render_graph/nodes/vk_end_rendering_node.hh @@ -0,0 +1,64 @@ +/* SPDX-FileCopyrightText: 2024 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +#include "vk_node_info.hh" + +namespace blender::gpu::render_graph { + +/** + * Information stored inside the render graph node. See `VKRenderGraphNode`. + */ +struct VKEndRenderingData {}; + +/** + * End rendering node + * + * - Contains logic to copy relevant data to the VKRenderGraphNode. + * - Determine read/write resource dependencies. + * - Add commands to a command builder. + */ +class VKEndRenderingNode : public VKNodeInfo { + public: + /** + * Update the node data with the data inside create_info. + * + * Has been implemented as a template to ensure all node specific data + * (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The + * actual node data (`VKRenderGraphNode` includes all header files.) + */ + template void set_node_data(Node &node, const CreateInfo &create_info) + { + node.end_rendering = create_info; + } + + /** + * Extract read/write resource dependencies from `create_info` and add them to `node_links`. + */ + void build_links(VKResourceStateTracker & /*resources*/, + VKRenderGraphNodeLinks & /*node_links*/, + const CreateInfo & /*create_info*/) override + { + } + + /** + * Build the commands and add them to the command_buffer. + */ + void build_commands(VKCommandBufferInterface &command_buffer, + const Data & /*data*/, + VKBoundPipelines & /*r_bound_pipelines*/) override + { + command_buffer.end_rendering(); + } +}; +} // namespace blender::gpu::render_graph diff --git a/source/blender/gpu/vulkan/render_graph/nodes/vk_node_info.hh b/source/blender/gpu/vulkan/render_graph/nodes/vk_node_info.hh index 7353b5218d4..6b1c15f267e 100644 --- a/source/blender/gpu/vulkan/render_graph/nodes/vk_node_info.hh +++ b/source/blender/gpu/vulkan/render_graph/nodes/vk_node_info.hh @@ -21,6 +21,9 @@ namespace blender::gpu::render_graph { */ enum class VKNodeType { UNUSED, + BEGIN_RENDERING, + END_RENDERING, + CLEAR_ATTACHMENTS, CLEAR_COLOR_IMAGE, CLEAR_DEPTH_STENCIL_IMAGE, FILL_BUFFER, @@ -47,7 +50,7 @@ enum class VKNodeType { template class VKNodeInfo : public NonCopyable { diff --git a/source/blender/gpu/vulkan/render_graph/tests/vk_render_graph_test_render.cc b/source/blender/gpu/vulkan/render_graph/tests/vk_render_graph_test_render.cc new file mode 100644 index 00000000000..cb6ea8ca3e3 --- /dev/null +++ b/source/blender/gpu/vulkan/render_graph/tests/vk_render_graph_test_render.cc @@ -0,0 +1,136 @@ +/* SPDX-FileCopyrightText: 2024 Blender Authors + * + * SPDX-License-Identifier: Apache-2.0 */ + +#include "testing/testing.h" + +#include "vk_render_graph_test_types.hh" + +namespace blender::gpu::render_graph { + +TEST(vk_render_graph, begin_clear_attachments_end_read_back) +{ + VkHandle image(1u); + VkHandle image_view(2u); + VkHandle buffer(3u); + + Vector log; + VKCommandBufferWrapper wrapper; + VKResourceStateTracker resources; + VKRenderGraph render_graph(std::make_unique(log), resources); + resources.add_image(image, VK_IMAGE_LAYOUT_UNDEFINED, ResourceOwner::APPLICATION); + resources.add_buffer(buffer); + + { + VKResourceAccessInfo access_info = {}; + access_info.images.append( + {image, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_ASPECT_COLOR_BIT}); + VKBeginRenderingNode::CreateInfo begin_rendering(access_info); + begin_rendering.node_data.color_attachments[0].sType = + VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR; + begin_rendering.node_data.color_attachments[0].imageLayout = + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + begin_rendering.node_data.color_attachments[0].imageView = image_view; + begin_rendering.node_data.color_attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + begin_rendering.node_data.color_attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + begin_rendering.node_data.vk_rendering_info.sType = VK_STRUCTURE_TYPE_RENDERING_INFO; + begin_rendering.node_data.vk_rendering_info.colorAttachmentCount = 1; + begin_rendering.node_data.vk_rendering_info.layerCount = 1; + begin_rendering.node_data.vk_rendering_info.pColorAttachments = + begin_rendering.node_data.color_attachments; + + render_graph.add_node(begin_rendering); + } + + { + VKClearAttachmentsNode::CreateInfo clear_attachments = {}; + clear_attachments.attachment_count = 1; + clear_attachments.attachments[0].aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + clear_attachments.attachments[0].clearValue.color.float32[0] = 0.2; + clear_attachments.attachments[0].clearValue.color.float32[1] = 0.4; + clear_attachments.attachments[0].clearValue.color.float32[2] = 0.6; + clear_attachments.attachments[0].clearValue.color.float32[3] = 1.0; + clear_attachments.attachments[0].colorAttachment = 0; + clear_attachments.vk_clear_rect.baseArrayLayer = 0; + clear_attachments.vk_clear_rect.layerCount = 1; + clear_attachments.vk_clear_rect.rect.extent.width = 1920; + clear_attachments.vk_clear_rect.rect.extent.height = 1080; + render_graph.add_node(clear_attachments); + } + + { + VKEndRenderingNode::CreateInfo end_rendering = {}; + render_graph.add_node(end_rendering); + } + + { + VKCopyImageToBufferNode::CreateInfo copy_image_to_buffer = {}; + copy_image_to_buffer.src_image = image; + copy_image_to_buffer.dst_buffer = buffer; + copy_image_to_buffer.region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + render_graph.add_node(copy_image_to_buffer); + } + + render_graph.submit_buffer_for_read(buffer); + EXPECT_EQ(6, log.size()); + EXPECT_EQ( + "pipeline_barrier(src_stage_mask=VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, " + "dst_stage_mask=VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT" + + endl() + + " - image_barrier(src_access_mask=, " + "dst_access_mask=VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, " + "old_layout=VK_IMAGE_LAYOUT_UNDEFINED, " + "new_layout=VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, image=0x1, " + "subresource_range=" + + endl() + + " aspect_mask=VK_IMAGE_ASPECT_COLOR_BIT, base_mip_level=0, level_count=4294967295, " + "base_array_layer=0, layer_count=4294967295 )" + + endl() + ")", + log[0]); + EXPECT_EQ("begin_rendering(p_rendering_info=flags=, render_area=" + endl() + + " offset=" + endl() + " x=0, y=0 , extent=" + endl() + + " width=0, height=0 , layer_count=1, view_mask=0, color_attachment_count=1, " + "p_color_attachments=" + + endl() + + " image_view=0x2, image_layout=VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, " + "resolve_mode=VK_RESOLVE_MODE_NONE, resolve_image_view=0, " + "resolve_image_layout=VK_IMAGE_LAYOUT_UNDEFINED, " + "load_op=VK_ATTACHMENT_LOAD_OP_DONT_CARE, store_op=VK_ATTACHMENT_STORE_OP_STORE" + + endl() + ")", + log[1]); + EXPECT_EQ( + "clear_attachments( - attachment(aspect_mask=VK_IMAGE_ASPECT_COLOR_BIT, " + "color_attachment=0)" + + endl() + " - rect(rect=" + endl() + " offset=" + endl() + + " x=0, y=0 , extent=" + endl() + + " width=1920, height=1080 , base_array_layer=0, layer_count=1)" + endl() + ")", + log[2]); + EXPECT_EQ("end_rendering()", log[3]); + EXPECT_EQ( + "pipeline_barrier(src_stage_mask=VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, " + "dst_stage_mask=VK_PIPELINE_STAGE_TRANSFER_BIT" + + endl() + + " - image_barrier(src_access_mask=VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, " + "dst_access_mask=VK_ACCESS_TRANSFER_READ_BIT, " + "old_layout=VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, " + "new_layout=VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image=0x1, subresource_range=" + + endl() + + " aspect_mask=VK_IMAGE_ASPECT_COLOR_BIT, base_mip_level=0, level_count=4294967295, " + "base_array_layer=0, layer_count=4294967295 )" + + endl() + ")", + log[4]); + EXPECT_EQ( + "copy_image_to_buffer(src_image=0x1, src_image_layout=VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, " + "dst_buffer=0x3" + + endl() + + " - region(buffer_offset=0, buffer_row_length=0, buffer_image_height=0, " + "image_subresource=" + + endl() + + " aspect_mask=VK_IMAGE_ASPECT_COLOR_BIT, mip_level=0, base_array_layer=0, " + "layer_count=0 , image_offset=" + + endl() + " x=0, y=0, z=0 , image_extent=" + endl() + + " width=0, height=0, depth=0 )" + endl() + ")", + log[5]); +} + +} // namespace blender::gpu::render_graph diff --git a/source/blender/gpu/vulkan/render_graph/tests/vk_render_graph_test_types.hh b/source/blender/gpu/vulkan/render_graph/tests/vk_render_graph_test_types.hh index 70d55c3bc7e..89278b3c5bd 100644 --- a/source/blender/gpu/vulkan/render_graph/tests/vk_render_graph_test_types.hh +++ b/source/blender/gpu/vulkan/render_graph/tests/vk_render_graph_test_types.hh @@ -332,7 +332,19 @@ class CommandBufferLog : public VKCommandBufferInterface { { UNUSED_VARS(attachment_count, p_attachments, rect_count, p_rects); EXPECT_TRUE(is_recording_); - GTEST_FAIL() << __func__ << " not implemented!"; + std::stringstream ss; + ss << "clear_attachments("; + for (const VkClearAttachment &attachment : + Span(p_attachments, attachment_count)) + { + ss << " - attachment(" << to_string(attachment, 1) << ")" << std::endl; + } + for (const VkClearRect &rect : Span(p_rects, rect_count)) { + ss << " - rect(" << to_string(rect, 1) << ")" << std::endl; + } + ss << ")"; + + log_.append(ss.str()); } void pipeline_barrier(VkPipelineStageFlags src_stage_mask, diff --git a/source/blender/gpu/vulkan/render_graph/vk_render_graph.hh b/source/blender/gpu/vulkan/render_graph/vk_render_graph.hh index 45fc6689b8c..3e72bcfa1fb 100644 --- a/source/blender/gpu/vulkan/render_graph/vk_render_graph.hh +++ b/source/blender/gpu/vulkan/render_graph/vk_render_graph.hh @@ -128,6 +128,18 @@ class VKRenderGraph : public NonCopyable { } public: + void add_node(const VKBeginRenderingNode::CreateInfo &begin_rendering) + { + add_node(begin_rendering); + } + void add_node(const VKEndRenderingNode::CreateInfo &end_rendering) + { + add_node(end_rendering); + } + void add_node(const VKClearAttachmentsNode::CreateInfo &clear_attachments) + { + add_node(clear_attachments); + } void add_node(const VKClearColorImageNode::CreateInfo &clear_color_image) { add_node(clear_color_image); diff --git a/source/blender/gpu/vulkan/render_graph/vk_render_graph_node.hh b/source/blender/gpu/vulkan/render_graph/vk_render_graph_node.hh index b8ee0db97fc..4e3d3896c85 100644 --- a/source/blender/gpu/vulkan/render_graph/vk_render_graph_node.hh +++ b/source/blender/gpu/vulkan/render_graph/vk_render_graph_node.hh @@ -8,7 +8,9 @@ #pragma once +#include "nodes/vk_begin_rendering_node.hh" #include "nodes/vk_blit_image_node.hh" +#include "nodes/vk_clear_attachments_node.hh" #include "nodes/vk_clear_color_image_node.hh" #include "nodes/vk_clear_depth_stencil_image_node.hh" #include "nodes/vk_copy_buffer_node.hh" @@ -17,6 +19,7 @@ #include "nodes/vk_copy_image_to_buffer_node.hh" #include "nodes/vk_dispatch_indirect_node.hh" #include "nodes/vk_dispatch_node.hh" +#include "nodes/vk_end_rendering_node.hh" #include "nodes/vk_fill_buffer_node.hh" #include "nodes/vk_synchronization_node.hh" @@ -38,12 +41,15 @@ struct VKRenderGraphNode { VKNodeType type; union { VKBlitImageNode::Data blit_image; + VKBeginRenderingNode::Data begin_rendering; + VKClearAttachmentsNode::Data clear_attachments; VKClearColorImageNode::Data clear_color_image; VKClearDepthStencilImageNode::Data clear_depth_stencil_image; VKCopyBufferNode::Data copy_buffer; VKCopyBufferToImageNode::Data copy_buffer_to_image; VKCopyImageNode::Data copy_image; VKCopyImageToBufferNode::Data copy_image_to_buffer; + VKEndRenderingNode::Data end_rendering; VKDispatchNode::Data dispatch; VKDispatchIndirectNode::Data dispatch_indirect; VKFillBufferNode::Data fill_buffer; @@ -94,10 +100,16 @@ struct VKRenderGraphNode { switch (type) { case VKNodeType::UNUSED: return VK_PIPELINE_STAGE_NONE; + case VKNodeType::BEGIN_RENDERING: + return VKBeginRenderingNode::pipeline_stage; + case VKNodeType::CLEAR_ATTACHMENTS: + return VKClearAttachmentsNode::pipeline_stage; case VKNodeType::CLEAR_COLOR_IMAGE: return VKClearColorImageNode::pipeline_stage; case VKNodeType::CLEAR_DEPTH_STENCIL_IMAGE: return VKClearDepthStencilImageNode::pipeline_stage; + case VKNodeType::END_RENDERING: + return VKEndRenderingNode::pipeline_stage; case VKNodeType::FILL_BUFFER: return VKFillBufferNode::pipeline_stage; case VKNodeType::COPY_BUFFER: @@ -135,6 +147,18 @@ struct VKRenderGraphNode { break; } + case VKNodeType::BEGIN_RENDERING: { + VKBeginRenderingNode node_info; + node_info.build_commands(command_buffer, begin_rendering, r_bound_pipelines); + break; + } + + case VKNodeType::CLEAR_ATTACHMENTS: { + VKClearAttachmentsNode node_info; + node_info.build_commands(command_buffer, clear_attachments, r_bound_pipelines); + break; + } + case VKNodeType::CLEAR_COLOR_IMAGE: { VKClearColorImageNode node_info; node_info.build_commands(command_buffer, clear_color_image, r_bound_pipelines); @@ -147,6 +171,12 @@ struct VKRenderGraphNode { break; } + case VKNodeType::END_RENDERING: { + VKEndRenderingNode node_info; + node_info.build_commands(command_buffer, end_rendering, r_bound_pipelines); + break; + } + case VKNodeType::FILL_BUFFER: { VKFillBufferNode node_info; node_info.build_commands(command_buffer, fill_buffer, r_bound_pipelines); @@ -222,8 +252,11 @@ struct VKRenderGraphNode { } case VKNodeType::UNUSED: + case VKNodeType::BEGIN_RENDERING: + case VKNodeType::CLEAR_ATTACHMENTS: case VKNodeType::CLEAR_COLOR_IMAGE: case VKNodeType::CLEAR_DEPTH_STENCIL_IMAGE: + case VKNodeType::END_RENDERING: case VKNodeType::FILL_BUFFER: case VKNodeType::COPY_BUFFER: case VKNodeType::COPY_IMAGE: diff --git a/source/blender/gpu/vulkan/vk_context.cc b/source/blender/gpu/vulkan/vk_context.cc index afd058b6752..85da0af7a23 100644 --- a/source/blender/gpu/vulkan/vk_context.cc +++ b/source/blender/gpu/vulkan/vk_context.cc @@ -174,7 +174,12 @@ void VKContext::activate_framebuffer(VKFrameBuffer &framebuffer) active_fb = &framebuffer; framebuffer.update_size(); framebuffer.update_srgb(); - command_buffers_get().begin_render_pass(framebuffer); + if (use_render_graph) { + framebuffer.rendering_reset(); + } + else { + command_buffers_get().begin_render_pass(framebuffer); + } } VKFrameBuffer *VKContext::active_framebuffer_get() const @@ -191,10 +196,23 @@ void VKContext::deactivate_framebuffer() { VKFrameBuffer *framebuffer = active_framebuffer_get(); BLI_assert(framebuffer != nullptr); - command_buffers_get().end_render_pass(*framebuffer); + if (use_render_graph) { + framebuffer->rendering_end(*this); + } + else { + command_buffers_get().end_render_pass(*framebuffer); + } active_fb = nullptr; } +void VKContext::rendering_end() +{ + VKFrameBuffer *framebuffer = active_framebuffer_get(); + if (framebuffer) { + framebuffer->rendering_end(*this); + } +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/gpu/vulkan/vk_context.hh b/source/blender/gpu/vulkan/vk_context.hh index 13a1f081f24..b58a9156518 100644 --- a/source/blender/gpu/vulkan/vk_context.hh +++ b/source/blender/gpu/vulkan/vk_context.hh @@ -72,6 +72,15 @@ class VKContext : public Context, NonCopyable { void deactivate_framebuffer(); VKFrameBuffer *active_framebuffer_get() const; + /** + * Ensure that the active framebuffer isn't rendering. + * + * Between `vkCmdBeginRendering` and `vkCmdEndRendering` the framebuffer is rendering. Dispatch + * and transfer commands cannot be called between these commands. They can call this method to + * ensure that the framebuffer is outside these calls. + */ + void rendering_end(); + void bind_compute_pipeline(); render_graph::VKResourceAccessInfo &update_and_get_access_info(); diff --git a/source/blender/gpu/vulkan/vk_framebuffer.cc b/source/blender/gpu/vulkan/vk_framebuffer.cc index de564227b9d..34f51754a35 100644 --- a/source/blender/gpu/vulkan/vk_framebuffer.cc +++ b/source/blender/gpu/vulkan/vk_framebuffer.cc @@ -99,19 +99,23 @@ void VKFrameBuffer::build_clear_attachments_depth_stencil( const eGPUFrameBufferBits buffers, float clear_depth, uint32_t clear_stencil, - Vector &r_attachments) const + render_graph::VKClearAttachmentsNode::CreateInfo &clear_attachments) const { - VkClearAttachment clear_attachment = {}; - clear_attachment.aspectMask = (buffers & GPU_DEPTH_BIT ? VK_IMAGE_ASPECT_DEPTH_BIT : 0) | - (buffers & GPU_STENCIL_BIT ? VK_IMAGE_ASPECT_STENCIL_BIT : 0); + VkImageAspectFlags aspect_mask = (buffers & GPU_DEPTH_BIT ? VK_IMAGE_ASPECT_DEPTH_BIT : 0) | + (buffers & GPU_STENCIL_BIT ? VK_IMAGE_ASPECT_STENCIL_BIT : 0); + + VkClearAttachment &clear_attachment = + clear_attachments.attachments[clear_attachments.attachment_count++]; + clear_attachment.aspectMask = aspect_mask; clear_attachment.clearValue.depthStencil.depth = clear_depth; clear_attachment.clearValue.depthStencil.stencil = clear_stencil; - r_attachments.append(clear_attachment); + clear_attachment.colorAttachment = 0; } -void VKFrameBuffer::build_clear_attachments_color(const float (*clear_colors)[4], - const bool multi_clear_colors, - Vector &r_attachments) const +void VKFrameBuffer::build_clear_attachments_color( + const float (*clear_colors)[4], + const bool multi_clear_colors, + render_graph::VKClearAttachmentsNode::CreateInfo &clear_attachments) const { int color_index = 0; for (int color_slot = 0; color_slot < GPU_FB_MAX_COLOR_ATTACHMENT; color_slot++) { @@ -119,13 +123,13 @@ void VKFrameBuffer::build_clear_attachments_color(const float (*clear_colors)[4] if (attachment.tex == nullptr) { continue; } - VkClearAttachment clear_attachment = {}; + VkClearAttachment &clear_attachment = + clear_attachments.attachments[clear_attachments.attachment_count++]; clear_attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; clear_attachment.colorAttachment = color_slot; eGPUDataFormat data_format = to_data_format(GPU_texture_format(attachment.tex)); clear_attachment.clearValue.color = to_vk_clear_color_value(data_format, &clear_colors[color_index]); - r_attachments.append(clear_attachment); color_index += multi_clear_colors ? 1 : 0; } @@ -135,19 +139,19 @@ void VKFrameBuffer::build_clear_attachments_color(const float (*clear_colors)[4] /** \name Clear * \{ */ -void VKFrameBuffer::clear(const Span attachments) const +void VKFrameBuffer::clear(render_graph::VKClearAttachmentsNode::CreateInfo &clear_attachments) { - if (attachments.is_empty()) { - return; - } - VkClearRect clear_rect = {}; - clear_rect.rect = vk_render_areas_get()[0]; - clear_rect.baseArrayLayer = 0; - clear_rect.layerCount = 1; - VKContext &context = *VKContext::get(); - VKCommandBuffers &command_buffers = context.command_buffers_get(); - command_buffers.clear(attachments, Span(&clear_rect, 1)); + if (use_render_graph) { + rendering_ensure(context); + context.render_graph.add_node(clear_attachments); + } + else { + VKCommandBuffers &command_buffers = context.command_buffers_get(); + command_buffers.clear( + Span(clear_attachments.attachments, clear_attachments.attachment_count), + Span(&clear_attachments.vk_clear_rect, 1)); + } } void VKFrameBuffer::clear(const eGPUFrameBufferBits buffers, @@ -155,7 +159,11 @@ void VKFrameBuffer::clear(const eGPUFrameBufferBits buffers, float clear_depth, uint clear_stencil) { - Vector attachments; + render_graph::VKClearAttachmentsNode::CreateInfo clear_attachments = {}; + clear_attachments.vk_clear_rect.rect = vk_render_areas_get()[0]; + clear_attachments.vk_clear_rect.baseArrayLayer = 0; + clear_attachments.vk_clear_rect.layerCount = 1; + if (buffers & (GPU_DEPTH_BIT | GPU_STENCIL_BIT)) { VKContext &context = *VKContext::get(); eGPUWriteMask needed_mask = GPU_WRITE_NONE; @@ -169,7 +177,8 @@ void VKFrameBuffer::clear(const eGPUFrameBufferBits buffers, /* Clearing depth via vkCmdClearAttachments requires a render pass with write depth or stencil * enabled. When not enabled, clearing should be done via texture directly. */ if ((context.state_manager_get().state.write_mask & needed_mask) == needed_mask) { - build_clear_attachments_depth_stencil(buffers, clear_depth, clear_stencil, attachments); + build_clear_attachments_depth_stencil( + buffers, clear_depth, clear_stencil, clear_attachments); } else { VKTexture *depth_texture = unwrap(unwrap(depth_tex())); @@ -187,19 +196,25 @@ void VKFrameBuffer::clear(const eGPUFrameBufferBits buffers, if (buffers & GPU_COLOR_BIT) { float clear_color_single[4]; copy_v4_v4(clear_color_single, clear_color); - build_clear_attachments_color(&clear_color_single, false, attachments); + build_clear_attachments_color(&clear_color_single, false, clear_attachments); } - if (!attachments.is_empty()) { - clear(attachments); + if (clear_attachments.attachment_count) { + clear(clear_attachments); } } void VKFrameBuffer::clear_multi(const float (*clear_color)[4]) { - Vector attachments; - build_clear_attachments_color(clear_color, true, attachments); - clear(attachments); + render_graph::VKClearAttachmentsNode::CreateInfo clear_attachments = {}; + clear_attachments.vk_clear_rect.rect = vk_render_areas_get()[0]; + clear_attachments.vk_clear_rect.baseArrayLayer = 0; + clear_attachments.vk_clear_rect.layerCount = 1; + + build_clear_attachments_color(clear_color, true, clear_attachments); + if (clear_attachments.attachment_count) { + clear(clear_attachments); + } } void VKFrameBuffer::clear_attachment(GPUAttachmentType /*type*/, @@ -661,4 +676,62 @@ int VKFrameBuffer::color_attachments_resource_size() const /** \} */ +void VKFrameBuffer::rendering_reset() +{ + is_rendering_ = false; +} + +void VKFrameBuffer::rendering_ensure(VKContext &context) +{ + if (is_rendering_) { + return; + } + is_rendering_ = true; + + render_graph::VKResourceAccessInfo access_info; + render_graph::VKBeginRenderingNode::CreateInfo begin_rendering(access_info); + begin_rendering.node_data.vk_rendering_info.sType = VK_STRUCTURE_TYPE_RENDERING_INFO; + begin_rendering.node_data.vk_rendering_info.layerCount = 1; + begin_rendering.node_data.vk_rendering_info.renderArea = vk_render_areas_get()[0]; + + for (int color_slot : IndexRange(GPU_FB_MAX_COLOR_ATTACHMENT)) { + VKTexture *color_texture = unwrap(unwrap(color_tex(color_slot))); + if (color_texture != nullptr) { + VkRenderingAttachmentInfo &attachment_info = + begin_rendering.node_data.color_attachments[begin_rendering.node_data.vk_rendering_info + .colorAttachmentCount++]; + attachment_info.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; + /* TODO attachment mip/layer */ + attachment_info.imageView = color_texture->image_view_get().vk_handle(); + attachment_info.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + /* TODO add load store ops. */ + attachment_info.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + attachment_info.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + access_info.images.append( + {color_texture->vk_image_handle(), + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + VK_IMAGE_ASPECT_COLOR_BIT}); + + begin_rendering.node_data.vk_rendering_info.pColorAttachments = + begin_rendering.node_data.color_attachments; + } + } + + context.render_graph.add_node(begin_rendering); +} + +void VKFrameBuffer::rendering_end(VKContext &context) +{ + if (!is_rendering_ && use_explicit_load_store_) { + rendering_ensure(context); + } + + if (is_rendering_) { + render_graph::VKEndRenderingNode::CreateInfo end_rendering = {}; + context.render_graph.add_node(end_rendering); + is_rendering_ = false; + } +} + } // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_framebuffer.hh b/source/blender/gpu/vulkan/vk_framebuffer.hh index 401ff1e2a6e..35d8df2aab2 100644 --- a/source/blender/gpu/vulkan/vk_framebuffer.hh +++ b/source/blender/gpu/vulkan/vk_framebuffer.hh @@ -15,6 +15,7 @@ #include "gpu_framebuffer_private.hh" +#include "render_graph/vk_render_graph.hh" #include "vk_common.hh" #include "vk_image_view.hh" @@ -37,6 +38,7 @@ class VKFrameBuffer : public FrameBuffer { /** Is the first attachment an SRGB texture. */ bool srgb_; bool enabled_srgb_; + bool is_rendering_ = false; public: /** @@ -59,6 +61,9 @@ class VKFrameBuffer : public FrameBuffer { void attachment_set_loadstore_op(GPUAttachmentType type, GPULoadStore /*ls*/) override; + void begin_rendering(VKContext &context); + void end_rendering(VKContext &context); + protected: void subpass_transition_impl(const GPUAttachmentState depth_attachment_state, Span color_attachment_states) override; @@ -117,6 +122,27 @@ class VKFrameBuffer : public FrameBuffer { void update_srgb(); + /** + * Mark this framebuffer to be not being rendered on. + * + * Between binding a framebuffer and actually using it the state and clear operations can change. + * The rendering state is used to find out if the framebuffer begin rendering command should be + * recorded + */ + void rendering_reset(); + + /** + * Ensure that the framebuffer is ready to be rendered on and that its state is up to date with + * the latest changes that can happen between drawing commands inside `VKStateManager`. + */ + void rendering_ensure(VKContext &context); + + /** + * End the rendering on this framebuffer. + * Is being triggered when framebuffer is deactivated or when + */ + void rendering_end(VKContext &context); + /** * Return the number of color attachments of this frame buffer, including unused color * attachments. @@ -132,14 +158,16 @@ class VKFrameBuffer : public FrameBuffer { void render_pass_create(); /* Clearing attachments */ - void build_clear_attachments_depth_stencil(eGPUFrameBufferBits buffers, - float clear_depth, - uint32_t clear_stencil, - Vector &r_attachments) const; - void build_clear_attachments_color(const float (*clear_colors)[4], - const bool multi_clear_colors, - Vector &r_attachments) const; - void clear(Span attachments) const; + void build_clear_attachments_depth_stencil( + eGPUFrameBufferBits buffers, + float clear_depth, + uint32_t clear_stencil, + render_graph::VKClearAttachmentsNode::CreateInfo &clear_attachments) const; + void build_clear_attachments_color( + const float (*clear_colors)[4], + const bool multi_clear_colors, + render_graph::VKClearAttachmentsNode::CreateInfo &clear_attachments) const; + void clear(render_graph::VKClearAttachmentsNode::CreateInfo &clear_attachments); }; static inline VKFrameBuffer *unwrap(FrameBuffer *framebuffer) diff --git a/source/blender/gpu/vulkan/vk_texture.cc b/source/blender/gpu/vulkan/vk_texture.cc index e60be318d5b..b81e3f2562c 100644 --- a/source/blender/gpu/vulkan/vk_texture.cc +++ b/source/blender/gpu/vulkan/vk_texture.cc @@ -11,6 +11,7 @@ #include "vk_buffer.hh" #include "vk_context.hh" #include "vk_data_conversion.hh" +#include "vk_framebuffer.hh" #include "vk_memory.hh" #include "vk_shader.hh" #include "vk_shader_interface.hh" @@ -285,6 +286,7 @@ void VKTexture::read_sub( VKContext &context = *VKContext::get(); if (use_render_graph) { + context.rendering_end(); context.render_graph.add_node(copy_image_to_buffer); context.render_graph.submit_buffer_for_read(staging_buffer.vk_handle()); } diff --git a/source/blender/gpu/vulkan/vk_to_string.cc b/source/blender/gpu/vulkan/vk_to_string.cc index 6dc4fe0bde6..0fdf8b9d631 100644 --- a/source/blender/gpu/vulkan/vk_to_string.cc +++ b/source/blender/gpu/vulkan/vk_to_string.cc @@ -30,6 +30,11 @@ std::string to_string(VkImage vk_handle) return to_string_handle(uint64_t(vk_handle)); } +std::string to_string(VkImageView vk_handle) +{ + return to_string_handle(uint64_t(vk_handle)); +} + std::string to_string(VkRenderPass vk_handle) { return to_string_handle(uint64_t(vk_handle)); @@ -835,10 +840,10 @@ std::string to_string(const VkRenderingAttachmentInfo &vk_rendering_attachment_i { UNUSED_VARS(indentation_level); std::stringstream ss; - ss << "image_view=" << vk_rendering_attachment_info.imageView; + ss << "image_view=" << to_string(vk_rendering_attachment_info.imageView); ss << ", image_layout=" << to_string(vk_rendering_attachment_info.imageLayout); ss << ", resolve_mode=" << to_string(vk_rendering_attachment_info.resolveMode); - ss << ", resolve_image_view=" << vk_rendering_attachment_info.resolveImageView; + ss << ", resolve_image_view=" << to_string(vk_rendering_attachment_info.resolveImageView); ss << ", resolve_image_layout=" << to_string(vk_rendering_attachment_info.resolveImageLayout); ss << ", load_op=" << to_string(vk_rendering_attachment_info.loadOp); ss << ", store_op=" << to_string(vk_rendering_attachment_info.storeOp); diff --git a/source/blender/gpu/vulkan/vk_to_string.hh b/source/blender/gpu/vulkan/vk_to_string.hh index 8c7c1ea62fb..44e7212d123 100644 --- a/source/blender/gpu/vulkan/vk_to_string.hh +++ b/source/blender/gpu/vulkan/vk_to_string.hh @@ -12,6 +12,7 @@ namespace blender::gpu { std::string to_string(VkImage vk_handle); +std::string to_string(VkImageView vk_handle); std::string to_string(VkBuffer vk_handle); std::string to_string(VkDescriptorSet vk_handle); std::string to_string(VkPipeline vk_handle);