Blender V4.3
mtl_command_buffer.mm
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2022-2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "DNA_userdef_types.h"
6
7#include "mtl_backend.hh"
8#include "mtl_common.hh"
9#include "mtl_context.hh"
10#include "mtl_debug.hh"
11#include "mtl_framebuffer.hh"
12
14
15#include <fstream>
16
17using namespace blender;
18using namespace blender::gpu;
19
20namespace blender::gpu {
21
22/* Counter for active command buffers. */
24
25/* -------------------------------------------------------------------- */
29void MTLCommandBufferManager::prepare(bool /*supports_render*/)
30{
31 render_pass_state_.reset_state();
32 compute_state_.reset_state();
33}
34
35void MTLCommandBufferManager::register_encoder_counters()
36{
37 encoder_count_++;
38 empty_ = false;
39}
40
41id<MTLCommandBuffer> MTLCommandBufferManager::ensure_begin()
42{
43 if (active_command_buffer_ == nil) {
44
45 /* Verify number of active command buffers is below limit.
46 * Exceeding this limit will mean we either have a command buffer leak/GPU hang
47 * or we should increase the command buffer limit during MTLQueue creation.
48 * Excessive command buffers can also be caused by frequent GPUContext switches, which cause
49 * the GPU pipeline to flush. This is common during indirect light baking operations.
50 *
51 * NOTE: We currently stall until completion of GPU work upon ::submit if we have reached the
52 * in-flight command buffer limit. */
55
56 if (G.debug & G_DEBUG_GPU) {
57 /* Debug: Enable Advanced Errors for GPU work execution. */
58 MTLCommandBufferDescriptor *desc = [[MTLCommandBufferDescriptor alloc] init];
59 desc.errorOptions = MTLCommandBufferErrorOptionEncoderExecutionStatus;
60 desc.retainedReferences = YES;
61 BLI_assert(context_.queue != nil);
62 active_command_buffer_ = [context_.queue commandBufferWithDescriptor:desc];
63 }
64
65 /* Ensure command buffer is created if debug command buffer unavailable. */
66 if (active_command_buffer_ == nil) {
67 active_command_buffer_ = [context_.queue commandBuffer];
68 }
69
70 [active_command_buffer_ retain];
72
73 /* Ensure we begin new Scratch Buffer if we are on a new frame. */
76
77 /* Reset Command buffer heuristics. */
78 this->reset_counters();
79
80 /* Clear debug stacks. */
81 debug_group_stack.clear();
82 debug_group_pushed_stack.clear();
83 }
84 BLI_assert(active_command_buffer_ != nil);
85 return active_command_buffer_;
86}
87
88/* If wait is true, CPU will stall until GPU work has completed. */
90{
91 /* Skip submission if command buffer is empty. */
92 if (empty_ || active_command_buffer_ == nil) {
93 return false;
94 }
95
96 /* Ensure current encoders are finished. */
98 BLI_assert(active_command_encoder_type_ == MTL_NO_COMMAND_ENCODER);
99
100 /* Flush active ScratchBuffer associated with parent MTLContext. */
102
103 /*** Submit Command Buffer. ***/
104 /* Command buffer lifetime tracking. */
105 /* Increment current MTLSafeFreeList reference counter to flag MTLBuffers freed within
106 * the current command buffer lifetime as used.
107 * This ensures that in-use resources are not prematurely de-referenced and returned to the
108 * available buffer pool while they are in-use by the GPU. */
109 MTLSafeFreeList *cmd_free_buffer_list =
111 BLI_assert(cmd_free_buffer_list);
112 cmd_free_buffer_list->increment_reference();
113
114 id<MTLCommandBuffer> cmd_buffer_ref = active_command_buffer_;
115 [cmd_buffer_ref retain];
116
117 [cmd_buffer_ref addCompletedHandler:^(id<MTLCommandBuffer> /*cb*/) {
118 /* Upon command buffer completion, decrement MTLSafeFreeList reference count
119 * to allow buffers no longer in use by this CommandBuffer to be freed. */
120 cmd_free_buffer_list->decrement_reference();
121
122 /* Release command buffer after completion callback handled. */
123 [cmd_buffer_ref release];
124
125 /* Decrement count. */
127 }];
128
129 /* Submit command buffer to GPU. */
130 [active_command_buffer_ commit];
131
132 /* If we have too many active command buffers in flight, wait until completed to avoid running
133 * out. We can increase */
136 {
137 wait = true;
139 "Maximum number of command buffers in flight. Host will wait until GPU work has "
140 "completed. Consider increasing GHOST_ContextCGL::max_command_buffer_count or reducing "
141 "work fragmentation to better utilize system hardware. Command buffers are flushed upon "
142 "GPUContext switches, this is the most common cause of excessive command buffer "
143 "generation.");
144 }
145
146 if (wait || (G.debug & G_DEBUG_GPU)) {
147 /* Wait until current GPU work has finished executing. */
148 [active_command_buffer_ waitUntilCompleted];
149
150 /* Command buffer execution debugging can return an error message if
151 * execution has failed or encountered GPU-side errors. */
152 if (G.debug & G_DEBUG_GPU) {
153
154 NSError *error = [active_command_buffer_ error];
155 if (error != nil) {
156 NSLog(@"%@", error);
157 BLI_assert(false);
158 }
159 }
160 }
161
162 /* Release previous frames command buffer and reset active cmd buffer. */
163 if (last_submitted_command_buffer_ != nil) {
164
165 BLI_assert(MTLBackend::get()->is_inside_render_boundary());
166 [last_submitted_command_buffer_ autorelease];
167 last_submitted_command_buffer_ = nil;
168 }
169 last_submitted_command_buffer_ = active_command_buffer_;
170 active_command_buffer_ = nil;
171
172 return true;
173}
174
177/* -------------------------------------------------------------------- */
181/* Fetch/query current encoder. */
183{
184 return (active_command_encoder_type_ == MTL_RENDER_COMMAND_ENCODER);
185}
186
188{
189 return (active_command_encoder_type_ == MTL_BLIT_COMMAND_ENCODER);
190}
191
193{
194 return (active_command_encoder_type_ == MTL_COMPUTE_COMMAND_ENCODER);
195}
196
198{
199 /* Calling code should check if inside render pass. Otherwise nil. */
200 return active_render_command_encoder_;
201}
202
204{
205 /* Calling code should check if inside render pass. Otherwise nil. */
206 return active_blit_command_encoder_;
207}
208
210{
211 /* Calling code should check if inside render pass. Otherwise nil. */
212 return active_compute_command_encoder_;
213}
214
216{
217 /* If outside of RenderPass, nullptr will be returned. */
218 if (this->is_inside_render_pass()) {
219 return active_frame_buffer_;
220 }
221 return nullptr;
222}
223
224/* Encoder and Pass management. */
225/* End currently active MTLCommandEncoder. */
227{
228
229 /* End active encoder if one is active. */
230 if (active_command_encoder_type_ != MTL_NO_COMMAND_ENCODER) {
231
232 switch (active_command_encoder_type_) {
233 case MTL_RENDER_COMMAND_ENCODER: {
234 /* Verify a RenderCommandEncoder is active and end. */
235 BLI_assert(active_render_command_encoder_ != nil);
236
237 /* Complete Encoding. */
238 [active_render_command_encoder_ endEncoding];
239 [active_render_command_encoder_ release];
240 active_render_command_encoder_ = nil;
241 active_command_encoder_type_ = MTL_NO_COMMAND_ENCODER;
242
243 /* Reset associated frame-buffer flag. */
244 active_frame_buffer_ = nullptr;
245 active_pass_descriptor_ = nullptr;
246 return true;
247 }
248
249 case MTL_BLIT_COMMAND_ENCODER: {
250 /* Verify a RenderCommandEncoder is active and end. */
251 BLI_assert(active_blit_command_encoder_ != nil);
252 [active_blit_command_encoder_ endEncoding];
253 [active_blit_command_encoder_ release];
254 active_blit_command_encoder_ = nil;
255 active_command_encoder_type_ = MTL_NO_COMMAND_ENCODER;
256 return true;
257 }
258
259 case MTL_COMPUTE_COMMAND_ENCODER: {
260 /* Verify a RenderCommandEncoder is active and end. */
261 BLI_assert(active_compute_command_encoder_ != nil);
262 [active_compute_command_encoder_ endEncoding];
263 [active_compute_command_encoder_ release];
264 active_compute_command_encoder_ = nil;
265 active_command_encoder_type_ = MTL_NO_COMMAND_ENCODER;
266 return true;
267 }
268
269 default: {
270 BLI_assert(false && "Invalid command encoder type");
271 return false;
272 }
273 };
274 }
275 else {
276 /* MTL_NO_COMMAND_ENCODER. */
277 BLI_assert(active_render_command_encoder_ == nil);
278 BLI_assert(active_blit_command_encoder_ == nil);
279 BLI_assert(active_compute_command_encoder_ == nil);
280 return false;
281 }
282}
283
285 MTLFrameBuffer *ctx_framebuffer, bool force_begin, bool *r_new_pass)
286{
287 /* Ensure valid frame-buffer. */
288 BLI_assert(ctx_framebuffer != nullptr);
289
290 /* Ensure active command buffer. */
291 id<MTLCommandBuffer> cmd_buf = this->ensure_begin();
292 BLI_assert(cmd_buf);
293
294 /* Begin new command encoder if the currently active one is
295 * incompatible or requires updating. */
296 if (active_command_encoder_type_ != MTL_RENDER_COMMAND_ENCODER ||
297 active_frame_buffer_ != ctx_framebuffer || force_begin)
298 {
300
301 /* Determine if this is a re-bind of the same frame-buffer. */
302 bool is_rebind = (active_frame_buffer_ == ctx_framebuffer);
303
304 /* Generate RenderPassDescriptor from bound frame-buffer. */
305 BLI_assert(ctx_framebuffer);
306 active_frame_buffer_ = ctx_framebuffer;
307 active_pass_descriptor_ = active_frame_buffer_->bake_render_pass_descriptor(
308 is_rebind && (!active_frame_buffer_->get_pending_clear()));
309
310 /* Determine if there is a visibility buffer assigned to the context. */
311 gpu::MTLBuffer *visibility_buffer = context_.get_visibility_buffer();
312 this->active_pass_descriptor_.visibilityResultBuffer =
313 (visibility_buffer) ? visibility_buffer->get_metal_buffer() : nil;
314 context_.clear_visibility_dirty();
315
316 /* Ensure we have already cleaned up our previous render command encoder. */
317 BLI_assert(active_render_command_encoder_ == nil);
318
319 /* Unroll pending debug groups. */
320 if (G.debug & G_DEBUG_GPU) {
321 unfold_pending_debug_groups();
322 }
323
324 /* Create new RenderCommandEncoder based on descriptor (and begin encoding). */
325 active_render_command_encoder_ = [cmd_buf
326 renderCommandEncoderWithDescriptor:active_pass_descriptor_];
327 [active_render_command_encoder_ retain];
328 active_command_encoder_type_ = MTL_RENDER_COMMAND_ENCODER;
329
330 /* Add debug label. */
331 if (G.debug & G_DEBUG_GPU) {
332 std::string debug_name = "RenderCmdEncoder: Unnamed";
333 if (!debug_group_pushed_stack.empty()) {
334 debug_name = "RenderCmdEncoder: " + debug_group_pushed_stack.back();
335 }
336 debug_name += " (FrameBuffer: " + std::string(active_frame_buffer_->name_get()) + ")";
337 active_render_command_encoder_.label = [NSString stringWithUTF8String:debug_name.c_str()];
338 }
339
340 /* Update command buffer encoder heuristics. */
341 this->register_encoder_counters();
342
343 /* Apply initial state. */
344 /* Update Viewport and Scissor State */
345 active_frame_buffer_->apply_state();
346
347 /* FLAG FRAMEBUFFER AS CLEARED -- A clear only lasts as long as one has been specified.
348 * After this, resets to Load attachments to parallel GL behavior. */
349 active_frame_buffer_->mark_cleared();
350
351 /* Reset RenderPassState to ensure resource bindings are re-applied. */
352 render_pass_state_.reset_state();
353
354 /* Return true as new pass started. */
355 *r_new_pass = true;
356 }
357 else {
358 /* No new pass. */
359 *r_new_pass = false;
360 }
361
362 BLI_assert(active_render_command_encoder_ != nil);
363 return active_render_command_encoder_;
364}
365
367{
368 /* Ensure active command buffer. */
369 id<MTLCommandBuffer> cmd_buf = this->ensure_begin();
370 BLI_assert(cmd_buf);
371
372 /* Ensure no existing command encoder of a different type is active. */
373 if (active_command_encoder_type_ != MTL_BLIT_COMMAND_ENCODER) {
375 }
376
377 /* Begin new Blit Encoder. */
378 if (active_blit_command_encoder_ == nil) {
379 /* Unroll pending debug groups. */
380 if (G.debug & G_DEBUG_GPU) {
381 unfold_pending_debug_groups();
382 }
383
384 active_blit_command_encoder_ = [cmd_buf blitCommandEncoder];
385 BLI_assert(active_blit_command_encoder_ != nil);
386 [active_blit_command_encoder_ retain];
387 active_command_encoder_type_ = MTL_BLIT_COMMAND_ENCODER;
388
389 /* Add debug label. */
390 if (G.debug & G_DEBUG_GPU) {
391 std::string debug_name = "BlitCmdEncoder: Unnamed";
392 if (!debug_group_pushed_stack.empty()) {
393 debug_name = "BlitCmdEncoder: " + debug_group_pushed_stack.back();
394 }
395 active_blit_command_encoder_.label = [NSString stringWithUTF8String:debug_name.c_str()];
396 }
397
398 /* Update command buffer encoder heuristics. */
399 this->register_encoder_counters();
400 }
401 BLI_assert(active_blit_command_encoder_ != nil);
402 return active_blit_command_encoder_;
403}
404
406{
407 /* Ensure active command buffer. */
408 id<MTLCommandBuffer> cmd_buf = this->ensure_begin();
409 BLI_assert(cmd_buf);
410
411 /* Ensure no existing command encoder of a different type is active. */
412 if (active_command_encoder_type_ != MTL_COMPUTE_COMMAND_ENCODER) {
414 }
415
416 /* Begin new Compute Encoder. */
417 if (active_compute_command_encoder_ == nil) {
418 /* Unroll pending debug groups. */
419 if (G.debug & G_DEBUG_GPU) {
420 unfold_pending_debug_groups();
421 }
422
423 active_compute_command_encoder_ = [cmd_buf computeCommandEncoder];
424 BLI_assert(active_compute_command_encoder_ != nil);
425 [active_compute_command_encoder_ retain];
426 active_command_encoder_type_ = MTL_COMPUTE_COMMAND_ENCODER;
427
428 /* Add debug label. */
429 if (G.debug & G_DEBUG_GPU) {
430 std::string debug_name = "ComputeCmdEncoder: Unnamed";
431 if (!debug_group_pushed_stack.empty()) {
432 debug_name = "ComputeCmdEncoder: " + debug_group_pushed_stack.back();
433 }
434 active_compute_command_encoder_.label = [NSString stringWithUTF8String:debug_name.c_str()];
435 }
436
437 /* Update command buffer encoder heuristics. */
438 this->register_encoder_counters();
439
440 /* Reset RenderPassState to ensure resource bindings are re-applied. */
441 compute_state_.reset_state();
442 }
443 BLI_assert(active_compute_command_encoder_ != nil);
444 return active_compute_command_encoder_;
445}
446
449/* -------------------------------------------------------------------- */
453/* Rendering Heuristics. */
455{
456 current_draw_call_count_++;
457 vertex_submitted_count_ += vertex_submission;
458 empty_ = false;
459}
460
461/* Reset workload counters. */
463{
464 empty_ = true;
465 current_draw_call_count_ = 0;
466 encoder_count_ = 0;
467 vertex_submitted_count_ = 0;
468}
469
470/* Workload evaluation. */
472{
473 /* Skip if no active command buffer. */
474 if (active_command_buffer_ == nil) {
475 return false;
476 }
477
478 /* Use optimized heuristic to split heavy command buffer submissions to better saturate the
479 * hardware and also reduce stalling from individual large submissions. */
482 {
483 return ((current_draw_call_count_ > 30000) || (vertex_submitted_count_ > 100000000) ||
484 (encoder_count_ > 25));
485 }
486 else {
487 /* Apple Silicon is less efficient if splitting submissions. */
488 return false;
489 }
490}
491
494/* -------------------------------------------------------------------- */
498/* Debug. */
499void MTLCommandBufferManager::push_debug_group(const char *name, int /*index*/)
500{
501 /* Only perform this operation if capturing. */
502 MTLCaptureManager *capture_manager = [MTLCaptureManager sharedCaptureManager];
503 if (![capture_manager isCapturing]) {
504 return;
505 }
506
507 id<MTLCommandBuffer> cmd = this->ensure_begin();
508 if (cmd != nil) {
509 if (active_command_encoder_type_ != MTL_NO_COMMAND_ENCODER) {
511 }
512
513 debug_group_stack.push_back(std::string(name));
514 }
515}
516
518{
519 /* Only perform this operation if capturing. */
520 MTLCaptureManager *capture_manager = [MTLCaptureManager sharedCaptureManager];
521 if (![capture_manager isCapturing]) {
522 return;
523 }
524
525 id<MTLCommandBuffer> cmd = this->ensure_begin();
526 if (cmd != nil) {
527 if (active_command_encoder_type_ != MTL_NO_COMMAND_ENCODER) {
529 }
530
531#if METAL_DEBUG_CAPTURE_HIDE_EMPTY == 0
532 /* Unfold pending groups to display empty groups. */
533 unfold_pending_debug_groups();
534#endif
535
536 /* If we have pending debug groups, first pop the last pending one. */
537 if (debug_group_stack.size() > 0) {
538 debug_group_stack.pop_back();
539 }
540 else {
541 /* Otherwise, close last active pushed group. */
542 if (debug_group_pushed_stack.size() > 0) {
543 debug_group_pushed_stack.pop_back();
544
545 if (debug_group_pushed_stack.size() < uint(METAL_DEBUG_CAPTURE_MAX_NESTED_GROUPS)) {
546 [cmd popDebugGroup];
547 }
548 }
549 }
550 }
551}
552
553void MTLCommandBufferManager::unfold_pending_debug_groups()
554{
555 /* Only perform this operation if capturing. */
556 MTLCaptureManager *capture_manager = [MTLCaptureManager sharedCaptureManager];
557 if (![capture_manager isCapturing]) {
558 return;
559 }
560
561 if (active_command_buffer_ != nil) {
562 for (const std::string &name : debug_group_stack) {
563 if (debug_group_pushed_stack.size() < uint(METAL_DEBUG_CAPTURE_MAX_NESTED_GROUPS)) {
564 [active_command_buffer_ pushDebugGroup:[NSString stringWithFormat:@"%s", name.c_str()]];
565 }
566 debug_group_pushed_stack.push_back(name);
567 }
568 debug_group_stack.clear();
569 }
570}
571
572/* Workload Synchronization. */
574 eGPUStageBarrierBits before_stages,
575 eGPUStageBarrierBits after_stages)
576{
577 /* Apple Silicon does not support memory barriers for RenderCommandEncoder's.
578 * We do not currently need these due to implicit API guarantees. However, render->render
579 * resource dependencies are only evaluated at RenderCommandEncoder boundaries due to work
580 * execution on TBDR architecture.
581 *
582 * NOTE: Render barriers are therefore inherently expensive. Where possible, opt for local
583 * synchronization using raster order groups, or, prefer compute to avoid subsequent passes
584 * re-loading pass attachments which are not needed. */
585 const bool is_tile_based_arch = (GPU_platform_architecture() == GPU_ARCHITECTURE_TBDR);
586 if (is_tile_based_arch) {
587 if (active_command_encoder_type_ == MTL_RENDER_COMMAND_ENCODER) {
588 /* Break render pass to ensure final pass results are visible to subsequent calls. */
590 return true;
591 }
592 else {
593 /* Skip all barriers for compute and blit passes as Metal will resolve these dependencies. */
594 return false;
595 }
596 }
597
598 /* Resolve scope. */
599 MTLBarrierScope scope = 0;
600 if (barrier_bits & GPU_BARRIER_SHADER_IMAGE_ACCESS || barrier_bits & GPU_BARRIER_TEXTURE_FETCH) {
601 bool is_compute = (active_command_encoder_type_ != MTL_RENDER_COMMAND_ENCODER);
602 scope |= (is_compute ? 0 : MTLBarrierScopeRenderTargets) | MTLBarrierScopeTextures;
603 }
604 if (barrier_bits & GPU_BARRIER_SHADER_STORAGE ||
605 barrier_bits & GPU_BARRIER_VERTEX_ATTRIB_ARRAY || barrier_bits & GPU_BARRIER_ELEMENT_ARRAY ||
606 barrier_bits & GPU_BARRIER_UNIFORM || barrier_bits & GPU_BARRIER_BUFFER_UPDATE)
607 {
608 scope = scope | MTLBarrierScopeBuffers;
609 }
610
611 if (scope != 0) {
612 /* Issue barrier based on encoder. */
613 switch (active_command_encoder_type_) {
614 case MTL_NO_COMMAND_ENCODER:
615 case MTL_BLIT_COMMAND_ENCODER: {
616 /* No barrier to be inserted. */
617 return false;
618 }
619
620 /* Rendering. */
621 case MTL_RENDER_COMMAND_ENCODER: {
622 /* Currently flagging both stages -- can use bits above to filter on stage type --
623 * though full barrier is safe for now. */
624 MTLRenderStages before_stage_flags = 0;
625 MTLRenderStages after_stage_flags = 0;
626 if (before_stages & GPU_BARRIER_STAGE_VERTEX &&
627 !(before_stages & GPU_BARRIER_STAGE_FRAGMENT))
628 {
629 before_stage_flags = before_stage_flags | MTLRenderStageVertex;
630 }
631 if (before_stages & GPU_BARRIER_STAGE_FRAGMENT) {
632 before_stage_flags = before_stage_flags | MTLRenderStageFragment;
633 }
634 if (after_stages & GPU_BARRIER_STAGE_VERTEX) {
635 after_stage_flags = after_stage_flags | MTLRenderStageVertex;
636 }
637 if (after_stages & GPU_BARRIER_STAGE_FRAGMENT) {
638 after_stage_flags = MTLRenderStageFragment;
639 }
640
641 id<MTLRenderCommandEncoder> rec = this->get_active_render_command_encoder();
642 BLI_assert(rec != nil);
643 [rec memoryBarrierWithScope:scope
644 afterStages:after_stage_flags
645 beforeStages:before_stage_flags];
646 return true;
647 }
648
649 /* Compute. */
650 case MTL_COMPUTE_COMMAND_ENCODER: {
651 id<MTLComputeCommandEncoder> rec = this->get_active_compute_command_encoder();
652 BLI_assert(rec != nil);
653 [rec memoryBarrierWithScope:scope];
654 return true;
655 }
656 }
657 }
658 /* No barrier support. */
659 return false;
660}
661
662void MTLCommandBufferManager::encode_signal_event(id<MTLEvent> event, uint64_t signal_value)
663{
664 /* Ensure active command buffer. */
665 id<MTLCommandBuffer> cmd_buf = this->ensure_begin();
666 BLI_assert(cmd_buf);
668 [cmd_buf encodeSignalEvent:event value:signal_value];
669 register_encoder_counters();
670}
671
672void MTLCommandBufferManager::encode_wait_for_event(id<MTLEvent> event, uint64_t signal_value)
673{
674 /* Ensure active command buffer. */
675 id<MTLCommandBuffer> cmd_buf = this->ensure_begin();
676 BLI_assert(cmd_buf);
678 [cmd_buf encodeWaitForEvent:event value:signal_value];
679 register_encoder_counters();
680}
681
684/* -------------------------------------------------------------------- */
687/* Reset binding state when a new RenderCommandEncoder is bound, to ensure
688 * pipeline resources are re-applied to the new Encoder.
689 * NOTE: In Metal, state is only persistent within an MTLCommandEncoder,
690 * not globally. */
692{
693 /* Reset Cached pipeline state. */
694 this->bound_pso = nil;
695 this->bound_ds_state = nil;
696
697 /* Clear shader binding. */
698 this->last_bound_shader_state.set(nullptr, 0);
699
700 /* Other states. */
703 this->last_scissor_rect = {0,
704 0,
705 (uint)((fb != nullptr) ? fb->get_width() : 0),
706 (uint)((fb != nullptr) ? fb->get_height() : 0)};
707
708 /* Reset cached resource binding state */
709 for (int ubo = 0; ubo < MTL_MAX_BUFFER_BINDINGS; ubo++) {
710 this->cached_vertex_buffer_bindings[ubo].is_bytes = false;
713
714 this->cached_fragment_buffer_bindings[ubo].is_bytes = false;
717 }
718
719 /* Reset cached texture and sampler state binding state. */
720 for (int tex = 0; tex < MTL_MAX_TEXTURE_SLOTS; tex++) {
724
728 }
729}
730
732{
733 /* Reset Cached pipeline state. */
734 this->bound_pso = nil;
735
736 /* Reset cached resource binding state */
737 for (int ubo = 0; ubo < MTL_MAX_BUFFER_BINDINGS; ubo++) {
738 this->cached_compute_buffer_bindings[ubo].is_bytes = false;
741 }
742
743 /* Reset cached texture and sampler state binding state. */
744 for (int tex = 0; tex < MTL_MAX_TEXTURE_SLOTS; tex++) {
748 }
749}
750
751/* Bind Texture to current RenderCommandEncoder. */
753{
754 if (this->cached_vertex_texture_bindings[slot].metal_texture != tex) {
755 id<MTLRenderCommandEncoder> rec = this->cmd.get_active_render_command_encoder();
756 BLI_assert(rec != nil);
757 [rec setVertexTexture:tex atIndex:slot];
759 }
760}
761
763{
764 if (this->cached_fragment_texture_bindings[slot].metal_texture != tex) {
765 id<MTLRenderCommandEncoder> rec = this->cmd.get_active_render_command_encoder();
766 BLI_assert(rec != nil);
767 [rec setFragmentTexture:tex atIndex:slot];
769 }
770}
771
773{
774 if (this->cached_compute_texture_bindings[slot].metal_texture != tex) {
775 id<MTLComputeCommandEncoder> rec = this->cmd.get_active_compute_command_encoder();
776 BLI_assert(rec != nil);
777 [rec setTexture:tex atIndex:slot];
778
780 }
781}
782
784 bool use_argument_buffer_for_samplers,
785 uint slot)
786{
787 /* Range check. */
789 BLI_assert(slot >= 0);
790 BLI_assert(slot <= shader_interface->get_max_texture_index());
792 UNUSED_VARS_NDEBUG(shader_interface);
793
794 /* If sampler state has not changed for the given slot, we do not need to fetch. */
795 if (this->cached_vertex_sampler_state_bindings[slot].sampler_state == nil ||
796 !(this->cached_vertex_sampler_state_bindings[slot].binding_state == sampler_binding.state) ||
797 use_argument_buffer_for_samplers)
798 {
799
800 id<MTLSamplerState> sampler_state = (sampler_binding.state == DEFAULT_SAMPLER_STATE) ?
803 if (!use_argument_buffer_for_samplers) {
804 /* Update binding and cached state. */
805 id<MTLRenderCommandEncoder> rec = this->cmd.get_active_render_command_encoder();
806 BLI_assert(rec != nil);
807 [rec setVertexSamplerState:sampler_state atIndex:slot];
809 this->cached_vertex_sampler_state_bindings[slot].sampler_state = sampler_state;
810 }
811
812 /* Flag last binding type. */
814 use_argument_buffer_for_samplers;
815
816 /* Always assign to argument buffer samplers binding array - Efficiently ensures the value in
817 * the samplers array is always up to date. */
818 ctx.samplers_.mtl_sampler[slot] = sampler_state;
819 ctx.samplers_.mtl_sampler_flags[slot] = sampler_binding.state;
820 }
821}
822
824 bool use_argument_buffer_for_samplers,
825 uint slot)
826{
827 /* Range check. */
829 BLI_assert(slot >= 0);
830 BLI_assert(slot <= shader_interface->get_max_texture_index());
832 UNUSED_VARS_NDEBUG(shader_interface);
833
834 /* If sampler state has not changed for the given slot, we do not need to fetch. */
835 if (this->cached_fragment_sampler_state_bindings[slot].sampler_state == nil ||
836 !(this->cached_fragment_sampler_state_bindings[slot].binding_state ==
837 sampler_binding.state) ||
838 use_argument_buffer_for_samplers)
839 {
840
841 id<MTLSamplerState> sampler_state = (sampler_binding.state == DEFAULT_SAMPLER_STATE) ?
844 if (!use_argument_buffer_for_samplers) {
845 /* Update binding and cached state. */
846 id<MTLRenderCommandEncoder> rec = this->cmd.get_active_render_command_encoder();
847 BLI_assert(rec != nil);
848 [rec setFragmentSamplerState:sampler_state atIndex:slot];
850 this->cached_fragment_sampler_state_bindings[slot].sampler_state = sampler_state;
851 }
852
853 /* Flag last binding type */
855 use_argument_buffer_for_samplers;
856
857 /* Always assign to argument buffer samplers binding array - Efficiently ensures the value in
858 * the samplers array is always up to date. */
859 ctx.samplers_.mtl_sampler[slot] = sampler_state;
860 ctx.samplers_.mtl_sampler_flags[slot] = sampler_binding.state;
861 }
862}
863
865 bool use_argument_buffer_for_samplers,
866 uint slot)
867{
868 /* Range check. */
870 BLI_assert(slot >= 0);
871 BLI_assert(slot <= shader_interface->get_max_texture_index());
873 UNUSED_VARS_NDEBUG(shader_interface);
874
875 /* If sampler state has not changed for the given slot, we do not need to fetch. */
876 if (this->cached_compute_sampler_state_bindings[slot].sampler_state == nil ||
877 !(this->cached_compute_sampler_state_bindings[slot].binding_state ==
878 sampler_binding.state) ||
879 use_argument_buffer_for_samplers)
880 {
881
882 id<MTLSamplerState> sampler_state = (sampler_binding.state == DEFAULT_SAMPLER_STATE) ?
885 if (!use_argument_buffer_for_samplers) {
886 /* Update binding and cached state. */
887 id<MTLComputeCommandEncoder> rec = this->cmd.get_active_compute_command_encoder();
888 BLI_assert(rec != nil);
889 [rec setSamplerState:sampler_state atIndex:slot];
891 this->cached_compute_sampler_state_bindings[slot].sampler_state = sampler_state;
892 }
893
894 /* Flag last binding type */
896 use_argument_buffer_for_samplers;
897
898 /* Always assign to argument buffer samplers binding array - Efficiently ensures the value in
899 * the samplers array is always up to date. */
900 ctx.samplers_.mtl_sampler[slot] = sampler_state;
901 ctx.samplers_.mtl_sampler_flags[slot] = sampler_binding.state;
902 }
903}
904
906 uint64_t buffer_offset,
907 uint index)
908{
909 BLI_assert(index >= 0 && index < MTL_MAX_BUFFER_BINDINGS);
910 BLI_assert(buffer_offset >= 0);
911 BLI_assert(buffer != nil);
912
913 BufferBindingCached &current_vert_ubo_binding = this->cached_vertex_buffer_bindings[index];
914 if (current_vert_ubo_binding.offset != buffer_offset ||
915 current_vert_ubo_binding.metal_buffer != buffer || current_vert_ubo_binding.is_bytes)
916 {
917
918 id<MTLRenderCommandEncoder> rec = this->cmd.get_active_render_command_encoder();
919 BLI_assert(rec != nil);
920
921 if (current_vert_ubo_binding.metal_buffer == buffer) {
922 /* If buffer is the same, but offset has changed. */
923 [rec setVertexBufferOffset:buffer_offset atIndex:index];
924 }
925 else {
926 /* Bind Vertex Buffer. */
927 [rec setVertexBuffer:buffer offset:buffer_offset atIndex:index];
928 }
929
930 /* Update Bind-state cache. */
931 this->cached_vertex_buffer_bindings[index].is_bytes = false;
932 this->cached_vertex_buffer_bindings[index].metal_buffer = buffer;
933 this->cached_vertex_buffer_bindings[index].offset = buffer_offset;
934 }
935}
936
938 uint64_t buffer_offset,
939 uint index)
940{
941 BLI_assert(index >= 0 && index < MTL_MAX_BUFFER_BINDINGS);
942 BLI_assert(buffer_offset >= 0);
943 BLI_assert(buffer != nil);
944
945 BufferBindingCached &current_frag_ubo_binding = this->cached_fragment_buffer_bindings[index];
946 if (current_frag_ubo_binding.offset != buffer_offset ||
947 current_frag_ubo_binding.metal_buffer != buffer || current_frag_ubo_binding.is_bytes)
948 {
949
950 id<MTLRenderCommandEncoder> rec = this->cmd.get_active_render_command_encoder();
951 BLI_assert(rec != nil);
952
953 if (current_frag_ubo_binding.metal_buffer == buffer) {
954 /* If buffer is the same, but offset has changed. */
955 [rec setFragmentBufferOffset:buffer_offset atIndex:index];
956 }
957 else {
958 /* Bind Fragment Buffer */
959 [rec setFragmentBuffer:buffer offset:buffer_offset atIndex:index];
960 }
961
962 /* Update Bind-state cache */
963 this->cached_fragment_buffer_bindings[index].is_bytes = false;
964 this->cached_fragment_buffer_bindings[index].metal_buffer = buffer;
965 this->cached_fragment_buffer_bindings[index].offset = buffer_offset;
966 }
967}
968
969void MTLComputeState::bind_compute_buffer(id<MTLBuffer> buffer, uint64_t buffer_offset, uint index)
970{
971 BLI_assert(index >= 0 && index < MTL_MAX_BUFFER_BINDINGS);
972 BLI_assert(buffer_offset >= 0);
973 BLI_assert(buffer != nil);
974
975 BufferBindingCached &current_comp_ubo_binding = this->cached_compute_buffer_bindings[index];
976 if (current_comp_ubo_binding.offset != buffer_offset ||
977 current_comp_ubo_binding.metal_buffer != buffer || current_comp_ubo_binding.is_bytes)
978 {
979
980 id<MTLComputeCommandEncoder> rec = this->cmd.get_active_compute_command_encoder();
981 BLI_assert(rec != nil);
982
983 if (current_comp_ubo_binding.metal_buffer == buffer) {
984 /* If buffer is the same, but offset has changed. */
985 [rec setBufferOffset:buffer_offset atIndex:index];
986 }
987 else {
988 /* Bind Compute Buffer */
989 [rec setBuffer:buffer offset:buffer_offset atIndex:index];
990 }
991
992 /* Update Bind-state cache */
993 this->cached_compute_buffer_bindings[index].is_bytes = false;
994 this->cached_compute_buffer_bindings[index].metal_buffer = buffer;
995 this->cached_compute_buffer_bindings[index].offset = buffer_offset;
996 }
997}
998
999void MTLRenderPassState::bind_vertex_bytes(const void *bytes, uint64_t length, uint index)
1000{
1001 /* Bytes always updated as source data may have changed. */
1002 BLI_assert(index >= 0 && index < MTL_MAX_BUFFER_BINDINGS);
1003 BLI_assert(length > 0);
1004 BLI_assert(bytes != nullptr);
1005
1006 if (length < MTL_MAX_SET_BYTES_SIZE) {
1007 id<MTLRenderCommandEncoder> rec = this->cmd.get_active_render_command_encoder();
1008 [rec setVertexBytes:bytes length:length atIndex:index];
1009
1010 /* Update Bind-state cache */
1011 this->cached_vertex_buffer_bindings[index].is_bytes = true;
1013 this->cached_vertex_buffer_bindings[index].offset = -1;
1014 }
1015 else {
1016 /* We have run over the setBytes limit, bind buffer instead. */
1017 MTLTemporaryBuffer range =
1019 memcpy(range.data, bytes, length);
1020 this->bind_vertex_buffer(range.metal_buffer, range.buffer_offset, index);
1021 }
1022}
1023
1024void MTLRenderPassState::bind_fragment_bytes(const void *bytes, uint64_t length, uint index)
1025{
1026 /* Bytes always updated as source data may have changed. */
1027 BLI_assert(index >= 0 && index < MTL_MAX_BUFFER_BINDINGS);
1028 BLI_assert(length > 0);
1029 BLI_assert(bytes != nullptr);
1030
1031 if (length < MTL_MAX_SET_BYTES_SIZE) {
1032 id<MTLRenderCommandEncoder> rec = this->cmd.get_active_render_command_encoder();
1033 [rec setFragmentBytes:bytes length:length atIndex:index];
1034
1035 /* Update Bind-state cache. */
1036 this->cached_fragment_buffer_bindings[index].is_bytes = true;
1038 this->cached_fragment_buffer_bindings[index].offset = -1;
1039 }
1040 else {
1041 /* We have run over the setBytes limit, bind buffer instead. */
1042 MTLTemporaryBuffer range =
1044 memcpy(range.data, bytes, length);
1045 this->bind_fragment_buffer(range.metal_buffer, range.buffer_offset, index);
1046 }
1047}
1048
1049void MTLComputeState::bind_compute_bytes(const void *bytes, uint64_t length, uint index)
1050{
1051 /* Bytes always updated as source data may have changed. */
1052 BLI_assert(index >= 0 && index < MTL_MAX_BUFFER_BINDINGS);
1053 BLI_assert(length > 0);
1054 BLI_assert(bytes != nullptr);
1055
1056 if (length < MTL_MAX_SET_BYTES_SIZE) {
1057 id<MTLComputeCommandEncoder> rec = this->cmd.get_active_compute_command_encoder();
1058 [rec setBytes:bytes length:length atIndex:index];
1059
1060 /* Update Bind-state cache. */
1061 this->cached_compute_buffer_bindings[index].is_bytes = true;
1063 this->cached_compute_buffer_bindings[index].offset = -1;
1064 }
1065 else {
1066 /* We have run over the setBytes limit, bind buffer instead. */
1067 MTLTemporaryBuffer range =
1069 memcpy(range.data, bytes, length);
1070 this->bind_compute_buffer(range.metal_buffer, range.buffer_offset, index);
1071 }
1072}
1073
1074void MTLComputeState::bind_pso(id<MTLComputePipelineState> pso)
1075{
1076 if (this->bound_pso != pso) {
1077 id<MTLComputeCommandEncoder> rec = this->cmd.get_active_compute_command_encoder();
1078 [rec setComputePipelineState:pso];
1079 this->bound_pso = pso;
1080 }
1081}
1082
1085} // namespace blender::gpu
@ G_DEBUG_GPU
#define BLI_assert(a)
Definition BLI_assert.h:50
unsigned int uint
#define UNUSED_VARS_NDEBUG(...)
@ GPU_DRIVER_ANY
@ GPU_ARCHITECTURE_TBDR
@ GPU_OS_ANY
@ GPU_DEVICE_ATI
@ GPU_DEVICE_INTEL
bool GPU_type_matches(eGPUDeviceType device, eGPUOSType os, eGPUDriverType driver)
GPUArchitectureType GPU_platform_architecture()
eGPUStageBarrierBits
Definition GPU_state.hh:65
@ GPU_BARRIER_STAGE_FRAGMENT
Definition GPU_state.hh:67
@ GPU_BARRIER_STAGE_VERTEX
Definition GPU_state.hh:66
eGPUBarrier
Definition GPU_state.hh:29
@ GPU_BARRIER_SHADER_STORAGE
Definition GPU_state.hh:48
@ GPU_BARRIER_TEXTURE_FETCH
Definition GPU_state.hh:37
@ GPU_BARRIER_BUFFER_UPDATE
Definition GPU_state.hh:56
@ GPU_BARRIER_ELEMENT_ARRAY
Definition GPU_state.hh:52
@ GPU_BARRIER_UNIFORM
Definition GPU_state.hh:54
@ GPU_BARRIER_SHADER_IMAGE_ACCESS
Definition GPU_state.hh:35
@ GPU_BARRIER_VERTEX_ATTRIB_ARRAY
Definition GPU_state.hh:50
void init()
SIMD_FORCE_INLINE btScalar length() const
Return the length of the vector.
Definition btVector3.h:257
static const int max_command_buffer_count
const char *const name_get() const
static MTLBackend * get()
MTLSafeFreeList * get_current_safe_list()
id< MTLBuffer > get_metal_buffer() const
void encode_signal_event(id< MTLEvent > event, uint64_t value)
id< MTLComputeCommandEncoder > get_active_compute_command_encoder()
void register_draw_counters(int vertex_submission)
void prepare(bool supports_render=true)
id< MTLRenderCommandEncoder > ensure_begin_render_command_encoder(MTLFrameBuffer *ctx_framebuffer, bool force_begin, bool *r_new_pass)
id< MTLComputeCommandEncoder > ensure_begin_compute_encoder()
bool insert_memory_barrier(eGPUBarrier barrier_bits, eGPUStageBarrierBits before_stages, eGPUStageBarrierBits after_stages)
id< MTLBlitCommandEncoder > ensure_begin_blit_encoder()
void push_debug_group(const char *name, int index)
id< MTLRenderCommandEncoder > get_active_render_command_encoder()
void encode_wait_for_event(id< MTLEvent > event, uint64_t value)
id< MTLBlitCommandEncoder > get_active_blit_command_encoder()
void bind_compute_bytes(const void *bytes, uint64_t length, uint index)
id< MTLComputePipelineState > bound_pso
void bind_compute_sampler(MTLSamplerBinding &sampler_binding, bool use_argument_buffer_for_samplers, uint slot)
MTLCommandBufferManager & cmd
BufferBindingCached cached_compute_buffer_bindings[MTL_MAX_BUFFER_BINDINGS]
void bind_compute_texture(id< MTLTexture > tex, uint slot)
TextureBindingCached cached_compute_texture_bindings[MTL_MAX_TEXTURE_SLOTS]
void bind_compute_buffer(id< MTLBuffer > buffer, uint64_t buffer_offset, uint index)
void bind_pso(id< MTLComputePipelineState > pso)
SamplerStateBindingCached cached_compute_sampler_state_bindings[MTL_MAX_TEXTURE_SLOTS]
id< MTLSamplerState > get_sampler_from_state(MTLSamplerState state)
gpu::MTLBuffer * get_visibility_buffer() const
MTLScratchBufferManager memory_manager
MTLContextGlobalShaderPipelineState pipeline_state
id< MTLSamplerState > get_default_sampler_state()
MTLScratchBufferManager & get_scratchbuffer_manager()
id< MTLCommandQueue > queue
static MTLBufferPool * get_global_memory_manager()
MTLRenderPassDescriptor * bake_render_pass_descriptor(bool load_contents)
void bind_fragment_sampler(MTLSamplerBinding &sampler_binding, bool use_argument_buffer_for_samplers, uint slot)
void bind_fragment_bytes(const void *bytes, uint64_t length, uint index)
SamplerStateBindingCached cached_fragment_sampler_state_bindings[MTL_MAX_TEXTURE_SLOTS]
void bind_vertex_sampler(MTLSamplerBinding &sampler_binding, bool use_argument_buffer_for_samplers, uint slot)
void bind_vertex_buffer(id< MTLBuffer > buffer, uint64_t buffer_offset, uint index)
void bind_vertex_texture(id< MTLTexture > tex, uint slot)
void bind_vertex_bytes(const void *bytes, uint64_t length, uint index)
id< MTLDepthStencilState > bound_ds_state
TextureBindingCached cached_vertex_texture_bindings[MTL_MAX_TEXTURE_SLOTS]
SamplerStateBindingCached cached_vertex_sampler_state_bindings[MTL_MAX_TEXTURE_SLOTS]
MTLCommandBufferManager & cmd
BufferBindingCached cached_fragment_buffer_bindings[MTL_MAX_BUFFER_BINDINGS]
id< MTLRenderPipelineState > bound_pso
BufferBindingCached cached_vertex_buffer_bindings[MTL_MAX_BUFFER_BINDINGS]
TextureBindingCached cached_fragment_texture_bindings[MTL_MAX_TEXTURE_SLOTS]
MTLBoundShaderState last_bound_shader_state
void bind_fragment_texture(id< MTLTexture > tex, uint slot)
void bind_fragment_buffer(id< MTLBuffer > buffer, uint64_t buffer_offset, uint index)
MTLTemporaryBuffer scratch_buffer_allocate_range_aligned(uint64_t alloc_size, uint alignment)
MTLShaderInterface * get_interface()
BLI_INLINE float fb(float length, float L)
#define G(x, y, z)
static void error(const char *str)
#define MTL_MAX_BUFFER_BINDINGS
#define MTL_MAX_TEXTURE_SLOTS
#define MTL_MAX_SET_BYTES_SIZE
Definition mtl_common.hh:12
#define METAL_DEBUG_CAPTURE_MAX_NESTED_GROUPS
Definition mtl_debug.hh:19
#define MTL_LOG_WARNING(info,...)
Definition mtl_debug.hh:44
static int sampler_binding(int32_t program, uint32_t uniform_index, int32_t uniform_location, int *sampler_len)
const MTLSamplerState DEFAULT_SAMPLER_STATE
unsigned __int64 uint64_t
Definition stdint.h:90
void set(MTLShader *shader, uint pso_index)
MTLSamplerState mtl_sampler_flags[MTL_MAX_TEXTURE_SLOTS]
id< MTLSamplerState > mtl_sampler[MTL_MAX_TEXTURE_SLOTS]