Blender V5.0
vk_command_builder.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
10#include "vk_backend.hh"
11#include "vk_render_graph.hh"
12#include "vk_to_string.hh"
13
14#include <sstream>
15
17
18/* -------------------------------------------------------------------- */
21
23 VKCommandBufferInterface &command_buffer,
24 Span<NodeHandle> node_handles)
25{
26 groups_init(render_graph, node_handles);
27 groups_extract_barriers(
28 render_graph, node_handles, command_buffer.use_dynamic_rendering_local_read);
29}
30
32 VKCommandBufferInterface &command_buffer,
33 Span<NodeHandle> node_handles)
34{
35 groups_build_commands(render_graph, command_buffer, node_handles);
36}
37
38void VKCommandBuilder::groups_init(const VKRenderGraph &render_graph,
39 Span<NodeHandle> node_handles)
40{
41 group_nodes_.clear();
42 IndexRange nodes_range = node_handles.index_range();
43 while (!nodes_range.is_empty()) {
44 IndexRange node_group = nodes_range.slice(0, 1);
45 NodeHandle node_handle = node_handles[nodes_range.first()];
46 const VKRenderGraphNode &node = render_graph.nodes_[node_handle];
47 while (node_type_is_rendering(node.type) && node_group.size() < nodes_range.size()) {
48 NodeHandle node_handle = node_handles[nodes_range[node_group.size()]];
49 const VKRenderGraphNode &node = render_graph.nodes_[node_handle];
51 break;
52 }
53 node_group = nodes_range.slice(0, node_group.size() + 1);
54 }
55
56 group_nodes_.append(node_group);
57 nodes_range = nodes_range.drop_front(node_group.size());
58 }
59}
60
61void VKCommandBuilder::groups_extract_barriers(VKRenderGraph &render_graph,
62 Span<NodeHandle> node_handles,
63 bool use_local_read)
64{
65 barrier_list_.clear();
66 vk_buffer_memory_barriers_.clear();
67 vk_image_memory_barriers_.clear();
68
69 ImageTracker image_tracker(*this);
70
71 /* Extract barriers. */
72 group_pre_barriers_.clear();
73 group_post_barriers_.clear();
74 node_pre_barriers_.resize(node_handles.size());
75
76 /* Keep track of the post barriers that needs to be added. The pre barriers will be stored
77 * directly in `barrier_list_` but may not mingle with the pre barriers. Most barriers are
78 * group pre barriers. */
79 Vector<Barrier> post_barriers;
80 /* Keep track of the node pre barriers that needs to be added. The pre barriers will be stored
81 * directly in `barrier_list_` but may not mingle with the group barriers. */
82 Vector<Barrier> node_pre_barriers;
83
84 NodeHandle rendering_scope;
85 bool rendering_active = false;
86
87 for (const int64_t group_index : group_nodes_.index_range()) {
88 /* Extract the pre-barriers of this group. */
89 Barriers group_pre_barriers(barrier_list_.size(), 0);
90 const GroupNodes &node_group = group_nodes_[group_index];
91 for (const int64_t group_node_index : node_group) {
92 NodeHandle node_handle = node_handles[group_node_index];
93 VKRenderGraphNode &node = render_graph.nodes_[node_handle];
94 Barrier barrier = {};
95 build_pipeline_barriers(
96 render_graph, node_handle, node.pipeline_stage_get(), image_tracker, barrier);
97 if (!barrier.is_empty()) {
98#if 0
99 std::cout << __func__ << ": node_group=" << group_index
100 << ", node_group_range=" << node_group.first() << "-" << node_group.last()
101 << ", node_handle=" << node_handle << ", node_type=" << node.type
102 << ", debug_group=" << render_graph.full_debug_group(node_handle) << "\n";
103 std::cout << __func__ << ": " << to_string_barrier(barrier);
104#endif
105 barrier_list_.append(barrier);
106 }
107 /* Check for additional barriers when resuming rendering.
108 *
109 * Between suspending rendering and resuming the state/layout of resources can change and
110 * require additional barriers.
111 */
112 if (node.type == VKNodeType::BEGIN_RENDERING) {
113 /* Begin rendering scope. */
114 BLI_assert(!rendering_active);
115 rendering_scope = node_handle;
116 rendering_active = true;
117 image_tracker.begin(render_graph, node_handle);
118 }
119
120 else if (node.type == VKNodeType::END_RENDERING) {
121 /* End rendering scope. */
122 BLI_assert(rendering_active);
123 rendering_scope = 0;
124 rendering_active = false;
125
126 /* Any specific layout changes needs to be reverted, so the global resource state tracker
127 * reflects the correct state. These barriers needs to be added as node post barriers. We
128 * assume that END_RENDERING is always the last node of a group. */
129 Barrier barrier = {};
130 image_tracker.end(barrier, use_local_read);
131 if (!barrier.is_empty()) {
132 post_barriers.append(barrier);
133 }
134 }
135
136 else if (rendering_active && !node_type_is_within_rendering(node.type)) {
137 /* Suspend active rendering scope. */
138 rendering_active = false;
139
140 /* Any specific layout changes needs to be reverted, so the global resource state tracker
141 * reflects the correct state. These barriers needs to be added as node post barriers.
142 */
143 Barrier barrier = {};
144 image_tracker.suspend(barrier, use_local_read);
145 if (!barrier.is_empty()) {
146 post_barriers.append(barrier);
147 }
148 }
149
150 else if (!rendering_active && node_type_is_within_rendering(node.type)) {
151 /* Resume rendering scope. */
152 VKRenderGraphNode &rendering_node = render_graph.nodes_[rendering_scope];
153 Barrier barrier = {};
154 build_pipeline_barriers(render_graph,
155 rendering_scope,
156 rendering_node.pipeline_stage_get(),
157 image_tracker,
158 barrier);
159 if (!barrier.is_empty()) {
160 barrier_list_.append(barrier);
161 }
162
163 /* Resume layered tracking. Each layer that has an override will be transition back to
164 * the layer specific image layout. */
165 barrier = {};
166 image_tracker.resume(barrier, use_local_read);
167 if (!barrier.is_empty()) {
168 barrier_list_.append(barrier);
169 }
170
171 rendering_active = true;
172 }
173
174 /* Extract pre barriers for nodes. */
175 if (use_local_read && node_type_is_within_rendering(node.type) &&
176 node_has_input_attachments(render_graph, node_handle))
177 {
178 Barrier barrier = {};
179 build_pipeline_barriers(
180 render_graph, node_handle, node.pipeline_stage_get(), image_tracker, barrier, true);
181 if (!barrier.is_empty()) {
182 node_pre_barriers.append(barrier);
183 }
184 }
185 }
186 if (rendering_active) {
187 /* Suspend layered image tracker. When active the next group will always be a compute/data
188 * transfer group.
189 *
190 * Any specific layout changes needs to be reverted, so the global resource state tracker
191 * reflects the correct state. These barriers needs to be added as node post barriers.
192 */
193 Barrier barrier = {};
194 image_tracker.suspend(barrier, use_local_read);
195 if (!barrier.is_empty()) {
196 post_barriers.append(barrier);
197 }
198 rendering_active = false;
199 }
200
201 /* Update the group pre and post barriers. Pre barriers are already stored in the
202 * barrier_list_. The post barriers are appended after the pre barriers. */
203 int64_t barrier_list_size = barrier_list_.size();
204 group_pre_barriers_.append(group_pre_barriers.with_new_end(barrier_list_size));
205 barrier_list_.extend(std::move(post_barriers));
206 group_post_barriers_.append(
207 IndexRange::from_begin_end(barrier_list_size, barrier_list_.size()));
208 if (!node_pre_barriers.is_empty()) {
209 barrier_list_size = barrier_list_.size();
210 barrier_list_.extend(std::move(node_pre_barriers));
211 /* Shift all node pre barrier references to the new location in the barrier_list_. */
212 for (const int64_t group_node_index : node_group) {
213 NodeHandle node_handle = node_handles[group_node_index];
214 if (!node_pre_barriers_[node_handle].is_empty()) {
215 node_pre_barriers_[node_handle].from_begin_size(
216 node_pre_barriers_[node_handle].start() + barrier_list_size, 1);
217 }
218 }
219 }
220 }
221
222 BLI_assert(group_pre_barriers_.size() == group_nodes_.size());
223 BLI_assert(group_post_barriers_.size() == group_nodes_.size());
224}
225
226void VKCommandBuilder::groups_build_commands(VKRenderGraph &render_graph,
227 VKCommandBufferInterface &command_buffer,
228 Span<NodeHandle> node_handles)
229{
230 DebugGroups debug_groups = {};
231 VKBoundPipelines active_pipelines = {};
232
233 NodeHandle rendering_scope = 0;
234 bool rendering_active = false;
235
236 for (int64_t group_index : group_nodes_.index_range()) {
237 IndexRange group_nodes = group_nodes_[group_index];
238 Span<NodeHandle> group_node_handles = node_handles.slice(group_nodes);
239
240 /* Record group pre barriers. */
241 for (BarrierIndex barrier_index : group_pre_barriers_[group_index]) {
242 BLI_assert_msg(!rendering_active,
243 "Pre group barriers must be executed outside a rendering scope.");
244 Barrier &barrier = barrier_list_[barrier_index];
245#if 0
246 std::cout << __func__ << ": node_group=" << group_index
247 << ", node_group_range=" << group_node_handles.first() << "-"
248 << group_node_handles.last() << ", pre_barrier=(" << to_string_barrier(barrier)
249 << ")\n";
250#endif
251 send_pipeline_barriers(command_buffer, barrier, false);
252 }
253
254 /* Record group node commands. */
255 for (NodeHandle node_handle : group_node_handles) {
256 VKRenderGraphNode &node = render_graph.nodes_[node_handle];
257
258 if (G.debug & G_DEBUG_GPU) {
259 activate_debug_group(render_graph, command_buffer, debug_groups, node_handle);
260 }
261
262 if (node.type == VKNodeType::BEGIN_RENDERING) {
263 rendering_scope = node_handle;
264 rendering_active = true;
265 }
266
267 else if (node.type == VKNodeType::END_RENDERING) {
268 rendering_active = false;
269 }
270 else if (node_type_is_within_rendering(node.type)) {
271 if (!rendering_active) {
272 /* Restart rendering scope. */
273 VKRenderGraphNode &rendering_node = render_graph.nodes_[rendering_scope];
275 render_graph.storage_.begin_rendering[rendering_node.storage_index]);
276 rendering_node.build_commands(command_buffer, render_graph.storage_, active_pipelines);
277 rendering_active = true;
278 }
279 }
280
281 /* Record group node barriers. (VK_EXT_dynamic_rendering_local_read) */
282 for (BarrierIndex node_pre_barrier_index : node_pre_barriers_[node_handle]) {
283 Barrier &barrier = barrier_list_[node_pre_barrier_index];
284#if 0
285 std::cout << __func__ << ": node_group=" << group_index
286 << ", node_group_range=" << group_node_handles.first() << "-"
287 << group_node_handles.last() << ", node_pre_barrier=(" << to_string_barrier(barrier)
288 << ")\n";
289#endif
290 /* TODO: Barrier should already contain the changes for local read. */
291 send_pipeline_barriers(command_buffer, barrier, true);
292 }
293
294#if 0
295 std::cout << __func__ << ": node_group=" << group_index
296 << ", node_group_range=" << group_node_handles.first() << "-"
297 << group_node_handles.last() << ", node_handle=" << node_handle
298 << ", node_type=" << node.type
299 << ", debug group=" << render_graph.full_debug_group(node_handle) << "\n";
300#endif
301 node.build_commands(command_buffer, render_graph.storage_, active_pipelines);
302 }
303
304 if (rendering_active) {
305 /* Suspend rendering as the next node group will contain data transfer/dispatch commands.
306 */
307 rendering_active = false;
308 command_buffer.end_rendering();
309 }
310
311 /* Record group post barriers. */
312 for (BarrierIndex barrier_index : group_post_barriers_[group_index]) {
313 BLI_assert_msg(!rendering_active,
314 "Post group barriers must be executed outside a rendering scope.");
315 Barrier &barrier = barrier_list_[barrier_index];
316#if 0
317 std::cout << __func__ << ": node_group=" << group_index
318 << ", node_group_range=" << group_node_handles.first() << "-"
319 << group_node_handles.last() << ", post_barrier=(" << to_string_barrier(barrier)
320 << ")\n";
321#endif
322 send_pipeline_barriers(command_buffer, barrier, false);
323 }
324 }
325
326 finish_debug_groups(command_buffer, debug_groups);
327}
328
329bool VKCommandBuilder::node_has_input_attachments(const VKRenderGraph &render_graph,
330 NodeHandle node)
331{
332 const VKRenderGraphNodeLinks &links = render_graph.links_[node];
333 const Vector<VKRenderGraphLink> &inputs = links.inputs;
334 return std::any_of(inputs.begin(), inputs.end(), [](const VKRenderGraphLink &input) {
335 return input.vk_access_flags & VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
336 });
337}
338
339void VKCommandBuilder::activate_debug_group(VKRenderGraph &render_graph,
340 VKCommandBufferInterface &command_buffer,
341 DebugGroups &debug_groups,
342 NodeHandle node_handle)
343{
344 VKRenderGraph::DebugGroupID debug_group = render_graph.debug_.node_group_map[node_handle];
345 if (debug_group == debug_groups.active_debug_group_id) {
346 return;
347 }
348
349 /* Determine the number of pops and pushes that will happen on the debug stack. */
350 int num_ends = 0;
351 int num_begins = 0;
352
353 if (debug_group == -1) {
354 num_ends = debug_groups.debug_level;
355 }
356 else {
357 Vector<VKRenderGraph::DebugGroupNameID> &to_group =
358 render_graph.debug_.used_groups[debug_group];
359 if (debug_groups.active_debug_group_id != -1) {
360 Vector<VKRenderGraph::DebugGroupNameID> &from_group =
361 render_graph.debug_.used_groups[debug_groups.active_debug_group_id];
362
363 num_ends = max_ii(from_group.size() - to_group.size(), 0);
364 int num_checks = min_ii(from_group.size(), to_group.size());
365 for (int index : IndexRange(num_checks)) {
366 if (from_group[index] != to_group[index]) {
367 num_ends += num_checks - index;
368 break;
369 }
370 }
371 }
372
373 num_begins = to_group.size() - (debug_groups.debug_level - num_ends);
374 }
375
376 /* Perform the pops from the debug stack. */
377 for (int index = 0; index < num_ends; index++) {
378 command_buffer.end_debug_utils_label();
379 }
380 debug_groups.debug_level -= num_ends;
381
382 /* Perform the pushes to the debug stack. */
383 if (num_begins > 0) {
384 Vector<VKRenderGraph::DebugGroupNameID> &to_group =
385 render_graph.debug_.used_groups[debug_group];
386 VkDebugUtilsLabelEXT debug_utils_label = {};
387 debug_utils_label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
388 for (int index : IndexRange(debug_groups.debug_level, num_begins)) {
389 const VKRenderGraph::DebugGroup &debug_group = render_graph.debug_.groups[to_group[index]];
390 debug_utils_label.pLabelName = debug_group.name.c_str();
391 copy_v4_v4(debug_utils_label.color, debug_group.color);
392 command_buffer.begin_debug_utils_label(&debug_utils_label);
393 }
394 }
395
396 debug_groups.debug_level += num_begins;
397 debug_groups.active_debug_group_id = debug_group;
398}
399
400void VKCommandBuilder::finish_debug_groups(VKCommandBufferInterface &command_buffer,
401 DebugGroups &debug_groups)
402{
403 for (int i = 0; i < debug_groups.debug_level; i++) {
404 command_buffer.end_debug_utils_label();
405 }
406 debug_groups.debug_level = 0;
407}
408
409void VKCommandBuilder::build_pipeline_barriers(VKRenderGraph &render_graph,
410 NodeHandle node_handle,
411 VkPipelineStageFlags pipeline_stage,
412 ImageTracker &image_tracker,
413 Barrier &r_barrier,
414 bool within_rendering)
415{
416 reset_barriers(r_barrier);
417 add_image_barriers(
418 render_graph, node_handle, pipeline_stage, image_tracker, r_barrier, within_rendering);
419 add_buffer_barriers(render_graph, node_handle, pipeline_stage, r_barrier);
420}
421
423
424/* -------------------------------------------------------------------- */
427
428void VKCommandBuilder::reset_barriers(Barrier &r_barrier)
429{
430 r_barrier.dst_stage_mask = r_barrier.src_stage_mask = VK_PIPELINE_STAGE_NONE;
431}
432
433void VKCommandBuilder::send_pipeline_barriers(VKCommandBufferInterface &command_buffer,
434 const Barrier &barrier,
435 bool within_rendering)
436{
437 if (barrier.is_empty()) {
438 return;
439 }
440
441 /* When no resources have been used, we can start the barrier at the top of the pipeline.
442 * It is not allowed to set it to None. */
443 /* TODO: VK_KHR_synchronization2 allows setting src_stage_mask to NONE. */
444 /* When no resources have been used, we can start the barrier at the top of the pipeline.
445 * It is not allowed to set it to None. */
446 VkPipelineStageFlags src_stage_mask = (barrier.src_stage_mask == VK_PIPELINE_STAGE_NONE) ?
447 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT :
448 VkPipelineStageFlagBits(barrier.src_stage_mask);
449
450 VkPipelineStageFlags dst_stage_mask = barrier.dst_stage_mask;
451 /* TODO: this should be done during barrier extraction making within_rendering obsolete. */
452 if (within_rendering) {
453 /* See: VUID - `vkCmdPipelineBarrier` - `srcStageMask` - 09556
454 * If `vkCmdPipelineBarrier` is called within a render pass instance started with
455 * `vkCmdBeginRendering`, this command must only specify frame-buffer-space stages in
456 * `srcStageMask` and `dstStageMask`. */
457 src_stage_mask = dst_stage_mask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
458 VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
459 VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
460 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
461 }
462
463 Span<VkBufferMemoryBarrier> buffer_barriers = vk_buffer_memory_barriers_.as_span().slice(
464 barrier.buffer_memory_barriers);
465 Span<VkImageMemoryBarrier> image_barriers = vk_image_memory_barriers_.as_span().slice(
466 barrier.image_memory_barriers);
467
468 command_buffer.pipeline_barrier(src_stage_mask,
469 dst_stage_mask,
470 VK_DEPENDENCY_BY_REGION_BIT,
471 0,
472 nullptr,
473 buffer_barriers.size(),
474 buffer_barriers.data(),
475 image_barriers.size(),
476 image_barriers.data());
477}
478
479void VKCommandBuilder::add_buffer_barriers(VKRenderGraph &render_graph,
480 NodeHandle node_handle,
481 VkPipelineStageFlags node_stages,
482 Barrier &r_barrier)
483{
484 r_barrier.buffer_memory_barriers = IndexRange(vk_buffer_memory_barriers_.size(), 0);
485 add_buffer_read_barriers(render_graph, node_handle, node_stages, r_barrier);
486 add_buffer_write_barriers(render_graph, node_handle, node_stages, r_barrier);
487 r_barrier.buffer_memory_barriers = r_barrier.buffer_memory_barriers.with_new_end(
488 vk_buffer_memory_barriers_.size());
489}
490
491void VKCommandBuilder::add_buffer_read_barriers(VKRenderGraph &render_graph,
492 NodeHandle node_handle,
493 VkPipelineStageFlags node_stages,
494 Barrier &r_barrier)
495{
496 for (const VKRenderGraphLink &link : render_graph.links_[node_handle].inputs) {
497 if (!link.is_link_to_buffer()) {
498 continue;
499 }
500 const ResourceWithStamp &versioned_resource = link.resource;
501 VKResourceStateTracker::Resource &resource = render_graph.resources_.resources_.lookup(
502 versioned_resource.handle);
503 VKResourceBarrierState &resource_state = resource.barrier_state;
504 const bool is_first_read = resource_state.is_new_stamp();
505 if (!is_first_read &&
506 (resource_state.vk_access & link.vk_access_flags) == link.vk_access_flags &&
507 (resource_state.vk_pipeline_stages & node_stages) == node_stages)
508 {
509 /* Has already been covered in a previous call no need to add this one. */
510 continue;
511 }
512
513 const VkAccessFlags wait_access = resource_state.vk_access;
514
515 r_barrier.src_stage_mask |= resource_state.vk_pipeline_stages;
516 r_barrier.dst_stage_mask |= node_stages;
517
518 if (is_first_read) {
519 resource_state.vk_access = link.vk_access_flags;
520 resource_state.vk_pipeline_stages = node_stages;
521 }
522 else {
523 resource_state.vk_access |= link.vk_access_flags;
524 resource_state.vk_pipeline_stages |= node_stages;
525 }
526
527 add_buffer_barrier(resource.buffer.vk_buffer, r_barrier, wait_access, link.vk_access_flags);
528 }
529}
530
531void VKCommandBuilder::add_buffer_write_barriers(VKRenderGraph &render_graph,
532 NodeHandle node_handle,
533 VkPipelineStageFlags node_stages,
534 Barrier &r_barrier)
535{
536 for (const VKRenderGraphLink link : render_graph.links_[node_handle].outputs) {
537 if (!link.is_link_to_buffer()) {
538 continue;
539 }
540 const ResourceWithStamp &versioned_resource = link.resource;
541 VKResourceStateTracker::Resource &resource = render_graph.resources_.resources_.lookup(
542 versioned_resource.handle);
543 VKResourceBarrierState &resource_state = resource.barrier_state;
544 const VkAccessFlags wait_access = resource_state.vk_access;
545
546 r_barrier.src_stage_mask |= resource_state.vk_pipeline_stages;
547 r_barrier.dst_stage_mask |= node_stages;
548
549 resource_state.vk_access = link.vk_access_flags;
550 resource_state.vk_pipeline_stages = node_stages;
551
552 if (wait_access != VK_ACCESS_NONE) {
553 add_buffer_barrier(resource.buffer.vk_buffer, r_barrier, wait_access, link.vk_access_flags);
554 }
555 }
556}
557
558void VKCommandBuilder::add_buffer_barrier(VkBuffer vk_buffer,
559 Barrier &r_barrier,
560 VkAccessFlags src_access_mask,
561 VkAccessFlags dst_access_mask)
562{
563 for (VkBufferMemoryBarrier &vk_buffer_memory_barrier :
564 vk_buffer_memory_barriers_.as_mutable_span().drop_front(
565 r_barrier.buffer_memory_barriers.start()))
566 {
567 if (vk_buffer_memory_barrier.buffer == vk_buffer) {
568 /* When registering read/write buffers, it can be that the node internally requires
569 * read/write. In this case we adjust the dstAccessMask of the read barrier. */
570 if ((vk_buffer_memory_barrier.dstAccessMask & src_access_mask) == src_access_mask) {
571 vk_buffer_memory_barrier.dstAccessMask |= dst_access_mask;
572 return;
573 }
574 /* When re-registering resources we can skip if access mask already contain all the flags.
575 */
576 if ((vk_buffer_memory_barrier.dstAccessMask & dst_access_mask) == dst_access_mask &&
577 (vk_buffer_memory_barrier.srcAccessMask & src_access_mask) == src_access_mask)
578 {
579 return;
580 }
581 }
582 }
583
584 vk_buffer_memory_barriers_.append({VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
585 nullptr,
586 src_access_mask,
587 dst_access_mask,
588 VK_QUEUE_FAMILY_IGNORED,
589 VK_QUEUE_FAMILY_IGNORED,
590 vk_buffer,
591 0,
592 VK_WHOLE_SIZE});
593}
594
595void VKCommandBuilder::add_image_barriers(VKRenderGraph &render_graph,
596 NodeHandle node_handle,
597 VkPipelineStageFlags node_stages,
598 ImageTracker &image_tracker,
599 Barrier &r_barrier,
600 bool within_rendering)
601{
602 r_barrier.image_memory_barriers = IndexRange(vk_image_memory_barriers_.size(), 0);
603 add_image_read_barriers(
604 render_graph, node_handle, node_stages, image_tracker, r_barrier, within_rendering);
605 add_image_write_barriers(
606 render_graph, node_handle, node_stages, image_tracker, r_barrier, within_rendering);
607 r_barrier.image_memory_barriers = r_barrier.image_memory_barriers.with_new_end(
608 vk_image_memory_barriers_.size());
609}
610
611void VKCommandBuilder::add_image_read_barriers(VKRenderGraph &render_graph,
612 NodeHandle node_handle,
613 VkPipelineStageFlags node_stages,
614 ImageTracker &image_tracker,
615 Barrier &r_barrier,
616 bool within_rendering)
617{
618 for (const VKRenderGraphLink &link : render_graph.links_[node_handle].inputs) {
619 if (link.is_link_to_buffer()) {
620 continue;
621 }
622 const ResourceWithStamp &versioned_resource = link.resource;
623 VKResourceStateTracker::Resource &resource = render_graph.resources_.resources_.lookup(
624 versioned_resource.handle);
625 VKResourceBarrierState &resource_state = resource.barrier_state;
626 const bool is_first_read = resource_state.is_new_stamp();
627 if ((!is_first_read) &&
628 (resource_state.vk_access & link.vk_access_flags) == link.vk_access_flags &&
629 (resource_state.vk_pipeline_stages & node_stages) == node_stages &&
630 resource_state.image_layout == link.vk_image_layout)
631 {
632 /* Has already been covered in previous barrier no need to add this one. */
633 continue;
634 }
635 if (within_rendering && link.vk_image_layout != VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR) {
636 /* Allow only local read barriers inside rendering scope */
637 continue;
638 }
639
640 if (resource_state.image_layout != link.vk_image_layout &&
641 image_tracker.contains(resource.image.vk_image))
642 {
643 image_tracker.update(resource.image.vk_image,
644 link.subimage,
645 resource_state.image_layout,
646 link.vk_image_layout,
647 r_barrier);
648 continue;
649 }
650
651 VkAccessFlags wait_access = resource_state.vk_access;
652
653 r_barrier.src_stage_mask |= resource_state.vk_pipeline_stages;
654 r_barrier.dst_stage_mask |= node_stages;
655
656 if (is_first_read) {
657 resource_state.vk_access = link.vk_access_flags;
658 resource_state.vk_pipeline_stages = node_stages;
659 }
660 else {
661 resource_state.vk_access |= link.vk_access_flags;
662 resource_state.vk_pipeline_stages |= node_stages;
663 }
664
665 add_image_barrier(resource.image.vk_image,
666 r_barrier,
667 wait_access,
668 link.vk_access_flags,
669 resource_state.image_layout,
670 link.vk_image_layout,
671 link.vk_image_aspect,
672 {});
673 resource_state.image_layout = link.vk_image_layout;
674 }
675}
676
677void VKCommandBuilder::add_image_write_barriers(VKRenderGraph &render_graph,
678 NodeHandle node_handle,
679 VkPipelineStageFlags node_stages,
680 ImageTracker &image_tracker,
681 Barrier &r_barrier,
682 bool within_rendering)
683{
684 for (const VKRenderGraphLink link : render_graph.links_[node_handle].outputs) {
685 if (link.is_link_to_buffer()) {
686 continue;
687 }
688 const ResourceWithStamp &versioned_resource = link.resource;
689 VKResourceStateTracker::Resource &resource = render_graph.resources_.resources_.lookup(
690 versioned_resource.handle);
691 VKResourceBarrierState &resource_state = resource.barrier_state;
692 const VkAccessFlags wait_access = resource_state.vk_access;
693 if (within_rendering && link.vk_image_layout != VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR) {
694 /* Allow only local read barriers inside rendering scope */
695 continue;
696 }
697 if (image_tracker.contains(resource.image.vk_image) &&
698 resource_state.image_layout != link.vk_image_layout)
699 {
700 image_tracker.update(resource.image.vk_image,
701 link.subimage,
702 resource_state.image_layout,
703 link.vk_image_layout,
704 r_barrier);
705
706 continue;
707 }
708
709 r_barrier.src_stage_mask |= resource_state.vk_pipeline_stages;
710 r_barrier.dst_stage_mask |= node_stages;
711
712 resource_state.vk_access = link.vk_access_flags;
713 resource_state.vk_pipeline_stages = node_stages;
714
715 if (wait_access != VK_ACCESS_NONE || link.vk_image_layout != resource_state.image_layout) {
716 add_image_barrier(resource.image.vk_image,
717 r_barrier,
718 wait_access,
719 link.vk_access_flags,
720 resource_state.image_layout,
721 link.vk_image_layout,
722 link.vk_image_aspect,
723 {});
724 resource_state.image_layout = link.vk_image_layout;
725 }
726 }
727}
728
729void VKCommandBuilder::add_image_barrier(VkImage vk_image,
730 Barrier &r_barrier,
731 VkAccessFlags src_access_mask,
732 VkAccessFlags dst_access_mask,
733 VkImageLayout old_layout,
734 VkImageLayout new_layout,
735 VkImageAspectFlags aspect_mask,
736 const VKSubImageRange &subimage)
737{
738 BLI_assert(aspect_mask != VK_IMAGE_ASPECT_NONE);
739 for (VkImageMemoryBarrier &vk_image_memory_barrier :
740 vk_image_memory_barriers_.as_mutable_span().drop_front(
741 r_barrier.image_memory_barriers.start()))
742 {
743 if (vk_image_memory_barrier.image == vk_image) {
744 /* When registering read/write buffers, it can be that the node internally requires
745 * read/write. In this case we adjust the dstAccessMask of the read barrier. An example is
746 * EEVEE update HIZ compute shader and shadow tagging. */
747 if ((vk_image_memory_barrier.dstAccessMask & src_access_mask) == src_access_mask) {
748 vk_image_memory_barrier.dstAccessMask |= dst_access_mask;
749 return;
750 }
751 /* When re-registering resources we can skip if access mask already contain all the flags.
752 */
753 if ((vk_image_memory_barrier.dstAccessMask & dst_access_mask) == dst_access_mask &&
754 (vk_image_memory_barrier.srcAccessMask & src_access_mask) == src_access_mask &&
755 old_layout == new_layout)
756 {
757 return;
758 }
759 }
760 }
761
762 vk_image_memory_barriers_.append({VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
763 nullptr,
764 src_access_mask,
765 dst_access_mask,
766 old_layout,
767 new_layout,
768 VK_QUEUE_FAMILY_IGNORED,
769 VK_QUEUE_FAMILY_IGNORED,
770 vk_image,
771 {aspect_mask,
772 subimage.mipmap_level,
773 subimage.mipmap_count,
774 subimage.layer_base,
775 subimage.layer_count}});
776}
777
779
780/* -------------------------------------------------------------------- */
783
784void VKCommandBuilder::ImageTracker::begin(const VKRenderGraph &render_graph,
785 NodeHandle node_handle)
786{
787 BLI_assert(render_graph.nodes_[node_handle].type == VKNodeType::BEGIN_RENDERING);
788 tracked_attachments.clear();
789 changes.clear();
790
791 const VKRenderGraphNodeLinks &links = render_graph.links_[node_handle];
792 for (const VKRenderGraphLink &link : links.outputs) {
793 VKResourceStateTracker::Resource &resource = render_graph.resources_.resources_.lookup(
794 link.resource.handle);
795 if (resource.use_subresource_tracking()) {
796 tracked_attachments.add(resource.image.vk_image);
797 }
798 }
799}
800
801void VKCommandBuilder::ImageTracker::update(VkImage vk_image,
802 const VKSubImageRange &subimage,
803 VkImageLayout old_layout,
804 VkImageLayout new_layout,
805 Barrier &r_barrier)
806{
807 for (const SubImageChange &change : changes) {
808 if (change.vk_image == vk_image && ((subimage.layer_count != VK_REMAINING_ARRAY_LAYERS &&
809 change.subimage.layer_base == subimage.layer_base) ||
810 (subimage.mipmap_count != VK_REMAINING_MIP_LEVELS &&
811 change.subimage.mipmap_level != subimage.mipmap_level)))
812 {
814 change.vk_image_layout == new_layout,
815 "We don't support more that one change of the same subimage multiple times during a "
816 "rendering scope.");
817 /* Early exit as layer is in correct layout. This is a normal case as we expect multiple
818 * draw commands to take place during a rendering scope with the same layer access. */
819 return;
820 }
821 }
822
823 changes.append({vk_image, new_layout, subimage});
824
825 /* We should be able to do better. BOTTOM/TOP is really a worst case barrier. */
826 r_barrier.src_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
827 r_barrier.dst_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
828 command_builder.add_image_barrier(vk_image,
829 r_barrier,
830 VK_ACCESS_TRANSFER_WRITE_BIT,
831 VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT |
832 VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
833 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
834 VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
835 old_layout,
836 new_layout,
837 VK_IMAGE_ASPECT_COLOR_BIT,
838 subimage);
839}
840
841void VKCommandBuilder::ImageTracker::end(Barrier &r_barrier, bool use_local_read)
842{
843 suspend(r_barrier, use_local_read);
844 tracked_attachments.clear();
845 changes.clear();
846}
847
848void VKCommandBuilder::ImageTracker::suspend(Barrier &r_barrier, bool use_local_read)
849
850{
851 if (changes.is_empty()) {
852 return;
853 }
854
855 command_builder.reset_barriers(r_barrier);
856 /* We should be able to do better. BOTTOM/TOP is really a worst case barrier. */
857 r_barrier.src_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
858 r_barrier.dst_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
859 int64_t start_index = command_builder.vk_image_memory_barriers_.size();
860 r_barrier.image_memory_barriers = IndexRange::from_begin_size(start_index, 0);
861 for (const SubImageChange &change : changes) {
862 command_builder.add_image_barrier(
863 change.vk_image,
864 r_barrier,
865 VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT |
866 VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
867 VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
868 VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT |
869 VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
870 VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
871 change.vk_image_layout,
872 use_local_read ? VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR :
873 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
874 VK_IMAGE_ASPECT_COLOR_BIT,
875 change.subimage);
876 r_barrier.image_memory_barriers = r_barrier.image_memory_barriers.with_new_end(
877 command_builder.vk_image_memory_barriers_.size());
878
879#if 0
880 std::cout << __func__ << ": transition layout image=" << binding.vk_image
881 << ", layer=" << binding.layer << ", count=" << binding.layer_count
882 << ", from_layout=" << to_string(binding.vk_image_layout)
883 << ", to_layout=" << to_string(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) << "\n";
884#endif
885 }
886}
887
888void VKCommandBuilder::ImageTracker::resume(Barrier &r_barrier, bool use_local_read)
889{
890 if (changes.is_empty()) {
891 return;
892 }
893
894 command_builder.reset_barriers(r_barrier);
895 /* We should be able to do better. BOTTOM/TOP is really a worst case barrier. */
896 r_barrier.src_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
897 r_barrier.dst_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
898 int64_t start_index = command_builder.vk_image_memory_barriers_.size();
899 r_barrier.image_memory_barriers = IndexRange::from_begin_size(start_index, 0);
900
901 for (const SubImageChange &change : changes) {
902 command_builder.add_image_barrier(
903 change.vk_image,
904 r_barrier,
905 VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT |
906 VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
907 VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
908 VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT |
909 VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
910 VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
911 use_local_read ? VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR :
912 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
913 change.vk_image_layout,
914 VK_IMAGE_ASPECT_COLOR_BIT,
915 change.subimage);
916#if 0
917 std::cout << __func__ << ": transition layout image=" << binding.vk_image
918 << ", layer=" << binding.layer << ", count=" << binding.layer_count
919 << ", from_layout=" << to_string(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)
920 << ", to_layout=" << to_string(binding.vk_image_layout) << "\n";
921#endif
922 }
923}
925
926/* -------------------------------------------------------------------- */
929
930std::string VKCommandBuilder::to_string_barrier(const Barrier &barrier)
931{
932 std::stringstream ss;
933 ss << "src_stage_mask=" << to_string_vk_pipeline_stage_flags(barrier.src_stage_mask)
934 << ", dst_stage_mask=" << to_string_vk_pipeline_stage_flags(barrier.dst_stage_mask) << "\n";
935 for (const VkBufferMemoryBarrier &buffer_memory_barrier :
936 vk_buffer_memory_barriers_.as_span().slice(barrier.buffer_memory_barriers))
937 {
938 ss << " - src_access_mask=" << to_string_vk_access_flags(buffer_memory_barrier.srcAccessMask)
939 << ", dst_access_mask=" << to_string_vk_access_flags(buffer_memory_barrier.dstAccessMask)
940 << ", vk_buffer=" << to_string(buffer_memory_barrier.buffer) << "\n";
941 }
942
943 for (const VkImageMemoryBarrier &image_memory_barrier :
944 vk_image_memory_barriers_.as_span().slice(barrier.image_memory_barriers))
945 {
946 ss << " - src_access_mask=" << to_string_vk_access_flags(image_memory_barrier.srcAccessMask)
947 << ", dst_access_mask=" << to_string_vk_access_flags(image_memory_barrier.dstAccessMask)
948 << ", vk_image=" << to_string(image_memory_barrier.image)
949 << ", old_layout=" << to_string(image_memory_barrier.oldLayout)
950 << ", new_layout=" << to_string(image_memory_barrier.newLayout)
951 << ", subresource_range=" << to_string(image_memory_barrier.subresourceRange, 2) << "\n";
952 }
953
954 return ss.str();
955}
956
958
959} // namespace blender::gpu::render_graph
@ G_DEBUG_GPU
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
MINLINE int min_ii(int a, int b)
MINLINE int max_ii(int a, int b)
MINLINE void copy_v4_v4(float r[4], const float a[4])
long long int int64_t
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:137
constexpr const T * data() const
Definition BLI_span.hh:215
constexpr const T & first() const
Definition BLI_span.hh:315
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr const T & last(const int64_t n=0) const
Definition BLI_span.hh:325
int64_t size() const
void append(const T &value)
bool is_empty() const
constexpr int64_t first() const
constexpr int64_t size() const
constexpr bool is_empty() const
static constexpr IndexRange from_begin_end(const int64_t begin, const int64_t end)
static constexpr IndexRange from_begin_size(const int64_t begin, const int64_t size)
constexpr IndexRange slice(int64_t start, int64_t size) const
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
static void reconfigure_for_restart(VKBeginRenderingData &begin_rendering_data)
void record_commands(VKRenderGraph &render_graph, VKCommandBufferInterface &command_buffer, Span< NodeHandle > node_handles)
void build_nodes(VKRenderGraph &render_graph, VKCommandBufferInterface &command_buffer, Span< NodeHandle > node_handles)
#define resource
#define input
#define G(x, y, z)
BLI_INLINE bool node_type_is_rendering(VKNodeType node_type)
BLI_INLINE bool node_type_is_within_rendering(VKNodeType node_type)
const char * to_string(ShaderStage stage)
Definition mtl_shader.mm:51
std::string to_string_vk_pipeline_stage_flags(const VkPipelineStageFlags vk_pipeline_stage_flags)
std::string to_string_vk_access_flags(const VkAccessFlags vk_access_flags)
static blender::bke::bNodeSocketTemplate inputs[]
i
Definition text_draw.cc:230