Refactor: Vulkan: Store large data in separate vectors

VKRenderGraphNode is 892 bytes and most of the bytes are used for
specific nodes. By storing large structs in separate vectors we can
reduce the needed memory and improve cache pre-fetching.

With this change the VKRenderGraphNode is reduced to 64 bytes.

On a (50 frames shader_balls.blend) the end user performance is improved by
2%.

| **Platform**     | **Before** | **After** |
| ---------------- | ---------- | --------- |
| AMD W7700        | 1409 ms    | 1383 ms   |
| NVIDIA RTX 6000  | 1443 ms    | 1428 ms   |

Pull Request: https://projects.blender.org/blender/blender/pulls/133317
This commit is contained in:
Jeroen Bakker 2025-01-21 14:49:23 +01:00
parent 2114620c28
commit ff804882bd
28 changed files with 160 additions and 75 deletions

View file

@ -41,7 +41,8 @@ class VKBeginQueryNode : public VKNodeInfo<VKNodeType::BEGIN_QUERY,
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The * (`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.) * actual node data (`VKRenderGraphNode` includes all header files.)
*/ */
template<typename Node> void set_node_data(Node &node, const CreateInfo &create_info) template<typename Node, typename Storage>
void set_node_data(Node &node, Storage & /* storage */, const CreateInfo &create_info)
{ {
node.begin_query = create_info; node.begin_query = create_info;
} }

View file

@ -50,7 +50,8 @@ class VKBeginRenderingNode : public VKNodeInfo<VKNodeType::BEGIN_RENDERING,
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The * (`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.) * actual node data (`VKRenderGraphNode` includes all header files.)
*/ */
template<typename Node> void set_node_data(Node &node, const CreateInfo &create_info) template<typename Node, typename Storage>
void set_node_data(Node &node, Storage &storage, const CreateInfo &create_info)
{ {
const bool use_render_pass = create_info.node_data.vk_render_pass_begin_info.renderPass != const bool use_render_pass = create_info.node_data.vk_render_pass_begin_info.renderPass !=
VK_NULL_HANDLE; VK_NULL_HANDLE;
@ -73,7 +74,7 @@ class VKBeginRenderingNode : public VKNodeInfo<VKNodeType::BEGIN_RENDERING,
&create_info.node_data.stencil_attachment), &create_info.node_data.stencil_attachment),
"When create_info.node_data.vk_rendering_info.pStencilAttachment points to " "When create_info.node_data.vk_rendering_info.pStencilAttachment points to "
"something, it should point to create_info.node_data.stencil_attachment."); "something, it should point to create_info.node_data.stencil_attachment.");
node.begin_rendering = create_info.node_data; node.storage_index = storage.begin_rendering.append_and_get_index(create_info.node_data);
/* NOTE: pointers in vk_rendering_info will be set to the correct location just before sending /* NOTE: pointers in vk_rendering_info will be set to the correct location just before sending
* to the command buffer. In the meantime these pointers are invalid. * to the command buffer. In the meantime these pointers are invalid.
* VKRenderingAttachmentInfo's should be used instead. */ * VKRenderingAttachmentInfo's should be used instead. */

View file

@ -42,9 +42,10 @@ class VKBlitImageNode : public VKNodeInfo<VKNodeType::BLIT_IMAGE,
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The * (`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.) * actual node data (`VKRenderGraphNode` includes all header files.)
*/ */
template<typename Node> void set_node_data(Node &node, const CreateInfo &create_info) template<typename Node, typename Storage>
void set_node_data(Node &node, Storage &storage, const CreateInfo &create_info)
{ {
node.blit_image = create_info; node.storage_index = storage.blit_image.append_and_get_index(create_info);
} }
/** /**

View file

@ -37,9 +37,10 @@ class VKClearAttachmentsNode : public VKNodeInfo<VKNodeType::CLEAR_ATTACHMENTS,
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The * (`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.) * actual node data (`VKRenderGraphNode` includes all header files.)
*/ */
template<typename Node> static void set_node_data(Node &node, const CreateInfo &create_info) template<typename Node, typename Storage>
void set_node_data(Node &node, Storage &storage, const CreateInfo &create_info)
{ {
node.clear_attachments = create_info; node.storage_index = storage.clear_attachments.append_and_get_index(create_info);
} }
/** /**

View file

@ -34,7 +34,8 @@ class VKClearColorImageNode : public VKNodeInfo<VKNodeType::CLEAR_COLOR_IMAGE,
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The * (`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.) * actual node data (`VKRenderGraphNode` includes all header files.)
*/ */
template<typename Node> static void set_node_data(Node &node, const CreateInfo &create_info) template<typename Node, typename Storage>
void set_node_data(Node &node, Storage & /* storage */, const CreateInfo &create_info)
{ {
node.clear_color_image = create_info; node.clear_color_image = create_info;
} }

View file

@ -45,7 +45,8 @@ class VKClearDepthStencilImageNode : public VKNodeInfo<VKNodeType::CLEAR_DEPTH_S
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The * (`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.) * actual node data (`VKRenderGraphNode` includes all header files.)
*/ */
template<typename Node> static void set_node_data(Node &node, const CreateInfo &create_info) template<typename Node, typename Storage>
static void set_node_data(Node &node, Storage & /* storage */, const CreateInfo &create_info)
{ {
node.clear_depth_stencil_image = create_info.node_data; node.clear_depth_stencil_image = create_info.node_data;
} }

View file

@ -33,7 +33,8 @@ class VKCopyBufferNode : public VKNodeInfo<VKNodeType::COPY_BUFFER,
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The * (`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.) * actual node data (`VKRenderGraphNode` includes all header files.)
*/ */
template<typename Node> static void set_node_data(Node &node, const CreateInfo &create_info) template<typename Node, typename Storage>
static void set_node_data(Node &node, Storage & /* storage */, const CreateInfo &create_info)
{ {
node.copy_buffer = create_info; node.copy_buffer = create_info;
} }

View file

@ -38,9 +38,10 @@ class VKCopyBufferToImageNode : public VKNodeInfo<VKNodeType::COPY_BUFFER_TO_IMA
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The * (`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.) * actual node data (`VKRenderGraphNode` includes all header files.)
*/ */
template<typename Node> static void set_node_data(Node &node, const CreateInfo &create_info) template<typename Node, typename Storage>
static void set_node_data(Node &node, Storage &storage, const CreateInfo &create_info)
{ {
node.copy_buffer_to_image = create_info.node_data; node.storage_index = storage.copy_buffer_to_image.append_and_get_index(create_info.node_data);
} }
/** /**

View file

@ -42,9 +42,10 @@ class VKCopyImageNode : public VKNodeInfo<VKNodeType::COPY_IMAGE,
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The * (`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.) * actual node data (`VKRenderGraphNode` includes all header files.)
*/ */
template<typename Node> static void set_node_data(Node &node, const CreateInfo &create_info) template<typename Node, typename Storage>
static void set_node_data(Node &node, Storage &storage, const CreateInfo &create_info)
{ {
node.copy_image = create_info.node_data; node.storage_index = storage.copy_image.append_and_get_index(create_info.node_data);
} }
/** /**

View file

@ -42,9 +42,10 @@ class VKCopyImageToBufferNode : public VKNodeInfo<VKNodeType::COPY_IMAGE_TO_BUFF
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The * (`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.) * actual node data (`VKRenderGraphNode` includes all header files.)
*/ */
template<typename Node> static void set_node_data(Node &node, const CreateInfo &create_info) template<typename Node, typename Storage>
static void set_node_data(Node &node, Storage &storage, const CreateInfo &create_info)
{ {
node.copy_image_to_buffer = create_info.node_data; node.storage_index = storage.copy_image_to_buffer.append_and_get_index(create_info.node_data);
} }
/** /**

View file

@ -49,7 +49,8 @@ class VKDispatchIndirectNode
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The * (`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.) * actual node data (`VKRenderGraphNode` includes all header files.)
*/ */
template<typename Node> static void set_node_data(Node &node, const CreateInfo &create_info) template<typename Node, typename Storage>
static void set_node_data(Node &node, Storage & /* storage */, const CreateInfo &create_info)
{ {
node.dispatch_indirect = create_info.dispatch_indirect_node; node.dispatch_indirect = create_info.dispatch_indirect_node;
vk_pipeline_data_copy(node.dispatch_indirect.pipeline_data, vk_pipeline_data_copy(node.dispatch_indirect.pipeline_data,

View file

@ -45,7 +45,8 @@ class VKDispatchNode : public VKNodeInfo<VKNodeType::DISPATCH,
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The * (`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.) * actual node data (`VKRenderGraphNode` includes all header files.)
*/ */
template<typename Node> static void set_node_data(Node &node, const CreateInfo &create_info) template<typename Node, typename Storage>
static void set_node_data(Node &node, Storage & /* storage */, const CreateInfo &create_info)
{ {
node.dispatch = create_info.dispatch_node; node.dispatch = create_info.dispatch_node;
vk_pipeline_data_copy(node.dispatch.pipeline_data, create_info.dispatch_node.pipeline_data); vk_pipeline_data_copy(node.dispatch.pipeline_data, create_info.dispatch_node.pipeline_data);

View file

@ -46,10 +46,11 @@ class VKDrawIndexedIndirectNode
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The * (`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.) * actual node data (`VKRenderGraphNode` includes all header files.)
*/ */
template<typename Node> static void set_node_data(Node &node, const CreateInfo &create_info) template<typename Node, typename Storage>
static void set_node_data(Node &node, Storage &storage, const CreateInfo &create_info)
{ {
node.draw_indexed_indirect = create_info.node_data; node.storage_index = storage.draw_indexed_indirect.append_and_get_index(create_info.node_data);
vk_pipeline_data_copy(node.draw_indexed_indirect.pipeline_data, vk_pipeline_data_copy(storage.draw_indexed_indirect[node.storage_index].pipeline_data,
create_info.node_data.pipeline_data); create_info.node_data.pipeline_data);
} }

View file

@ -46,10 +46,12 @@ class VKDrawIndexedNode : public VKNodeInfo<VKNodeType::DRAW_INDEXED,
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The * (`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.) * actual node data (`VKRenderGraphNode` includes all header files.)
*/ */
template<typename Node> static void set_node_data(Node &node, const CreateInfo &create_info) template<typename Node, typename Storage>
static void set_node_data(Node &node, Storage &storage, const CreateInfo &create_info)
{ {
node.draw_indexed = create_info.node_data; node.storage_index = storage.draw_indexed.append_and_get_index(create_info.node_data);
vk_pipeline_data_copy(node.draw_indexed.pipeline_data, create_info.node_data.pipeline_data); vk_pipeline_data_copy(storage.draw_indexed[node.storage_index].pipeline_data,
create_info.node_data.pipeline_data);
} }
/** /**

View file

@ -44,10 +44,12 @@ class VKDrawIndirectNode : public VKNodeInfo<VKNodeType::DRAW_INDIRECT,
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The * (`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.) * actual node data (`VKRenderGraphNode` includes all header files.)
*/ */
template<typename Node> static void set_node_data(Node &node, const CreateInfo &create_info) template<typename Node, typename Storage>
static void set_node_data(Node &node, Storage &storage, const CreateInfo &create_info)
{ {
node.draw_indirect = create_info.node_data; node.storage_index = storage.draw_indirect.append_and_get_index(create_info.node_data);
vk_pipeline_data_copy(node.draw_indirect.pipeline_data, create_info.node_data.pipeline_data); vk_pipeline_data_copy(storage.draw_indirect[node.storage_index].pipeline_data,
create_info.node_data.pipeline_data);
} }
/** /**

View file

@ -44,10 +44,12 @@ class VKDrawNode : public VKNodeInfo<VKNodeType::DRAW,
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The * (`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.) * actual node data (`VKRenderGraphNode` includes all header files.)
*/ */
template<typename Node> static void set_node_data(Node &node, const CreateInfo &create_info) template<typename Node, typename Storage>
static void set_node_data(Node &node, Storage &storage, const CreateInfo &create_info)
{ {
node.draw = create_info.node_data; node.storage_index = storage.draw.append_and_get_index(create_info.node_data);
vk_pipeline_data_copy(node.draw.pipeline_data, create_info.node_data.pipeline_data); vk_pipeline_data_copy(storage.draw[node.storage_index].pipeline_data,
create_info.node_data.pipeline_data);
} }
/** /**

View file

@ -40,7 +40,8 @@ class VKEndQueryNode : public VKNodeInfo<VKNodeType::END_QUERY,
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The * (`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.) * actual node data (`VKRenderGraphNode` includes all header files.)
*/ */
template<typename Node> void set_node_data(Node &node, const CreateInfo &create_info) template<typename Node, typename Storage>
void set_node_data(Node &node, Storage & /*storage*/, const CreateInfo &create_info)
{ {
node.end_query = create_info; node.end_query = create_info;
} }

View file

@ -40,7 +40,8 @@ class VKEndRenderingNode : public VKNodeInfo<VKNodeType::END_RENDERING,
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The * (`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.) * actual node data (`VKRenderGraphNode` includes all header files.)
*/ */
template<typename Node> void set_node_data(Node &node, const CreateInfo &create_info) template<typename Node, typename Storage>
void set_node_data(Node &node, Storage & /*storage*/, const CreateInfo &create_info)
{ {
node.end_rendering = create_info; node.end_rendering = create_info;
} }

View file

@ -33,7 +33,8 @@ class VKFillBufferNode : public VKNodeInfo<VKNodeType::FILL_BUFFER,
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The * (`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.) * actual node data (`VKRenderGraphNode` includes all header files.)
*/ */
template<typename Node> static void set_node_data(Node &node, const CreateInfo &create_info) template<typename Node, typename Storage>
static void set_node_data(Node &node, Storage & /* storage */, const CreateInfo &create_info)
{ {
node.fill_buffer = create_info; node.fill_buffer = create_info;
} }

View file

@ -191,7 +191,8 @@ class VKNodeInfo : public NonCopyable {
* This function must be implemented by all node classes. But due to cyclic inclusion of header * This function must be implemented by all node classes. But due to cyclic inclusion of header
* files it is implemented as a template function. * files it is implemented as a template function.
*/ */
template<typename Node> static void set_node_data(Node &node, const CreateInfo &create_info); template<typename Node, typename Storage>
static void set_node_data(Node &node, Storage & /* storage */, const CreateInfo &create_info);
/** /**
* Extract read/write resource dependencies from `create_info` and add them to `node_links`. * Extract read/write resource dependencies from `create_info` and add them to `node_links`.

View file

@ -41,7 +41,8 @@ class VKResetQueryPoolNode : public VKNodeInfo<VKNodeType::RESET_QUERY_POOL,
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The * (`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.) * actual node data (`VKRenderGraphNode` includes all header files.)
*/ */
template<typename Node> void set_node_data(Node &node, const CreateInfo &create_info) template<typename Node, typename Storage>
void set_node_data(Node &node, Storage & /*storage*/, const CreateInfo &create_info)
{ {
node.reset_query_pool = create_info; node.reset_query_pool = create_info;
} }

View file

@ -39,7 +39,8 @@ class VKSynchronizationNode : public VKNodeInfo<VKNodeType::SYNCHRONIZATION,
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The * (`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.) * actual node data (`VKRenderGraphNode` includes all header files.)
*/ */
template<typename Node> static void set_node_data(Node &node, const CreateInfo &create_info) template<typename Node, typename Storage>
static void set_node_data(Node &node, Storage & /* storage */, const CreateInfo &create_info)
{ {
UNUSED_VARS(create_info); UNUSED_VARS(create_info);
node.synchronization = {}; node.synchronization = {};

View file

@ -34,7 +34,8 @@ class VKUpdateBufferNode : public VKNodeInfo<VKNodeType::UPDATE_BUFFER,
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The * (`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.) * actual node data (`VKRenderGraphNode` includes all header files.)
*/ */
template<typename Node> static void set_node_data(Node &node, const CreateInfo &create_info) template<typename Node, typename Storage>
static void set_node_data(Node &node, Storage & /* storage */, const CreateInfo &create_info)
{ {
node.update_buffer = create_info; node.update_buffer = create_info;
} }

View file

@ -41,7 +41,8 @@ class VKUpdateMipmapsNode : public VKNodeInfo<VKNodeType::UPDATE_MIPMAPS,
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The * (`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.) * actual node data (`VKRenderGraphNode` includes all header files.)
*/ */
template<typename Node> void set_node_data(Node &node, const CreateInfo &create_info) template<typename Node, typename Storage>
void set_node_data(Node &node, Storage & /*storage*/, const CreateInfo &create_info)
{ {
node.update_mipmaps = create_info; node.update_mipmaps = create_info;
} }

View file

@ -265,7 +265,8 @@ void VKCommandBuilder::groups_build_commands(VKRenderGraph &render_graph,
const VKRenderGraphNode &last_node = render_graph.nodes_[group_node_handles.last()]; const VKRenderGraphNode &last_node = render_graph.nodes_[group_node_handles.last()];
bool will_be_suspended = last_node.type != VKNodeType::END_RENDERING; bool will_be_suspended = last_node.type != VKNodeType::END_RENDERING;
if (will_be_suspended) { if (will_be_suspended) {
node.begin_rendering.vk_rendering_info.flags = VK_RENDERING_SUSPENDING_BIT; render_graph.storage_.begin_rendering[node.storage_index].vk_rendering_info.flags =
VK_RENDERING_SUSPENDING_BIT;
} }
} }
@ -276,8 +277,9 @@ void VKCommandBuilder::groups_build_commands(VKRenderGraph &render_graph,
if (!rendering_active) { if (!rendering_active) {
/* Resume rendering scope. */ /* Resume rendering scope. */
VKRenderGraphNode &rendering_node = render_graph.nodes_[rendering_scope]; VKRenderGraphNode &rendering_node = render_graph.nodes_[rendering_scope];
rendering_node.begin_rendering.vk_rendering_info.flags = VK_RENDERING_RESUMING_BIT; render_graph.storage_.begin_rendering[rendering_node.storage_index]
rendering_node.build_commands(command_buffer, active_pipelines); .vk_rendering_info.flags = VK_RENDERING_RESUMING_BIT;
rendering_node.build_commands(command_buffer, render_graph.storage_, active_pipelines);
rendering_active = true; rendering_active = true;
} }
} }
@ -302,7 +304,7 @@ void VKCommandBuilder::groups_build_commands(VKRenderGraph &render_graph,
<< ", node_type=" << node.type << ", node_type=" << node.type
<< ", debug group=" << render_graph.full_debug_group(node_handle) << "\n"; << ", debug group=" << render_graph.full_debug_group(node_handle) << "\n";
#endif #endif
node.build_commands(command_buffer, active_pipelines); node.build_commands(command_buffer, render_graph.storage_, active_pipelines);
} }
if (rendering_active) { if (rendering_active) {
@ -317,7 +319,8 @@ void VKCommandBuilder::groups_build_commands(VKRenderGraph &render_graph,
} }
VKRenderGraphNode &rendering_node = render_graph.nodes_[rendering_scope]; VKRenderGraphNode &rendering_node = render_graph.nodes_[rendering_scope];
rendering_node.begin_rendering.vk_rendering_info.flags = VK_RENDERING_RESUMING_BIT; render_graph.storage_.begin_rendering[rendering_node.storage_index].vk_rendering_info.flags =
VK_RENDERING_RESUMING_BIT;
} }
/* Record group post barriers. */ /* Record group post barriers. */

View file

@ -29,9 +29,10 @@ void VKRenderGraph::remove_nodes(Span<NodeHandle> node_handles)
"needs to be fixed when implementing a better scheduler."); "needs to be fixed when implementing a better scheduler.");
links_.clear(); links_.clear();
for (VKRenderGraphNode &node : nodes_) { for (VKRenderGraphNode &node : nodes_) {
node.free_data(); node.free_data(storage_);
} }
nodes_.clear(); nodes_.clear();
storage_.reset();
debug_.node_group_map.clear(); debug_.node_group_map.clear();
debug_.used_groups.clear(); debug_.used_groups.clear();

View file

@ -72,6 +72,9 @@ class VKRenderGraph : public NonCopyable {
Vector<VKRenderGraphNodeLinks> links_; Vector<VKRenderGraphNodeLinks> links_;
/** All nodes inside the graph indexable via NodeHandle. */ /** All nodes inside the graph indexable via NodeHandle. */
Vector<VKRenderGraphNode> nodes_; Vector<VKRenderGraphNode> nodes_;
/** Storage for large node datas to improve CPU cache pre-loading. */
VKRenderGraphStorage storage_;
/** Scheduler decides which nodes to select and in what order to execute them. */ /** Scheduler decides which nodes to select and in what order to execute them. */
VKScheduler scheduler_; VKScheduler scheduler_;
/** /**
@ -164,7 +167,7 @@ class VKRenderGraph : public NonCopyable {
links_.resize(nodes_.size()); links_.resize(nodes_.size());
} }
VKRenderGraphNode &node = nodes_[node_handle]; VKRenderGraphNode &node = nodes_[node_handle];
node.set_node_data<NodeInfo>(create_info); node.set_node_data<NodeInfo>(storage_, create_info);
VKRenderGraphNodeLinks &node_links = links_[node_handle]; VKRenderGraphNodeLinks &node_links = links_[node_handle];
BLI_assert(node_links.inputs.is_empty()); BLI_assert(node_links.inputs.is_empty());

View file

@ -39,6 +39,41 @@ namespace blender::gpu::render_graph {
*/ */
using NodeHandle = uint64_t; using NodeHandle = uint64_t;
/**
* Node storage for nodes that uses large data structs.
*
* Some node structs are to large to store them as part of the node. The data are stored as a
* vector of structs. Typically structs that occupy more than one cache line (64 bytes) should be
* considered to be moved here.
*
*/
struct VKRenderGraphStorage {
Vector<VKBeginRenderingNode::Data> begin_rendering;
Vector<VKClearAttachmentsNode::Data> clear_attachments;
Vector<VKBlitImageNode::Data> blit_image;
Vector<VKCopyBufferToImageNode::Data> copy_buffer_to_image;
Vector<VKCopyImageNode::Data> copy_image;
Vector<VKCopyImageToBufferNode::Data> copy_image_to_buffer;
Vector<VKDrawNode::Data> draw;
Vector<VKDrawIndexedNode::Data> draw_indexed;
Vector<VKDrawIndexedIndirectNode::Data> draw_indexed_indirect;
Vector<VKDrawIndirectNode::Data> draw_indirect;
void reset()
{
begin_rendering.clear();
clear_attachments.clear();
blit_image.clear();
copy_buffer_to_image.clear();
copy_image.clear();
copy_image_to_buffer.clear();
draw.clear();
draw_indexed.clear();
draw_indexed_indirect.clear();
draw_indirect.clear();
}
};
/** /**
* Node stored inside a render graph. * Node stored inside a render graph.
* *
@ -50,21 +85,11 @@ struct VKRenderGraphNode {
VKNodeType type; VKNodeType type;
union { union {
VKBeginQueryNode::Data begin_query; VKBeginQueryNode::Data begin_query;
VKBeginRenderingNode::Data begin_rendering;
VKBlitImageNode::Data blit_image;
VKClearAttachmentsNode::Data clear_attachments;
VKClearColorImageNode::Data clear_color_image; VKClearColorImageNode::Data clear_color_image;
VKClearDepthStencilImageNode::Data clear_depth_stencil_image; VKClearDepthStencilImageNode::Data clear_depth_stencil_image;
VKCopyBufferNode::Data copy_buffer; VKCopyBufferNode::Data copy_buffer;
VKCopyBufferToImageNode::Data copy_buffer_to_image;
VKCopyImageNode::Data copy_image;
VKCopyImageToBufferNode::Data copy_image_to_buffer;
VKDispatchNode::Data dispatch; VKDispatchNode::Data dispatch;
VKDispatchIndirectNode::Data dispatch_indirect; VKDispatchIndirectNode::Data dispatch_indirect;
VKDrawNode::Data draw;
VKDrawIndexedNode::Data draw_indexed;
VKDrawIndexedIndirectNode::Data draw_indexed_indirect;
VKDrawIndirectNode::Data draw_indirect;
VKEndQueryNode::Data end_query; VKEndQueryNode::Data end_query;
VKEndRenderingNode::Data end_rendering; VKEndRenderingNode::Data end_rendering;
VKFillBufferNode::Data fill_buffer; VKFillBufferNode::Data fill_buffer;
@ -72,6 +97,7 @@ struct VKRenderGraphNode {
VKSynchronizationNode::Data synchronization; VKSynchronizationNode::Data synchronization;
VKUpdateBufferNode::Data update_buffer; VKUpdateBufferNode::Data update_buffer;
VKUpdateMipmapsNode::Data update_mipmaps; VKUpdateMipmapsNode::Data update_mipmaps;
int64_t storage_index = -1;
}; };
/** /**
@ -82,14 +108,16 @@ struct VKRenderGraphNode {
* between consecutive use. Checking for unused node types will ensure that previous usage has * between consecutive use. Checking for unused node types will ensure that previous usage has
* been reset. Resetting is done as part of `free_data` * been reset. Resetting is done as part of `free_data`
*/ */
template<typename NodeInfo> void set_node_data(const typename NodeInfo::CreateInfo &create_info) template<typename NodeInfo>
void set_node_data(VKRenderGraphStorage &storage,
const typename NodeInfo::CreateInfo &create_info)
{ {
BLI_assert(type == VKNodeType::UNUSED); BLI_assert(type == VKNodeType::UNUSED);
/* Instance of NodeInfo is needed to call virtual methods. CPP doesn't support overloading of /* Instance of NodeInfo is needed to call virtual methods. CPP doesn't support overloading of
* static methods. */ * static methods. */
NodeInfo node_info; NodeInfo node_info;
type = NodeInfo::node_type; type = NodeInfo::node_type;
node_info.set_node_data(*this, create_info); node_info.set_node_data(*this, storage, create_info);
} }
/** /**
@ -176,12 +204,21 @@ struct VKRenderGraphNode {
* `VKCommandBuilder::build_node` and `VKCommandBuilder::build_pipeline_barriers. * `VKCommandBuilder::build_node` and `VKCommandBuilder::build_pipeline_barriers.
*/ */
void build_commands(VKCommandBufferInterface &command_buffer, void build_commands(VKCommandBufferInterface &command_buffer,
VKRenderGraphStorage &storage,
VKBoundPipelines &r_bound_pipelines) VKBoundPipelines &r_bound_pipelines)
{ {
switch (type) { switch (type) {
case VKNodeType::UNUSED: { case VKNodeType::UNUSED: {
break; break;
} }
#define BUILD_COMMANDS_STORAGE(NODE_TYPE, NODE_CLASS, ATTRIBUTE_NAME) \
case NODE_TYPE: { \
NODE_CLASS node_info; \
node_info.build_commands( \
command_buffer, storage.ATTRIBUTE_NAME[storage_index], r_bound_pipelines); \
break; \
}
#define BUILD_COMMANDS(NODE_TYPE, NODE_CLASS, ATTRIBUTE_NAME) \ #define BUILD_COMMANDS(NODE_TYPE, NODE_CLASS, ATTRIBUTE_NAME) \
case NODE_TYPE: { \ case NODE_TYPE: { \
NODE_CLASS node_info; \ NODE_CLASS node_info; \
@ -190,8 +227,9 @@ struct VKRenderGraphNode {
} }
BUILD_COMMANDS(VKNodeType::BEGIN_QUERY, VKBeginQueryNode, begin_query) BUILD_COMMANDS(VKNodeType::BEGIN_QUERY, VKBeginQueryNode, begin_query)
BUILD_COMMANDS(VKNodeType::BEGIN_RENDERING, VKBeginRenderingNode, begin_rendering) BUILD_COMMANDS_STORAGE(VKNodeType::BEGIN_RENDERING, VKBeginRenderingNode, begin_rendering)
BUILD_COMMANDS(VKNodeType::CLEAR_ATTACHMENTS, VKClearAttachmentsNode, clear_attachments) BUILD_COMMANDS_STORAGE(
VKNodeType::CLEAR_ATTACHMENTS, VKClearAttachmentsNode, clear_attachments)
BUILD_COMMANDS(VKNodeType::CLEAR_COLOR_IMAGE, VKClearColorImageNode, clear_color_image) BUILD_COMMANDS(VKNodeType::CLEAR_COLOR_IMAGE, VKClearColorImageNode, clear_color_image)
BUILD_COMMANDS(VKNodeType::CLEAR_DEPTH_STENCIL_IMAGE, BUILD_COMMANDS(VKNodeType::CLEAR_DEPTH_STENCIL_IMAGE,
VKClearDepthStencilImageNode, VKClearDepthStencilImageNode,
@ -201,33 +239,41 @@ struct VKRenderGraphNode {
BUILD_COMMANDS(VKNodeType::FILL_BUFFER, VKFillBufferNode, fill_buffer) BUILD_COMMANDS(VKNodeType::FILL_BUFFER, VKFillBufferNode, fill_buffer)
BUILD_COMMANDS(VKNodeType::UPDATE_BUFFER, VKUpdateBufferNode, update_buffer) BUILD_COMMANDS(VKNodeType::UPDATE_BUFFER, VKUpdateBufferNode, update_buffer)
BUILD_COMMANDS(VKNodeType::COPY_BUFFER, VKCopyBufferNode, copy_buffer) BUILD_COMMANDS(VKNodeType::COPY_BUFFER, VKCopyBufferNode, copy_buffer)
BUILD_COMMANDS( BUILD_COMMANDS_STORAGE(
VKNodeType::COPY_BUFFER_TO_IMAGE, VKCopyBufferToImageNode, copy_buffer_to_image) VKNodeType::COPY_BUFFER_TO_IMAGE, VKCopyBufferToImageNode, copy_buffer_to_image)
BUILD_COMMANDS(VKNodeType::COPY_IMAGE, VKCopyImageNode, copy_image) BUILD_COMMANDS_STORAGE(VKNodeType::COPY_IMAGE, VKCopyImageNode, copy_image)
BUILD_COMMANDS( BUILD_COMMANDS_STORAGE(
VKNodeType::COPY_IMAGE_TO_BUFFER, VKCopyImageToBufferNode, copy_image_to_buffer) VKNodeType::COPY_IMAGE_TO_BUFFER, VKCopyImageToBufferNode, copy_image_to_buffer)
BUILD_COMMANDS(VKNodeType::BLIT_IMAGE, VKBlitImageNode, blit_image) BUILD_COMMANDS_STORAGE(VKNodeType::BLIT_IMAGE, VKBlitImageNode, blit_image)
BUILD_COMMANDS(VKNodeType::RESET_QUERY_POOL, VKResetQueryPoolNode, reset_query_pool) BUILD_COMMANDS(VKNodeType::RESET_QUERY_POOL, VKResetQueryPoolNode, reset_query_pool)
BUILD_COMMANDS(VKNodeType::SYNCHRONIZATION, VKSynchronizationNode, synchronization) BUILD_COMMANDS(VKNodeType::SYNCHRONIZATION, VKSynchronizationNode, synchronization)
BUILD_COMMANDS(VKNodeType::UPDATE_MIPMAPS, VKUpdateMipmapsNode, update_mipmaps) BUILD_COMMANDS(VKNodeType::UPDATE_MIPMAPS, VKUpdateMipmapsNode, update_mipmaps)
BUILD_COMMANDS(VKNodeType::DISPATCH, VKDispatchNode, dispatch) BUILD_COMMANDS(VKNodeType::DISPATCH, VKDispatchNode, dispatch)
BUILD_COMMANDS(VKNodeType::DISPATCH_INDIRECT, VKDispatchIndirectNode, dispatch_indirect) BUILD_COMMANDS(VKNodeType::DISPATCH_INDIRECT, VKDispatchIndirectNode, dispatch_indirect)
BUILD_COMMANDS(VKNodeType::DRAW, VKDrawNode, draw) BUILD_COMMANDS_STORAGE(VKNodeType::DRAW, VKDrawNode, draw)
BUILD_COMMANDS(VKNodeType::DRAW_INDEXED, VKDrawIndexedNode, draw_indexed) BUILD_COMMANDS_STORAGE(VKNodeType::DRAW_INDEXED, VKDrawIndexedNode, draw_indexed)
BUILD_COMMANDS( BUILD_COMMANDS_STORAGE(
VKNodeType::DRAW_INDEXED_INDIRECT, VKDrawIndexedIndirectNode, draw_indexed_indirect) VKNodeType::DRAW_INDEXED_INDIRECT, VKDrawIndexedIndirectNode, draw_indexed_indirect)
BUILD_COMMANDS(VKNodeType::DRAW_INDIRECT, VKDrawIndirectNode, draw_indirect) BUILD_COMMANDS_STORAGE(VKNodeType::DRAW_INDIRECT, VKDrawIndirectNode, draw_indirect)
#undef BUILD_COMMANDS #undef BUILD_COMMANDS
#undef BUILD_COMMANDS_STORAGE
} }
} }
/** /**
* Free data kept by the node * Free data kept by the node
*/ */
void free_data() void free_data(VKRenderGraphStorage &storage)
{ {
switch (type) { switch (type) {
#define FREE_DATA_STORAGE(NODE_TYPE, NODE_CLASS, ATTRIBUTE_NAME) \
case NODE_TYPE: { \
NODE_CLASS node_info; \
node_info.free_data(storage.ATTRIBUTE_NAME[storage_index]); \
break; \
}
#define FREE_DATA(NODE_TYPE, NODE_CLASS, ATTRIBUTE_NAME) \ #define FREE_DATA(NODE_TYPE, NODE_CLASS, ATTRIBUTE_NAME) \
case NODE_TYPE: { \ case NODE_TYPE: { \
NODE_CLASS node_info; \ NODE_CLASS node_info; \
@ -237,13 +283,14 @@ struct VKRenderGraphNode {
FREE_DATA(VKNodeType::DISPATCH, VKDispatchNode, dispatch) FREE_DATA(VKNodeType::DISPATCH, VKDispatchNode, dispatch)
FREE_DATA(VKNodeType::DISPATCH_INDIRECT, VKDispatchIndirectNode, dispatch_indirect) FREE_DATA(VKNodeType::DISPATCH_INDIRECT, VKDispatchIndirectNode, dispatch_indirect)
FREE_DATA(VKNodeType::DRAW, VKDrawNode, draw) FREE_DATA_STORAGE(VKNodeType::DRAW, VKDrawNode, draw)
FREE_DATA(VKNodeType::DRAW_INDEXED, VKDrawIndexedNode, draw_indexed) FREE_DATA_STORAGE(VKNodeType::DRAW_INDEXED, VKDrawIndexedNode, draw_indexed)
FREE_DATA( FREE_DATA_STORAGE(
VKNodeType::DRAW_INDEXED_INDIRECT, VKDrawIndexedIndirectNode, draw_indexed_indirect) VKNodeType::DRAW_INDEXED_INDIRECT, VKDrawIndexedIndirectNode, draw_indexed_indirect)
FREE_DATA(VKNodeType::DRAW_INDIRECT, VKDrawIndirectNode, draw_indirect) FREE_DATA_STORAGE(VKNodeType::DRAW_INDIRECT, VKDrawIndirectNode, draw_indirect)
FREE_DATA(VKNodeType::UPDATE_BUFFER, VKUpdateBufferNode, update_buffer) FREE_DATA(VKNodeType::UPDATE_BUFFER, VKUpdateBufferNode, update_buffer)
#undef FREE_DATA #undef FREE_DATA
#undef FREE_DATA_STORAGE
case VKNodeType::UNUSED: case VKNodeType::UNUSED:
case VKNodeType::BEGIN_QUERY: case VKNodeType::BEGIN_QUERY:
@ -272,12 +319,16 @@ struct VKRenderGraphNode {
* Nodes are reset so they can be reused in consecutive calls. Data allocated by the node are * Nodes are reset so they can be reused in consecutive calls. Data allocated by the node are
* freed. This function dispatches the free_data to the actual node implementation. * freed. This function dispatches the free_data to the actual node implementation.
*/ */
void reset() void reset(VKRenderGraphStorage &storage)
{ {
free_data(); free_data(storage);
memset(this, 0, sizeof(VKRenderGraphNode));
type = VKNodeType::UNUSED; type = VKNodeType::UNUSED;
storage_index = -1;
} }
}; };
BLI_STATIC_ASSERT(sizeof(VKRenderGraphNode) <= 64,
"VKRenderGraphNode should be kept small. Consider moving data to the "
"VKRenderGraphStorage class.");
} // namespace blender::gpu::render_graph } // namespace blender::gpu::render_graph