There is an implementation flaw in the render graph where local pointers cannot be updated, but the data it refers to can be reallocated to another location. The cause of this is that the nodes use an union, which can only contain simple constructed structs (eg memcpy). this union is stored in a vector and can relocate the union. Any local pointers can (and will) become invalid. This PR is a quick fix by updating the pointers just before sending them to the command buffer. In future a better fox needs to be done as part of #121649. Pull Request: https://projects.blender.org/blender/blender/pulls/121723
127 lines
4.7 KiB
C++
127 lines
4.7 KiB
C++
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup gpu
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "vk_common.hh"
|
|
#include "vk_render_graph_node.hh"
|
|
#include "vk_scheduler.hh"
|
|
|
|
namespace blender::gpu::render_graph {
|
|
class VKRenderGraph;
|
|
|
|
/**
|
|
* Build the command buffer for sending to the device queue.
|
|
*
|
|
* Determine which nodes needs to be scheduled, Then for each node generate the needed pipeline
|
|
* barriers and commands.
|
|
*/
|
|
class VKCommandBuilder {
|
|
private:
|
|
/* Pool of VKBufferMemoryBarriers that can be reused when building barriers */
|
|
Vector<VkBufferMemoryBarrier> vk_buffer_memory_barriers_;
|
|
Vector<VkImageMemoryBarrier> vk_image_memory_barriers_;
|
|
|
|
/** Template buffer memory barrier. */
|
|
VkBufferMemoryBarrier vk_buffer_memory_barrier_;
|
|
/** Template image memory barrier. */
|
|
VkImageMemoryBarrier vk_image_memory_barrier_;
|
|
|
|
struct {
|
|
/**
|
|
* State of the bound pipelines during command building.
|
|
*/
|
|
VKBoundPipelines active_pipelines;
|
|
|
|
/**
|
|
* When building memory barriers we need to track the src_stage_mask and dst_stage_mask and
|
|
* pass them to
|
|
* `https://docs.vulkan.org/spec/latest/chapters/synchronization.html#vkCmdPipelineBarrier`
|
|
*
|
|
* NOTE: Only valid between `reset_barriers` and `send_pipeline_barriers`.
|
|
*/
|
|
VkPipelineStageFlags src_stage_mask = VK_PIPELINE_STAGE_NONE;
|
|
VkPipelineStageFlags dst_stage_mask = VK_PIPELINE_STAGE_NONE;
|
|
} state_;
|
|
|
|
public:
|
|
VKCommandBuilder();
|
|
|
|
/**
|
|
* Build the commands of the nodes provided by the `node_handles` parameter. The commands are
|
|
* recorded into the given `command_buffer`.
|
|
*
|
|
* Pre-condition:
|
|
* - `command_buffer` must not be in initial state according to
|
|
* https://docs.vulkan.org/spec/latest/chapters/cmdbuffers.html#commandbuffers-lifecycle
|
|
*
|
|
* Post-condition:
|
|
* - `command_buffer` will be in executable state according to
|
|
* https://docs.vulkan.org/spec/latest/chapters/cmdbuffers.html#commandbuffers-lifecycle
|
|
*/
|
|
void build_nodes(VKRenderGraph &render_graph,
|
|
VKCommandBufferInterface &command_buffer,
|
|
Span<NodeHandle> node_handles);
|
|
|
|
private:
|
|
/**
|
|
* Build the commands for the given node_handle and node.
|
|
*
|
|
* The dependencies of the node handle are checked and when needed a pipeline barrier will be
|
|
* generated and added to the command buffer.
|
|
*
|
|
* Based on the node.type the correct node class will be used for adding commands to the command
|
|
* buffer.
|
|
*/
|
|
void build_node(VKRenderGraph &render_graph,
|
|
VKCommandBufferInterface &command_buffer,
|
|
NodeHandle node_handle,
|
|
VKRenderGraphNode &node);
|
|
|
|
/**
|
|
* Build the pipeline barriers that should be recorded before the given node handle.
|
|
*/
|
|
void build_pipeline_barriers(VKRenderGraph &render_graph,
|
|
VKCommandBufferInterface &command_buffer,
|
|
NodeHandle node_handle,
|
|
VkPipelineStageFlags pipeline_stage);
|
|
void reset_barriers();
|
|
void send_pipeline_barriers(VKCommandBufferInterface &command_buffer);
|
|
|
|
void add_buffer_barriers(VKRenderGraph &render_graph,
|
|
NodeHandle node_handle,
|
|
VkPipelineStageFlags node_stages);
|
|
void add_buffer_barrier(VkBuffer vk_buffer,
|
|
VkAccessFlags src_access_mask,
|
|
VkAccessFlags dst_access_mask);
|
|
void add_buffer_read_barriers(VKRenderGraph &render_graph,
|
|
NodeHandle node_handle,
|
|
VkPipelineStageFlags node_stages);
|
|
void add_buffer_write_barriers(VKRenderGraph &render_graph,
|
|
NodeHandle node_handle,
|
|
VkPipelineStageFlags node_stages);
|
|
|
|
void add_image_barriers(VKRenderGraph &render_graph,
|
|
NodeHandle node_handle,
|
|
VkPipelineStageFlags node_stages);
|
|
void add_image_barrier(VkImage vk_image,
|
|
VkAccessFlags src_access_mask,
|
|
VkAccessFlags dst_access_mask,
|
|
VkImageLayout old_image_layout,
|
|
VkImageLayout new_image_layout,
|
|
VkImageAspectFlags aspect_mask);
|
|
void add_image_read_barriers(VKRenderGraph &render_graph,
|
|
NodeHandle node_handle,
|
|
VkPipelineStageFlags node_stages);
|
|
void add_image_write_barriers(VKRenderGraph &render_graph,
|
|
NodeHandle node_handle,
|
|
VkPipelineStageFlags node_stages);
|
|
};
|
|
|
|
} // namespace blender::gpu::render_graph
|