Files
test/source/blender/gpu/vulkan/render_graph/vk_render_graph.cc
Jeroen Bakker 22d352dacc Vulkan: Render graph drawing
This PR adds drawing support to the render graph. It adds support for
draw, indirect draw, indexed draw and indexed indirect draw.

Draw commands can only be executed within a rendering scope. Data
transfer commands and dispatch commands cannot be executed within a
rendering scope. Blender can still send in commands in any order and
the render graph needs to find out the best order to minimize context
switches (rendering/begin/end). This is the responsibility of the
scheduler.

The scheduler will push data transfer and dispatch commands outside the
rendering scope:
- data transfer and dispatch commands at the beginning are done before
  the rendering begin.
- data transfer and dispatch commands at the end are done after the
  rendering end.
- data transfer and dispatches in between draw commands will be pushed
  to the beginning if they are not yet being used.
- for all other data transfer and dispatch commands the rendering is
  suspenderd and will be continued afterwards.

Within a rendering context it is not allowed to perform synchronization
commands. Any synchronization commands inside a rendering scope will be
performed before the rendering scope begins. Nodes are now organized
in groups to simplify the code around this area.

Pull Request: https://projects.blender.org/blender/blender/pulls/123168
2024-06-13 09:37:17 +02:00

111 lines
3.5 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)
{
}
void VKRenderGraph::free_data()
{
command_buffer_.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();
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();
remove_nodes(node_handles);
command_buffer_->wait_for_cpu_synchronization();
}
void VKRenderGraph::submit()
{
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();
remove_nodes(node_handles);
command_buffer_->wait_for_cpu_synchronization();
}
/** \} */
/* -------------------------------------------------------------------- */
/** \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;
}
/** \} */
} // namespace blender::gpu::render_graph