Files
test2/source/blender/gpu/vulkan/render_graph/vk_scheduler.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

198 lines
6.3 KiB
C++

/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup gpu
*/
#include <sstream>
#include "vk_render_graph.hh"
#include "vk_scheduler.hh"
#include "BLI_index_range.hh"
#include "BLI_set.hh"
namespace blender::gpu::render_graph {
Span<NodeHandle> VKScheduler::select_nodes_for_image(const VKRenderGraph &render_graph,
VkImage vk_image)
{
UNUSED_VARS(vk_image);
select_all_nodes(render_graph);
reorder_nodes(render_graph);
return result_;
}
Span<NodeHandle> VKScheduler::select_nodes_for_buffer(const VKRenderGraph &render_graph,
VkBuffer vk_buffer)
{
UNUSED_VARS(vk_buffer);
select_all_nodes(render_graph);
reorder_nodes(render_graph);
return result_;
}
Span<NodeHandle> VKScheduler::select_nodes(const VKRenderGraph &render_graph)
{
select_all_nodes(render_graph);
reorder_nodes(render_graph);
return result_;
}
void VKScheduler::select_all_nodes(const VKRenderGraph &render_graph)
{
/* TODO: This will not work when we extract subgraphs. When subgraphs are removed the order in
* the render graph may not follow the order the nodes were added. */
result_.clear();
for (NodeHandle node_handle : render_graph.nodes_.index_range()) {
result_.append(node_handle);
}
}
/* -------------------------------------------------------------------- */
/** \name Reorder - move data transfer and dispatches outside rendering scope
* \{ */
void VKScheduler::reorder_nodes(const VKRenderGraph &render_graph)
{
move_transfer_and_dispatch_outside_rendering_scope(render_graph);
}
std::optional<std::pair<int64_t, int64_t>> VKScheduler::find_rendering_scope(
const VKRenderGraph &render_graph, IndexRange search_range) const
{
int64_t rendering_start = -1;
for (int64_t index : search_range) {
NodeHandle node_handle = result_[index];
const VKRenderGraphNode &node = render_graph.nodes_[node_handle];
if (node.type == VKNodeType::BEGIN_RENDERING) {
rendering_start = index;
}
if (node.type == VKNodeType::END_RENDERING && rendering_start != -1) {
return std::pair(rendering_start, index);
}
}
BLI_assert(rendering_start == -1);
return std::nullopt;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Reorder - move data transfer and dispatches outside rendering scope
* \{ */
void VKScheduler::move_transfer_and_dispatch_outside_rendering_scope(
const VKRenderGraph &render_graph)
{
Vector<NodeHandle> pre_rendering_handles;
Vector<NodeHandle> rendering_handles;
Set<ResourceHandle> accessed_resources;
foreach_rendering_scope(render_graph, [&](int64_t start_index, int64_t end_index) {
/* Move end_rendering right after the last graphics node. */
for (int index = end_index - 1; index >= start_index; index--) {
NodeHandle node_handle = result_[index];
const VKRenderGraphNode &node = render_graph.nodes_[node_handle];
if (node_type_is_rendering(node.type)) {
break;
}
std::swap(result_[end_index], result_[index]);
end_index -= 1;
}
/* Move begin_rendering right before the first graphics node. */
for (int index = start_index + 1; index < end_index; index++) {
NodeHandle node_handle = result_[index];
const VKRenderGraphNode &node = render_graph.nodes_[node_handle];
if (node_type_is_rendering(node.type)) {
break;
}
std::swap(result_[start_index], result_[index]);
start_index += 1;
}
/* Move all other none graphics commands to before the begin_rendering unless the resource they
* are modifying is already being used by a draw command. Uses temporary allocated vectors to
* reduce the amount and size of copying data. */
pre_rendering_handles.clear();
rendering_handles.clear();
accessed_resources.clear();
for (int index = start_index + 1; index < end_index; index++) {
NodeHandle node_handle = result_[index];
const VKRenderGraphNode &node = render_graph.nodes_[node_handle];
if (pre_rendering_handles.is_empty()) {
if (!node_type_is_rendering(node.type)) {
rendering_handles.extend(&result_[start_index], index - start_index);
pre_rendering_handles.append(node_handle);
}
}
else {
const VKRenderGraphNodeLinks &node_links = render_graph.links_[node_handle];
if (node_type_is_rendering(node.type)) {
rendering_handles.append(node_handle);
for (const VKRenderGraphLink &input : node_links.inputs) {
accessed_resources.add(input.resource.handle);
}
}
else {
bool prepend = true;
for (const VKRenderGraphLink &output : node_links.outputs) {
if (accessed_resources.contains(output.resource.handle)) {
accessed_resources.remove(output.resource.handle);
prepend = false;
}
}
if (prepend) {
pre_rendering_handles.append(node_handle);
}
else {
// This adds a none rendering node into a rendering scope.
// later on the rendering will be suspended when the commands
// for these nodes are build.
rendering_handles.append(node_handle);
}
}
}
}
if (!pre_rendering_handles.is_empty()) {
MutableSpan<NodeHandle> store_none_rendering = result_.as_mutable_span().slice(
start_index, pre_rendering_handles.size());
MutableSpan<NodeHandle> store_rendering = result_.as_mutable_span().slice(
start_index + pre_rendering_handles.size(), rendering_handles.size());
store_none_rendering.copy_from(pre_rendering_handles);
store_rendering.copy_from(rendering_handles);
start_index += pre_rendering_handles.size();
}
});
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Debug
* \{ */
void VKScheduler::debug_print(const VKRenderGraph &render_graph) const
{
std::stringstream ss;
for (int index : result_.index_range()) {
const NodeHandle node_handle = result_[index];
const VKRenderGraphNode &node = render_graph.nodes_[node_handle];
ss << node.type << ", ";
}
ss << "\n";
std::cout << ss.str();
}
/** \} */
} // namespace blender::gpu::render_graph