Blender V4.5
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 LayeredImageTracker layered_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(), layered_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 layered_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 layered_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 layered_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 layered_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 layered_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(), layered_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 layered_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 if (command_buffer.use_dynamic_rendering) {
309 command_buffer.end_rendering();
310 }
311 else {
312 command_buffer.end_render_pass();
313 }
314 }
315
316 /* Record group post barriers. */
317 for (BarrierIndex barrier_index : group_post_barriers_[group_index]) {
318 BLI_assert_msg(!rendering_active,
319 "Post group barriers must be executed outside a rendering scope.");
320 Barrier &barrier = barrier_list_[barrier_index];
321#if 0
322 std::cout << __func__ << ": node_group=" << group_index
323 << ", node_group_range=" << group_node_handles.first() << "-"
324 << group_node_handles.last() << ", post_barrier=(" << to_string_barrier(barrier)
325 << ")\n";
326#endif
327 send_pipeline_barriers(command_buffer, barrier, false);
328 }
329 }
330
331 finish_debug_groups(command_buffer, debug_groups);
332}
333
334bool VKCommandBuilder::node_has_input_attachments(const VKRenderGraph &render_graph,
335 NodeHandle node)
336{
337 const VKRenderGraphNodeLinks &links = render_graph.links_[node];
338 const Vector<VKRenderGraphLink> &inputs = links.inputs;
339 return std::any_of(inputs.begin(), inputs.end(), [](const VKRenderGraphLink &input) {
340 return input.vk_access_flags & VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
341 });
342}
343
344void VKCommandBuilder::activate_debug_group(VKRenderGraph &render_graph,
345 VKCommandBufferInterface &command_buffer,
346 DebugGroups &debug_groups,
347 NodeHandle node_handle)
348{
349 VKRenderGraph::DebugGroupID debug_group = render_graph.debug_.node_group_map[node_handle];
350 if (debug_group == debug_groups.active_debug_group_id) {
351 return;
352 }
353
354 /* Determine the number of pops and pushes that will happen on the debug stack. */
355 int num_ends = 0;
356 int num_begins = 0;
357
358 if (debug_group == -1) {
359 num_ends = debug_groups.debug_level;
360 }
361 else {
363 render_graph.debug_.used_groups[debug_group];
364 if (debug_groups.active_debug_group_id != -1) {
366 render_graph.debug_.used_groups[debug_groups.active_debug_group_id];
367
368 num_ends = max_ii(from_group.size() - to_group.size(), 0);
369 int num_checks = min_ii(from_group.size(), to_group.size());
370 for (int index : IndexRange(num_checks)) {
371 if (from_group[index] != to_group[index]) {
372 num_ends += num_checks - index;
373 break;
374 }
375 }
376 }
377
378 num_begins = to_group.size() - (debug_groups.debug_level - num_ends);
379 }
380
381 /* Perform the pops from the debug stack. */
382 for (int index = 0; index < num_ends; index++) {
383 command_buffer.end_debug_utils_label();
384 }
385 debug_groups.debug_level -= num_ends;
386
387 /* Perform the pushes to the debug stack. */
388 if (num_begins > 0) {
390 render_graph.debug_.used_groups[debug_group];
391 VkDebugUtilsLabelEXT debug_utils_label = {};
392 debug_utils_label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
393 for (int index : IndexRange(debug_groups.debug_level, num_begins)) {
394 const VKRenderGraph::DebugGroup &debug_group = render_graph.debug_.groups[to_group[index]];
395 debug_utils_label.pLabelName = debug_group.name.c_str();
396 copy_v4_v4(debug_utils_label.color, debug_group.color);
397 command_buffer.begin_debug_utils_label(&debug_utils_label);
398 }
399 }
400
401 debug_groups.debug_level += num_begins;
402 debug_groups.active_debug_group_id = debug_group;
403}
404
405void VKCommandBuilder::finish_debug_groups(VKCommandBufferInterface &command_buffer,
406 DebugGroups &debug_groups)
407{
408 for (int i = 0; i < debug_groups.debug_level; i++) {
409 command_buffer.end_debug_utils_label();
410 }
411 debug_groups.debug_level = 0;
412}
413
414void VKCommandBuilder::build_pipeline_barriers(VKRenderGraph &render_graph,
415 NodeHandle node_handle,
416 VkPipelineStageFlags pipeline_stage,
417 LayeredImageTracker &layered_tracker,
418 Barrier &r_barrier,
419 bool within_rendering)
420{
421 reset_barriers(r_barrier);
422 add_image_barriers(
423 render_graph, node_handle, pipeline_stage, layered_tracker, r_barrier, within_rendering);
424 add_buffer_barriers(render_graph, node_handle, pipeline_stage, r_barrier);
425}
426
428
429/* -------------------------------------------------------------------- */
432
433void VKCommandBuilder::reset_barriers(Barrier &r_barrier)
434{
435 r_barrier.dst_stage_mask = r_barrier.src_stage_mask = VK_PIPELINE_STAGE_NONE;
436}
437
438void VKCommandBuilder::send_pipeline_barriers(VKCommandBufferInterface &command_buffer,
439 const Barrier &barrier,
440 bool within_rendering)
441{
442 if (barrier.is_empty()) {
443 return;
444 }
445
446 /* When no resources have been used, we can start the barrier at the top of the pipeline.
447 * It is not allowed to set it to None. */
448 /* TODO: VK_KHR_synchronization2 allows setting src_stage_mask to NONE. */
449 /* When no resources have been used, we can start the barrier at the top of the pipeline.
450 * It is not allowed to set it to None. */
451 VkPipelineStageFlags src_stage_mask = (barrier.src_stage_mask == VK_PIPELINE_STAGE_NONE) ?
452 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT :
453 VkPipelineStageFlagBits(barrier.src_stage_mask);
454
455 VkPipelineStageFlags dst_stage_mask = barrier.dst_stage_mask;
456 /* TODO: this should be done during barrier extraction making within_rendering obsolete. */
457 if (within_rendering) {
458 /* See: VUID - `vkCmdPipelineBarrier` - `srcStageMask` - 09556
459 * If `vkCmdPipelineBarrier` is called within a render pass instance started with
460 * `vkCmdBeginRendering`, this command must only specify frame-buffer-space stages in
461 * `srcStageMask` and `dstStageMask`. */
462 src_stage_mask = dst_stage_mask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
463 VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
464 VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
465 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
466 }
467
468 Span<VkBufferMemoryBarrier> buffer_barriers = vk_buffer_memory_barriers_.as_span().slice(
469 barrier.buffer_memory_barriers);
470 Span<VkImageMemoryBarrier> image_barriers = vk_image_memory_barriers_.as_span().slice(
471 barrier.image_memory_barriers);
472
473 command_buffer.pipeline_barrier(src_stage_mask,
474 dst_stage_mask,
475 VK_DEPENDENCY_BY_REGION_BIT,
476 0,
477 nullptr,
478 buffer_barriers.size(),
479 buffer_barriers.data(),
480 image_barriers.size(),
481 image_barriers.data());
482}
483
484void VKCommandBuilder::add_buffer_barriers(VKRenderGraph &render_graph,
485 NodeHandle node_handle,
486 VkPipelineStageFlags node_stages,
487 Barrier &r_barrier)
488{
489 r_barrier.buffer_memory_barriers = IndexRange(vk_buffer_memory_barriers_.size(), 0);
490 add_buffer_read_barriers(render_graph, node_handle, node_stages, r_barrier);
491 add_buffer_write_barriers(render_graph, node_handle, node_stages, r_barrier);
492 r_barrier.buffer_memory_barriers = r_barrier.buffer_memory_barriers.with_new_end(
493 vk_buffer_memory_barriers_.size());
494}
495
496void VKCommandBuilder::add_buffer_read_barriers(VKRenderGraph &render_graph,
497 NodeHandle node_handle,
498 VkPipelineStageFlags node_stages,
499 Barrier &r_barrier)
500{
501 for (const VKRenderGraphLink &link : render_graph.links_[node_handle].inputs) {
502 if (!link.is_link_to_buffer()) {
503 continue;
504 }
505 const ResourceWithStamp &versioned_resource = link.resource;
506 VKResourceStateTracker::Resource &resource = render_graph.resources_.resources_.lookup(
507 versioned_resource.handle);
508 VKResourceBarrierState &resource_state = resource.barrier_state;
509 const bool is_first_read = resource_state.is_new_stamp();
510 if (!is_first_read &&
511 (resource_state.vk_access & link.vk_access_flags) == link.vk_access_flags &&
512 (resource_state.vk_pipeline_stages & node_stages) == node_stages)
513 {
514 /* Has already been covered in a previous call no need to add this one. */
515 continue;
516 }
517
518 const VkAccessFlags wait_access = resource_state.vk_access;
519
520 r_barrier.src_stage_mask |= resource_state.vk_pipeline_stages;
521 r_barrier.dst_stage_mask |= node_stages;
522
523 if (is_first_read) {
524 resource_state.vk_access = link.vk_access_flags;
525 resource_state.vk_pipeline_stages = node_stages;
526 }
527 else {
528 resource_state.vk_access |= link.vk_access_flags;
529 resource_state.vk_pipeline_stages |= node_stages;
530 }
531
532 add_buffer_barrier(resource.buffer.vk_buffer, r_barrier, wait_access, link.vk_access_flags);
533 }
534}
535
536void VKCommandBuilder::add_buffer_write_barriers(VKRenderGraph &render_graph,
537 NodeHandle node_handle,
538 VkPipelineStageFlags node_stages,
539 Barrier &r_barrier)
540{
541 for (const VKRenderGraphLink link : render_graph.links_[node_handle].outputs) {
542 if (!link.is_link_to_buffer()) {
543 continue;
544 }
545 const ResourceWithStamp &versioned_resource = link.resource;
546 VKResourceStateTracker::Resource &resource = render_graph.resources_.resources_.lookup(
547 versioned_resource.handle);
548 VKResourceBarrierState &resource_state = resource.barrier_state;
549 const VkAccessFlags wait_access = resource_state.vk_access;
550
551 r_barrier.src_stage_mask |= resource_state.vk_pipeline_stages;
552 r_barrier.dst_stage_mask |= node_stages;
553
554 resource_state.vk_access = link.vk_access_flags;
555 resource_state.vk_pipeline_stages = node_stages;
556
557 if (wait_access != VK_ACCESS_NONE) {
558 add_buffer_barrier(resource.buffer.vk_buffer, r_barrier, wait_access, link.vk_access_flags);
559 }
560 }
561}
562
563void VKCommandBuilder::add_buffer_barrier(VkBuffer vk_buffer,
564 Barrier &r_barrier,
565 VkAccessFlags src_access_mask,
566 VkAccessFlags dst_access_mask)
567{
568 for (VkBufferMemoryBarrier &vk_buffer_memory_barrier :
569 vk_buffer_memory_barriers_.as_mutable_span().drop_front(
570 r_barrier.buffer_memory_barriers.start()))
571 {
572 if (vk_buffer_memory_barrier.buffer == vk_buffer) {
573 /* When registering read/write buffers, it can be that the node internally requires
574 * read/write. In this case we adjust the dstAccessMask of the read barrier. */
575 if ((vk_buffer_memory_barrier.dstAccessMask & src_access_mask) == src_access_mask) {
576 vk_buffer_memory_barrier.dstAccessMask |= dst_access_mask;
577 return;
578 }
579 /* When re-registering resources we can skip if access mask already contain all the flags.
580 */
581 if ((vk_buffer_memory_barrier.dstAccessMask & dst_access_mask) == dst_access_mask &&
582 (vk_buffer_memory_barrier.srcAccessMask & src_access_mask) == src_access_mask)
583 {
584 return;
585 }
586 }
587 }
588
589 vk_buffer_memory_barriers_.append({VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
590 nullptr,
591 src_access_mask,
592 dst_access_mask,
593 VK_QUEUE_FAMILY_IGNORED,
594 VK_QUEUE_FAMILY_IGNORED,
595 vk_buffer,
596 0,
597 VK_WHOLE_SIZE});
598}
599
600void VKCommandBuilder::add_image_barriers(VKRenderGraph &render_graph,
601 NodeHandle node_handle,
602 VkPipelineStageFlags node_stages,
603 LayeredImageTracker &layered_tracker,
604 Barrier &r_barrier,
605 bool within_rendering)
606{
607 r_barrier.image_memory_barriers = IndexRange(vk_image_memory_barriers_.size(), 0);
608 add_image_read_barriers(
609 render_graph, node_handle, node_stages, layered_tracker, r_barrier, within_rendering);
610 add_image_write_barriers(
611 render_graph, node_handle, node_stages, layered_tracker, r_barrier, within_rendering);
612 r_barrier.image_memory_barriers = r_barrier.image_memory_barriers.with_new_end(
613 vk_image_memory_barriers_.size());
614}
615
616void VKCommandBuilder::add_image_read_barriers(VKRenderGraph &render_graph,
617 NodeHandle node_handle,
618 VkPipelineStageFlags node_stages,
619 LayeredImageTracker &layered_tracker,
620 Barrier &r_barrier,
621 bool within_rendering)
622{
623 for (const VKRenderGraphLink &link : render_graph.links_[node_handle].inputs) {
624 if (link.is_link_to_buffer()) {
625 continue;
626 }
627 const ResourceWithStamp &versioned_resource = link.resource;
628 VKResourceStateTracker::Resource &resource = render_graph.resources_.resources_.lookup(
629 versioned_resource.handle);
630 VKResourceBarrierState &resource_state = resource.barrier_state;
631 const bool is_first_read = resource_state.is_new_stamp();
632 if ((!is_first_read) &&
633 (resource_state.vk_access & link.vk_access_flags) == link.vk_access_flags &&
634 (resource_state.vk_pipeline_stages & node_stages) == node_stages &&
635 resource_state.image_layout == link.vk_image_layout)
636 {
637 /* Has already been covered in previous barrier no need to add this one. */
638 continue;
639 }
640 if (within_rendering && link.vk_image_layout != VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR) {
641 /* Allow only local read barriers inside rendering scope */
642 continue;
643 }
644
645 if (resource_state.image_layout != link.vk_image_layout &&
646 layered_tracker.contains(resource.image.vk_image))
647 {
648 layered_tracker.update(resource.image.vk_image,
649 link.layer_base,
650 link.layer_count,
651 resource_state.image_layout,
652 link.vk_image_layout,
653 r_barrier);
654 continue;
655 }
656
657 VkAccessFlags wait_access = resource_state.vk_access;
658
659 r_barrier.src_stage_mask |= resource_state.vk_pipeline_stages;
660 r_barrier.dst_stage_mask |= node_stages;
661
662 if (is_first_read) {
663 resource_state.vk_access = link.vk_access_flags;
664 resource_state.vk_pipeline_stages = node_stages;
665 }
666 else {
667 resource_state.vk_access |= link.vk_access_flags;
668 resource_state.vk_pipeline_stages |= node_stages;
669 }
670
671 add_image_barrier(resource.image.vk_image,
672 r_barrier,
673 wait_access,
674 link.vk_access_flags,
675 resource_state.image_layout,
676 link.vk_image_layout,
677 link.vk_image_aspect);
678 resource_state.image_layout = link.vk_image_layout;
679 }
680}
681
682void VKCommandBuilder::add_image_write_barriers(VKRenderGraph &render_graph,
683 NodeHandle node_handle,
684 VkPipelineStageFlags node_stages,
685 LayeredImageTracker &layered_tracker,
686 Barrier &r_barrier,
687 bool within_rendering)
688{
689 for (const VKRenderGraphLink link : render_graph.links_[node_handle].outputs) {
690 if (link.is_link_to_buffer()) {
691 continue;
692 }
693 const ResourceWithStamp &versioned_resource = link.resource;
694 VKResourceStateTracker::Resource &resource = render_graph.resources_.resources_.lookup(
695 versioned_resource.handle);
696 VKResourceBarrierState &resource_state = resource.barrier_state;
697 const VkAccessFlags wait_access = resource_state.vk_access;
698 if (within_rendering && link.vk_image_layout != VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR) {
699 /* Allow only local read barriers inside rendering scope */
700 continue;
701 }
702 if (layered_tracker.contains(resource.image.vk_image) &&
703 resource_state.image_layout != link.vk_image_layout)
704 {
705 layered_tracker.update(resource.image.vk_image,
706 link.layer_base,
707 link.layer_count,
708 resource_state.image_layout,
709 link.vk_image_layout,
710 r_barrier);
711
712 continue;
713 }
714
715 r_barrier.src_stage_mask |= resource_state.vk_pipeline_stages;
716 r_barrier.dst_stage_mask |= node_stages;
717
718 resource_state.vk_access = link.vk_access_flags;
719 resource_state.vk_pipeline_stages = node_stages;
720
721 if (wait_access != VK_ACCESS_NONE || link.vk_image_layout != resource_state.image_layout) {
722 add_image_barrier(resource.image.vk_image,
723 r_barrier,
724 wait_access,
725 link.vk_access_flags,
726 resource_state.image_layout,
727 link.vk_image_layout,
728 link.vk_image_aspect);
729 resource_state.image_layout = link.vk_image_layout;
730 }
731 }
732}
733
734void VKCommandBuilder::add_image_barrier(VkImage vk_image,
735 Barrier &r_barrier,
736 VkAccessFlags src_access_mask,
737 VkAccessFlags dst_access_mask,
738 VkImageLayout old_layout,
739 VkImageLayout new_layout,
740 VkImageAspectFlags aspect_mask,
741 uint32_t layer_base,
742 uint32_t layer_count)
743{
744 BLI_assert(aspect_mask != VK_IMAGE_ASPECT_NONE);
745 for (VkImageMemoryBarrier &vk_image_memory_barrier :
746 vk_image_memory_barriers_.as_mutable_span().drop_front(
747 r_barrier.image_memory_barriers.start()))
748 {
749 if (vk_image_memory_barrier.image == vk_image) {
750 /* When registering read/write buffers, it can be that the node internally requires
751 * read/write. In this case we adjust the dstAccessMask of the read barrier. An example is
752 * EEVEE update HIZ compute shader and shadow tagging. */
753 if ((vk_image_memory_barrier.dstAccessMask & src_access_mask) == src_access_mask) {
754 vk_image_memory_barrier.dstAccessMask |= dst_access_mask;
755 return;
756 }
757 /* When re-registering resources we can skip if access mask already contain all the flags.
758 */
759 if ((vk_image_memory_barrier.dstAccessMask & dst_access_mask) == dst_access_mask &&
760 (vk_image_memory_barrier.srcAccessMask & src_access_mask) == src_access_mask &&
761 old_layout == new_layout)
762 {
763 return;
764 }
765 }
766 }
767
768 vk_image_memory_barriers_.append(
769 {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
770 nullptr,
771 src_access_mask,
772 dst_access_mask,
773 old_layout,
774 new_layout,
775 VK_QUEUE_FAMILY_IGNORED,
776 VK_QUEUE_FAMILY_IGNORED,
777 vk_image,
778 {aspect_mask, 0, VK_REMAINING_MIP_LEVELS, layer_base, layer_count}});
779}
780
782
783/* -------------------------------------------------------------------- */
786
787void VKCommandBuilder::LayeredImageTracker::begin(const VKRenderGraph &render_graph,
788 NodeHandle node_handle)
789{
790 BLI_assert(render_graph.nodes_[node_handle].type == VKNodeType::BEGIN_RENDERING);
791 layered_attachments.clear();
792 layered_bindings.clear();
793
794 const VKRenderGraphNodeLinks &links = render_graph.links_[node_handle];
795 for (const VKRenderGraphLink &link : links.outputs) {
796 VKResourceStateTracker::Resource &resource = render_graph.resources_.resources_.lookup(
797 link.resource.handle);
798 if (resource.has_multiple_layers()) {
799 layered_attachments.add(resource.image.vk_image);
800 }
801 }
802}
803
804void VKCommandBuilder::LayeredImageTracker::update(VkImage vk_image,
805 uint32_t layer,
806 uint32_t layer_count,
807 VkImageLayout old_layout,
808 VkImageLayout new_layout,
809 Barrier &r_barrier)
810{
811 for (const TrackedImage &binding : layered_bindings) {
812 if (binding.vk_image == vk_image && binding.layer == layer) {
813 BLI_assert_msg(binding.vk_image_layout == new_layout,
814 "We don't support that one layer transitions multiple times during a "
815 "rendering scope.");
816 /* Early exit as layer is in correct layout. This is a normal case as we expect multiple
817 * draw commands to take place during a rendering scope with the same layer access. */
818 return;
819 }
820 }
821
822 layered_bindings.append({vk_image, new_layout, layer, layer_count});
823
824 /* We should be able to do better. BOTTOM/TOP is really a worst case barrier. */
825 r_barrier.src_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
826 r_barrier.dst_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
827 command_builder.add_image_barrier(vk_image,
828 r_barrier,
829 VK_ACCESS_TRANSFER_WRITE_BIT,
830 VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT |
831 VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
832 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
833 VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
834 old_layout,
835 new_layout,
836 VK_IMAGE_ASPECT_COLOR_BIT,
837 layer,
838 layer_count);
839}
840
841void VKCommandBuilder::LayeredImageTracker::end(Barrier &r_barrier, bool use_local_read)
842{
843 suspend(r_barrier, use_local_read);
844 layered_attachments.clear();
845 layered_bindings.clear();
846}
847
848void VKCommandBuilder::LayeredImageTracker::suspend(Barrier &r_barrier, bool use_local_read)
849
850{
851 if (layered_bindings.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 TrackedImage &binding : layered_bindings) {
862 command_builder.add_image_barrier(
863 binding.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 binding.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 binding.layer,
876 binding.layer_count);
877 r_barrier.image_memory_barriers = r_barrier.image_memory_barriers.with_new_end(
878 command_builder.vk_image_memory_barriers_.size());
879
880#if 0
881 std::cout << __func__ << ": transition layout image=" << binding.vk_image
882 << ", layer=" << binding.layer << ", count=" << binding.layer_count
883 << ", from_layout=" << to_string(binding.vk_image_layout)
884 << ", to_layout=" << to_string(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) << "\n";
885#endif
886 }
887}
888
889void VKCommandBuilder::LayeredImageTracker::resume(Barrier &r_barrier, bool use_local_read)
890{
891 if (layered_bindings.is_empty()) {
892 return;
893 }
894
895 command_builder.reset_barriers(r_barrier);
896 /* We should be able to do better. BOTTOM/TOP is really a worst case barrier. */
897 r_barrier.src_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
898 r_barrier.dst_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
899 int64_t start_index = command_builder.vk_image_memory_barriers_.size();
900 r_barrier.image_memory_barriers = IndexRange::from_begin_size(start_index, 0);
901
902 for (const TrackedImage &binding : layered_bindings) {
903 command_builder.add_image_barrier(
904 binding.vk_image,
905 r_barrier,
906 VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT |
907 VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
908 VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
909 VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT |
910 VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
911 VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
912 use_local_read ? VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR :
913 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
914 binding.vk_image_layout,
915 VK_IMAGE_ASPECT_COLOR_BIT,
916 binding.layer,
917 binding.layer_count);
918#if 0
919 std::cout << __func__ << ": transition layout image=" << binding.vk_image
920 << ", layer=" << binding.layer << ", count=" << binding.layer_count
921 << ", from_layout=" << to_string(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)
922 << ", to_layout=" << to_string(binding.vk_image_layout) << "\n";
923#endif
924 }
925}
927
928/* -------------------------------------------------------------------- */
931
932std::string VKCommandBuilder::to_string_barrier(const Barrier &barrier)
933{
934 std::stringstream ss;
935 ss << "src_stage_mask=" << to_string_vk_pipeline_stage_flags(barrier.src_stage_mask)
936 << ", dst_stage_mask=" << to_string_vk_pipeline_stage_flags(barrier.dst_stage_mask) << "\n";
937 for (const VkBufferMemoryBarrier &buffer_memory_barrier :
938 vk_buffer_memory_barriers_.as_span().slice(barrier.buffer_memory_barriers))
939 {
940 ss << " - src_access_mask=" << to_string_vk_access_flags(buffer_memory_barrier.srcAccessMask)
941 << ", dst_access_mask=" << to_string_vk_access_flags(buffer_memory_barrier.dstAccessMask)
942 << ", vk_buffer=" << to_string(buffer_memory_barrier.buffer) << "\n";
943 }
944
945 for (const VkImageMemoryBarrier &image_memory_barrier :
946 vk_image_memory_barriers_.as_span().slice(barrier.image_memory_barriers))
947 {
948 ss << " - src_access_mask=" << to_string_vk_access_flags(image_memory_barrier.srcAccessMask)
949 << ", dst_access_mask=" << to_string_vk_access_flags(image_memory_barrier.dstAccessMask)
950 << ", vk_image=" << to_string(image_memory_barrier.image)
951 << ", old_layout=" << to_string(image_memory_barrier.oldLayout)
952 << ", new_layout=" << to_string(image_memory_barrier.newLayout)
953 << ", subresource_range=" << to_string(image_memory_barrier.subresourceRange, 2) << "\n";
954 }
955
956 return ss.str();
957}
958
960
961} // 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])
float[3] Vector
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:52
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