Blender V5.0
vk_scheduler.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <sstream>
10
11#include "vk_render_graph.hh"
12#include "vk_scheduler.hh"
13
14#include "BLI_index_range.hh"
15#include "BLI_set.hh"
16
18
20{
21 result_.clear();
22 for (NodeHandle node_handle : render_graph.nodes_.index_range()) {
23 result_.append(node_handle);
24 }
25 reorder_nodes(render_graph);
26 return result_;
27}
28
29/* -------------------------------------------------------------------- */
32
33void VKScheduler::reorder_nodes(const VKRenderGraph &render_graph)
34{
35 move_initial_transfer_to_start(render_graph);
36 move_transfer_and_dispatch_outside_rendering_scope(render_graph);
37}
38
39std::optional<std::pair<int64_t, int64_t>> VKScheduler::find_rendering_scope(
40 const VKRenderGraph &render_graph, IndexRange search_range) const
41{
42 int64_t rendering_start = -1;
43
44 for (int64_t index : search_range) {
45 NodeHandle node_handle = result_[index];
46 const VKRenderGraphNode &node = render_graph.nodes_[node_handle];
47 if (node.type == VKNodeType::BEGIN_RENDERING) {
48 rendering_start = index;
49 }
50 if (node.type == VKNodeType::END_RENDERING && rendering_start != -1) {
51 return std::pair(rendering_start, index);
52 }
53 }
54 BLI_assert(rendering_start == -1);
55
56 return std::nullopt;
57}
58
60
61/* -------------------------------------------------------------------- */
64
65void VKScheduler::move_initial_transfer_to_start(const VKRenderGraph &render_graph)
66{
67 Vector<NodeHandle> data_transfers;
68 Vector<NodeHandle> other_nodes;
69
70 data_transfers.reserve(result_.size());
71 other_nodes.reserve(result_.size());
72
73 for (const int64_t index : result_.index_range()) {
74 NodeHandle node_handle = result_[index];
75 const VKRenderGraphNode &node = render_graph.nodes_[node_handle];
76 if (ELEM(node.type,
80 {
81 const VKRenderGraphNodeLinks &links = render_graph.links_[node_handle];
82 if (links.inputs[0].resource.stamp == 0 && links.outputs[0].resource.stamp == 0) {
83 data_transfers.append(index);
84 continue;
85 }
86 }
88 const VKRenderGraphNodeLinks &links = render_graph.links_[node_handle];
89 if (links.outputs[0].resource.stamp == 0) {
90 data_transfers.append(index);
91 continue;
92 }
93 }
94
95 other_nodes.append(index);
96 }
97
98 MutableSpan<NodeHandle> store_data_transfers = result_.as_mutable_span().slice(
99 0, data_transfers.size());
100 MutableSpan<NodeHandle> store_other = result_.as_mutable_span().slice(data_transfers.size(),
101 other_nodes.size());
102 store_data_transfers.copy_from(data_transfers);
103 store_other.copy_from(other_nodes);
104}
105
107
108/* -------------------------------------------------------------------- */
111
112void VKScheduler::move_transfer_and_dispatch_outside_rendering_scope(
113 const VKRenderGraph &render_graph)
114{
115 Vector<NodeHandle> pre_rendering_scope;
116 Vector<NodeHandle> rendering_scope;
117 Set<ResourceHandle> used_buffers;
118
119 foreach_rendering_scope(render_graph, [&](int64_t start_index, int64_t end_index) {
120 /* Move end_rendering right after the last graphics node. */
121 for (int index = end_index - 1; index >= start_index; index--) {
122 NodeHandle node_handle = result_[index];
123 const VKRenderGraphNode &node = render_graph.nodes_[node_handle];
124 if (node_type_is_rendering(node.type)) {
125 break;
126 }
127 std::swap(result_[end_index], result_[index]);
128 end_index -= 1;
129 }
130
131 /* Move begin_rendering right before the first graphics node. */
132 for (int index = start_index + 1; index < end_index; index++) {
133 NodeHandle node_handle = result_[index];
134 const VKRenderGraphNode &node = render_graph.nodes_[node_handle];
135 if (node_type_is_rendering(node.type)) {
136 break;
137 }
138 std::swap(result_[start_index], result_[index]);
139 start_index += 1;
140 }
141
142 /* Move buffer update buffer commands to before the rendering scope, unless the buffer is
143 * already being used by a draw command. Images modification could also be moved outside the
144 * rendering scope, but it is more tricky as they could also be attached to the frame-buffer.
145 */
146 pre_rendering_scope.clear();
147 rendering_scope.clear();
148 used_buffers.clear();
149
150 for (int index = start_index + 1; index < end_index; index++) {
151 NodeHandle node_handle = result_[index];
152 const VKRenderGraphNode &node = render_graph.nodes_[node_handle];
153 /* Should we add this node to the rendering scope. This is only done when we need to reorder
154 * nodes. In that case the rendering_scope has already an item and we should add this node to
155 * or the rendering scope or before the rendering scope. Adding nodes before rendering scope
156 * is done in the VKNodeType::UPDATE_BUFFER branch. */
157 bool add_to_rendering_scope = !rendering_scope.is_empty();
158 if (node.type == VKNodeType::UPDATE_BUFFER) {
159 /* Checking the node links to reduce potential locking the resource mutex. */
160 if (!used_buffers.contains(render_graph.links_[node_handle].outputs[0].resource.handle)) {
161 /* Buffer isn't used by this rendering scope so we can safely move it before the
162 * rendering scope begins. */
163 pre_rendering_scope.append(node_handle);
164 add_to_rendering_scope = false;
165 /* When this is the first time we move a node before the rendering we should start
166 * building up the rendering scope as well. This is postponed so we can safe some cycles
167 * when no nodes needs to be moved at all. */
168 if (rendering_scope.is_empty()) {
169 rendering_scope.extend(Span<NodeHandle>(&result_[start_index], index - start_index));
170 }
171 }
172 }
173 if (add_to_rendering_scope) {
174 /* When rendering scope has an item we are rewriting the execution order and need to track
175 * what should be inside the rendering scope. */
176 rendering_scope.append(node_handle);
177 }
178
179 /* Any read/write to buffer resources should be added to used_buffers in order to detect if
180 * it is safe to move a node before the rendering scope. */
181 const VKRenderGraphNodeLinks &links = render_graph.links_[node_handle];
182 for (const VKRenderGraphLink &input : links.inputs) {
183 if (input.is_link_to_buffer()) {
184 used_buffers.add(input.resource.handle);
185 }
186 }
187 for (const VKRenderGraphLink &output : links.outputs) {
188 if (output.is_link_to_buffer()) {
189 used_buffers.add(output.resource.handle);
190 }
191 }
192 }
193
194 /* When pre_rendering_scope has an item we want to rewrite the order.
195 * The number of nodes are not changed, so we can do this inline. */
196 if (!pre_rendering_scope.is_empty()) {
197 MutableSpan<NodeHandle> store_none_rendering = result_.as_mutable_span().slice(
198 start_index, pre_rendering_scope.size());
199 MutableSpan<NodeHandle> store_rendering = result_.as_mutable_span().slice(
200 start_index + pre_rendering_scope.size(), rendering_scope.size());
201 store_none_rendering.copy_from(pre_rendering_scope);
202 store_rendering.copy_from(rendering_scope);
203 start_index += pre_rendering_scope.size();
204 }
205 });
206}
207
209
210/* -------------------------------------------------------------------- */
213
214void VKScheduler::debug_print(const VKRenderGraph &render_graph) const
215{
216 std::stringstream ss;
217 int indent = 0;
218
219 for (int index : result_.index_range()) {
220 const NodeHandle node_handle = result_[index];
221 const VKRenderGraphNode &node = render_graph.nodes_[node_handle];
222 if (node.type == VKNodeType::END_RENDERING) {
223 indent--;
224 }
225 for (int i = 0; i < indent; i++) {
226 ss << " ";
227 }
228 ss << node.type << "\n";
229#if 0
230 render_graph.debug_print(node_handle);
231#endif
232 if (node.type == VKNodeType::BEGIN_RENDERING) {
233 indent++;
234 }
235 }
236 ss << "\n";
237
238 std::cout << ss.str();
239}
240
242
243} // namespace blender::gpu::render_graph
#define BLI_assert(a)
Definition BLI_assert.h:46
#define ELEM(...)
long long int int64_t
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:739
bool contains(const Key &key) const
Definition BLI_set.hh:310
bool add(const Key &key)
Definition BLI_set.hh:248
void clear()
Definition BLI_set.hh:551
int64_t size() const
void append(const T &value)
bool is_empty() const
void reserve(const int64_t min_capacity)
void extend(Span< T > array)
void clear()
Span< NodeHandle > select_nodes(const VKRenderGraph &render_graph)
#define input
#define output
BLI_INLINE bool node_type_is_rendering(VKNodeType node_type)
i
Definition text_draw.cc:230