Vulkan: pipeline barriers extraction
Pipeline barriers were extracted when recording commands. This works, but had the downside that it locked the device resources. Extracting pipeline barriers is fairly small task compared to recording commands. This PR will perform the extraction of pipelines separate from command recording. Code is easier to follow and when working with multiple threads this will reduce locking (enabling this will be done in separate PR). Original developed in !131965 Pull Request: https://projects.blender.org/blender/blender/pulls/132989
This commit is contained in:
@@ -11,77 +11,333 @@
|
||||
#include "vk_render_graph.hh"
|
||||
#include "vk_to_string.hh"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
|
||||
VKCommandBuilder::VKCommandBuilder()
|
||||
{
|
||||
vk_buffer_memory_barrier_ = {};
|
||||
vk_buffer_memory_barrier_.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
|
||||
vk_buffer_memory_barrier_.pNext = nullptr;
|
||||
vk_buffer_memory_barrier_.srcAccessMask = VK_ACCESS_NONE;
|
||||
vk_buffer_memory_barrier_.dstAccessMask = VK_ACCESS_NONE;
|
||||
vk_buffer_memory_barrier_.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
vk_buffer_memory_barrier_.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
vk_buffer_memory_barrier_.buffer = VK_NULL_HANDLE;
|
||||
vk_buffer_memory_barrier_.offset = 0;
|
||||
vk_buffer_memory_barrier_.size = VK_WHOLE_SIZE;
|
||||
|
||||
vk_image_memory_barrier_ = {};
|
||||
vk_image_memory_barrier_.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
vk_image_memory_barrier_.pNext = nullptr;
|
||||
vk_image_memory_barrier_.srcAccessMask = VK_ACCESS_NONE;
|
||||
vk_image_memory_barrier_.dstAccessMask = VK_ACCESS_NONE;
|
||||
vk_image_memory_barrier_.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
vk_image_memory_barrier_.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
vk_image_memory_barrier_.image = VK_NULL_HANDLE;
|
||||
vk_image_memory_barrier_.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
vk_image_memory_barrier_.newLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
vk_image_memory_barrier_.subresourceRange.aspectMask = VK_IMAGE_ASPECT_NONE;
|
||||
vk_image_memory_barrier_.subresourceRange.baseArrayLayer = 0;
|
||||
vk_image_memory_barrier_.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
|
||||
vk_image_memory_barrier_.subresourceRange.baseMipLevel = 0;
|
||||
vk_image_memory_barrier_.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Build nodes
|
||||
* \{ */
|
||||
|
||||
void VKCommandBuilder::build_nodes(VKRenderGraph &render_graph,
|
||||
VKCommandBufferInterface &command_buffer,
|
||||
Span<NodeHandle> nodes)
|
||||
Span<NodeHandle> node_handles)
|
||||
{
|
||||
/* Swap chain images layouts needs to be reset as the image layouts are changed externally. */
|
||||
/* Swap chain images layouts needs to be reset as the image layouts are changed externally. */
|
||||
render_graph.resources_.reset_image_layouts();
|
||||
|
||||
state_.active_pipelines = {};
|
||||
groups_init(render_graph, node_handles);
|
||||
groups_extract_barriers(
|
||||
render_graph, node_handles, command_buffer.use_dynamic_rendering_local_read);
|
||||
|
||||
command_buffer.begin_recording();
|
||||
state_.debug_level = 0;
|
||||
state_.active_debug_group_id = -1;
|
||||
std::optional<NodeHandle> rendering_scope;
|
||||
IndexRange nodes_range = nodes.index_range();
|
||||
groups_build_commands(render_graph, command_buffer, node_handles);
|
||||
command_buffer.end_recording();
|
||||
}
|
||||
|
||||
void VKCommandBuilder::groups_init(const VKRenderGraph &render_graph,
|
||||
Span<NodeHandle> node_handles)
|
||||
{
|
||||
group_nodes_.clear();
|
||||
IndexRange nodes_range = node_handles.index_range();
|
||||
while (!nodes_range.is_empty()) {
|
||||
IndexRange node_group = nodes_range.slice(0, 1);
|
||||
NodeHandle node_handle = nodes[nodes_range.first()];
|
||||
VKRenderGraphNode &node = render_graph.nodes_[node_handle];
|
||||
NodeHandle node_handle = node_handles[nodes_range.first()];
|
||||
const VKRenderGraphNode &node = render_graph.nodes_[node_handle];
|
||||
while (node_type_is_rendering(node.type) && node_group.size() < nodes_range.size()) {
|
||||
NodeHandle node_handle = nodes[nodes_range[node_group.size()]];
|
||||
VKRenderGraphNode &node = render_graph.nodes_[node_handle];
|
||||
NodeHandle node_handle = node_handles[nodes_range[node_group.size()]];
|
||||
const VKRenderGraphNode &node = render_graph.nodes_[node_handle];
|
||||
if (!node_type_is_rendering(node.type) || node.type == VKNodeType::BEGIN_RENDERING) {
|
||||
break;
|
||||
}
|
||||
node_group = nodes_range.slice(0, node_group.size() + 1);
|
||||
}
|
||||
|
||||
build_node_group(render_graph, command_buffer, nodes.slice(node_group), rendering_scope);
|
||||
group_nodes_.append(node_group);
|
||||
nodes_range = nodes_range.drop_front(node_group.size());
|
||||
}
|
||||
}
|
||||
|
||||
finish_debug_groups(command_buffer);
|
||||
state_.debug_level = 0;
|
||||
void VKCommandBuilder::groups_extract_barriers(VKRenderGraph &render_graph,
|
||||
Span<NodeHandle> node_handles,
|
||||
bool use_local_read)
|
||||
{
|
||||
barrier_list_.clear();
|
||||
vk_buffer_memory_barriers_.clear();
|
||||
vk_image_memory_barriers_.clear();
|
||||
|
||||
command_buffer.end_recording();
|
||||
LayeredImageTracker layered_tracker(*this);
|
||||
|
||||
/* Extract barriers. */
|
||||
group_pre_barriers_.clear();
|
||||
group_post_barriers_.clear();
|
||||
node_pre_barriers_.resize(node_handles.size());
|
||||
|
||||
/* Keep track of the post barriers that needs to be added. The pre barriers will be stored
|
||||
* directly in `barrier_list_` but may not mingle with the pre barriers. Most barriers are group
|
||||
* pre barriers. */
|
||||
Vector<Barrier> post_barriers;
|
||||
/* Keep track of the node pre barriers that needs to be added. The pre barriers will be stored
|
||||
* directly in `barrier_list_` but may not mingle with the group barriers. */
|
||||
Vector<Barrier> node_pre_barriers;
|
||||
|
||||
NodeHandle rendering_scope;
|
||||
bool rendering_active = false;
|
||||
|
||||
for (const int64_t group_index : group_nodes_.index_range()) {
|
||||
/* Extract the pre-barriers of this group. */
|
||||
Barriers group_pre_barriers(barrier_list_.size(), 0);
|
||||
const GroupNodes &node_group = group_nodes_[group_index];
|
||||
for (const int64_t group_node_index : node_group) {
|
||||
NodeHandle node_handle = node_handles[group_node_index];
|
||||
VKRenderGraphNode &node = render_graph.nodes_[node_handle];
|
||||
Barrier barrier = {};
|
||||
build_pipeline_barriers(
|
||||
render_graph, node_handle, node.pipeline_stage_get(), layered_tracker, barrier);
|
||||
if (!barrier.is_empty()) {
|
||||
#if 0
|
||||
std::cout << __func__ << ": node_group=" << group_index
|
||||
<< ", node_group_range=" << node_group.first() << "-" << node_group.last()
|
||||
<< ", node_handle=" << node_handle << ", node_type=" << node.type
|
||||
<< ", debug_group=" << render_graph.full_debug_group(node_handle) << "\n";
|
||||
std::cout << __func__ << ": " << to_string_barrier(barrier);
|
||||
#endif
|
||||
barrier_list_.append(barrier);
|
||||
}
|
||||
/* Check for additional barriers when resuming rendering.
|
||||
*
|
||||
* Between suspending rendering and resuming the state/layout of resources can change and
|
||||
* require additional barriers.
|
||||
*/
|
||||
if (node.type == VKNodeType::BEGIN_RENDERING) {
|
||||
/* Begin rendering scope. */
|
||||
BLI_assert(!rendering_active);
|
||||
rendering_scope = node_handle;
|
||||
rendering_active = true;
|
||||
layered_tracker.begin(render_graph, node_handle);
|
||||
}
|
||||
|
||||
else if (node.type == VKNodeType::END_RENDERING) {
|
||||
/* End rendering scope. */
|
||||
BLI_assert(rendering_active);
|
||||
rendering_scope = 0;
|
||||
rendering_active = false;
|
||||
|
||||
/* Any specific layout changes needs to be reverted, so the global resource state tracker
|
||||
* reflects the correct state. These barriers needs to be added as node post barriers. We
|
||||
* assume that END_RENDERING is always the last node of a group. */
|
||||
Barrier barrier = {};
|
||||
layered_tracker.end(barrier, use_local_read);
|
||||
if (!barrier.is_empty()) {
|
||||
post_barriers.append(barrier);
|
||||
}
|
||||
}
|
||||
|
||||
else if (rendering_active && !node_type_is_within_rendering(node.type)) {
|
||||
/* Suspend active rendering scope. */
|
||||
rendering_active = false;
|
||||
|
||||
/* Any specific layout changes needs to be reverted, so the global resource state tracker
|
||||
* reflects the correct state. These barriers needs to be added as node post barriers.
|
||||
*/
|
||||
Barrier barrier = {};
|
||||
layered_tracker.suspend(barrier, use_local_read);
|
||||
if (!barrier.is_empty()) {
|
||||
post_barriers.append(barrier);
|
||||
}
|
||||
}
|
||||
|
||||
else if (!rendering_active && node_type_is_within_rendering(node.type)) {
|
||||
/* Resume rendering scope. */
|
||||
VKRenderGraphNode &rendering_node = render_graph.nodes_[rendering_scope];
|
||||
Barrier barrier = {};
|
||||
build_pipeline_barriers(render_graph,
|
||||
rendering_scope,
|
||||
rendering_node.pipeline_stage_get(),
|
||||
layered_tracker,
|
||||
barrier);
|
||||
if (!barrier.is_empty()) {
|
||||
barrier_list_.append(barrier);
|
||||
}
|
||||
|
||||
/* Resume layered tracking. Each layer that has an override will be transition back to the
|
||||
* layer specific image layout. */
|
||||
barrier = {};
|
||||
layered_tracker.resume(barrier, use_local_read);
|
||||
if (!barrier.is_empty()) {
|
||||
barrier_list_.append(barrier);
|
||||
}
|
||||
|
||||
rendering_active = true;
|
||||
}
|
||||
|
||||
/* Extract pre barriers for nodes. */
|
||||
if (use_local_read && node_type_is_within_rendering(node.type) &&
|
||||
node_has_input_attachments(render_graph, node_handle))
|
||||
{
|
||||
Barrier barrier = {};
|
||||
build_pipeline_barriers(
|
||||
render_graph, node_handle, node.pipeline_stage_get(), layered_tracker, barrier, true);
|
||||
if (!barrier.is_empty()) {
|
||||
node_pre_barriers.append(barrier);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rendering_active) {
|
||||
/* Suspend layered image tracker. When active the next group will always be a compute/data
|
||||
* transfer group.
|
||||
*
|
||||
* Any specific layout changes needs to be reverted, so the global resource state tracker
|
||||
* reflects the correct state. These barriers needs to be added as node post barriers.
|
||||
*/
|
||||
Barrier barrier = {};
|
||||
layered_tracker.suspend(barrier, use_local_read);
|
||||
if (!barrier.is_empty()) {
|
||||
post_barriers.append(barrier);
|
||||
}
|
||||
rendering_active = false;
|
||||
}
|
||||
|
||||
/* Update the group pre and post barriers. Pre barriers are already stored in the
|
||||
* barrier_list_. The post barriers are appended after the pre barriers. */
|
||||
int64_t barrier_list_size = barrier_list_.size();
|
||||
group_pre_barriers_.append(group_pre_barriers.with_new_end(barrier_list_size));
|
||||
barrier_list_.extend(std::move(post_barriers));
|
||||
group_post_barriers_.append(
|
||||
IndexRange::from_begin_end(barrier_list_size, barrier_list_.size()));
|
||||
if (!node_pre_barriers.is_empty()) {
|
||||
barrier_list_size = barrier_list_.size();
|
||||
barrier_list_.extend(std::move(node_pre_barriers));
|
||||
/* Shift all node pre barrier references to the new location in the barrier_list_. */
|
||||
for (const int64_t group_node_index : node_group) {
|
||||
NodeHandle node_handle = node_handles[group_node_index];
|
||||
if (!node_pre_barriers_[node_handle].is_empty()) {
|
||||
node_pre_barriers_[node_handle].from_begin_size(
|
||||
node_pre_barriers_[node_handle].start() + barrier_list_size, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BLI_assert(group_pre_barriers_.size() == group_nodes_.size());
|
||||
BLI_assert(group_post_barriers_.size() == group_nodes_.size());
|
||||
}
|
||||
|
||||
void VKCommandBuilder::groups_build_commands(VKRenderGraph &render_graph,
|
||||
VKCommandBufferInterface &command_buffer,
|
||||
Span<NodeHandle> node_handles)
|
||||
{
|
||||
DebugGroups debug_groups = {};
|
||||
VKBoundPipelines active_pipelines = {};
|
||||
|
||||
NodeHandle rendering_scope = 0;
|
||||
bool rendering_active = false;
|
||||
|
||||
for (int64_t group_index : group_nodes_.index_range()) {
|
||||
IndexRange group_nodes = group_nodes_[group_index];
|
||||
Span<NodeHandle> group_node_handles = node_handles.slice(group_nodes);
|
||||
|
||||
/* Record group pre barriers. */
|
||||
for (BarrierIndex barrier_index : group_pre_barriers_[group_index]) {
|
||||
BLI_assert_msg(!rendering_active,
|
||||
"Pre group barriers must be executed outside a rendering scope.");
|
||||
Barrier &barrier = barrier_list_[barrier_index];
|
||||
#if 0
|
||||
std::cout << __func__ << ": node_group=" << group_index
|
||||
<< ", node_group_range=" << group_node_handles.first() << "-"
|
||||
<< group_node_handles.last() << ", pre_barrier=(" << to_string_barrier(barrier)
|
||||
<< ")\n";
|
||||
#endif
|
||||
send_pipeline_barriers(command_buffer, barrier, false);
|
||||
}
|
||||
|
||||
/* Record group node commands. */
|
||||
for (NodeHandle node_handle : group_node_handles) {
|
||||
VKRenderGraphNode &node = render_graph.nodes_[node_handle];
|
||||
|
||||
if (G.debug & G_DEBUG_GPU) {
|
||||
activate_debug_group(render_graph, command_buffer, debug_groups, node_handle);
|
||||
}
|
||||
|
||||
if (node.type == VKNodeType::BEGIN_RENDERING) {
|
||||
rendering_scope = node_handle;
|
||||
rendering_active = true;
|
||||
|
||||
/* Check of the group spans a full rendering scope. In that case we don't need to set
|
||||
* the VK_RENDERING_SUSPENDING_BIT. */
|
||||
const VKRenderGraphNode &last_node = render_graph.nodes_[group_node_handles.last()];
|
||||
bool will_be_suspended = last_node.type != VKNodeType::END_RENDERING;
|
||||
if (will_be_suspended) {
|
||||
node.begin_rendering.vk_rendering_info.flags = VK_RENDERING_SUSPENDING_BIT;
|
||||
}
|
||||
}
|
||||
|
||||
else if (node.type == VKNodeType::END_RENDERING) {
|
||||
rendering_active = false;
|
||||
}
|
||||
else if (node_type_is_within_rendering(node.type)) {
|
||||
if (!rendering_active) {
|
||||
/* Resume rendering scope. */
|
||||
VKRenderGraphNode &rendering_node = render_graph.nodes_[rendering_scope];
|
||||
rendering_node.begin_rendering.vk_rendering_info.flags = VK_RENDERING_RESUMING_BIT;
|
||||
rendering_node.build_commands(command_buffer, active_pipelines);
|
||||
rendering_active = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Record group node barriers. (VK_EXT_dynamic_rendering_local_read) */
|
||||
for (BarrierIndex node_pre_barrier_index : node_pre_barriers_[node_handle]) {
|
||||
Barrier &barrier = barrier_list_[node_pre_barrier_index];
|
||||
#if 0
|
||||
std::cout << __func__ << ": node_group=" << group_index
|
||||
<< ", node_group_range=" << group_node_handles.first() << "-"
|
||||
<< group_node_handles.last() << ", node_pre_barrier=(" << to_string_barrier(barrier)
|
||||
<< ")\n";
|
||||
#endif
|
||||
// TODO: Barrier should already contain the changes for local read.
|
||||
send_pipeline_barriers(command_buffer, barrier, true);
|
||||
}
|
||||
|
||||
#if 0
|
||||
std::cout << __func__ << ": node_group=" << group_index
|
||||
<< ", node_group_range=" << group_node_handles.first() << "-"
|
||||
<< group_node_handles.last() << ", node_handle=" << node_handle
|
||||
<< ", node_type=" << node.type
|
||||
<< ", debug group=" << render_graph.full_debug_group(node_handle) << "\n";
|
||||
#endif
|
||||
node.build_commands(command_buffer, active_pipelines);
|
||||
}
|
||||
|
||||
if (rendering_active) {
|
||||
/* Suspend rendering as the next node group will contain data transfer/dispatch commands.
|
||||
*/
|
||||
rendering_active = false;
|
||||
if (command_buffer.use_dynamic_rendering) {
|
||||
command_buffer.end_rendering();
|
||||
}
|
||||
else {
|
||||
command_buffer.end_render_pass();
|
||||
}
|
||||
|
||||
VKRenderGraphNode &rendering_node = render_graph.nodes_[rendering_scope];
|
||||
rendering_node.begin_rendering.vk_rendering_info.flags = VK_RENDERING_RESUMING_BIT;
|
||||
}
|
||||
|
||||
/* Record group post barriers. */
|
||||
for (BarrierIndex barrier_index : group_post_barriers_[group_index]) {
|
||||
BLI_assert_msg(!rendering_active,
|
||||
"Post group barriers must be executed outside a rendering scope.");
|
||||
Barrier &barrier = barrier_list_[barrier_index];
|
||||
#if 0
|
||||
std::cout << __func__ << ": node_group=" << group_index
|
||||
<< ", node_group_range=" << group_node_handles.first() << "-"
|
||||
<< group_node_handles.last() << ", post_barrier=(" << to_string_barrier(barrier)
|
||||
<< ")\n";
|
||||
#endif
|
||||
send_pipeline_barriers(command_buffer, barrier, false);
|
||||
}
|
||||
}
|
||||
|
||||
finish_debug_groups(command_buffer, debug_groups);
|
||||
}
|
||||
|
||||
bool VKCommandBuilder::node_has_input_attachments(const VKRenderGraph &render_graph,
|
||||
@@ -94,110 +350,13 @@ bool VKCommandBuilder::node_has_input_attachments(const VKRenderGraph &render_gr
|
||||
});
|
||||
}
|
||||
|
||||
void VKCommandBuilder::build_node_group(VKRenderGraph &render_graph,
|
||||
VKCommandBufferInterface &command_buffer,
|
||||
Span<NodeHandle> node_group,
|
||||
std::optional<NodeHandle> &r_rendering_scope)
|
||||
{
|
||||
bool is_rendering = false;
|
||||
const bool supports_local_read = command_buffer.use_dynamic_rendering_local_read;
|
||||
|
||||
for (NodeHandle node_handle : node_group) {
|
||||
VKRenderGraphNode &node = render_graph.nodes_[node_handle];
|
||||
#if 0
|
||||
std::cout << "node_group=" << node_group.first() << "-" << node_group.last()
|
||||
<< ", node_handle=" << node_handle << ", node_type=" << node.type
|
||||
<< ", debug_group=" << render_graph.full_debug_group(node_handle) << "\n";
|
||||
#endif
|
||||
#if 0
|
||||
render_graph.debug_print(node_handle);
|
||||
#endif
|
||||
build_pipeline_barriers(
|
||||
render_graph, command_buffer, node_handle, node.pipeline_stage_get(), false);
|
||||
if (node.type == VKNodeType::BEGIN_RENDERING) {
|
||||
layer_tracking_begin(render_graph, node_handle);
|
||||
}
|
||||
}
|
||||
|
||||
for (NodeHandle node_handle : node_group) {
|
||||
VKRenderGraphNode &node = render_graph.nodes_[node_handle];
|
||||
if (node.type == VKNodeType::BEGIN_RENDERING) {
|
||||
BLI_assert(!r_rendering_scope.has_value());
|
||||
BLI_assert(!is_rendering);
|
||||
r_rendering_scope = node_handle;
|
||||
is_rendering = true;
|
||||
|
||||
/* Check of the node_group spans a full rendering scope. In that case we don't need to set
|
||||
* the VK_RENDERING_SUSPENDING_BIT. */
|
||||
const VKRenderGraphNode &last_node = render_graph.nodes_[node_group[node_group.size() - 1]];
|
||||
bool will_be_suspended = last_node.type != VKNodeType::END_RENDERING;
|
||||
if (will_be_suspended) {
|
||||
node.begin_rendering.vk_rendering_info.flags = VK_RENDERING_SUSPENDING_BIT;
|
||||
}
|
||||
}
|
||||
|
||||
else if (node.type == VKNodeType::END_RENDERING) {
|
||||
BLI_assert(r_rendering_scope.has_value());
|
||||
r_rendering_scope.reset();
|
||||
is_rendering = false;
|
||||
}
|
||||
else if (node_type_is_within_rendering(node.type)) {
|
||||
BLI_assert(r_rendering_scope.has_value());
|
||||
if (!is_rendering) {
|
||||
// Resuming paused rendering scope.
|
||||
VKRenderGraphNode &rendering_node = render_graph.nodes_[*r_rendering_scope];
|
||||
build_pipeline_barriers(
|
||||
render_graph, command_buffer, *r_rendering_scope, rendering_node.pipeline_stage_get());
|
||||
if (state_.subresource_tracking_enabled()) {
|
||||
layer_tracking_resume(command_buffer);
|
||||
}
|
||||
rendering_node.begin_rendering.vk_rendering_info.flags = VK_RENDERING_RESUMING_BIT;
|
||||
rendering_node.build_commands(command_buffer, state_.active_pipelines);
|
||||
is_rendering = true;
|
||||
}
|
||||
else if (supports_local_read && node_has_input_attachments(render_graph, node_handle)) {
|
||||
build_pipeline_barriers(
|
||||
render_graph, command_buffer, node_handle, node.pipeline_stage_get(), true);
|
||||
}
|
||||
}
|
||||
if (G.debug & G_DEBUG_GPU) {
|
||||
activate_debug_group(render_graph, command_buffer, node_handle);
|
||||
}
|
||||
#if 0
|
||||
std::cout << "node_group=" << node_group.first() << "-" << node_group.last()
|
||||
<< ", node_handle=" << node_handle << ", node_type=" << node.type
|
||||
<< ", debug group=" << render_graph.full_debug_group(node_handle) << "\n";
|
||||
|
||||
#endif
|
||||
node.build_commands(command_buffer, state_.active_pipelines);
|
||||
|
||||
/* When layered image has different layouts we reset the layouts to
|
||||
* VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL. */
|
||||
if (node.type == VKNodeType::END_RENDERING && state_.subresource_tracking_enabled()) {
|
||||
layer_tracking_end(command_buffer);
|
||||
}
|
||||
}
|
||||
if (is_rendering) {
|
||||
/* Suspend rendering as the next node group will contain data transfer/dispatch commands. */
|
||||
is_rendering = false;
|
||||
if (command_buffer.use_dynamic_rendering) {
|
||||
command_buffer.end_rendering();
|
||||
}
|
||||
else {
|
||||
command_buffer.end_render_pass();
|
||||
}
|
||||
if (state_.subresource_tracking_enabled()) {
|
||||
layer_tracking_suspend(command_buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VKCommandBuilder::activate_debug_group(VKRenderGraph &render_graph,
|
||||
VKCommandBufferInterface &command_buffer,
|
||||
DebugGroups &debug_groups,
|
||||
NodeHandle node_handle)
|
||||
{
|
||||
VKRenderGraph::DebugGroupID debug_group = render_graph.debug_.node_group_map[node_handle];
|
||||
if (debug_group == state_.active_debug_group_id) {
|
||||
if (debug_group == debug_groups.active_debug_group_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -206,14 +365,14 @@ void VKCommandBuilder::activate_debug_group(VKRenderGraph &render_graph,
|
||||
int num_begins = 0;
|
||||
|
||||
if (debug_group == -1) {
|
||||
num_ends = state_.debug_level;
|
||||
num_ends = debug_groups.debug_level;
|
||||
}
|
||||
else {
|
||||
Vector<VKRenderGraph::DebugGroupNameID> &to_group =
|
||||
render_graph.debug_.used_groups[debug_group];
|
||||
if (state_.active_debug_group_id != -1) {
|
||||
if (debug_groups.active_debug_group_id != -1) {
|
||||
Vector<VKRenderGraph::DebugGroupNameID> &from_group =
|
||||
render_graph.debug_.used_groups[state_.active_debug_group_id];
|
||||
render_graph.debug_.used_groups[debug_groups.active_debug_group_id];
|
||||
|
||||
num_ends = max_ii(from_group.size() - to_group.size(), 0);
|
||||
int num_checks = min_ii(from_group.size(), to_group.size());
|
||||
@@ -225,14 +384,14 @@ void VKCommandBuilder::activate_debug_group(VKRenderGraph &render_graph,
|
||||
}
|
||||
}
|
||||
|
||||
num_begins = to_group.size() - (state_.debug_level - num_ends);
|
||||
num_begins = to_group.size() - (debug_groups.debug_level - num_ends);
|
||||
}
|
||||
|
||||
/* Perform the pops from the debug stack. */
|
||||
for (int index = 0; index < num_ends; index++) {
|
||||
command_buffer.end_debug_utils_label();
|
||||
}
|
||||
state_.debug_level -= num_ends;
|
||||
debug_groups.debug_level -= num_ends;
|
||||
|
||||
/* Perform the pushes to the debug stack. */
|
||||
if (num_begins > 0) {
|
||||
@@ -240,7 +399,7 @@ void VKCommandBuilder::activate_debug_group(VKRenderGraph &render_graph,
|
||||
render_graph.debug_.used_groups[debug_group];
|
||||
VkDebugUtilsLabelEXT debug_utils_label = {};
|
||||
debug_utils_label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
|
||||
for (int index : IndexRange(state_.debug_level, num_begins)) {
|
||||
for (int index : IndexRange(debug_groups.debug_level, num_begins)) {
|
||||
const VKRenderGraph::DebugGroup &debug_group = render_graph.debug_.groups[to_group[index]];
|
||||
debug_utils_label.pLabelName = debug_group.name.c_str();
|
||||
copy_v4_v4(debug_utils_label.color, debug_group.color);
|
||||
@@ -248,28 +407,30 @@ void VKCommandBuilder::activate_debug_group(VKRenderGraph &render_graph,
|
||||
}
|
||||
}
|
||||
|
||||
state_.debug_level += num_begins;
|
||||
state_.active_debug_group_id = debug_group;
|
||||
debug_groups.debug_level += num_begins;
|
||||
debug_groups.active_debug_group_id = debug_group;
|
||||
}
|
||||
|
||||
void VKCommandBuilder::finish_debug_groups(VKCommandBufferInterface &command_buffer)
|
||||
void VKCommandBuilder::finish_debug_groups(VKCommandBufferInterface &command_buffer,
|
||||
DebugGroups &debug_groups)
|
||||
{
|
||||
for (int i = 0; i < state_.debug_level; i++) {
|
||||
for (int i = 0; i < debug_groups.debug_level; i++) {
|
||||
command_buffer.end_debug_utils_label();
|
||||
}
|
||||
state_.debug_level = 0;
|
||||
debug_groups.debug_level = 0;
|
||||
}
|
||||
|
||||
void VKCommandBuilder::build_pipeline_barriers(VKRenderGraph &render_graph,
|
||||
VKCommandBufferInterface &command_buffer,
|
||||
NodeHandle node_handle,
|
||||
VkPipelineStageFlags pipeline_stage,
|
||||
LayeredImageTracker &layered_tracker,
|
||||
Barrier &r_barrier,
|
||||
bool within_rendering)
|
||||
{
|
||||
reset_barriers();
|
||||
add_image_barriers(render_graph, node_handle, pipeline_stage, within_rendering);
|
||||
add_buffer_barriers(render_graph, node_handle, pipeline_stage);
|
||||
send_pipeline_barriers(command_buffer, within_rendering);
|
||||
reset_barriers(r_barrier);
|
||||
add_image_barriers(
|
||||
render_graph, node_handle, pipeline_stage, layered_tracker, r_barrier, within_rendering);
|
||||
add_buffer_barriers(render_graph, node_handle, pipeline_stage, r_barrier);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -278,68 +439,73 @@ void VKCommandBuilder::build_pipeline_barriers(VKRenderGraph &render_graph,
|
||||
/** \name Pipeline barriers
|
||||
* \{ */
|
||||
|
||||
void VKCommandBuilder::reset_barriers()
|
||||
void VKCommandBuilder::reset_barriers(Barrier &r_barrier)
|
||||
{
|
||||
vk_buffer_memory_barriers_.clear();
|
||||
vk_image_memory_barriers_.clear();
|
||||
state_.src_stage_mask = VK_PIPELINE_STAGE_NONE;
|
||||
state_.dst_stage_mask = VK_PIPELINE_STAGE_NONE;
|
||||
r_barrier.dst_stage_mask = r_barrier.src_stage_mask = VK_PIPELINE_STAGE_NONE;
|
||||
}
|
||||
|
||||
void VKCommandBuilder::send_pipeline_barriers(VKCommandBufferInterface &command_buffer,
|
||||
const Barrier &barrier,
|
||||
bool within_rendering)
|
||||
{
|
||||
if (vk_image_memory_barriers_.is_empty() && vk_buffer_memory_barriers_.is_empty()) {
|
||||
reset_barriers();
|
||||
if (barrier.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* When no resources have been used, we can start the barrier at the top of the pipeline.
|
||||
* It is not allowed to set it to None. */
|
||||
/* TODO: VK_KHR_synchronization2 allows setting src_stage_mask to NONE. */
|
||||
if (state_.src_stage_mask == VK_PIPELINE_STAGE_NONE) {
|
||||
state_.src_stage_mask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||
}
|
||||
/* When no resources have been used, we can start the barrier at the top of the pipeline.
|
||||
* It is not allowed to set it to None. */
|
||||
VkPipelineStageFlags src_stage_mask = (barrier.src_stage_mask == VK_PIPELINE_STAGE_NONE) ?
|
||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT :
|
||||
barrier.src_stage_mask;
|
||||
|
||||
VkPipelineStageFlags src_stage_mask;
|
||||
VkPipelineStageFlags dst_stage_mask;
|
||||
VkPipelineStageFlags dst_stage_mask = barrier.dst_stage_mask;
|
||||
// TODO: this should be done during barrier extraction making within_rendering obsolete.
|
||||
if (within_rendering) {
|
||||
/* See: VUID - vkCmdPipelineBarrier - srcStageMask - 09556
|
||||
* If vkCmdPipelineBarrier is called within a render pass instance started with
|
||||
* vkCmdBeginRendering, this command must only specify framebuffer-space stages in srcStageMask
|
||||
* and dstStageMask */
|
||||
* vkCmdBeginRendering, this command must only specify framebuffer-space stages in
|
||||
* srcStageMask and dstStageMask */
|
||||
src_stage_mask = dst_stage_mask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
|
||||
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
|
||||
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
}
|
||||
else {
|
||||
src_stage_mask = state_.src_stage_mask;
|
||||
dst_stage_mask = state_.dst_stage_mask;
|
||||
}
|
||||
|
||||
Span<VkBufferMemoryBarrier> buffer_barriers = vk_buffer_memory_barriers_.as_span().slice(
|
||||
barrier.buffer_memory_barriers);
|
||||
Span<VkImageMemoryBarrier> image_barriers = vk_image_memory_barriers_.as_span().slice(
|
||||
barrier.image_memory_barriers);
|
||||
|
||||
command_buffer.pipeline_barrier(src_stage_mask,
|
||||
dst_stage_mask,
|
||||
VK_DEPENDENCY_BY_REGION_BIT,
|
||||
0,
|
||||
nullptr,
|
||||
vk_buffer_memory_barriers_.size(),
|
||||
vk_buffer_memory_barriers_.data(),
|
||||
vk_image_memory_barriers_.size(),
|
||||
vk_image_memory_barriers_.data());
|
||||
reset_barriers();
|
||||
buffer_barriers.size(),
|
||||
buffer_barriers.data(),
|
||||
image_barriers.size(),
|
||||
image_barriers.data());
|
||||
}
|
||||
|
||||
void VKCommandBuilder::add_buffer_barriers(VKRenderGraph &render_graph,
|
||||
NodeHandle node_handle,
|
||||
VkPipelineStageFlags node_stages)
|
||||
VkPipelineStageFlags node_stages,
|
||||
Barrier &r_barrier)
|
||||
{
|
||||
add_buffer_read_barriers(render_graph, node_handle, node_stages);
|
||||
add_buffer_write_barriers(render_graph, node_handle, node_stages);
|
||||
r_barrier.buffer_memory_barriers = IndexRange(vk_buffer_memory_barriers_.size(), 0);
|
||||
add_buffer_read_barriers(render_graph, node_handle, node_stages, r_barrier);
|
||||
add_buffer_write_barriers(render_graph, node_handle, node_stages, r_barrier);
|
||||
r_barrier.buffer_memory_barriers = r_barrier.buffer_memory_barriers.with_new_end(
|
||||
vk_buffer_memory_barriers_.size());
|
||||
}
|
||||
|
||||
void VKCommandBuilder::add_buffer_read_barriers(VKRenderGraph &render_graph,
|
||||
NodeHandle node_handle,
|
||||
VkPipelineStageFlags node_stages)
|
||||
VkPipelineStageFlags node_stages,
|
||||
Barrier &r_barrier)
|
||||
{
|
||||
for (const VKRenderGraphLink &link : render_graph.links_[node_handle].inputs) {
|
||||
const ResourceWithStamp &versioned_resource = link.resource;
|
||||
@@ -361,8 +527,8 @@ void VKCommandBuilder::add_buffer_read_barriers(VKRenderGraph &render_graph,
|
||||
|
||||
const VkAccessFlags wait_access = resource_state.vk_access;
|
||||
|
||||
state_.src_stage_mask |= resource_state.vk_pipeline_stages;
|
||||
state_.dst_stage_mask |= node_stages;
|
||||
r_barrier.src_stage_mask |= resource_state.vk_pipeline_stages;
|
||||
r_barrier.dst_stage_mask |= node_stages;
|
||||
|
||||
if (is_first_read) {
|
||||
resource_state.vk_access = link.vk_access_flags;
|
||||
@@ -373,13 +539,14 @@ void VKCommandBuilder::add_buffer_read_barriers(VKRenderGraph &render_graph,
|
||||
resource_state.vk_pipeline_stages |= node_stages;
|
||||
}
|
||||
|
||||
add_buffer_barrier(resource.buffer.vk_buffer, wait_access, link.vk_access_flags);
|
||||
add_buffer_barrier(resource.buffer.vk_buffer, r_barrier, wait_access, link.vk_access_flags);
|
||||
}
|
||||
}
|
||||
|
||||
void VKCommandBuilder::add_buffer_write_barriers(VKRenderGraph &render_graph,
|
||||
NodeHandle node_handle,
|
||||
VkPipelineStageFlags node_stages)
|
||||
VkPipelineStageFlags node_stages,
|
||||
Barrier &r_barrier)
|
||||
{
|
||||
for (const VKRenderGraphLink link : render_graph.links_[node_handle].outputs) {
|
||||
const ResourceWithStamp &versioned_resource = link.resource;
|
||||
@@ -392,23 +559,27 @@ void VKCommandBuilder::add_buffer_write_barriers(VKRenderGraph &render_graph,
|
||||
VKResourceBarrierState &resource_state = resource.barrier_state;
|
||||
const VkAccessFlags wait_access = resource_state.vk_access;
|
||||
|
||||
state_.src_stage_mask |= resource_state.vk_pipeline_stages;
|
||||
state_.dst_stage_mask |= node_stages;
|
||||
r_barrier.src_stage_mask |= resource_state.vk_pipeline_stages;
|
||||
r_barrier.dst_stage_mask |= node_stages;
|
||||
|
||||
resource_state.vk_access = link.vk_access_flags;
|
||||
resource_state.vk_pipeline_stages = node_stages;
|
||||
|
||||
if (wait_access != VK_ACCESS_NONE) {
|
||||
add_buffer_barrier(resource.buffer.vk_buffer, wait_access, link.vk_access_flags);
|
||||
add_buffer_barrier(resource.buffer.vk_buffer, r_barrier, wait_access, link.vk_access_flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VKCommandBuilder::add_buffer_barrier(VkBuffer vk_buffer,
|
||||
Barrier &r_barrier,
|
||||
VkAccessFlags src_access_mask,
|
||||
VkAccessFlags dst_access_mask)
|
||||
{
|
||||
for (VkBufferMemoryBarrier &vk_buffer_memory_barrier : vk_buffer_memory_barriers_) {
|
||||
for (VkBufferMemoryBarrier &vk_buffer_memory_barrier :
|
||||
vk_buffer_memory_barriers_.as_mutable_span().drop_front(
|
||||
r_barrier.buffer_memory_barriers.start()))
|
||||
{
|
||||
if (vk_buffer_memory_barrier.buffer == vk_buffer) {
|
||||
/* When registering read/write buffers, it can be that the node internally requires
|
||||
* read/write. In this case we adjust the dstAccessMask of the read barrier. */
|
||||
@@ -416,7 +587,8 @@ void VKCommandBuilder::add_buffer_barrier(VkBuffer vk_buffer,
|
||||
vk_buffer_memory_barrier.dstAccessMask |= dst_access_mask;
|
||||
return;
|
||||
}
|
||||
/* When re-registering resources we can skip if access mask already contain all the flags. */
|
||||
/* When re-registering resources we can skip if access mask already contain all the flags.
|
||||
*/
|
||||
if ((vk_buffer_memory_barrier.dstAccessMask & dst_access_mask) == dst_access_mask &&
|
||||
(vk_buffer_memory_barrier.srcAccessMask & src_access_mask) == src_access_mask)
|
||||
{
|
||||
@@ -425,27 +597,38 @@ void VKCommandBuilder::add_buffer_barrier(VkBuffer vk_buffer,
|
||||
}
|
||||
}
|
||||
|
||||
vk_buffer_memory_barrier_.srcAccessMask = src_access_mask;
|
||||
vk_buffer_memory_barrier_.dstAccessMask = dst_access_mask;
|
||||
vk_buffer_memory_barrier_.buffer = vk_buffer;
|
||||
vk_buffer_memory_barriers_.append(vk_buffer_memory_barrier_);
|
||||
vk_buffer_memory_barrier_.srcAccessMask = VK_ACCESS_NONE;
|
||||
vk_buffer_memory_barrier_.dstAccessMask = VK_ACCESS_NONE;
|
||||
vk_buffer_memory_barrier_.buffer = VK_NULL_HANDLE;
|
||||
vk_buffer_memory_barriers_.append({VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
|
||||
nullptr,
|
||||
src_access_mask,
|
||||
dst_access_mask,
|
||||
VK_QUEUE_FAMILY_IGNORED,
|
||||
VK_QUEUE_FAMILY_IGNORED,
|
||||
vk_buffer,
|
||||
0,
|
||||
VK_WHOLE_SIZE});
|
||||
}
|
||||
|
||||
void VKCommandBuilder::add_image_barriers(VKRenderGraph &render_graph,
|
||||
NodeHandle node_handle,
|
||||
VkPipelineStageFlags node_stages,
|
||||
LayeredImageTracker &layered_tracker,
|
||||
Barrier &r_barrier,
|
||||
bool within_rendering)
|
||||
{
|
||||
add_image_read_barriers(render_graph, node_handle, node_stages, within_rendering);
|
||||
add_image_write_barriers(render_graph, node_handle, node_stages, within_rendering);
|
||||
r_barrier.image_memory_barriers = IndexRange(vk_image_memory_barriers_.size(), 0);
|
||||
add_image_read_barriers(
|
||||
render_graph, node_handle, node_stages, layered_tracker, r_barrier, within_rendering);
|
||||
add_image_write_barriers(
|
||||
render_graph, node_handle, node_stages, layered_tracker, r_barrier, within_rendering);
|
||||
r_barrier.image_memory_barriers = r_barrier.image_memory_barriers.with_new_end(
|
||||
vk_image_memory_barriers_.size());
|
||||
}
|
||||
|
||||
void VKCommandBuilder::add_image_read_barriers(VKRenderGraph &render_graph,
|
||||
NodeHandle node_handle,
|
||||
VkPipelineStageFlags node_stages,
|
||||
LayeredImageTracker &layered_tracker,
|
||||
Barrier &r_barrier,
|
||||
bool within_rendering)
|
||||
{
|
||||
for (const VKRenderGraphLink &link : render_graph.links_[node_handle].inputs) {
|
||||
@@ -471,21 +654,22 @@ void VKCommandBuilder::add_image_read_barriers(VKRenderGraph &render_graph,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (state_.layered_attachments.contains(resource.image.vk_image) &&
|
||||
resource_state.image_layout != link.vk_image_layout)
|
||||
if (resource_state.image_layout != link.vk_image_layout &&
|
||||
layered_tracker.contains(resource.image.vk_image))
|
||||
{
|
||||
layer_tracking_update(resource.image.vk_image,
|
||||
link.layer_base,
|
||||
link.layer_count,
|
||||
resource_state.image_layout,
|
||||
link.vk_image_layout);
|
||||
layered_tracker.update(resource.image.vk_image,
|
||||
link.layer_base,
|
||||
link.layer_count,
|
||||
resource_state.image_layout,
|
||||
link.vk_image_layout,
|
||||
r_barrier);
|
||||
continue;
|
||||
}
|
||||
|
||||
VkAccessFlags wait_access = resource_state.vk_access;
|
||||
|
||||
state_.src_stage_mask |= resource_state.vk_pipeline_stages;
|
||||
state_.dst_stage_mask |= node_stages;
|
||||
r_barrier.src_stage_mask |= resource_state.vk_pipeline_stages;
|
||||
r_barrier.dst_stage_mask |= node_stages;
|
||||
|
||||
if (is_first_read) {
|
||||
resource_state.vk_access = link.vk_access_flags;
|
||||
@@ -497,6 +681,7 @@ void VKCommandBuilder::add_image_read_barriers(VKRenderGraph &render_graph,
|
||||
}
|
||||
|
||||
add_image_barrier(resource.image.vk_image,
|
||||
r_barrier,
|
||||
wait_access,
|
||||
link.vk_access_flags,
|
||||
resource_state.image_layout,
|
||||
@@ -509,6 +694,8 @@ void VKCommandBuilder::add_image_read_barriers(VKRenderGraph &render_graph,
|
||||
void VKCommandBuilder::add_image_write_barriers(VKRenderGraph &render_graph,
|
||||
NodeHandle node_handle,
|
||||
VkPipelineStageFlags node_stages,
|
||||
LayeredImageTracker &layered_tracker,
|
||||
Barrier &r_barrier,
|
||||
bool within_rendering)
|
||||
{
|
||||
for (const VKRenderGraphLink link : render_graph.links_[node_handle].outputs) {
|
||||
@@ -525,26 +712,28 @@ void VKCommandBuilder::add_image_write_barriers(VKRenderGraph &render_graph,
|
||||
/* Allow only local read barriers inside rendering scope */
|
||||
continue;
|
||||
}
|
||||
if (state_.layered_attachments.contains(resource.image.vk_image) &&
|
||||
if (layered_tracker.contains(resource.image.vk_image) &&
|
||||
resource_state.image_layout != link.vk_image_layout)
|
||||
{
|
||||
layer_tracking_update(resource.image.vk_image,
|
||||
link.layer_base,
|
||||
link.layer_count,
|
||||
resource_state.image_layout,
|
||||
link.vk_image_layout);
|
||||
layered_tracker.update(resource.image.vk_image,
|
||||
link.layer_base,
|
||||
link.layer_count,
|
||||
resource_state.image_layout,
|
||||
link.vk_image_layout,
|
||||
r_barrier);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
state_.src_stage_mask |= resource_state.vk_pipeline_stages;
|
||||
state_.dst_stage_mask |= node_stages;
|
||||
r_barrier.src_stage_mask |= resource_state.vk_pipeline_stages;
|
||||
r_barrier.dst_stage_mask |= node_stages;
|
||||
|
||||
resource_state.vk_access = link.vk_access_flags;
|
||||
resource_state.vk_pipeline_stages = node_stages;
|
||||
|
||||
if (wait_access != VK_ACCESS_NONE || link.vk_image_layout != resource_state.image_layout) {
|
||||
add_image_barrier(resource.image.vk_image,
|
||||
r_barrier,
|
||||
wait_access,
|
||||
link.vk_access_flags,
|
||||
resource_state.image_layout,
|
||||
@@ -556,6 +745,7 @@ void VKCommandBuilder::add_image_write_barriers(VKRenderGraph &render_graph,
|
||||
}
|
||||
|
||||
void VKCommandBuilder::add_image_barrier(VkImage vk_image,
|
||||
Barrier &r_barrier,
|
||||
VkAccessFlags src_access_mask,
|
||||
VkAccessFlags dst_access_mask,
|
||||
VkImageLayout old_layout,
|
||||
@@ -565,7 +755,10 @@ void VKCommandBuilder::add_image_barrier(VkImage vk_image,
|
||||
uint32_t layer_count)
|
||||
{
|
||||
BLI_assert(aspect_mask != VK_IMAGE_ASPECT_NONE);
|
||||
for (VkImageMemoryBarrier &vk_image_memory_barrier : vk_image_memory_barriers_) {
|
||||
for (VkImageMemoryBarrier &vk_image_memory_barrier :
|
||||
vk_image_memory_barriers_.as_mutable_span().drop_front(
|
||||
r_barrier.image_memory_barriers.start()))
|
||||
{
|
||||
if (vk_image_memory_barrier.image == vk_image) {
|
||||
/* When registering read/write buffers, it can be that the node internally requires
|
||||
* read/write. In this case we adjust the dstAccessMask of the read barrier. An example is
|
||||
@@ -574,7 +767,8 @@ void VKCommandBuilder::add_image_barrier(VkImage vk_image,
|
||||
vk_image_memory_barrier.dstAccessMask |= dst_access_mask;
|
||||
return;
|
||||
}
|
||||
/* When re-registering resources we can skip if access mask already contain all the flags. */
|
||||
/* When re-registering resources we can skip if access mask already contain all the flags.
|
||||
*/
|
||||
if ((vk_image_memory_barrier.dstAccessMask & dst_access_mask) == dst_access_mask &&
|
||||
(vk_image_memory_barrier.srcAccessMask & src_access_mask) == src_access_mask &&
|
||||
old_layout == new_layout)
|
||||
@@ -584,24 +778,17 @@ void VKCommandBuilder::add_image_barrier(VkImage vk_image,
|
||||
}
|
||||
}
|
||||
|
||||
vk_image_memory_barrier_.srcAccessMask = src_access_mask;
|
||||
vk_image_memory_barrier_.dstAccessMask = dst_access_mask;
|
||||
vk_image_memory_barrier_.image = vk_image;
|
||||
vk_image_memory_barrier_.oldLayout = old_layout;
|
||||
vk_image_memory_barrier_.newLayout = new_layout;
|
||||
vk_image_memory_barrier_.subresourceRange.aspectMask = aspect_mask;
|
||||
vk_image_memory_barrier_.subresourceRange.baseArrayLayer = layer_base;
|
||||
vk_image_memory_barrier_.subresourceRange.layerCount = layer_count;
|
||||
vk_image_memory_barriers_.append(vk_image_memory_barrier_);
|
||||
/* Reset state for reuse. */
|
||||
vk_image_memory_barrier_.srcAccessMask = VK_ACCESS_NONE;
|
||||
vk_image_memory_barrier_.dstAccessMask = VK_ACCESS_NONE;
|
||||
vk_image_memory_barrier_.image = VK_NULL_HANDLE;
|
||||
vk_image_memory_barrier_.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
vk_image_memory_barrier_.newLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
vk_image_memory_barrier_.subresourceRange.aspectMask = VK_IMAGE_ASPECT_NONE;
|
||||
vk_image_memory_barrier_.subresourceRange.baseArrayLayer = 0;
|
||||
vk_image_memory_barrier_.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
|
||||
vk_image_memory_barriers_.append(
|
||||
{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
nullptr,
|
||||
src_access_mask,
|
||||
dst_access_mask,
|
||||
old_layout,
|
||||
new_layout,
|
||||
VK_QUEUE_FAMILY_IGNORED,
|
||||
VK_QUEUE_FAMILY_IGNORED,
|
||||
vk_image,
|
||||
{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, layer_base, layer_count}});
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -610,80 +797,84 @@ void VKCommandBuilder::add_image_barrier(VkImage vk_image,
|
||||
/** \name Sub-resource tracking
|
||||
* \{ */
|
||||
|
||||
void VKCommandBuilder::layer_tracking_begin(const VKRenderGraph &render_graph,
|
||||
NodeHandle node_handle)
|
||||
void VKCommandBuilder::LayeredImageTracker::begin(const VKRenderGraph &render_graph,
|
||||
NodeHandle node_handle)
|
||||
{
|
||||
BLI_assert(render_graph.nodes_[node_handle].type == VKNodeType::BEGIN_RENDERING);
|
||||
state_.layered_attachments.clear();
|
||||
state_.layered_bindings.clear();
|
||||
layered_attachments.clear();
|
||||
layered_bindings.clear();
|
||||
|
||||
const VKRenderGraphNodeLinks &links = render_graph.links_[node_handle];
|
||||
for (const VKRenderGraphLink &link : links.outputs) {
|
||||
VKResourceStateTracker::Resource &resource = render_graph.resources_.resources_.lookup(
|
||||
link.resource.handle);
|
||||
if (resource.has_multiple_layers()) {
|
||||
state_.layered_attachments.add(resource.image.vk_image);
|
||||
layered_attachments.add(resource.image.vk_image);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VKCommandBuilder::layer_tracking_update(VkImage vk_image,
|
||||
uint32_t layer,
|
||||
uint32_t layer_count,
|
||||
VkImageLayout old_layout,
|
||||
VkImageLayout new_layout)
|
||||
void VKCommandBuilder::LayeredImageTracker::update(VkImage vk_image,
|
||||
uint32_t layer,
|
||||
uint32_t layer_count,
|
||||
VkImageLayout old_layout,
|
||||
VkImageLayout new_layout,
|
||||
Barrier &r_barrier)
|
||||
{
|
||||
for (const LayeredImageBinding &binding : state_.layered_bindings) {
|
||||
for (const TrackedImage &binding : layered_bindings) {
|
||||
if (binding.vk_image == vk_image && binding.layer == layer) {
|
||||
BLI_assert_msg(binding.vk_image_layout == new_layout,
|
||||
"We don't support that one layer transitions multiple times during a "
|
||||
"rendering scope.");
|
||||
/* Early exit as layer is in correct layout. This is a normal case as we expect multiple draw
|
||||
* commands to take place during a rendering scope with the same layer access. */
|
||||
/* Early exit as layer is in correct layout. This is a normal case as we expect multiple
|
||||
* draw commands to take place during a rendering scope with the same layer access. */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
state_.layered_bindings.append({vk_image, new_layout, layer, layer_count});
|
||||
layered_bindings.append({vk_image, new_layout, layer, layer_count});
|
||||
|
||||
/* We should be able to do better. BOTTOM/TOP is really a worst case barrier. */
|
||||
state_.src_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
|
||||
state_.dst_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
|
||||
add_image_barrier(vk_image,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT |
|
||||
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
|
||||
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT |
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
old_layout,
|
||||
new_layout,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
layer,
|
||||
layer_count);
|
||||
r_barrier.src_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
|
||||
r_barrier.dst_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
|
||||
command_builder.add_image_barrier(vk_image,
|
||||
r_barrier,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT |
|
||||
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
|
||||
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
|
||||
VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
old_layout,
|
||||
new_layout,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
layer,
|
||||
layer_count);
|
||||
}
|
||||
|
||||
void VKCommandBuilder::layer_tracking_end(VKCommandBufferInterface &command_buffer)
|
||||
void VKCommandBuilder::LayeredImageTracker::end(Barrier &r_barrier, bool use_local_read)
|
||||
{
|
||||
layer_tracking_suspend(command_buffer);
|
||||
state_.layered_attachments.clear();
|
||||
state_.layered_bindings.clear();
|
||||
suspend(r_barrier, use_local_read);
|
||||
layered_attachments.clear();
|
||||
layered_bindings.clear();
|
||||
}
|
||||
|
||||
void VKCommandBuilder::layer_tracking_suspend(VKCommandBufferInterface &command_buffer)
|
||||
{
|
||||
const bool supports_local_read = command_buffer.use_dynamic_rendering_local_read;
|
||||
void VKCommandBuilder::LayeredImageTracker::suspend(Barrier &r_barrier, bool use_local_read)
|
||||
|
||||
if (state_.layered_bindings.is_empty()) {
|
||||
{
|
||||
if (layered_bindings.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
reset_barriers();
|
||||
command_builder.reset_barriers(r_barrier);
|
||||
/* We should be able to do better. BOTTOM/TOP is really a worst case barrier. */
|
||||
state_.src_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
|
||||
state_.dst_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
|
||||
for (const LayeredImageBinding &binding : state_.layered_bindings) {
|
||||
add_image_barrier(
|
||||
r_barrier.src_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
|
||||
r_barrier.dst_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
|
||||
int64_t start_index = command_builder.vk_image_memory_barriers_.size();
|
||||
r_barrier.image_memory_barriers = IndexRange::from_begin_size(start_index, 0);
|
||||
for (const TrackedImage &binding : layered_bindings) {
|
||||
command_builder.add_image_barrier(
|
||||
binding.vk_image,
|
||||
r_barrier,
|
||||
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT |
|
||||
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
|
||||
VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
@@ -691,11 +882,14 @@ void VKCommandBuilder::layer_tracking_suspend(VKCommandBufferInterface &command_
|
||||
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
|
||||
VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
binding.vk_image_layout,
|
||||
supports_local_read ? VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR :
|
||||
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||
use_local_read ? VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR :
|
||||
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
binding.layer,
|
||||
binding.layer_count);
|
||||
r_barrier.image_memory_barriers = r_barrier.image_memory_barriers.with_new_end(
|
||||
command_builder.vk_image_memory_barriers_.size());
|
||||
|
||||
#if 0
|
||||
std::cout << __func__ << ": transition layout image=" << binding.vk_image
|
||||
<< ", layer=" << binding.layer << ", count=" << binding.layer_count
|
||||
@@ -703,31 +897,33 @@ void VKCommandBuilder::layer_tracking_suspend(VKCommandBufferInterface &command_
|
||||
<< ", to_layout=" << to_string(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) << "\n";
|
||||
#endif
|
||||
}
|
||||
send_pipeline_barriers(command_buffer, false);
|
||||
}
|
||||
|
||||
void VKCommandBuilder::layer_tracking_resume(VKCommandBufferInterface &command_buffer)
|
||||
void VKCommandBuilder::LayeredImageTracker::resume(Barrier &r_barrier, bool use_local_read)
|
||||
{
|
||||
if (state_.layered_bindings.is_empty()) {
|
||||
if (layered_bindings.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
reset_barriers();
|
||||
command_builder.reset_barriers(r_barrier);
|
||||
/* We should be able to do better. BOTTOM/TOP is really a worst case barrier. */
|
||||
state_.src_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
|
||||
state_.dst_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
|
||||
const bool supports_local_read = command_buffer.use_dynamic_rendering_local_read;
|
||||
for (const LayeredImageBinding &binding : state_.layered_bindings) {
|
||||
add_image_barrier(
|
||||
r_barrier.src_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
|
||||
r_barrier.dst_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
|
||||
int64_t start_index = command_builder.vk_image_memory_barriers_.size();
|
||||
r_barrier.image_memory_barriers = IndexRange::from_begin_size(start_index, 0);
|
||||
|
||||
for (const TrackedImage &binding : layered_bindings) {
|
||||
command_builder.add_image_barrier(
|
||||
binding.vk_image,
|
||||
r_barrier,
|
||||
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT |
|
||||
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
|
||||
VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT |
|
||||
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
|
||||
VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
supports_local_read ? VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR :
|
||||
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||
use_local_read ? VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR :
|
||||
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||
binding.vk_image_layout,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
binding.layer,
|
||||
@@ -739,8 +935,40 @@ void VKCommandBuilder::layer_tracking_resume(VKCommandBufferInterface &command_b
|
||||
<< ", to_layout=" << to_string(binding.vk_image_layout) << "\n";
|
||||
#endif
|
||||
}
|
||||
send_pipeline_barriers(command_buffer, false);
|
||||
}
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Debugging tools
|
||||
* \{ */
|
||||
|
||||
std::string VKCommandBuilder::to_string_barrier(const Barrier &barrier)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "src_stage_mask=" << to_string_vk_pipeline_stage_flags(barrier.src_stage_mask)
|
||||
<< ", dst_stage_mask=" << to_string_vk_pipeline_stage_flags(barrier.dst_stage_mask) << "\n";
|
||||
for (const VkBufferMemoryBarrier &buffer_memory_barrier :
|
||||
vk_buffer_memory_barriers_.as_span().slice(barrier.buffer_memory_barriers))
|
||||
{
|
||||
ss << " - src_access_mask=" << to_string_vk_access_flags(buffer_memory_barrier.srcAccessMask)
|
||||
<< ", dst_access_mask=" << to_string_vk_access_flags(buffer_memory_barrier.dstAccessMask)
|
||||
<< ", vk_buffer=" << to_string(buffer_memory_barrier.buffer) << "\n";
|
||||
}
|
||||
|
||||
for (const VkImageMemoryBarrier &image_memory_barrier :
|
||||
vk_image_memory_barriers_.as_span().slice(barrier.image_memory_barriers))
|
||||
{
|
||||
ss << " - src_access_mask=" << to_string_vk_access_flags(image_memory_barrier.srcAccessMask)
|
||||
<< ", dst_access_mask=" << to_string_vk_access_flags(image_memory_barrier.dstAccessMask)
|
||||
<< ", vk_image=" << to_string(image_memory_barrier.image)
|
||||
<< ", old_layout=" << to_string(image_memory_barrier.oldLayout)
|
||||
<< ", new_layout=" << to_string(image_memory_barrier.newLayout)
|
||||
<< ", subresource_range=" << to_string(image_memory_barrier.subresourceRange, 2) << "\n";
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::gpu::render_graph
|
||||
|
||||
@@ -32,58 +32,150 @@ struct LayeredImageBinding {
|
||||
*/
|
||||
class VKCommandBuilder {
|
||||
private:
|
||||
/* Pool of VKBufferMemoryBarriers that can be reused when building barriers */
|
||||
/**
|
||||
* List of all extracted VkBufferMemoryBarriers. These barriers will be referenced by
|
||||
* Barrier::buffer_memory_barriers.
|
||||
*/
|
||||
Vector<VkBufferMemoryBarrier> vk_buffer_memory_barriers_;
|
||||
/**
|
||||
* List of all extracted VkImageMemoryBarriers. These barriers will be referenced by
|
||||
* Barrier::image_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 Barrier {
|
||||
/** Index range into `VKCommandBuilder::vk_buffer_memory_barriers_` */
|
||||
IndexRange buffer_memory_barriers;
|
||||
/** Index range into `VKCommandBuilder::vk_image_memory_barriers_` */
|
||||
IndexRange image_memory_barriers;
|
||||
|
||||
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;
|
||||
|
||||
bool is_empty() const
|
||||
{
|
||||
return buffer_memory_barriers.is_empty() && image_memory_barriers.is_empty();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* An image can consist out of layers. In some cases each layer can get different
|
||||
* VkImageLayouts. To not complicate the global state tracker we track layered
|
||||
* resources only when building the barriers.
|
||||
*/
|
||||
struct LayeredImageTracker {
|
||||
/**
|
||||
* Index of the active debug_group. Points to an element in
|
||||
* `VKRenderGraph.debug_.used_groups`.
|
||||
* Local reference to the active command builder.
|
||||
*
|
||||
* The reference is used to add VkImageMemoryBarrier's to the command builder.
|
||||
*/
|
||||
int64_t active_debug_group_id = -1;
|
||||
/** Current level of debug groups. (number of nested debug groups). */
|
||||
int debug_level = 0;
|
||||
VKCommandBuilder &command_builder;
|
||||
LayeredImageTracker(VKCommandBuilder &command_builder) : command_builder(command_builder) {}
|
||||
|
||||
/**
|
||||
* All layered attachments of the last rendering scope (VKNodeType::BEGIN_RENDERING).
|
||||
*
|
||||
* when binding layer from these images we expect that they aren't used as attachment and can
|
||||
* When binding layer from these images we expect that they aren't used as attachment and can
|
||||
* be transitioned into a different image layout. These image layouts are stored in
|
||||
* `layered_bindings`.
|
||||
*/
|
||||
Set<VkImage> layered_attachments;
|
||||
|
||||
Vector<LayeredImageBinding> layered_bindings;
|
||||
const bool subresource_tracking_enabled() const
|
||||
struct TrackedImage {
|
||||
VkImage vk_image;
|
||||
VkImageLayout vk_image_layout;
|
||||
uint32_t layer;
|
||||
uint32_t layer_count;
|
||||
};
|
||||
Vector<TrackedImage> layered_bindings;
|
||||
|
||||
/**
|
||||
* Update the layered attachments list when beginning a new render scope.
|
||||
*
|
||||
* node_handle should be a handle that points to a VKNodeType::BEGIN_RENDERING.
|
||||
* Any attachments that are layered will be added to the `layered_attachments` list.
|
||||
*/
|
||||
void begin(const VKRenderGraph &render_graph, NodeHandle node_handle);
|
||||
|
||||
/**
|
||||
* Is layered tracking enabled for the given vk_image.
|
||||
*/
|
||||
inline bool contains(VkImage vk_image) const
|
||||
{
|
||||
return !layered_attachments.is_empty();
|
||||
return layered_attachments.contains(vk_image);
|
||||
}
|
||||
} state_;
|
||||
|
||||
/**
|
||||
* Ensure the layout of a layer.
|
||||
*
|
||||
* - `old_layout` should be the expected layout of the full image.
|
||||
*/
|
||||
void update(VkImage vk_image,
|
||||
uint32_t layer,
|
||||
uint32_t layer_count,
|
||||
VkImageLayout old_layout,
|
||||
VkImageLayout new_layout,
|
||||
Barrier &r_barrier);
|
||||
|
||||
/**
|
||||
* End layer tracking.
|
||||
*
|
||||
* All modified layers (layer_tracking_update) will be changed back to the image layout of
|
||||
* the texture (most likely a `VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL`).
|
||||
*
|
||||
* Render suspension/resuming will not work after calling this method.
|
||||
*/
|
||||
void end(Barrier &r_barrier, bool use_local_read);
|
||||
|
||||
/**
|
||||
* Suspend layer tracking
|
||||
*
|
||||
* Temporarily suspend layer tracking. This transits all modified layers back to its original
|
||||
* layout.
|
||||
* NOTE: Only call this method when you the rendering will be resumed, otherwise use
|
||||
* `layer_tracking_end`.
|
||||
*/
|
||||
void suspend(Barrier &r_barrier, bool use_local_read);
|
||||
|
||||
/**
|
||||
* Resume suspended layer tracking.
|
||||
*
|
||||
* Resume suspended layer tracking. This transits all registered layers back to its modified
|
||||
* state.
|
||||
*/
|
||||
void resume(Barrier &r_barrier, bool use_local_read);
|
||||
};
|
||||
|
||||
/**
|
||||
* Index range of the nodes of a group.
|
||||
*
|
||||
* The indexes are to `VKScheduler::result_` that is passed along `Span<NodeHandle> nodes` of
|
||||
* `build_nodes`.
|
||||
*/
|
||||
using GroupNodes = IndexRange;
|
||||
|
||||
/**
|
||||
* Index range into barrier_list_;
|
||||
*/
|
||||
using Barriers = IndexRange;
|
||||
using BarrierIndex = int64_t;
|
||||
|
||||
/** Per group store the indices of the nodes. */
|
||||
Vector<GroupNodes> group_nodes_;
|
||||
/** Barriers that will be recorded just before the commands of a group are recorded. */
|
||||
Vector<Barriers> group_pre_barriers_;
|
||||
/** Barriers that will be recorded after a group is recorded. */
|
||||
Vector<Barriers> group_post_barriers_;
|
||||
/**
|
||||
* Barriers that will be recorded just befor the commands of a specific node are recorded. The
|
||||
* barriers are stored per NodeHandle and in the same order as `Span<NodeHandle> nodes`.
|
||||
*/
|
||||
Vector<Barriers> node_pre_barriers_;
|
||||
|
||||
/** List of all generated barriers. */
|
||||
Vector<Barrier> barrier_list_;
|
||||
|
||||
public:
|
||||
VKCommandBuilder();
|
||||
|
||||
/**
|
||||
* Build the commands of the nodes provided by the `node_handles` parameter. The commands are
|
||||
* recorded into the given `command_buffer`.
|
||||
@@ -102,49 +194,71 @@ class VKCommandBuilder {
|
||||
|
||||
private:
|
||||
/**
|
||||
* Build the commands of the node group provided by the `node_group` parameter. The commands are
|
||||
* recorded into the given `command_buffer`.
|
||||
* Split the node_handles in logical groups.
|
||||
*
|
||||
* build_nodes splits the given node_handles into groups. All synchronization events inside the
|
||||
* group will be pushed to the front or back of this group. This allows us to record resource
|
||||
* usage on node level, perform reordering and then invoke the synchronization events outside
|
||||
* rendering scopes.
|
||||
* A new group is created when the next node is switching from data/compute to graphics and each
|
||||
* data/compute is also put in its own group.
|
||||
*/
|
||||
void build_node_group(VKRenderGraph &render_graph,
|
||||
VKCommandBufferInterface &command_buffer,
|
||||
Span<NodeHandle> node_group,
|
||||
std::optional<NodeHandle> &r_rendering_scope);
|
||||
void groups_init(const VKRenderGraph &render_graph, Span<NodeHandle> node_handles);
|
||||
|
||||
/**
|
||||
* Extract the memory/buffer/image barriers from the command groups and add them to the pre/post
|
||||
* barriers.
|
||||
*
|
||||
* This process is single threaded as resource states change during the extraction process. The
|
||||
* result of this function would allow the sub builders to be built in parallel.
|
||||
*/
|
||||
void groups_extract_barriers(VKRenderGraph &render_graph,
|
||||
Span<NodeHandle> node_handles,
|
||||
bool supports_local_read);
|
||||
|
||||
/**
|
||||
* Record all the commands for all the groups to the command buffer.
|
||||
*/
|
||||
void groups_build_commands(VKRenderGraph &render_graph,
|
||||
VKCommandBufferInterface &command_buffer,
|
||||
Span<NodeHandle> node_handles);
|
||||
|
||||
/**
|
||||
* Build the pipeline barriers that should be recorded before any other commands of the node
|
||||
* group the given node is part of is being recorded.
|
||||
*/
|
||||
void build_pipeline_barriers(VKRenderGraph &render_graph,
|
||||
VKCommandBufferInterface &command_buffer,
|
||||
NodeHandle node_handle,
|
||||
VkPipelineStageFlags pipeline_stage,
|
||||
LayeredImageTracker &layered_tracker,
|
||||
Barrier &r_barrier,
|
||||
bool within_rendering = false);
|
||||
void reset_barriers();
|
||||
void send_pipeline_barriers(VKCommandBufferInterface &command_buffer, bool within_rendering);
|
||||
void reset_barriers(Barrier &r_barrier);
|
||||
void send_pipeline_barriers(VKCommandBufferInterface &command_buffer,
|
||||
const Barrier &barrier,
|
||||
bool within_rendering);
|
||||
|
||||
void add_buffer_barriers(VKRenderGraph &render_graph,
|
||||
NodeHandle node_handle,
|
||||
VkPipelineStageFlags node_stages);
|
||||
VkPipelineStageFlags node_stages,
|
||||
Barrier &r_barrier);
|
||||
void add_buffer_barrier(VkBuffer vk_buffer,
|
||||
Barrier &r_barrier,
|
||||
VkAccessFlags src_access_mask,
|
||||
VkAccessFlags dst_access_mask);
|
||||
void add_buffer_read_barriers(VKRenderGraph &render_graph,
|
||||
NodeHandle node_handle,
|
||||
VkPipelineStageFlags node_stages);
|
||||
VkPipelineStageFlags node_stages,
|
||||
Barrier &r_barrier);
|
||||
void add_buffer_write_barriers(VKRenderGraph &render_graph,
|
||||
NodeHandle node_handle,
|
||||
VkPipelineStageFlags node_stages);
|
||||
VkPipelineStageFlags node_stages,
|
||||
Barrier &r_barrier);
|
||||
|
||||
void add_image_barriers(VKRenderGraph &render_graph,
|
||||
NodeHandle node_handle,
|
||||
VkPipelineStageFlags node_stages,
|
||||
LayeredImageTracker &layered_tracker,
|
||||
Barrier &r_barrier,
|
||||
bool within_rendering);
|
||||
void add_image_barrier(VkImage vk_image,
|
||||
Barrier &r_barrier,
|
||||
VkAccessFlags src_access_mask,
|
||||
VkAccessFlags dst_access_mask,
|
||||
VkImageLayout old_image_layout,
|
||||
@@ -155,12 +269,26 @@ class VKCommandBuilder {
|
||||
void add_image_read_barriers(VKRenderGraph &render_graph,
|
||||
NodeHandle node_handle,
|
||||
VkPipelineStageFlags node_stages,
|
||||
LayeredImageTracker &layered_tracker,
|
||||
Barrier &r_barrier,
|
||||
bool within_rendering);
|
||||
void add_image_write_barriers(VKRenderGraph &render_graph,
|
||||
NodeHandle node_handle,
|
||||
VkPipelineStageFlags node_stages,
|
||||
LayeredImageTracker &layered_tracker,
|
||||
Barrier &r_barrier,
|
||||
bool within_rendering);
|
||||
|
||||
struct DebugGroups {
|
||||
/**
|
||||
* Index of the active debug_group. Points to an element in
|
||||
* `VKRenderGraph.debug_.used_groups`.
|
||||
*/
|
||||
int64_t active_debug_group_id = -1;
|
||||
/** Current level of debug groups. (number of nested debug groups). */
|
||||
int debug_level = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Ensure that the debug group associated with the given node_handle is activated.
|
||||
*
|
||||
@@ -171,12 +299,13 @@ class VKCommandBuilder {
|
||||
*/
|
||||
void activate_debug_group(VKRenderGraph &render_graph,
|
||||
VKCommandBufferInterface &command_buffer,
|
||||
DebugGroups &debug_groups,
|
||||
NodeHandle node_handle);
|
||||
|
||||
/**
|
||||
* Make sure no debugging groups are active anymore.
|
||||
*/
|
||||
void finish_debug_groups(VKCommandBufferInterface &command_buffer);
|
||||
void finish_debug_groups(VKCommandBufferInterface &command_buffer, DebugGroups &debug_groups);
|
||||
|
||||
private:
|
||||
/**
|
||||
@@ -224,6 +353,8 @@ class VKCommandBuilder {
|
||||
void layer_tracking_resume(VKCommandBufferInterface &command_buffer);
|
||||
|
||||
bool node_has_input_attachments(const VKRenderGraph &render_graph, NodeHandle node);
|
||||
|
||||
std::string to_string_barrier(const Barrier &barrier);
|
||||
};
|
||||
|
||||
} // namespace blender::gpu::render_graph
|
||||
|
||||
Reference in New Issue
Block a user