Files
test/source/blender/gpu/vulkan/render_graph/vk_render_graph.cc
Jeroen Bakker acb205763e Fix: Vulkan: Cycles CPU Synchronization
When using cycles in the viewport there it uses render threads and
workers to update the viewport. All threads can record commands to the
queue and needs to be synchronized. This didn't happen leading to
incorrect renders.

Detected when researching #128608

Pull Request: https://projects.blender.org/blender/blender/pulls/128975
2024-10-14 15:38:22 +02:00

143 lines
4.4 KiB
C++

/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup gpu
*/
#include "vk_render_graph.hh"
namespace blender::gpu::render_graph {
VKRenderGraph::VKRenderGraph(std::unique_ptr<VKCommandBufferInterface> command_buffer,
VKResourceStateTracker &resources)
: command_buffer_(std::move(command_buffer)), resources_(resources)
{
submission_id.reset();
}
void VKRenderGraph::remove_nodes(Span<NodeHandle> node_handles)
{
UNUSED_VARS_NDEBUG(node_handles);
BLI_assert_msg(node_handles.size() == nodes_.size(),
"Currently only supporting removing all nodes. The VKScheduler doesn't walk the "
"nodes, and will use incorrect ordering when not all nodes are removed. This "
"needs to be fixed when implementing a better scheduler.");
links_.clear();
for (VKRenderGraphNode &node : nodes_) {
node.free_data();
}
nodes_.clear();
debug_.node_group_map.clear();
debug_.used_groups.clear();
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Submit graph
* \{ */
void VKRenderGraph::submit_for_present(VkImage vk_swapchain_image)
{
/* Needs to be executed at forehand as `add_node` also locks the mutex. */
VKSynchronizationNode::CreateInfo synchronization = {};
synchronization.vk_image = vk_swapchain_image;
synchronization.vk_image_layout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
synchronization.vk_image_aspect = VK_IMAGE_ASPECT_COLOR_BIT;
add_node<VKSynchronizationNode>(synchronization);
std::scoped_lock lock(resources_.mutex);
Span<NodeHandle> node_handles = scheduler_.select_nodes_for_image(*this, vk_swapchain_image);
command_builder_.build_nodes(*this, *command_buffer_, node_handles);
/* TODO: To improve performance it could be better to return a semaphore. This semaphore can be
* passed in the swapchain to ensure GPU synchronization. This also require a second semaphore to
* pause drawing until the swapchain has completed its drawing phase.
*
* Currently using CPU synchronization for safety. */
command_buffer_->submit_with_cpu_synchronization();
submission_id.next();
remove_nodes(node_handles);
command_buffer_->wait_for_cpu_synchronization();
}
void VKRenderGraph::submit_buffer_for_read(VkBuffer vk_buffer)
{
std::scoped_lock lock(resources_.mutex);
Span<NodeHandle> node_handles = scheduler_.select_nodes_for_buffer(*this, vk_buffer);
command_builder_.build_nodes(*this, *command_buffer_, node_handles);
command_buffer_->submit_with_cpu_synchronization();
submission_id.next();
remove_nodes(node_handles);
command_buffer_->wait_for_cpu_synchronization();
}
void VKRenderGraph::submit()
{
/* Using `VK_NULL_HANDLE` will select the default VkFence of the command buffer. */
submit_synchronization_event(VK_NULL_HANDLE);
wait_synchronization_event(VK_NULL_HANDLE);
}
void VKRenderGraph::submit_synchronization_event(VkFence vk_fence)
{
std::scoped_lock lock(resources_.mutex);
Span<NodeHandle> node_handles = scheduler_.select_nodes(*this);
command_builder_.build_nodes(*this, *command_buffer_, node_handles);
command_buffer_->submit_with_cpu_synchronization(vk_fence);
submission_id.next();
remove_nodes(node_handles);
}
void VKRenderGraph::wait_synchronization_event(VkFence vk_fence)
{
command_buffer_->wait_for_cpu_synchronization(vk_fence);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Debug
* \{ */
void VKRenderGraph::debug_group_begin(const char *name)
{
DebugGroupNameID name_id = debug_.group_names.index_of_or_add(std::string(name));
debug_.group_stack.append(name_id);
debug_.group_used = false;
}
void VKRenderGraph::debug_group_end()
{
debug_.group_stack.pop_last();
debug_.group_used = false;
}
void VKRenderGraph::debug_print(NodeHandle node_handle) const
{
std::ostream &os = std::cout;
os << "NODE:\n";
const VKRenderGraphNode &node = nodes_[node_handle];
os << " type:" << node.type << "\n";
const VKRenderGraphNodeLinks &links = links_[node_handle];
os << " inputs:\n";
for (const VKRenderGraphLink &link : links.inputs) {
os << " ";
link.debug_print(os, resources_);
os << "\n";
}
os << " outputs:\n";
for (const VKRenderGraphLink &link : links.outputs) {
os << " ";
link.debug_print(os, resources_);
os << "\n";
}
}
/** \} */
} // namespace blender::gpu::render_graph