Vulkan: Render graph clear attachments #121073
|
@ -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()
|
||||
|
|
|
@ -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<VKNodeType::BEGIN_RENDERING,
|
||||
VKBeginRenderingCreateInfo,
|
||||
VKBeginRenderingData,
|
||||
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
|
||||
VKResourceType::IMAGE> {
|
||||
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<typename Node> 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
|
|
@ -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<VKNodeType::CLEAR_ATTACHMENTS,
|
||||
VKClearAttachmentsData,
|
||||
VKClearAttachmentsData,
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
|
||||
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
|
||||
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
|
||||
VKResourceType::IMAGE> {
|
||||
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<typename Node> 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
|
|
@ -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<VKNodeType::END_RENDERING,
|
||||
VKEndRenderingData,
|
||||
VKEndRenderingData,
|
||||
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
|
||||
VKResourceType::NONE> {
|
||||
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<typename Node> 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
|
|
@ -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<VKNodeType NodeType,
|
||||
typename NodeCreateInfo,
|
||||
typename NodeData,
|
||||
VkPipelineStageFlagBits PipelineStage,
|
||||
VkPipelineStageFlags PipelineStage,
|
||||
VKResourceType ResourceUsages>
|
||||
class VKNodeInfo : public NonCopyable {
|
||||
|
||||
|
|
|
@ -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<VkImage> image(1u);
|
||||
VkHandle<VkImageView> image_view(2u);
|
||||
VkHandle<VkBuffer> buffer(3u);
|
||||
|
||||
Vector<std::string> log;
|
||||
VKCommandBufferWrapper wrapper;
|
||||
VKResourceStateTracker resources;
|
||||
VKRenderGraph render_graph(std::make_unique<CommandBufferLog>(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
|
|
@ -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<VkClearAttachment>(p_attachments, attachment_count))
|
||||
{
|
||||
ss << " - attachment(" << to_string(attachment, 1) << ")" << std::endl;
|
||||
}
|
||||
for (const VkClearRect &rect : Span<VkClearRect>(p_rects, rect_count)) {
|
||||
ss << " - rect(" << to_string(rect, 1) << ")" << std::endl;
|
||||
}
|
||||
ss << ")";
|
||||
|
||||
log_.append(ss.str());
|
||||
}
|
||||
|
||||
void pipeline_barrier(VkPipelineStageFlags src_stage_mask,
|
||||
|
|
|
@ -128,6 +128,18 @@ class VKRenderGraph : public NonCopyable {
|
|||
}
|
||||
|
||||
public:
|
||||
void add_node(const VKBeginRenderingNode::CreateInfo &begin_rendering)
|
||||
{
|
||||
add_node<VKBeginRenderingNode>(begin_rendering);
|
||||
}
|
||||
void add_node(const VKEndRenderingNode::CreateInfo &end_rendering)
|
||||
{
|
||||
add_node<VKEndRenderingNode>(end_rendering);
|
||||
}
|
||||
void add_node(const VKClearAttachmentsNode::CreateInfo &clear_attachments)
|
||||
{
|
||||
add_node<VKClearAttachmentsNode>(clear_attachments);
|
||||
}
|
||||
void add_node(const VKClearColorImageNode::CreateInfo &clear_color_image)
|
||||
{
|
||||
add_node<VKClearColorImageNode>(clear_color_image);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -99,19 +99,23 @@ void VKFrameBuffer::build_clear_attachments_depth_stencil(
|
|||
const eGPUFrameBufferBits buffers,
|
||||
float clear_depth,
|
||||
uint32_t clear_stencil,
|
||||
Vector<VkClearAttachment> &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<VkClearAttachment> &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<VkClearAttachment> 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<VkClearRect>(&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<VkClearAttachment>(clear_attachments.attachments, clear_attachments.attachment_count),
|
||||
Span<VkClearRect>(&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<VkClearAttachment> 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<VkClearAttachment> 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
|
||||
|
|
|
@ -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<GPUAttachmentState> 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<VkClearAttachment> &r_attachments) const;
|
||||
void build_clear_attachments_color(const float (*clear_colors)[4],
|
||||
const bool multi_clear_colors,
|
||||
Vector<VkClearAttachment> &r_attachments) const;
|
||||
void clear(Span<VkClearAttachment> 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)
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue