Blender V5.0
mtl_context.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
8#include "mtl_context.hh"
9#include "mtl_debug.hh"
10#include "mtl_framebuffer.hh"
11#include "mtl_immediate.hh"
12#include "mtl_memory.hh"
13#include "mtl_primitive.hh"
14#include "mtl_shader.hh"
16#include "mtl_state.hh"
17#include "mtl_storage_buffer.hh"
18#include "mtl_uniform_buffer.hh"
19#include "mtl_vertex_buffer.hh"
20
21#include "DNA_userdef_types.h"
22
23#include "GPU_capabilities.hh"
24#include "GPU_matrix.hh"
25#include "GPU_shader.hh"
26#include "GPU_storage_buffer.hh"
27#include "GPU_texture.hh"
28#include "GPU_uniform_buffer.hh"
29#include "GPU_vertex_buffer.hh"
31
32#include "BLI_time.h"
33
34#include <fstream>
35#include <string>
36
37using namespace blender;
38using namespace blender::gpu;
39
40/* Fire off a single dispatch per encoder. Can make debugging view clearer for texture resources
41 * associated with each dispatch. */
42#if defined(NDEBUG)
43# define MTL_DEBUG_SINGLE_DISPATCH_PER_ENCODER 0
44#else
45# define MTL_DEBUG_SINGLE_DISPATCH_PER_ENCODER 1
46#endif
47
48/* Debug option to bind null buffer for missing UBOs. */
49#define DEBUG_BIND_NULL_BUFFER_FOR_MISSING_UBO 0
50
51/* Debug option to bind null buffer for missing SSBOs. NOTE: This is unsafe if replacing a
52 * write-enabled SSBO and should only be used for debugging to identify binding-related issues. */
53#define DEBUG_BIND_NULL_BUFFER_FOR_MISSING_SSBO 0
54
55/* Error or warning depending on debug flag. */
56#if DEBUG_BIND_NULL_BUFFER_FOR_MISSING_UBO == 1
57# define MTL_LOG_UBO_ERROR MTL_LOG_WARNING
58#else
59# define MTL_LOG_UBO_ERROR MTL_LOG_ERROR
60#endif
61
62#if DEBUG_BIND_NULL_BUFFER_FOR_MISSING_SSBO == 1
63# define MTL_LOG_SSBO_ERROR MTL_LOG_WARNING
64#else
65# define MTL_LOG_SSBO_ERROR MTL_LOG_ERROR
66#endif
67
68namespace blender::gpu {
69
70/* Global memory manager. */
74
75/* Swap-chain and latency management. */
76std::atomic<int> MTLContext::max_drawables_in_flight = 0;
77std::atomic<int64_t> MTLContext::avg_drawable_latency_us = 0;
79
80/* -------------------------------------------------------------------- */
83
84void MTLContext::set_ghost_context(GHOST_ContextHandle ghostCtxHandle)
85{
86 GHOST_Context *ghost_ctx = reinterpret_cast<GHOST_Context *>(ghostCtxHandle);
87 BLI_assert(ghost_ctx != nullptr);
88
89 /* Release old MTLTexture handle */
90 if (default_fbo_mtltexture_) {
91 [default_fbo_mtltexture_ release];
92 default_fbo_mtltexture_ = nil;
93 }
94
95 /* Release Framebuffer attachments */
96 MTLFrameBuffer *mtl_front_left = static_cast<MTLFrameBuffer *>(this->front_left);
97 MTLFrameBuffer *mtl_back_left = static_cast<MTLFrameBuffer *>(this->back_left);
98 mtl_front_left->remove_all_attachments();
99 mtl_back_left->remove_all_attachments();
100
101 GHOST_ContextMTL *ghost_mtl_ctx = dynamic_cast<GHOST_ContextMTL *>(ghost_ctx);
102 if (ghost_mtl_ctx != nullptr) {
103 default_fbo_mtltexture_ = ghost_mtl_ctx->metalOverlayTexture();
104
106 "Binding GHOST context MTL %p to GPU context %p. (Device: %p, queue: %p, texture: %p)",
107 ghost_mtl_ctx,
108 this,
109 this->device,
110 this->queue,
111 default_fbo_gputexture_);
112
113 /* Check if the GHOST Context provides a default framebuffer: */
114 if (default_fbo_mtltexture_) {
115
116 /* Release old gpu::Texture handle */
117 if (default_fbo_gputexture_) {
118 GPU_texture_free(default_fbo_gputexture_);
119 default_fbo_gputexture_ = nullptr;
120 }
121
122 /* Retain handle */
123 [default_fbo_mtltexture_ retain];
124
125 /*** Create front and back-buffers ***/
126 /* Create gpu::MTLTexture objects */
127 default_fbo_gputexture_ = new gpu::MTLTexture("MTL_BACKBUFFER",
128 TextureFormat::SFLOAT_16_16_16_16,
130 default_fbo_mtltexture_);
131
132 /* Update frame-buffers with new texture attachments. */
133 mtl_front_left->add_color_attachment(default_fbo_gputexture_, 0, 0, 0);
134 mtl_back_left->add_color_attachment(default_fbo_gputexture_, 0, 0, 0);
135#ifndef NDEBUG
136 this->label = default_fbo_mtltexture_.label;
137#endif
138 }
139 else {
140
141 /* Add default texture for cases where no other framebuffer is bound */
142 if (!default_fbo_gputexture_) {
143 default_fbo_gputexture_ = static_cast<gpu::MTLTexture *>(
144 GPU_texture_create_2d(__func__,
145 16,
146 16,
147 1,
148 TextureFormat::SFLOAT_16_16_16_16,
150 nullptr));
151 }
152 mtl_back_left->add_color_attachment(default_fbo_gputexture_, 0, 0, 0);
153
155 "-- Bound context %p for GPU context: %p is offscreen and does not have a default "
156 "framebuffer",
157 ghost_mtl_ctx,
158 this);
159#ifndef NDEBUG
160 this->label = @"Offscreen Metal Context";
161#endif
162 }
163 }
164 else {
166 " Failed to bind GHOST context to MTLContext -- GHOST_ContextMTL is null "
167 "(GhostContext: %p, GhostContext_MTL: %p)",
168 ghost_ctx,
169 ghost_mtl_ctx);
170 BLI_assert(false);
171 }
172}
173
174void MTLContext::set_ghost_window(GHOST_WindowHandle ghostWinHandle)
175{
176 GHOST_Window *ghostWin = reinterpret_cast<GHOST_Window *>(ghostWinHandle);
177 this->set_ghost_context((GHOST_ContextHandle)(ghostWin ? ghostWin->getContext() : nullptr));
178}
179
181
182/* -------------------------------------------------------------------- */
185
186/* Placeholder functions */
187MTLContext::MTLContext(void *ghost_window, void *ghost_context)
188 : memory_manager(*this), main_command_buffer(*this)
189{
190 /* Init debug. */
192
193 /* Initialize Render-pass and Frame-buffer State. */
194 this->back_left = nullptr;
195
196 /* Initialize command buffer state. */
197 this->main_command_buffer.prepare();
198
199 /* Initialize IMM and pipeline state */
200 this->pipeline_state.initialised = false;
201
202 /* Frame management. */
203 is_inside_frame_ = false;
204 current_frame_index_ = 0;
205
206 /* Prepare null data buffer. */
207 null_buffer_ = nil;
208 null_attribute_buffer_ = nil;
209
210 /* Zero-initialize MTL textures. */
211 default_fbo_mtltexture_ = nil;
212 default_fbo_gputexture_ = nullptr;
213
215 ghost_window_ = ghost_window;
216 if (ghost_window_ && ghost_context == nullptr) {
217 /* NOTE(Metal): Fetch ghost_context from ghost_window if it is not provided.
218 * Regardless of whether windowed or not, we need access to the GhostContext
219 * for presentation, and device/queue access. */
220 GHOST_Window *ghostWin = reinterpret_cast<GHOST_Window *>(ghost_window_);
221 ghost_context = (ghostWin ? ghostWin->getContext() : nullptr);
222 }
223 BLI_assert(ghost_context);
224 this->ghost_context_ = static_cast<GHOST_ContextMTL *>(ghost_context);
225 this->queue = (id<MTLCommandQueue>)this->ghost_context_->metalCommandQueue();
226 this->device = (id<MTLDevice>)this->ghost_context_->metalDevice();
227 BLI_assert(this->queue);
228 BLI_assert(this->device);
229 [this->queue retain];
230 [this->device retain];
231
232#pragma clang diagnostic push
233#pragma clang diagnostic ignored "-Wobjc-method-access"
234 /* Enable increased concurrent shader compiler limit.
235 * NOTE: Disable warning for missing method when building on older OS's, as compiled code will
236 * still work correctly when run on a system with the API available. */
237 if (@available(macOS 13.3, *)) {
238 [this->device setShouldMaximizeConcurrentCompilation:YES];
239 }
240#pragma clang diagnostic pop
241
242 /* Register present callback. */
243 this->ghost_context_->metalRegisterPresentCallback(&present);
244
245 /* Create FrameBuffer handles. */
246 MTLFrameBuffer *mtl_front_left = new MTLFrameBuffer(this, "front_left");
247 MTLFrameBuffer *mtl_back_left = new MTLFrameBuffer(this, "back_left");
248 this->front_left = mtl_front_left;
249 this->back_left = mtl_back_left;
250 this->active_fb = this->back_left;
251
252 /* Prepare platform and capabilities. (NOTE: With METAL, this needs to be done after CTX
253 * initialization). */
254 MTLBackend::platform_init(this);
255 MTLBackend::capabilities_init(this);
256
257 /* Ensure global memory manager is initialized. */
260
261 /* Initialize Metal modules. */
262 this->memory_manager.init();
263 this->state_manager = new MTLStateManager(this);
264 this->imm = new MTLImmediate(this);
265
266 /* Initialize texture read/update structures. */
267 this->get_texture_utils().init();
268
269 /* Bound Samplers struct. */
270 for (int i = 0; i < MTL_MAX_TEXTURE_SLOTS; i++) {
271 samplers_.mtl_sampler[i] = nil;
272 samplers_.mtl_sampler_flags[i] = DEFAULT_SAMPLER_STATE;
273 }
274
275 /* Initialize samplers. */
277}
278
280{
281 BLI_assert(this == MTLContext::get());
282 /* Ensure rendering is complete command encoders/command buffers are freed. */
283 if (MTLBackend::get()->is_inside_render_boundary()) {
284 this->finish();
285
286 /* End frame. */
287 if (this->get_inside_frame()) {
288 this->end_frame();
289 }
290 }
291
292 /* Wait for all GPU work to finish. */
293 main_command_buffer.wait_until_active_command_buffers_complete();
294
295 /* Free textures and frame-buffers in base class. */
297
298 /* Release context textures. */
299 if (default_fbo_gputexture_) {
300 GPU_texture_free(default_fbo_gputexture_);
301 default_fbo_gputexture_ = nullptr;
302 }
303 if (default_fbo_mtltexture_) {
304 [default_fbo_mtltexture_ release];
305 default_fbo_mtltexture_ = nil;
306 }
307
308 /* Release Memory Manager */
310
311 /* Release update/blit shaders. */
312 this->get_texture_utils().cleanup();
313 this->get_compute_utils().cleanup();
314
315 /* Detach resource references. */
317
318 /* Unbind UBOs. */
319 for (int i = 0; i < MTL_MAX_BUFFER_BINDINGS; i++) {
320 if (this->pipeline_state.ubo_bindings[i].bound &&
321 this->pipeline_state.ubo_bindings[i].ubo != nullptr)
322 {
323 gpu::UniformBuf *ubo = this->pipeline_state.ubo_bindings[i].ubo;
325 }
326 }
327
328 /* Unbind SSBOs. */
329 for (int i = 0; i < MTL_MAX_BUFFER_BINDINGS; i++) {
330 if (this->pipeline_state.ssbo_bindings[i].bound &&
331 this->pipeline_state.ssbo_bindings[i].ssbo != nullptr)
332 {
333 this->pipeline_state.ssbo_bindings[i].ssbo->unbind();
334 }
335 }
336
337 /* Release Dummy resources. */
338 this->free_dummy_resources();
339
340 /* Release Sampler States. */
341 for (int extend_yz_i = 0; extend_yz_i < GPU_SAMPLER_EXTEND_MODES_COUNT; extend_yz_i++) {
342 for (int extend_x_i = 0; extend_x_i < GPU_SAMPLER_EXTEND_MODES_COUNT; extend_x_i++) {
343 for (int filtering_i = 0; filtering_i < GPU_SAMPLER_FILTERING_TYPES_COUNT; filtering_i++) {
344 if (sampler_state_cache_[extend_yz_i][extend_x_i][filtering_i] != nil) {
345 [sampler_state_cache_[extend_yz_i][extend_x_i][filtering_i] release];
346 sampler_state_cache_[extend_yz_i][extend_x_i][filtering_i] = nil;
347 }
348 }
349 }
350 }
351
352 /* Release Custom Sampler States. */
353 for (int i = 0; i < GPU_SAMPLER_CUSTOM_TYPES_COUNT; i++) {
354 if (custom_sampler_state_cache_[i] != nil) {
355 [custom_sampler_state_cache_[i] release];
356 custom_sampler_state_cache_[i] = nil;
357 }
358 }
359
360 /* Empty cached sampler argument buffers. */
361 for (auto *entry : cached_sampler_buffers_.values()) {
362 entry->free();
363 }
364 cached_sampler_buffers_.clear();
365
366 /* Free null buffers. */
367 if (null_buffer_) {
368 [null_buffer_ release];
369 }
370 if (null_attribute_buffer_) {
371 [null_attribute_buffer_ release];
372 }
373
374 /* Release memory manager reference. */
376
377 /* Free Metal objects. */
378 if (this->queue) {
379 [this->queue release];
380 }
381 if (this->device) {
382 [this->device release];
383 }
384
385 this->process_frame_timings();
386}
387
389{
390 BLI_assert(MTLBackend::get()->is_inside_render_boundary());
391 if (this->get_inside_frame()) {
392 return;
393 }
394
395 /* Begin Command buffer for next frame. */
396 is_inside_frame_ = true;
397}
398
400{
402
403 /* Ensure pre-present work is committed. */
404 this->flush();
405
406 /* Increment frame counter. */
407 is_inside_frame_ = false;
408
409 this->process_frame_timings();
410}
411
412void MTLContext::check_error(const char * /*info*/)
413{
414 /* TODO(Metal): Implement. */
415}
416
418{
419 /* Make sure no other context is already bound to this thread. */
420 BLI_assert(is_active_ == false);
421 is_active_ = true;
422 thread_ = pthread_self();
423
424 /* Re-apply ghost window/context for resizing */
425 if (ghost_window_) {
426 this->set_ghost_window((GHOST_WindowHandle)ghost_window_);
427 }
428 else if (ghost_context_) {
429 this->set_ghost_context((GHOST_ContextHandle)ghost_context_);
430 }
431
432 /* Reset UBO bind state. */
433 for (int i = 0; i < MTL_MAX_BUFFER_BINDINGS; i++) {
434 if (this->pipeline_state.ubo_bindings[i].bound &&
435 this->pipeline_state.ubo_bindings[i].ubo != nullptr)
436 {
437 this->pipeline_state.ubo_bindings[i].bound = false;
438 this->pipeline_state.ubo_bindings[i].ubo = nullptr;
439 }
440 }
441
442 /* Reset SSBO bind state. */
443 for (int i = 0; i < MTL_MAX_BUFFER_BINDINGS; i++) {
444 if (this->pipeline_state.ssbo_bindings[i].bound &&
445 this->pipeline_state.ssbo_bindings[i].ssbo != nullptr)
446 {
447 this->pipeline_state.ssbo_bindings[i].bound = false;
448 this->pipeline_state.ssbo_bindings[i].ssbo = nullptr;
449 }
450 }
451
452 /* Ensure imm active. */
453 immActivate();
454}
455
457{
459 /* Flush context on deactivate. */
460 this->flush();
461 is_active_ = false;
463}
464
466{
467 this->main_command_buffer.submit(false);
468}
469
471{
472 this->main_command_buffer.submit(true);
473}
474
475void MTLContext::memory_statistics_get(int *r_total_mem, int *r_free_mem)
476{
477 /* TODO(Metal): Implement. */
478 *r_total_mem = 0;
479 *r_free_mem = 0;
480}
481
483{
484 /* We do not yet begin the pass -- We defer beginning the pass until a draw is requested. */
485 BLI_assert(framebuffer);
486 this->active_fb = framebuffer;
487}
488
490{
491 /* Bind default framebuffer from context --
492 * We defer beginning the pass until a draw is requested. */
493 this->active_fb = this->back_left;
494}
495
496id<MTLRenderCommandEncoder> MTLContext::ensure_begin_render_pass()
497{
498 BLI_assert(this);
499
500 /* Ensure the rendering frame has started. */
501 if (!this->get_inside_frame()) {
502 this->begin_frame();
503 }
504
505 /* Check whether a framebuffer is bound. */
506 if (!this->active_fb) {
507 BLI_assert(false && "No framebuffer is bound!");
508 return this->main_command_buffer.get_active_render_command_encoder();
509 }
510
511 /* Ensure command buffer workload submissions are optimal --
512 * Though do not split a batch mid-IMM recording. */
513 if (this->main_command_buffer.do_break_submission() &&
514 !((MTLImmediate *)(this->imm))->imm_is_recording())
515 {
516 this->flush();
517 }
518
519 /* Begin pass or perform a pass switch if the active framebuffer has been changed, or if the
520 * framebuffer state has been modified (is_dirty). */
521 if (!this->main_command_buffer.is_inside_render_pass() ||
522 this->active_fb != this->main_command_buffer.get_active_framebuffer() ||
523 this->main_command_buffer.get_active_framebuffer()->get_dirty() ||
524 this->is_visibility_dirty())
525 {
526
527 /* Validate bound framebuffer before beginning render pass. */
528 if (!static_cast<MTLFrameBuffer *>(this->active_fb)->validate_render_pass()) {
529 MTL_LOG_WARNING("Framebuffer validation failed, falling back to default framebuffer");
530 this->framebuffer_restore();
531
532 if (!static_cast<MTLFrameBuffer *>(this->active_fb)->validate_render_pass()) {
533 MTL_LOG_ERROR("CRITICAL: DEFAULT FRAMEBUFFER FAIL VALIDATION!!");
534 }
535 }
536
537 /* Begin RenderCommandEncoder on main CommandBuffer. */
538 bool new_render_pass = false;
539 id<MTLRenderCommandEncoder> new_enc =
540 this->main_command_buffer.ensure_begin_render_command_encoder(
541 static_cast<MTLFrameBuffer *>(this->active_fb), true, &new_render_pass);
542 if (new_render_pass) {
543 /* Flag context pipeline state as dirty - dynamic pipeline state need re-applying. */
545 }
546 return new_enc;
547 }
548 BLI_assert(!this->main_command_buffer.get_active_framebuffer()->get_dirty());
549 return this->main_command_buffer.get_active_render_command_encoder();
550}
551
553{
554 MTLFrameBuffer *last_bound = static_cast<MTLFrameBuffer *>(this->active_fb);
555 return last_bound ? last_bound : this->get_default_framebuffer();
556}
557
559{
560 return static_cast<MTLFrameBuffer *>(this->back_left);
561}
562
564{
565 return this->pipeline_state.active_shader;
566}
567
569{
570 if (null_buffer_ != nil) {
571 return null_buffer_;
572 }
573
574 /* TODO(mpw_apple_gpusw): Null buffer size temporarily increased to cover
575 * maximum possible UBO size. There are a number of cases which need to be
576 * resolved in the high level where an expected UBO does not have a bound
577 * buffer. The null buffer needs to at least cover the size of these
578 * UBOs to avoid any GPU memory issues. */
579 static const int null_buffer_size = 20480;
580 null_buffer_ = [this->device newBufferWithLength:null_buffer_size
581 options:MTLResourceStorageModeManaged];
582 [null_buffer_ retain];
583 uint32_t *null_data = (uint32_t *)calloc(1, null_buffer_size);
584 memcpy([null_buffer_ contents], null_data, null_buffer_size);
585 [null_buffer_ didModifyRange:NSMakeRange(0, null_buffer_size)];
586 free(null_data);
587
588 BLI_assert(null_buffer_ != nil);
589 return null_buffer_;
590}
591
593{
594 if (null_attribute_buffer_ != nil) {
595 return null_attribute_buffer_;
596 }
597
598 /* Allocate Null buffer if it has not yet been created.
599 * Min buffer size is 256 bytes -- though we only need 64 bytes of data. */
600 static const int null_buffer_size = 256;
601 null_attribute_buffer_ = [this->device newBufferWithLength:null_buffer_size
602 options:MTLResourceStorageModeManaged];
603 BLI_assert(null_attribute_buffer_ != nil);
604 [null_attribute_buffer_ retain];
605 float data[4] = {0.0f, 0.0f, 0.0f, 1.0f};
606 memcpy([null_attribute_buffer_ contents], data, sizeof(float) * 4);
607 [null_attribute_buffer_ didModifyRange:NSMakeRange(0, null_buffer_size)];
608
609 return null_attribute_buffer_;
610}
611
613 GPUSamplerFormat sampler_format)
614{
615 /* Decrement 1 from texture type as they start from 1 and go to 32 (inclusive). Remap to 0..31 */
616 gpu::MTLTexture *dummy_tex = dummy_textures_[sampler_format][type - 1];
617 if (dummy_tex != nullptr) {
618 return dummy_tex;
619 }
620 /* Determine format for dummy texture. */
621 TextureFormat format = TextureFormat::UNORM_8_8_8_8;
622 switch (sampler_format) {
624 format = TextureFormat::UNORM_8_8_8_8;
625 break;
627 format = TextureFormat::SINT_8_8_8_8;
628 break;
630 format = TextureFormat::UINT_8_8_8_8;
631 break;
633 format = TextureFormat::SFLOAT_32_DEPTH_UINT_8;
634 break;
635 default:
637 }
638
639 /* Create dummy texture based on desired type. */
640 gpu::Texture *tex = nullptr;
642 switch (type) {
643 case GPU_TEXTURE_1D:
644 tex = GPU_texture_create_1d("Dummy 1D", 128, 1, format, usage, nullptr);
645 break;
647 tex = GPU_texture_create_1d_array("Dummy 1DArray", 128, 1, 1, format, usage, nullptr);
648 break;
649 case GPU_TEXTURE_2D:
650 tex = GPU_texture_create_2d("Dummy 2D", 128, 128, 1, format, usage, nullptr);
651 break;
653 tex = GPU_texture_create_2d_array("Dummy 2DArray", 128, 128, 1, 1, format, usage, nullptr);
654 break;
655 case GPU_TEXTURE_3D:
656 tex = GPU_texture_create_3d("Dummy 3D", 128, 128, 1, 1, format, usage, nullptr);
657 break;
658 case GPU_TEXTURE_CUBE:
659 tex = GPU_texture_create_cube("Dummy Cube", 128, 1, format, usage, nullptr);
660 break;
662 tex = GPU_texture_create_cube_array("Dummy CubeArray", 128, 1, 1, format, usage, nullptr);
663 break;
665 if (!dummy_verts_[sampler_format]) {
666 GPU_vertformat_clear(&dummy_vertformat_[sampler_format]);
667
668 VertAttrType attr_type = VertAttrType::SFLOAT_32_32_32_32;
669
670 switch (sampler_format) {
673 attr_type = VertAttrType::SFLOAT_32_32_32_32;
674 break;
676 attr_type = VertAttrType::SINT_32_32_32_32;
677 break;
679 attr_type = VertAttrType::UINT_32_32_32_32;
680 break;
681 default:
683 }
684
685 GPU_vertformat_attr_add(&dummy_vertformat_[sampler_format], "dummy", attr_type);
686 dummy_verts_[sampler_format] = GPU_vertbuf_create_with_format_ex(
687 dummy_vertformat_[sampler_format],
689 GPU_vertbuf_data_alloc(*dummy_verts_[sampler_format], 64);
690 }
691 tex = GPU_texture_create_from_vertbuf("Dummy TextureBuffer", dummy_verts_[sampler_format]);
692 break;
693 default:
694 BLI_assert_msg(false, "Unrecognised texture type");
695 return nullptr;
696 }
697 gpu::MTLTexture *metal_tex = static_cast<gpu::MTLTexture *>(reinterpret_cast<Texture *>(tex));
698 dummy_textures_[sampler_format][type - 1] = metal_tex;
699 return metal_tex;
700}
701
703{
704 for (int format = 0; format < GPU_SAMPLER_TYPE_MAX; format++) {
705 for (int tex = 0; tex < GPU_TEXTURE_BUFFER; tex++) {
706 if (dummy_textures_[format][tex]) {
707 GPU_texture_free(reinterpret_cast<gpu::Texture *>(
708 static_cast<Texture *>(dummy_textures_[format][tex])));
709 dummy_textures_[format][tex] = nullptr;
710 }
711 }
712 if (dummy_verts_[format]) {
713 GPU_vertbuf_discard(dummy_verts_[format]);
714 }
715 }
716}
717
724
726
727/* -------------------------------------------------------------------- */
730
731/* Metal Context Pipeline State. */
733{
734 /*** Initialize state only once. ***/
735 if (!this->pipeline_state.initialised) {
736 this->pipeline_state.initialised = true;
737 this->pipeline_state.active_shader = nullptr;
738
739 /* Clear bindings state. */
740 for (int t = 0; t < GPU_max_textures(); t++) {
741 /* Textures. */
742 this->pipeline_state.texture_bindings[t].used = false;
743 this->pipeline_state.texture_bindings[t].texture_resource = nullptr;
744
745 /* Images. */
746 this->pipeline_state.image_bindings[t].used = false;
747 this->pipeline_state.image_bindings[t].texture_resource = nullptr;
748 }
749 for (int s = 0; s < MTL_MAX_SAMPLER_SLOTS; s++) {
750 this->pipeline_state.sampler_bindings[s].used = false;
751 }
752 for (int u = 0; u < MTL_MAX_BUFFER_BINDINGS; u++) {
753 this->pipeline_state.ubo_bindings[u].bound = false;
754 this->pipeline_state.ubo_bindings[u].ubo = nullptr;
755 }
756 for (int u = 0; u < MTL_MAX_BUFFER_BINDINGS; u++) {
757 this->pipeline_state.ssbo_bindings[u].bound = false;
758 this->pipeline_state.ssbo_bindings[u].ssbo = nullptr;
759 }
760 }
761
762 /*** State defaults -- restored by GPU_state_init. ***/
763 /* Clear blending State. */
764 this->pipeline_state.color_write_mask = MTLColorWriteMaskRed | MTLColorWriteMaskGreen |
765 MTLColorWriteMaskBlue | MTLColorWriteMaskAlpha;
766 this->pipeline_state.blending_enabled = false;
767 this->pipeline_state.alpha_blend_op = MTLBlendOperationAdd;
768 this->pipeline_state.rgb_blend_op = MTLBlendOperationAdd;
769 this->pipeline_state.dest_alpha_blend_factor = MTLBlendFactorZero;
770 this->pipeline_state.dest_rgb_blend_factor = MTLBlendFactorZero;
771 this->pipeline_state.src_alpha_blend_factor = MTLBlendFactorOne;
772 this->pipeline_state.src_rgb_blend_factor = MTLBlendFactorOne;
773
774 /* Viewport and scissor. */
775 for (int v = 0; v < GPU_MAX_VIEWPORTS; v++) {
776 this->pipeline_state.viewport_offset_x[v] = 0;
777 this->pipeline_state.viewport_offset_y[v] = 0;
778 this->pipeline_state.viewport_width[v] = 0;
779 this->pipeline_state.viewport_height[v] = 0;
780 }
781 this->pipeline_state.scissor_x = 0;
782 this->pipeline_state.scissor_y = 0;
783 this->pipeline_state.scissor_width = 0;
784 this->pipeline_state.scissor_height = 0;
785 this->pipeline_state.scissor_enabled = false;
786
787 /* Culling State. */
788 this->pipeline_state.culling_enabled = false;
789 this->pipeline_state.cull_mode = GPU_CULL_NONE;
790 this->pipeline_state.front_face = GPU_COUNTERCLOCKWISE;
791
792 /* DATA and IMAGE access state. */
793 this->pipeline_state.unpack_row_length = 0;
794
795 /* Depth State. */
796 this->pipeline_state.depth_stencil_state.depth_write_enable = false;
797 this->pipeline_state.depth_stencil_state.depth_test_enabled = false;
798 this->pipeline_state.depth_stencil_state.depth_range_near = 0.0;
799 this->pipeline_state.depth_stencil_state.depth_range_far = 1.0;
800 this->pipeline_state.depth_stencil_state.depth_function = MTLCompareFunctionAlways;
801 this->pipeline_state.depth_stencil_state.depth_bias = 0.0;
802 this->pipeline_state.depth_stencil_state.depth_slope_scale = 0.0;
803 this->pipeline_state.depth_stencil_state.depth_bias_enabled_for_points = false;
804 this->pipeline_state.depth_stencil_state.depth_bias_enabled_for_lines = false;
805 this->pipeline_state.depth_stencil_state.depth_bias_enabled_for_tris = false;
806
807 /* Stencil State. */
808 this->pipeline_state.depth_stencil_state.stencil_test_enabled = false;
809 this->pipeline_state.depth_stencil_state.stencil_read_mask = 0xFF;
810 this->pipeline_state.depth_stencil_state.stencil_write_mask = 0xFF;
811 this->pipeline_state.depth_stencil_state.stencil_ref = 0;
812 this->pipeline_state.depth_stencil_state.stencil_func = MTLCompareFunctionAlways;
813 this->pipeline_state.depth_stencil_state.stencil_op_front_stencil_fail = MTLStencilOperationKeep;
814 this->pipeline_state.depth_stencil_state.stencil_op_front_depth_fail = MTLStencilOperationKeep;
815 this->pipeline_state.depth_stencil_state.stencil_op_front_depthstencil_pass =
816 MTLStencilOperationKeep;
817 this->pipeline_state.depth_stencil_state.stencil_op_back_stencil_fail = MTLStencilOperationKeep;
818 this->pipeline_state.depth_stencil_state.stencil_op_back_depth_fail = MTLStencilOperationKeep;
819 this->pipeline_state.depth_stencil_state.stencil_op_back_depthstencil_pass =
820 MTLStencilOperationKeep;
821}
822
823void MTLContext::set_viewport(int origin_x, int origin_y, int width, int height)
824{
825 BLI_assert(this);
826 BLI_assert(width > 0);
827 BLI_assert(height > 0);
828 BLI_assert(origin_x >= 0);
829 BLI_assert(origin_y >= 0);
830 bool changed = (this->pipeline_state.viewport_offset_x[0] != origin_x) ||
831 (this->pipeline_state.viewport_offset_y[0] != origin_y) ||
832 (this->pipeline_state.viewport_width[0] != width) ||
833 (this->pipeline_state.viewport_height[0] != height) ||
834 (this->pipeline_state.num_active_viewports != 1);
835 this->pipeline_state.viewport_offset_x[0] = origin_x;
836 this->pipeline_state.viewport_offset_y[0] = origin_y;
837 this->pipeline_state.viewport_width[0] = width;
838 this->pipeline_state.viewport_height[0] = height;
839 this->pipeline_state.num_active_viewports = 1;
840
841 if (changed) {
842 this->pipeline_state.dirty_flags = (this->pipeline_state.dirty_flags |
844 }
845}
846
847void MTLContext::set_viewports(int count, const int (&viewports)[GPU_MAX_VIEWPORTS][4])
848{
849 BLI_assert(this);
850 bool changed = (this->pipeline_state.num_active_viewports != count);
851 for (int v = 0; v < count; v++) {
852 const int (&viewport_info)[4] = viewports[v];
853
854 BLI_assert(viewport_info[0] >= 0);
855 BLI_assert(viewport_info[1] >= 0);
856 BLI_assert(viewport_info[2] > 0);
857 BLI_assert(viewport_info[3] > 0);
858
859 changed = changed || (this->pipeline_state.viewport_offset_x[v] != viewport_info[0]) ||
860 (this->pipeline_state.viewport_offset_y[v] != viewport_info[1]) ||
861 (this->pipeline_state.viewport_width[v] != viewport_info[2]) ||
862 (this->pipeline_state.viewport_height[v] != viewport_info[3]);
863 this->pipeline_state.viewport_offset_x[v] = viewport_info[0];
864 this->pipeline_state.viewport_offset_y[v] = viewport_info[1];
865 this->pipeline_state.viewport_width[v] = viewport_info[2];
866 this->pipeline_state.viewport_height[v] = viewport_info[3];
867 }
868 this->pipeline_state.num_active_viewports = count;
869
870 if (changed) {
871 this->pipeline_state.dirty_flags = (this->pipeline_state.dirty_flags |
873 }
874}
875
876void MTLContext::set_scissor(int scissor_x, int scissor_y, int scissor_width, int scissor_height)
877{
878 BLI_assert(this);
879 bool changed = (this->pipeline_state.scissor_x != scissor_x) ||
880 (this->pipeline_state.scissor_y != scissor_y) ||
881 (this->pipeline_state.scissor_width != scissor_width) ||
882 (this->pipeline_state.scissor_height != scissor_height) ||
883 (this->pipeline_state.scissor_enabled != true);
884 this->pipeline_state.scissor_x = scissor_x;
885 this->pipeline_state.scissor_y = scissor_y;
886 this->pipeline_state.scissor_width = scissor_width;
887 this->pipeline_state.scissor_height = scissor_height;
888 this->pipeline_state.scissor_enabled = (scissor_width > 0 && scissor_height > 0);
889
890 if (changed) {
891 this->pipeline_state.dirty_flags = (this->pipeline_state.dirty_flags |
893 }
894}
895
896void MTLContext::set_scissor_enabled(bool scissor_enabled)
897{
898 /* Only turn on Scissor if requested scissor region is valid */
899 scissor_enabled = scissor_enabled && (this->pipeline_state.scissor_width > 0 &&
900 this->pipeline_state.scissor_height > 0);
901
902 bool changed = (this->pipeline_state.scissor_enabled != scissor_enabled);
903 this->pipeline_state.scissor_enabled = scissor_enabled;
904 if (changed) {
905 this->pipeline_state.dirty_flags = (this->pipeline_state.dirty_flags |
907 }
908}
909
911
912/* -------------------------------------------------------------------- */
922
923bool MTLContext::ensure_render_pipeline_state(MTLPrimitiveType mtl_prim_type)
924{
925 BLI_assert(this->pipeline_state.initialised);
926
927 /* Check if an active shader is bound. */
928 if (!this->pipeline_state.active_shader) {
929 MTL_LOG_WARNING("No Metal shader for bound GL shader");
930 return false;
931 }
932
933 /* Also ensure active shader is valid. */
934 if (!this->pipeline_state.active_shader->is_valid()) {
936 "Bound active shader is not valid (Missing/invalid implementation for Metal).", );
937 return false;
938 }
939
940 /* Apply global state. */
941 this->state_manager->apply_state();
942
943 /* Main command buffer tracks the current state of the render pass, based on bound
944 * MTLFrameBuffer. */
945 MTLRenderPassState &rps = this->main_command_buffer.get_render_pass_state();
946
947 /* Debug Check: Ensure Framebuffer instance is not dirty. */
948 BLI_assert(!this->main_command_buffer.get_active_framebuffer()->get_dirty());
949
950 /* Fetch shader interface. */
951 MTLShaderInterface *shader_interface = this->pipeline_state.active_shader->get_interface();
952 if (shader_interface == nullptr) {
953 MTL_LOG_WARNING("Bound active shader does not have a valid shader interface!", );
954 return false;
955 }
956
957 /* Fetch shader and bake valid PipelineStateObject (PSO) based on current
958 * shader and state combination. This PSO represents the final GPU-executable
959 * permutation of the shader. */
960 MTLRenderPipelineStateInstance *pipeline_state_instance =
961 this->pipeline_state.active_shader->bake_current_pipeline_state(
962 this, mtl_prim_type_to_topology_class(mtl_prim_type));
963 if (!pipeline_state_instance) {
964 MTL_LOG_ERROR("Failed to bake Metal pipeline state for shader: %s",
965 shader_interface->get_name());
966 return false;
967 }
968
969 bool result = false;
970 if (pipeline_state_instance->pso) {
971
972 /* Fetch render command encoder. A render pass should already be active.
973 * This will be NULL if invalid. */
974 id<MTLRenderCommandEncoder> rec =
975 this->main_command_buffer.get_active_render_command_encoder();
976 BLI_assert(rec);
977 if (rec == nil) {
978 MTL_LOG_ERROR("ensure_render_pipeline_state called while render pass is not active.");
979 return false;
980 }
981
982 /* Bind Render Pipeline State. */
983 BLI_assert(pipeline_state_instance->pso);
984 if (rps.bound_pso != pipeline_state_instance->pso) {
985 [rec setRenderPipelineState:pipeline_state_instance->pso];
986 rps.bound_pso = pipeline_state_instance->pso;
987 }
988
990 /* Texture Bindings. */
991 /* We will iterate through all texture bindings on the context and determine if any of the
992 * active slots match those in our shader interface. If so, textures will be bound. */
993 if (shader_interface->get_total_textures() > 0) {
994 this->ensure_texture_bindings(rec, shader_interface, pipeline_state_instance);
995 }
996
997 /* Bind buffers.
998 * NOTE: `ensure_buffer_bindings` must be called after `ensure_texture_bindings` to allow
999 * for binding of buffer-backed texture's data buffer and metadata. */
1000 this->ensure_buffer_bindings(rec, shader_interface, pipeline_state_instance);
1001
1002 /* Bind Null attribute buffer, if needed. */
1003 if (pipeline_state_instance->null_attribute_buffer_index >= 0) {
1004 if (G.debug & G_DEBUG_GPU) {
1005 MTL_LOG_DEBUG("Binding null attribute buffer at index: %d",
1006 pipeline_state_instance->null_attribute_buffer_index);
1007 }
1009 0,
1010 pipeline_state_instance->null_attribute_buffer_index);
1011 }
1012
1014 /* State: Viewport. */
1015 if (this->pipeline_state.num_active_viewports > 1) {
1016 /* Multiple Viewports. */
1017 MTLViewport viewports[GPU_MAX_VIEWPORTS];
1018 for (int v = 0; v < this->pipeline_state.num_active_viewports; v++) {
1019 MTLViewport &viewport = viewports[v];
1020 viewport.originX = (double)this->pipeline_state.viewport_offset_x[v];
1021 viewport.originY = (double)this->pipeline_state.viewport_offset_y[v];
1022 viewport.width = (double)this->pipeline_state.viewport_width[v];
1023 viewport.height = (double)this->pipeline_state.viewport_height[v];
1024 viewport.znear = this->pipeline_state.depth_stencil_state.depth_range_near;
1025 viewport.zfar = this->pipeline_state.depth_stencil_state.depth_range_far;
1026 }
1027 [rec setViewports:viewports count:this->pipeline_state.num_active_viewports];
1028 }
1029 else {
1030 /* Single Viewport. */
1031 MTLViewport viewport;
1032 viewport.originX = (double)this->pipeline_state.viewport_offset_x[0];
1033 viewport.originY = (double)this->pipeline_state.viewport_offset_y[0];
1034 viewport.width = (double)this->pipeline_state.viewport_width[0];
1035 viewport.height = (double)this->pipeline_state.viewport_height[0];
1036 viewport.znear = this->pipeline_state.depth_stencil_state.depth_range_near;
1037 viewport.zfar = this->pipeline_state.depth_stencil_state.depth_range_far;
1038 [rec setViewport:viewport];
1039 }
1040
1041 /* State: Scissor. */
1042 if (this->pipeline_state.dirty_flags & MTL_PIPELINE_STATE_SCISSOR_FLAG) {
1043
1044 /* Get FrameBuffer associated with active RenderCommandEncoder. */
1045 MTLFrameBuffer *render_fb = this->main_command_buffer.get_active_framebuffer();
1046
1047 MTLScissorRect scissor;
1048 if (this->pipeline_state.scissor_enabled) {
1049 scissor.x = this->pipeline_state.scissor_x;
1050 scissor.y = this->pipeline_state.scissor_y;
1051 scissor.width = this->pipeline_state.scissor_width;
1052 scissor.height = this->pipeline_state.scissor_height;
1053
1054 /* Some scissor assignments exceed the bounds of the viewport due to implicitly added
1055 * padding to the width/height - Clamp width/height. */
1056 BLI_assert(scissor.x >= 0 && scissor.x < render_fb->get_default_width());
1057 BLI_assert(scissor.y >= 0 && scissor.y < render_fb->get_default_height());
1058 scissor.width = (uint)min_ii(scissor.width,
1059 max_ii(render_fb->get_default_width() - (int)(scissor.x), 0));
1060 scissor.height = (uint)min_ii(
1061 scissor.height, max_ii(render_fb->get_default_height() - (int)(scissor.y), 0));
1062 BLI_assert(scissor.width > 0 &&
1063 (scissor.x + scissor.width <= render_fb->get_default_width()));
1064 BLI_assert(scissor.height > 0 && (scissor.height <= render_fb->get_default_height()));
1065 }
1066 else {
1067 /* Scissor is disabled, reset to default size as scissor state may have been previously
1068 * assigned on this encoder.
1069 * NOTE: If an attachment-less framebuffer is used, fetch specified width/height rather
1070 * than active attachment width/height as provided by get_default_w/h(). */
1071 uint default_w = render_fb->get_default_width();
1072 uint default_h = render_fb->get_default_height();
1073 bool is_attachmentless = (default_w == 0) && (default_h == 0);
1074 scissor.x = 0;
1075 scissor.y = 0;
1076 scissor.width = (is_attachmentless) ? render_fb->get_width() : default_w;
1077 scissor.height = (is_attachmentless) ? render_fb->get_height() : default_h;
1078 }
1079
1080 /* Scissor state can still be flagged as changed if it is toggled on and off, without
1081 * parameters changing between draws. */
1082 if (memcmp(&scissor, &rps.last_scissor_rect, sizeof(MTLScissorRect)) != 0) {
1083 [rec setScissorRect:scissor];
1084 rps.last_scissor_rect = scissor;
1085 }
1086 this->pipeline_state.dirty_flags = (this->pipeline_state.dirty_flags &
1088 }
1089
1090 /* State: Face winding. */
1091 if (this->pipeline_state.dirty_flags & MTL_PIPELINE_STATE_FRONT_FACING_FLAG) {
1092 /* We need to invert the face winding in Metal, to account for the inverted-Y coordinate
1093 * system. */
1094 MTLWinding winding = (this->pipeline_state.front_face == GPU_CLOCKWISE) ?
1095 MTLWindingClockwise :
1096 MTLWindingCounterClockwise;
1097 [rec setFrontFacingWinding:winding];
1098 this->pipeline_state.dirty_flags = (this->pipeline_state.dirty_flags &
1100 }
1101
1102 /* State: cull-mode. */
1103 if (this->pipeline_state.dirty_flags & MTL_PIPELINE_STATE_CULLMODE_FLAG) {
1104
1105 MTLCullMode mode = MTLCullModeNone;
1106 if (this->pipeline_state.culling_enabled) {
1107 switch (this->pipeline_state.cull_mode) {
1108 case GPU_CULL_NONE:
1109 mode = MTLCullModeNone;
1110 break;
1111 case GPU_CULL_FRONT:
1112 mode = MTLCullModeFront;
1113 break;
1114 case GPU_CULL_BACK:
1115 mode = MTLCullModeBack;
1116 break;
1117 default:
1119 break;
1120 }
1121 }
1122 [rec setCullMode:mode];
1123 this->pipeline_state.dirty_flags = (this->pipeline_state.dirty_flags &
1125 }
1126
1127 /* Pipeline state is now good. */
1128 result = true;
1129 }
1130 return result;
1131}
1132
1133/* Bind UBOs and SSBOs to an active render command encoder using the rendering state of the
1134 * current context -> Active shader, Bound UBOs). */
1136 id<MTLRenderCommandEncoder> /*rec*/,
1137 const MTLShaderInterface *shader_interface,
1138 const MTLRenderPipelineStateInstance *pipeline_state_instance)
1139{
1140 /* Fetch Render Pass state. */
1141 MTLRenderPassState &rps = this->main_command_buffer.get_render_pass_state();
1142
1143 /* Shader owned push constant block for uniforms.. */
1144 bool active_shader_changed = (rps.last_bound_shader_state.shader_ !=
1145 this->pipeline_state.active_shader ||
1146 rps.last_bound_shader_state.shader_ == nullptr ||
1148 pipeline_state_instance->shader_pso_index);
1149
1150 const MTLShaderBufferBlock &push_constant_block = shader_interface->get_push_constant_block();
1151 if (push_constant_block.size > 0) {
1152
1153 /* Fetch uniform buffer base binding index from pipeline_state_instance - There buffer index
1154 * will be offset by the number of bound VBOs. */
1155 uint32_t block_size = push_constant_block.size;
1156 uint32_t buffer_index = pipeline_state_instance->base_uniform_buffer_index +
1157 push_constant_block.buffer_index;
1158 BLI_assert(buffer_index >= 0 && buffer_index < MTL_MAX_BUFFER_BINDINGS);
1159
1160 /* Only need to rebind block if push constants have been modified -- or if no data is bound for
1161 * the current RenderCommandEncoder. */
1162 if (this->pipeline_state.active_shader->get_push_constant_is_dirty() ||
1163 active_shader_changed || !rps.cached_vertex_buffer_bindings[buffer_index].is_bytes ||
1164 !rps.cached_fragment_buffer_bindings[buffer_index].is_bytes || true)
1165 {
1166
1167 /* Bind push constant data. */
1168 BLI_assert(this->pipeline_state.active_shader->get_push_constant_data() != nullptr);
1170 this->pipeline_state.active_shader->get_push_constant_data(), block_size, buffer_index);
1172 this->pipeline_state.active_shader->get_push_constant_data(), block_size, buffer_index);
1173
1174 /* Only need to rebind block if it has been modified. */
1175 this->pipeline_state.active_shader->push_constant_bindstate_mark_dirty(false);
1176 }
1177 }
1178 rps.last_bound_shader_state.set(this->pipeline_state.active_shader,
1179 pipeline_state_instance->shader_pso_index);
1180
1181 /* Bind Global GPUUniformBuffers */
1182 /* Iterate through expected UBOs in the shader interface, and check if the globally bound ones
1183 * match. This is used to support the gpu_uniformbuffer module, where the uniform data is global,
1184 * and not owned by the shader instance. */
1185 for (const uint ubo_index : IndexRange(shader_interface->get_total_uniform_blocks())) {
1186 const MTLShaderBufferBlock &ubo = shader_interface->get_uniform_block(ubo_index);
1187
1188 if (ubo.buffer_index >= 0 && ubo.location >= 0) {
1189 /* Explicit lookup location for UBO in bind table. */
1190 const uint32_t ubo_location = ubo.location;
1191 /* buffer(N) index of where to bind the UBO. */
1192 const uint32_t buffer_index = ubo.buffer_index;
1193 id<MTLBuffer> ubo_buffer = nil;
1194 size_t ubo_size = 0;
1195
1196 bool bind_dummy_buffer = false;
1197 if (this->pipeline_state.ubo_bindings[ubo_location].bound) {
1198
1199 /* Fetch UBO global-binding properties from slot. */
1200 ubo_buffer = this->pipeline_state.ubo_bindings[ubo_location].ubo->get_metal_buffer();
1201 ubo_size = this->pipeline_state.ubo_bindings[ubo_location].ubo->get_size();
1202
1203 /* Use dummy zero buffer if no buffer assigned -- this is an optimization to avoid
1204 * allocating zero buffers. */
1205 if (ubo_buffer == nil) {
1206 bind_dummy_buffer = true;
1207 }
1208 else {
1209 BLI_assert(ubo_buffer != nil);
1210 BLI_assert(ubo_size > 0);
1211
1212 if (pipeline_state_instance->reflection_data_available) {
1213 /* NOTE: While the vertex and fragment stages have different UBOs, the indices in each
1214 * case will be the same for the same UBO.
1215 * We also determine expected size and then ensure buffer of the correct size
1216 * exists in one of the vertex/fragment shader binding tables. This path is used
1217 * to verify that the size of the bound UBO matches what is expected in the shader. */
1218 uint32_t expected_size =
1219 (buffer_index <
1220 pipeline_state_instance->buffer_bindings_reflection_data_vert.size()) ?
1221 pipeline_state_instance->buffer_bindings_reflection_data_vert[buffer_index]
1222 .size :
1223 0;
1224 if (expected_size == 0) {
1225 expected_size =
1226 (buffer_index <
1227 pipeline_state_instance->buffer_bindings_reflection_data_frag.size()) ?
1228 pipeline_state_instance->buffer_bindings_reflection_data_frag[buffer_index]
1229 .size :
1230 0;
1231 }
1233 expected_size > 0,
1234 "Shader interface expects UBO, but shader reflection data reports that it "
1235 "is not present");
1236
1237 /* If ubo size is smaller than the size expected by the shader, we need to bind the
1238 * dummy buffer, which will be big enough, to avoid an OOB error. */
1239 if (ubo_size < expected_size) {
1241 "[UBO] UBO (UBO Name: %s) bound at location: %d (buffer[[%d]]) with size "
1242 "%lu (Expected size "
1243 "%d) (Shader Name: %s) is too small -- binding NULL buffer. This is likely an "
1244 "over-binding, which is not used, but we need this to avoid validation "
1245 "issues",
1246 shader_interface->get_name_at_offset(ubo.name_offset),
1247 ubo_location,
1248 pipeline_state_instance->base_uniform_buffer_index + buffer_index,
1249 ubo_size,
1250 expected_size,
1251 shader_interface->get_name());
1252 bind_dummy_buffer = true;
1253 }
1254 }
1255 }
1256 }
1257 else {
1259 "[UBO] Shader '%s' expected UBO '%s' to be bound at buffer slot: %d "
1260 "(buffer[[%d]])-- but "
1261 "nothing was bound -- binding dummy buffer",
1262 shader_interface->get_name(),
1263 shader_interface->get_name_at_offset(ubo.name_offset),
1264 ubo_location,
1265 pipeline_state_instance->base_uniform_buffer_index + buffer_index);
1266 bind_dummy_buffer = true;
1267 }
1268
1269 if (bind_dummy_buffer) {
1270 /* Perform Dummy binding. */
1271 ubo_buffer = this->get_null_buffer();
1272 ubo_size = [ubo_buffer length];
1273 }
1274
1275 if (ubo_buffer != nil) {
1276
1277 uint32_t buffer_bind_index = pipeline_state_instance->base_uniform_buffer_index +
1278 buffer_index;
1279
1280 /* Bind Vertex UBO. */
1281 if (bool(ubo.stage_mask & ShaderStage::VERTEX)) {
1282 BLI_assert(buffer_bind_index >= 0 && buffer_bind_index < MTL_MAX_BUFFER_BINDINGS);
1283 rps.bind_vertex_buffer(ubo_buffer, 0, buffer_bind_index);
1284 }
1285
1286 /* Bind Fragment UBOs. */
1287 if (bool(ubo.stage_mask & ShaderStage::FRAGMENT)) {
1288 BLI_assert(buffer_bind_index >= 0 && buffer_bind_index < MTL_MAX_BUFFER_BINDINGS);
1289 rps.bind_fragment_buffer(ubo_buffer, 0, buffer_bind_index);
1290 }
1291 }
1292 else {
1294 "[UBO] Shader '%s' has UBO '%s' bound at buffer index: %d -- but MTLBuffer "
1295 "is NULL!",
1296 shader_interface->get_name(),
1297 shader_interface->get_name_at_offset(ubo.name_offset),
1298 buffer_index);
1299 }
1300 }
1301 }
1302
1303 /* Bind Global StorageBuf's */
1304 /* Iterate through expected SSBOs in the shader interface, and check if the globally bound ones
1305 * match. This is used to support the gpu_uniformbuffer module, where the uniform data is global,
1306 * and not owned by the shader instance. */
1307 for (const uint ssbo_index : IndexRange(shader_interface->get_total_storage_blocks())) {
1308 const MTLShaderBufferBlock &ssbo = shader_interface->get_storage_block(ssbo_index);
1309
1310 if (ssbo.buffer_index >= 0 && ssbo.location >= 0) {
1311 /* Explicit lookup location for SSBO in bind table. */
1312 const uint32_t ssbo_location = ssbo.location;
1313 /* buffer(N) index of where to bind the SSBO. */
1314 const uint32_t buffer_index = ssbo.buffer_index;
1315 id<MTLBuffer> ssbo_buffer = nil;
1316 size_t ssbo_size = 0;
1317 UNUSED_VARS_NDEBUG(ssbo_size);
1318
1319 if (this->pipeline_state.ssbo_bindings[ssbo_location].bound) {
1320
1321 /* Fetch SSBO global-binding properties from slot. */
1322 ssbo_buffer = this->pipeline_state.ssbo_bindings[ssbo_location].ssbo->get_metal_buffer();
1323 ssbo_size = this->pipeline_state.ssbo_bindings[ssbo_location].ssbo->get_size();
1324
1325 /* For SSBOs, we always need to ensure the buffer exists, as it may be written to. */
1326 BLI_assert(ssbo_buffer != nil);
1327 BLI_assert(ssbo_size > 0);
1328 }
1329 else {
1331 "[SSBO] Shader '%s' expected SSBO '%s' to be bound at buffer location: %d "
1332 "(buffer[[%d]]) -- "
1333 "but "
1334 "nothing was bound.",
1335 shader_interface->get_name(),
1336 shader_interface->get_name_at_offset(ssbo.name_offset),
1337 ssbo_location,
1338 pipeline_state_instance->base_storage_buffer_index + buffer_index);
1339
1340#if DEBUG_BIND_NULL_BUFFER_FOR_MISSING_SSBO == 1
1341 ssbo_buffer = this->get_null_buffer();
1342 ssbo_size = [ssbo_buffer length];
1343#endif
1344 }
1345
1346 if (ssbo_buffer != nil) {
1347 uint32_t buffer_bind_index = pipeline_state_instance->base_storage_buffer_index +
1348 buffer_index;
1349
1350 /* Bind Vertex SSBO. */
1351 if (bool(ssbo.stage_mask & ShaderStage::VERTEX)) {
1352 BLI_assert(buffer_bind_index >= 0 && buffer_bind_index < MTL_MAX_BUFFER_BINDINGS);
1353 rps.bind_vertex_buffer(ssbo_buffer, 0, buffer_bind_index);
1354 }
1355
1356 /* Bind Fragment SSBOs. */
1357 if (bool(ssbo.stage_mask & ShaderStage::FRAGMENT)) {
1358 BLI_assert(buffer_bind_index >= 0 && buffer_bind_index < MTL_MAX_BUFFER_BINDINGS);
1359 rps.bind_fragment_buffer(ssbo_buffer, 0, buffer_bind_index);
1360 }
1361 }
1362 else {
1364 "[SSBO] Shader '%s' had SSBO '%s' bound at SSBO location: %d "
1365 "(buffer[["
1366 "%d]]) -- but bound MTLStorageBuf was nil.",
1367 shader_interface->get_name(),
1368 shader_interface->get_name_at_offset(ssbo.name_offset),
1369 ssbo_location,
1370 pipeline_state_instance->base_storage_buffer_index + buffer_index);
1371 }
1372 }
1373 }
1374
1375 return true;
1376}
1377
1378/* Variant for compute. Bind UBOs and SSBOs to an active compute command encoder using the
1379 * rendering state of the current context -> Active shader, Bound UBOs). */
1381 id<MTLComputeCommandEncoder> /*rec*/,
1382 const MTLShaderInterface *shader_interface,
1383 const MTLComputePipelineStateInstance *pipeline_state_instance)
1384{
1385 /* Fetch Compute Pass state. */
1386 MTLComputeState &cs = this->main_command_buffer.get_compute_state();
1387
1388 /* Fetch push constant block and bind. */
1389 const MTLShaderBufferBlock &push_constant_block = shader_interface->get_push_constant_block();
1390 if (push_constant_block.size > 0) {
1391
1392 /* Fetch uniform buffer base binding index from pipeline_state_instance - There buffer index
1393 * will be offset by the number of bound VBOs. */
1394 uint32_t block_size = push_constant_block.size;
1395 uint32_t buffer_index = pipeline_state_instance->base_uniform_buffer_index +
1396 push_constant_block.buffer_index;
1397 BLI_assert(buffer_index >= 0 && buffer_index < MTL_MAX_BUFFER_BINDINGS);
1398
1399 /* For compute, we must always re-bind the push constant block as other compute
1400 * operations may have assigned resources over the top, outside of the compiled
1401 * compute shader path. */
1402 /* Bind push constant data. */
1403 BLI_assert(this->pipeline_state.active_shader->get_push_constant_data() != nullptr);
1405 this->pipeline_state.active_shader->get_push_constant_data(), block_size, buffer_index);
1406
1407 /* Only need to rebind block if it has been modified. */
1408 this->pipeline_state.active_shader->push_constant_bindstate_mark_dirty(false);
1409 }
1410
1411 /* Bind Global GPUUniformBuffers */
1412 /* Iterate through expected UBOs in the shader interface, and check if the globally bound ones
1413 * match. This is used to support the gpu_uniformbuffer module, where the uniform data is global,
1414 * and not owned by the shader instance. */
1415 for (const uint ubo_index : IndexRange(shader_interface->get_total_uniform_blocks())) {
1416 const MTLShaderBufferBlock &ubo = shader_interface->get_uniform_block(ubo_index);
1417
1418 if (ubo.buffer_index >= 0) {
1419 /* Explicit lookup location for UBO in bind table. */
1420 const uint32_t ubo_location = ubo.location;
1421 /* buffer(N) index of where to bind the UBO. */
1422 const uint32_t buffer_index = ubo.buffer_index;
1423 id<MTLBuffer> ubo_buffer = nil;
1424 size_t ubo_size = 0;
1425
1426 bool bind_dummy_buffer = false;
1427 if (this->pipeline_state.ubo_bindings[ubo_location].bound) {
1428
1429 /* Fetch UBO global-binding properties from slot. */
1430 ubo_buffer = this->pipeline_state.ubo_bindings[ubo_location].ubo->get_metal_buffer();
1431 ubo_size = this->pipeline_state.ubo_bindings[ubo_location].ubo->get_size();
1432 UNUSED_VARS_NDEBUG(ubo_size);
1433
1434 /* Use dummy zero buffer if no buffer assigned -- this is an optimization to avoid
1435 * allocating zero buffers. */
1436 if (ubo_buffer == nil) {
1437 bind_dummy_buffer = true;
1438 }
1439 else {
1440 BLI_assert(ubo_buffer != nil);
1441 BLI_assert(ubo_size > 0);
1442 }
1443 }
1444 else {
1446 "[UBO] Shader '%s' expected UBO '%s' to be bound at buffer location: %d "
1447 "(buffer[[%d]]) -- but "
1448 "nothing was bound -- binding dummy buffer",
1449 shader_interface->get_name(),
1450 shader_interface->get_name_at_offset(ubo.name_offset),
1451 ubo_location,
1452 pipeline_state_instance->base_uniform_buffer_index + buffer_index);
1453 bind_dummy_buffer = true;
1454 }
1455
1456 if (bind_dummy_buffer) {
1457 /* Perform Dummy binding. */
1458 ubo_buffer = this->get_null_buffer();
1459 ubo_size = [ubo_buffer length];
1460 }
1461
1462 if (ubo_buffer != nil) {
1463 uint32_t buffer_bind_index = pipeline_state_instance->base_uniform_buffer_index +
1464 buffer_index;
1465
1466 /* Bind Compute UBO. */
1467 if (bool(ubo.stage_mask & ShaderStage::COMPUTE)) {
1468 BLI_assert(buffer_bind_index >= 0 && buffer_bind_index < MTL_MAX_BUFFER_BINDINGS);
1469 cs.bind_compute_buffer(ubo_buffer, 0, buffer_bind_index);
1470 }
1471 }
1472 else {
1474 "[UBO] Compute Shader '%s' has UBO '%s' bound at buffer index: %d -- but MTLBuffer "
1475 "is NULL!",
1476 shader_interface->get_name(),
1477 shader_interface->get_name_at_offset(ubo.name_offset),
1478 buffer_index);
1479 }
1480 }
1481 }
1482
1483 /* Bind Global GPUStorageBuffers. */
1484 /* Iterate through expected SSBOs in the shader interface, and check if the globally bound ones
1485 * match. */
1486 for (const uint ssbo_index : IndexRange(shader_interface->get_total_storage_blocks())) {
1487 const MTLShaderBufferBlock &ssbo = shader_interface->get_storage_block(ssbo_index);
1488
1489 if (ssbo.buffer_index >= 0 && ssbo.location >= 0) {
1490 /* Explicit lookup location for SSBO in bind table. */
1491 const uint32_t ssbo_location = ssbo.location;
1492 /* buffer(N) index of where to bind the SSBO. */
1493 const uint32_t buffer_index = ssbo.buffer_index;
1494 id<MTLBuffer> ssbo_buffer = nil;
1495 int ssbo_size = 0;
1496
1497 if (this->pipeline_state.ssbo_bindings[ssbo_location].bound) {
1498
1499 /* Fetch UBO global-binding properties from slot. */
1500 ssbo_buffer = this->pipeline_state.ssbo_bindings[ssbo_location].ssbo->get_metal_buffer();
1501 ssbo_size = this->pipeline_state.ssbo_bindings[ssbo_location].ssbo->get_size();
1502 UNUSED_VARS_NDEBUG(ssbo_size);
1503
1504 /* For SSBOs, we always need to ensure the buffer exists, as it may be written to. */
1505 BLI_assert(ssbo_buffer != nil);
1506 BLI_assert(ssbo_size > 0);
1507 }
1508 else {
1510 "[SSBO] Shader '%s' expected SSBO '%s' to be bound at SSBO location: %d "
1511 "(buffer[["
1512 "%d]]) -- but "
1513 "nothing was bound.",
1514 shader_interface->get_name(),
1515 shader_interface->get_name_at_offset(ssbo.name_offset),
1516 ssbo_location,
1517 pipeline_state_instance->base_storage_buffer_index + buffer_index);
1518
1519#if DEBUG_BIND_NULL_BUFFER_FOR_MISSING_SSBO == 1
1520 ssbo_buffer = this->get_null_buffer();
1521 ssbo_size = [ssbo_buffer length];
1522#endif
1523 }
1524
1525 if (ssbo_buffer != nil) {
1526 uint32_t buffer_bind_index = pipeline_state_instance->base_storage_buffer_index +
1527 buffer_index;
1528
1529 /* Bind Compute SSBO. */
1530 if (bool(ssbo.stage_mask & ShaderStage::COMPUTE)) {
1531 BLI_assert(buffer_bind_index >= 0 && buffer_bind_index < MTL_MAX_BUFFER_BINDINGS);
1532 cs.bind_compute_buffer(ssbo_buffer, 0, buffer_bind_index);
1533 }
1534 }
1535 else {
1537 "[SSBO] Shader '%s' had SSBO '%s' bound at SSBO location: %d "
1538 "(buffer[["
1539 "%d]]) -- but bound MTLStorageBuf was nil.",
1540 shader_interface->get_name(),
1541 shader_interface->get_name_at_offset(ssbo.name_offset),
1542 ssbo_location,
1543 pipeline_state_instance->base_storage_buffer_index + buffer_index);
1544 }
1545 }
1546 }
1547
1548 return true;
1549}
1550
1551/* Ensure texture bindings are correct and up to date for current draw call. */
1553 id<MTLRenderCommandEncoder> rec,
1554 MTLShaderInterface *shader_interface,
1555 const MTLRenderPipelineStateInstance *pipeline_state_instance)
1556{
1557 BLI_assert(shader_interface != nil);
1558 BLI_assert(rec != nil);
1559 UNUSED_VARS_NDEBUG(rec);
1560
1561 /* Fetch Render Pass state. */
1562 MTLRenderPassState &rps = this->main_command_buffer.get_render_pass_state();
1563
1564 @autoreleasepool {
1565 int vertex_arg_buffer_bind_index = -1;
1566 int fragment_arg_buffer_bind_index = -1;
1567
1568 /* Argument buffers are used for samplers, when the limit of 16 is exceeded. */
1569 bool use_argument_buffer_for_samplers = shader_interface->uses_argument_buffer_for_samplers();
1570 vertex_arg_buffer_bind_index = shader_interface->get_argument_buffer_bind_index(
1572 fragment_arg_buffer_bind_index = shader_interface->get_argument_buffer_bind_index(
1574
1575 /* Loop through expected textures in shader interface and resolve bindings with currently
1576 * bound textures.. */
1577 for (const uint t : IndexRange(shader_interface->get_max_texture_index() + 1)) {
1578 /* Ensure the bound texture is compatible with the shader interface. If the
1579 * shader does not expect a texture to be bound for the current slot, we skip
1580 * binding.
1581 * NOTE: Global texture bindings may be left over from prior draw calls. */
1582 const MTLShaderTexture &shader_texture_info = shader_interface->get_texture(t);
1583 if (!shader_texture_info.used) {
1584 /* Skip unused binding points if explicit indices are specified. */
1585 continue;
1586 }
1587
1588 /* Determine bind lookup table depending on whether an image binding or texture.
1589 * NOTE: Images and Texture Samplers share a binding table in Metal. */
1590 bool is_resource_sampler = shader_texture_info.is_texture_sampler;
1591 MTLTextureBinding(&resource_bind_table)[MTL_MAX_TEXTURE_SLOTS] =
1592 (is_resource_sampler) ? this->pipeline_state.texture_bindings :
1593 this->pipeline_state.image_bindings;
1594
1595 /* Texture resource bind slot in shader `[[texture(n)]]`. */
1596 int slot = shader_texture_info.slot_index;
1597 /* Explicit bind location for texture. */
1598 int location = shader_texture_info.location;
1599 /* Default sampler. */
1600 MTLSamplerBinding default_binding = {true, DEFAULT_SAMPLER_STATE};
1601
1602 if (slot >= 0 && slot < GPU_max_textures()) {
1603 bool bind_dummy_texture = true;
1604 if (resource_bind_table[location].used) {
1605 gpu::MTLTexture *bound_texture = resource_bind_table[location].texture_resource;
1606 MTLSamplerBinding &bound_sampler = (is_resource_sampler) ?
1607 this->pipeline_state.sampler_bindings[location] :
1608 default_binding;
1609 BLI_assert(bound_texture);
1610 BLI_assert(bound_sampler.used);
1611
1612 if (shader_texture_info.type == bound_texture->type_) {
1613 /* Bind texture and sampler if the bound texture matches the type expected by the
1614 * shader. */
1615 id<MTLTexture> tex = bound_texture->get_metal_handle();
1616
1617 if (bool(shader_texture_info.stage_mask & ShaderStage::VERTEX)) {
1618 rps.bind_vertex_texture(tex, slot);
1619 rps.bind_vertex_sampler(bound_sampler, use_argument_buffer_for_samplers, slot);
1620 }
1621
1622 if (bool(shader_texture_info.stage_mask & ShaderStage::FRAGMENT)) {
1623 rps.bind_fragment_texture(tex, slot);
1624 rps.bind_fragment_sampler(bound_sampler, use_argument_buffer_for_samplers, slot);
1625 }
1626
1627 /* Bind texture buffer to associated SSBO slot. */
1628 if (shader_texture_info.texture_buffer_ssbo_location != -1) {
1629 BLI_assert(bound_texture->usage_get() & GPU_TEXTURE_USAGE_ATOMIC);
1630 MTLStorageBuf *tex_storage_buf = bound_texture->get_storagebuf();
1631 BLI_assert(tex_storage_buf != nullptr);
1632 tex_storage_buf->bind(shader_texture_info.texture_buffer_ssbo_location);
1633 /* Update bound texture metadata.
1634 * components packed int uint4 (sizeX, sizeY, sizeZ/Layers, bytes per row). */
1635 MTLShader *active_shader = this->pipeline_state.active_shader;
1636 const int *metadata = bound_texture->get_texture_metadata_ptr();
1637 BLI_assert(shader_texture_info.buffer_metadata_uniform_loc != -1);
1638 active_shader->uniform_int(
1639 shader_texture_info.buffer_metadata_uniform_loc, 4, 1, metadata);
1640 }
1641
1642 /* Texture state resolved, no need to bind dummy texture */
1643 bind_dummy_texture = false;
1644 }
1645 else {
1646 /* Texture type for bound texture (e.g. Texture2DArray) does not match what was
1647 * expected in the shader interface. This is a problem and we will need to bind
1648 * a dummy texture to ensure correct API usage. */
1650 "(Shader '%s') Texture (%s) %p bound to slot %d is incompatible -- Wrong "
1651 "texture target type. (Expecting type %d, actual type %d) (binding "
1652 "name:'%s')(texture name:'%s')",
1653 shader_interface->get_name(),
1654 is_resource_sampler ? "TextureSampler" : "TextureImage",
1655 bound_texture,
1656 slot,
1657 shader_texture_info.type,
1658 bound_texture->type_,
1659 shader_interface->get_name_at_offset(shader_texture_info.name_offset),
1660 bound_texture->get_name());
1661 }
1662 }
1663 else {
1665 "Shader '%s' expected texture (%s) to be bound to location %d (texture[[%d]]) -- No "
1666 "texture was "
1667 "bound. (name:'%s')",
1668 shader_interface->get_name(),
1669 is_resource_sampler ? "TextureSampler" : "TextureImage",
1670 location,
1671 slot,
1672 shader_interface->get_name_at_offset(shader_texture_info.name_offset));
1673 }
1674
1675 /* Bind Dummy texture -- will temporarily resolve validation issues while incorrect formats
1676 * are provided -- as certain configurations may not need any binding. These issues should
1677 * be fixed in the high-level, if problems crop up. */
1678 if (bind_dummy_texture) {
1679 if (bool(shader_texture_info.stage_mask & ShaderStage::VERTEX)) {
1681 get_dummy_texture(shader_texture_info.type, shader_texture_info.sampler_format)
1682 ->get_metal_handle(),
1683 slot);
1684
1685 /* Bind default sampler state. */
1686 rps.bind_vertex_sampler(default_binding, use_argument_buffer_for_samplers, slot);
1687 }
1688 if (bool(shader_texture_info.stage_mask & ShaderStage::FRAGMENT)) {
1690 get_dummy_texture(shader_texture_info.type, shader_texture_info.sampler_format)
1691 ->get_metal_handle(),
1692 slot);
1693
1694 /* Bind default sampler state. */
1695 rps.bind_fragment_sampler(default_binding, use_argument_buffer_for_samplers, slot);
1696 }
1697 }
1698 }
1699 else {
1701 "Shader %p expected texture (%s) to be bound to slot %d -- Slot exceeds the "
1702 "hardware/API limit of '%d'. (name:'%s')",
1703 this->pipeline_state.active_shader,
1704 is_resource_sampler ? "TextureSampler" : "TextureImage",
1705 slot,
1707 shader_interface->get_name_at_offset(shader_texture_info.name_offset));
1708 }
1709 }
1710
1711 /* Construct and Bind argument buffer.
1712 * NOTE(Metal): Samplers use an argument buffer when the limit of 16 samplers is exceeded. */
1713 if (use_argument_buffer_for_samplers) {
1714#ifndef NDEBUG
1715 /* Debug check to validate each expected texture in the shader interface has a valid
1716 * sampler object bound to the context. We will need all of these to be valid
1717 * when constructing the sampler argument buffer. */
1718 for (const uint i : IndexRange(shader_interface->get_max_texture_index() + 1)) {
1719 const MTLShaderTexture &texture = shader_interface->get_texture(i);
1720 if (texture.used) {
1721 BLI_assert(this->samplers_.mtl_sampler[i] != nil);
1722 }
1723 }
1724#endif
1725
1726 /* Check to ensure the buffer binding index for the argument buffer has been assigned.
1727 * This PSO property will be set if we expect to use argument buffers, and the shader
1728 * uses any amount of textures. */
1729 BLI_assert(vertex_arg_buffer_bind_index >= 0 || fragment_arg_buffer_bind_index >= 0);
1730 if (vertex_arg_buffer_bind_index >= 0 || fragment_arg_buffer_bind_index >= 0) {
1731 /* Offset binding index to be relative to the start of static uniform buffer binding slots.
1732 * The first N slots, prior to `pipeline_state_instance->base_uniform_buffer_index` are
1733 * used by vertex and index buffer bindings, and the number of buffers present will vary
1734 * between PSOs. */
1735 int arg_buffer_idx = (pipeline_state_instance->base_uniform_buffer_index +
1736 vertex_arg_buffer_bind_index);
1737 assert(arg_buffer_idx < 32);
1738 id<MTLArgumentEncoder> argument_encoder = shader_interface->find_argument_encoder(
1739 arg_buffer_idx);
1740 if (argument_encoder == nil) {
1741 argument_encoder = [pipeline_state_instance->vert
1742 newArgumentEncoderWithBufferIndex:arg_buffer_idx];
1743 shader_interface->insert_argument_encoder(arg_buffer_idx, argument_encoder);
1744 }
1745
1746 /* Generate or Fetch argument buffer sampler configuration.
1747 * NOTE(Metal): we need to base sampler counts off of the maximal texture
1748 * index. This is not the most optimal, but in practice, not a use-case
1749 * when argument buffers are required.
1750 * This is because with explicit texture indices, the binding indices
1751 * should match across draws, to allow the high-level to optimize bind-points. */
1752 gpu::MTLBuffer *encoder_buffer = nullptr;
1753 this->samplers_.num_samplers = shader_interface->get_max_texture_index() + 1;
1754
1755 gpu::MTLBuffer **cached_smp_buffer_search = this->cached_sampler_buffers_.lookup_ptr(
1756 this->samplers_);
1757 if (cached_smp_buffer_search != nullptr) {
1758 encoder_buffer = *cached_smp_buffer_search;
1759 }
1760 else {
1761 /* Populate argument buffer with current global sampler bindings. */
1762 size_t size = [argument_encoder encodedLength];
1763 size_t alignment = max_uu([argument_encoder alignment], 256);
1764 size_t size_align_delta = (size % alignment);
1765 size_t aligned_alloc_size = ((alignment > 1) && (size_align_delta > 0)) ?
1766 size + (alignment - (size % alignment)) :
1767 size;
1768
1769 /* Allocate buffer to store encoded sampler arguments. */
1770 encoder_buffer = MTLContext::get_global_memory_manager()->allocate(aligned_alloc_size,
1771 true);
1772 BLI_assert(encoder_buffer);
1773 BLI_assert(encoder_buffer->get_metal_buffer());
1774 [argument_encoder setArgumentBuffer:encoder_buffer->get_metal_buffer() offset:0];
1775 [argument_encoder
1776 setSamplerStates:this->samplers_.mtl_sampler
1777 withRange:NSMakeRange(0, shader_interface->get_max_texture_index() + 1)];
1778 encoder_buffer->flush();
1779
1780 /* Insert into cache. */
1781 this->cached_sampler_buffers_.add_new(this->samplers_, encoder_buffer);
1782 }
1783
1784 BLI_assert(encoder_buffer != nullptr);
1785 int vert_buffer_index = (pipeline_state_instance->base_uniform_buffer_index +
1786 vertex_arg_buffer_bind_index);
1787 rps.bind_vertex_buffer(encoder_buffer->get_metal_buffer(), 0, vert_buffer_index);
1788
1789 /* Fragment shader shares its argument buffer binding with the vertex shader, So no need to
1790 * re-encode. We can use the same argument buffer. */
1791 if (fragment_arg_buffer_bind_index >= 0) {
1792 BLI_assert(fragment_arg_buffer_bind_index);
1793 int frag_buffer_index = (pipeline_state_instance->base_uniform_buffer_index +
1794 fragment_arg_buffer_bind_index);
1795 rps.bind_fragment_buffer(encoder_buffer->get_metal_buffer(), 0, frag_buffer_index);
1796 }
1797 }
1798 }
1799 }
1800}
1801
1802/* Texture binding variant for compute command encoder.
1803 * Ensure bound texture resources are bound to the active MTLComputeCommandEncoder. */
1805 id<MTLComputeCommandEncoder> rec,
1806 MTLShaderInterface *shader_interface,
1807 const MTLComputePipelineStateInstance *pipeline_state_instance)
1808{
1809 BLI_assert(shader_interface != nil);
1810 BLI_assert(rec != nil);
1811 UNUSED_VARS_NDEBUG(rec);
1812
1813 /* Fetch Render Pass state. */
1814 MTLComputeState &cs = this->main_command_buffer.get_compute_state();
1815
1816 @autoreleasepool {
1817 int compute_arg_buffer_bind_index = -1;
1818
1819 /* Argument buffers are used for samplers, when the limit of 16 is exceeded.
1820 * NOTE: Compute uses vertex argument for arg buffer bind index. */
1821 bool use_argument_buffer_for_samplers = shader_interface->uses_argument_buffer_for_samplers();
1822 compute_arg_buffer_bind_index = shader_interface->get_argument_buffer_bind_index(
1824
1825 /* Loop through expected textures in shader interface and resolve bindings with currently
1826 * bound textures.. */
1827 for (const uint t : IndexRange(shader_interface->get_max_texture_index() + 1)) {
1828 /* Ensure the bound texture is compatible with the shader interface. If the
1829 * shader does not expect a texture to be bound for the current slot, we skip
1830 * binding.
1831 * NOTE: Global texture bindings may be left over from prior draw calls. */
1832 const MTLShaderTexture &shader_texture_info = shader_interface->get_texture(t);
1833 if (!shader_texture_info.used) {
1834 /* Skip unused binding points if explicit indices are specified. */
1835 continue;
1836 }
1837
1838 /* Determine bind lookup table depending on whether an image binding or texture.
1839 * NOTE: Images and Texture Samplers share a binding table in Metal. */
1840 bool is_resource_sampler = shader_texture_info.is_texture_sampler;
1841 MTLTextureBinding(&resource_bind_table)[MTL_MAX_TEXTURE_SLOTS] =
1842 (is_resource_sampler) ? this->pipeline_state.texture_bindings :
1843 this->pipeline_state.image_bindings;
1844
1845 /* Texture resource bind slot in shader `[[texture(n)]]`. */
1846 int slot = shader_texture_info.slot_index;
1847 /* Explicit bind location for texture. */
1848 int location = shader_texture_info.location;
1849 /* Default sampler. */
1850 MTLSamplerBinding default_binding = {true, DEFAULT_SAMPLER_STATE};
1851
1852 if (slot >= 0 && slot < GPU_max_textures()) {
1853 bool bind_dummy_texture = true;
1854 if (resource_bind_table[location].used) {
1855 gpu::MTLTexture *bound_texture = resource_bind_table[location].texture_resource;
1856 MTLSamplerBinding &bound_sampler = (is_resource_sampler) ?
1857 this->pipeline_state.sampler_bindings[location] :
1858 default_binding;
1859 BLI_assert(bound_texture);
1860 BLI_assert(bound_sampler.used);
1861
1862 if (shader_texture_info.type == bound_texture->type_) {
1863 /* Bind texture and sampler if the bound texture matches the type expected by the
1864 * shader. */
1865 id<MTLTexture> tex = bound_texture->get_metal_handle();
1866
1867 /* If texture resource is an image binding and has a non-default swizzle mask, we need
1868 * to bind the source texture resource to retain image write access. */
1869 if (!is_resource_sampler && bound_texture->has_custom_swizzle()) {
1870 tex = bound_texture->get_metal_handle_base();
1871 }
1872
1873 if (bool(shader_texture_info.stage_mask & ShaderStage::COMPUTE)) {
1874 cs.bind_compute_texture(tex, slot);
1875 cs.bind_compute_sampler(bound_sampler, use_argument_buffer_for_samplers, slot);
1876 }
1877
1878 /* Bind texture buffer to associated SSBO slot. */
1879 if (shader_texture_info.texture_buffer_ssbo_location != -1) {
1880 BLI_assert(bound_texture->usage_get() & GPU_TEXTURE_USAGE_ATOMIC);
1881 MTLStorageBuf *tex_storage_buf = bound_texture->get_storagebuf();
1882 BLI_assert(tex_storage_buf != nullptr);
1883 tex_storage_buf->bind(shader_texture_info.texture_buffer_ssbo_location);
1884 /* Update bound texture metadata.
1885 * components packed int uint4 (sizeX, sizeY, sizeZ/Layers, bytes per row). */
1886 MTLShader *active_shader = this->pipeline_state.active_shader;
1887 const int *metadata = bound_texture->get_texture_metadata_ptr();
1888 BLI_assert(shader_texture_info.buffer_metadata_uniform_loc != -1);
1889 active_shader->uniform_int(
1890 shader_texture_info.buffer_metadata_uniform_loc, 4, 1, metadata);
1891 }
1892
1893 /* Texture state resolved, no need to bind dummy texture */
1894 bind_dummy_texture = false;
1895 }
1896 else {
1897 /* Texture type for bound texture (e.g. Texture2DArray) does not match what was
1898 * expected in the shader interface. This is a problem and we will need to bind
1899 * a dummy texture to ensure correct API usage. */
1901 "(Shader '%s') Texture (%s) %p bound to slot %d is incompatible -- Wrong "
1902 "texture target type. (Expecting type %d, actual type %d) (binding "
1903 "name:'%s')(texture name:'%s')",
1904 shader_interface->get_name(),
1905 is_resource_sampler ? "TextureSampler" : "TextureImage",
1906 bound_texture,
1907 slot,
1908 shader_texture_info.type,
1909 bound_texture->type_,
1910 shader_interface->get_name_at_offset(shader_texture_info.name_offset),
1911 bound_texture->get_name());
1912 }
1913 }
1914 else {
1916 "Shader '%s' expected texture (%s) to be bound to location %d (texture[[%d]]) -- No "
1917 "texture was "
1918 "bound. (name:'%s')",
1919 shader_interface->get_name(),
1920 is_resource_sampler ? "TextureSampler" : "TextureImage",
1921 location,
1922 slot,
1923 shader_interface->get_name_at_offset(shader_texture_info.name_offset));
1924 }
1925
1926 /* Bind Dummy texture -- will temporarily resolve validation issues while incorrect formats
1927 * are provided -- as certain configurations may not need any binding. These issues should
1928 * be fixed in the high-level, if problems crop up. */
1929 if (bind_dummy_texture) {
1930 if (bool(shader_texture_info.stage_mask & ShaderStage::COMPUTE)) {
1932 get_dummy_texture(shader_texture_info.type, shader_texture_info.sampler_format)
1933 ->get_metal_handle(),
1934 slot);
1935
1936 /* Bind default sampler state. */
1937 MTLSamplerBinding default_binding = {true, DEFAULT_SAMPLER_STATE};
1938 cs.bind_compute_sampler(default_binding, use_argument_buffer_for_samplers, slot);
1939 }
1940 }
1941 }
1942 else {
1944 "Shader %p expected texture (%s) to be bound to slot %d -- Slot exceeds the "
1945 "hardware/API limit of '%d'. (name:'%s')",
1946 this->pipeline_state.active_shader,
1947 is_resource_sampler ? "TextureSampler" : "TextureImage",
1948 slot,
1950 shader_interface->get_name_at_offset(shader_texture_info.name_offset));
1951 }
1952 }
1953
1954 /* Construct and Bind argument buffer.
1955 * NOTE(Metal): Samplers use an argument buffer when the limit of 16 samplers is exceeded. */
1956 if (use_argument_buffer_for_samplers) {
1957#ifndef NDEBUG
1958 /* Debug check to validate each expected texture in the shader interface has a valid
1959 * sampler object bound to the context. We will need all of these to be valid
1960 * when constructing the sampler argument buffer. */
1961 for (const uint i : IndexRange(shader_interface->get_max_texture_index() + 1)) {
1962 const MTLShaderTexture &texture = shader_interface->get_texture(i);
1963 if (texture.used) {
1964 BLI_assert(this->samplers_.mtl_sampler[i] != nil);
1965 }
1966 }
1967#endif
1968
1969 /* Check to ensure the buffer binding index for the argument buffer has been assigned.
1970 * This PSO property will be set if we expect to use argument buffers, and the shader
1971 * uses any amount of textures. */
1972 BLI_assert(compute_arg_buffer_bind_index >= 0);
1973 if (compute_arg_buffer_bind_index >= 0) {
1974 /* Offset binding index to be relative to the start of static uniform buffer binding slots.
1975 * The first N slots, prior to `pipeline_state_instance->base_uniform_buffer_index` are
1976 * used by vertex and index buffer bindings, and the number of buffers present will vary
1977 * between PSOs. */
1978 int arg_buffer_idx = (pipeline_state_instance->base_uniform_buffer_index +
1979 compute_arg_buffer_bind_index);
1980 assert(arg_buffer_idx < 32);
1981 id<MTLArgumentEncoder> argument_encoder = shader_interface->find_argument_encoder(
1982 arg_buffer_idx);
1983 if (argument_encoder == nil) {
1984 argument_encoder = [pipeline_state_instance->compute
1985 newArgumentEncoderWithBufferIndex:arg_buffer_idx];
1986 shader_interface->insert_argument_encoder(arg_buffer_idx, argument_encoder);
1987 }
1988
1989 /* Generate or Fetch argument buffer sampler configuration.
1990 * NOTE(Metal): we need to base sampler counts off of the maximal texture
1991 * index. This is not the most optimal, but in practice, not a use-case
1992 * when argument buffers are required.
1993 * This is because with explicit texture indices, the binding indices
1994 * should match across draws, to allow the high-level to optimize bind-points. */
1995 gpu::MTLBuffer *encoder_buffer = nullptr;
1996 this->samplers_.num_samplers = shader_interface->get_max_texture_index() + 1;
1997
1998 gpu::MTLBuffer **cached_smp_buffer_search = this->cached_sampler_buffers_.lookup_ptr(
1999 this->samplers_);
2000 if (cached_smp_buffer_search != nullptr) {
2001 encoder_buffer = *cached_smp_buffer_search;
2002 }
2003 else {
2004 /* Populate argument buffer with current global sampler bindings. */
2005 size_t size = [argument_encoder encodedLength];
2006 size_t alignment = max_uu([argument_encoder alignment], 256);
2007 size_t size_align_delta = (size % alignment);
2008 size_t aligned_alloc_size = ((alignment > 1) && (size_align_delta > 0)) ?
2009 size + (alignment - (size % alignment)) :
2010 size;
2011
2012 /* Allocate buffer to store encoded sampler arguments. */
2013 encoder_buffer = MTLContext::get_global_memory_manager()->allocate(aligned_alloc_size,
2014 true);
2015 BLI_assert(encoder_buffer);
2016 BLI_assert(encoder_buffer->get_metal_buffer());
2017 [argument_encoder setArgumentBuffer:encoder_buffer->get_metal_buffer() offset:0];
2018 [argument_encoder
2019 setSamplerStates:this->samplers_.mtl_sampler
2020 withRange:NSMakeRange(0, shader_interface->get_max_texture_index() + 1)];
2021 encoder_buffer->flush();
2022
2023 /* Insert into cache. */
2024 this->cached_sampler_buffers_.add_new(this->samplers_, encoder_buffer);
2025 }
2026
2027 BLI_assert(encoder_buffer != nullptr);
2028 int compute_buffer_index = (pipeline_state_instance->base_uniform_buffer_index +
2029 compute_arg_buffer_bind_index);
2030 cs.bind_compute_buffer(encoder_buffer->get_metal_buffer(), 0, compute_buffer_index);
2031 }
2032 }
2033 }
2034}
2035
2036/* Encode latest depth-stencil state. */
2037void MTLContext::ensure_depth_stencil_state(MTLPrimitiveType prim_type)
2038{
2039 /* Check if we need to update state. */
2040 if (!(this->pipeline_state.dirty_flags & MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG)) {
2041 return;
2042 }
2043
2044 /* Fetch render command encoder. */
2045 id<MTLRenderCommandEncoder> rec = this->main_command_buffer.get_active_render_command_encoder();
2046 BLI_assert(rec);
2047
2048 /* Fetch Render Pass state. */
2049 MTLRenderPassState &rps = this->main_command_buffer.get_render_pass_state();
2050
2053 bool hasDepthTarget = fb->has_depth_attachment();
2054 bool hasStencilTarget = fb->has_stencil_attachment();
2055
2056 if (hasDepthTarget || hasStencilTarget) {
2057 /* Update FrameBuffer State. */
2058 this->pipeline_state.depth_stencil_state.has_depth_target = hasDepthTarget;
2059 this->pipeline_state.depth_stencil_state.has_stencil_target = hasStencilTarget;
2060
2061 /* Check if current MTLContextDepthStencilState maps to an existing state object in
2062 * the Depth-stencil state cache. */
2063 id<MTLDepthStencilState> ds_state = nil;
2064 id<MTLDepthStencilState> *depth_stencil_state_lookup =
2065 this->depth_stencil_state_cache.lookup_ptr(this->pipeline_state.depth_stencil_state);
2066
2067 /* If not, populate DepthStencil state descriptor. */
2068 if (depth_stencil_state_lookup == nullptr) {
2069
2070 MTLDepthStencilDescriptor *ds_state_desc = [[[MTLDepthStencilDescriptor alloc] init]
2071 autorelease];
2072
2073 if (hasDepthTarget) {
2074 ds_state_desc.depthWriteEnabled =
2075 this->pipeline_state.depth_stencil_state.depth_write_enable;
2076 ds_state_desc.depthCompareFunction =
2077 this->pipeline_state.depth_stencil_state.depth_test_enabled ?
2078 this->pipeline_state.depth_stencil_state.depth_function :
2079 MTLCompareFunctionAlways;
2080 }
2081
2082 if (hasStencilTarget) {
2083 ds_state_desc.backFaceStencil.readMask =
2084 this->pipeline_state.depth_stencil_state.stencil_read_mask;
2085 ds_state_desc.backFaceStencil.writeMask =
2086 this->pipeline_state.depth_stencil_state.stencil_write_mask;
2087 ds_state_desc.backFaceStencil.stencilFailureOperation =
2088 this->pipeline_state.depth_stencil_state.stencil_op_back_stencil_fail;
2089 ds_state_desc.backFaceStencil.depthFailureOperation =
2090 this->pipeline_state.depth_stencil_state.stencil_op_back_depth_fail;
2091 ds_state_desc.backFaceStencil.depthStencilPassOperation =
2092 this->pipeline_state.depth_stencil_state.stencil_op_back_depthstencil_pass;
2093 ds_state_desc.backFaceStencil.stencilCompareFunction =
2094 (this->pipeline_state.depth_stencil_state.stencil_test_enabled) ?
2095 this->pipeline_state.depth_stencil_state.stencil_func :
2096 MTLCompareFunctionAlways;
2097
2098 ds_state_desc.frontFaceStencil.readMask =
2099 this->pipeline_state.depth_stencil_state.stencil_read_mask;
2100 ds_state_desc.frontFaceStencil.writeMask =
2101 this->pipeline_state.depth_stencil_state.stencil_write_mask;
2102 ds_state_desc.frontFaceStencil.stencilFailureOperation =
2103 this->pipeline_state.depth_stencil_state.stencil_op_front_stencil_fail;
2104 ds_state_desc.frontFaceStencil.depthFailureOperation =
2105 this->pipeline_state.depth_stencil_state.stencil_op_front_depth_fail;
2106 ds_state_desc.frontFaceStencil.depthStencilPassOperation =
2107 this->pipeline_state.depth_stencil_state.stencil_op_front_depthstencil_pass;
2108 ds_state_desc.frontFaceStencil.stencilCompareFunction =
2109 (this->pipeline_state.depth_stencil_state.stencil_test_enabled) ?
2110 this->pipeline_state.depth_stencil_state.stencil_func :
2111 MTLCompareFunctionAlways;
2112 }
2113
2114 /* Bake new DS state. */
2115 ds_state = [this->device newDepthStencilStateWithDescriptor:ds_state_desc];
2116
2117 /* Store state in cache. */
2118 BLI_assert(ds_state != nil);
2119 this->depth_stencil_state_cache.add_new(this->pipeline_state.depth_stencil_state, ds_state);
2120 }
2121 else {
2122 ds_state = *depth_stencil_state_lookup;
2123 BLI_assert(ds_state != nil);
2124 }
2125
2126 /* Bind Depth Stencil State to render command encoder. */
2127 BLI_assert(ds_state != nil);
2128 if (ds_state != nil) {
2129 if (rps.bound_ds_state != ds_state) {
2130 [rec setDepthStencilState:ds_state];
2131 rps.bound_ds_state = ds_state;
2132 }
2133 }
2134
2135 /* Apply dynamic depth-stencil state on encoder. */
2136 if (hasStencilTarget) {
2137 uint32_t stencil_ref_value =
2138 (this->pipeline_state.depth_stencil_state.stencil_test_enabled) ?
2139 this->pipeline_state.depth_stencil_state.stencil_ref :
2140 0;
2141 if (stencil_ref_value != rps.last_used_stencil_ref_value) {
2142 [rec setStencilReferenceValue:stencil_ref_value];
2143 rps.last_used_stencil_ref_value = stencil_ref_value;
2144 }
2145 }
2146
2147 if (hasDepthTarget) {
2148 bool doBias = false;
2149 switch (prim_type) {
2150 case MTLPrimitiveTypeTriangle:
2151 case MTLPrimitiveTypeTriangleStrip:
2152 doBias = this->pipeline_state.depth_stencil_state.depth_bias_enabled_for_tris;
2153 break;
2154 case MTLPrimitiveTypeLine:
2155 case MTLPrimitiveTypeLineStrip:
2156 doBias = this->pipeline_state.depth_stencil_state.depth_bias_enabled_for_lines;
2157 break;
2158 case MTLPrimitiveTypePoint:
2159 doBias = this->pipeline_state.depth_stencil_state.depth_bias_enabled_for_points;
2160 break;
2161 }
2162 [rec setDepthBias:(doBias) ? this->pipeline_state.depth_stencil_state.depth_bias : 0
2163 slopeScale:(doBias) ? this->pipeline_state.depth_stencil_state.depth_slope_scale : 0
2164 clamp:0];
2165 }
2166 }
2167}
2168
2170
2171/* -------------------------------------------------------------------- */
2174
2176{
2177 /* Verify if bound shader is valid and fetch MTLComputePipelineStateInstance. */
2178 /* Check if an active shader is bound. */
2179 if (!this->pipeline_state.active_shader) {
2180 MTL_LOG_WARNING("No Metal shader bound!");
2181 return nullptr;
2182 }
2183 /* Also ensure active shader is valid. */
2184 if (!this->pipeline_state.active_shader->is_valid()) {
2186 "Bound active shader is not valid (Missing/invalid implementation for Metal).", );
2187 return nullptr;
2188 }
2189 /* Verify this is a compute shader. */
2190
2191 /* Fetch shader interface. */
2192 MTLShaderInterface *shader_interface = this->pipeline_state.active_shader->get_interface();
2193 if (shader_interface == nullptr) {
2194 MTL_LOG_WARNING("Bound active shader does not have a valid shader interface!", );
2195 return nullptr;
2196 }
2197
2198 MTLShader *active_shader = this->pipeline_state.active_shader;
2199
2200 /* Set descriptor to default shader constants . */
2201 MTLComputePipelineStateDescriptor compute_pipeline_descriptor(this->constants_state.values);
2202
2203 const MTLComputePipelineStateInstance *compute_pso_inst =
2204 active_shader->bake_compute_pipeline_state(this, compute_pipeline_descriptor);
2205
2206 if (compute_pso_inst == nullptr || compute_pso_inst->pso == nil) {
2207 MTL_LOG_WARNING("No valid compute PSO for compute dispatch!", );
2208 return nullptr;
2209 }
2210 return compute_pso_inst;
2211}
2212
2213void MTLContext::compute_dispatch(int groups_x_len, int groups_y_len, int groups_z_len)
2214{
2215 /* Ensure all resources required by upcoming compute submission are correctly bound to avoid
2216 * out of bounds reads/writes. */
2217 const MTLComputePipelineStateInstance *compute_pso_inst = this->ensure_compute_pipeline_state();
2218 if (compute_pso_inst == nullptr) {
2219 return;
2220 }
2221
2222#if MTL_DEBUG_SINGLE_DISPATCH_PER_ENCODER == 1
2223 GPU_flush();
2224#endif
2225
2226 /* Shader instance. */
2227 MTLShaderInterface *shader_interface = this->pipeline_state.active_shader->get_interface();
2228 BLI_assert(compute_pso_inst != nullptr);
2229
2230 /* Begin compute encoder. */
2231 id<MTLComputeCommandEncoder> compute_encoder =
2232 this->main_command_buffer.ensure_begin_compute_encoder();
2233 BLI_assert(compute_encoder != nil);
2234
2235 /* Bind PSO. */
2236 MTLComputeState &cs = this->main_command_buffer.get_compute_state();
2237 cs.bind_pso(compute_pso_inst->pso);
2238
2240 /* Texture Bindings. */
2241 /* We will iterate through all texture bindings on the context and determine if any of the
2242 * active slots match those in our shader interface. If so, textures will be bound. */
2243 if (shader_interface->get_total_textures() > 0) {
2244 this->ensure_texture_bindings(compute_encoder, shader_interface, compute_pso_inst);
2245 }
2246
2247 /* Bind buffers.
2248 * NOTE: `ensure_buffer_bindings` must be called after `ensure_texture_bindings` to allow
2249 * for binding of buffer-backed texture's data buffer and metadata. */
2250 this->ensure_buffer_bindings(compute_encoder, shader_interface, compute_pso_inst);
2251
2252 /* Dispatch compute. */
2253 const MTLComputePipelineStateCommon &compute_state_common =
2254 this->pipeline_state.active_shader->get_compute_common_state();
2255 [compute_encoder dispatchThreadgroups:MTLSizeMake(max_ii(groups_x_len, 1),
2256 max_ii(groups_y_len, 1),
2257 max_ii(groups_z_len, 1))
2258 threadsPerThreadgroup:MTLSizeMake(compute_state_common.threadgroup_x_len,
2259 compute_state_common.threadgroup_y_len,
2260 compute_state_common.threadgroup_z_len)];
2261#if MTL_DEBUG_SINGLE_DISPATCH_PER_ENCODER == 1
2262 GPU_flush();
2263#endif
2264}
2265
2267{
2268
2269#if MTL_DEBUG_SINGLE_DISPATCH_PER_ENCODER == 1
2270 GPU_flush();
2271#endif
2272
2273 /* Ensure all resources required by upcoming compute submission are correctly bound. */
2274 const MTLComputePipelineStateInstance *compute_pso_inst = this->ensure_compute_pipeline_state();
2275 BLI_assert(compute_pso_inst != nullptr);
2276
2277 /* Shader instance. */
2278 MTLShaderInterface *shader_interface = this->pipeline_state.active_shader->get_interface();
2279
2280 /* Begin compute encoder. */
2281 id<MTLComputeCommandEncoder> compute_encoder =
2282 this->main_command_buffer.ensure_begin_compute_encoder();
2283 BLI_assert(compute_encoder != nil);
2284
2285 /* Bind PSO. */
2286 MTLComputeState &cs = this->main_command_buffer.get_compute_state();
2287 cs.bind_pso(compute_pso_inst->pso);
2288
2290 /* Texture Bindings. */
2291 /* We will iterate through all texture bindings on the context and determine if any of the
2292 * active slots match those in our shader interface. If so, textures will be bound. */
2293 if (shader_interface->get_total_textures() > 0) {
2294 this->ensure_texture_bindings(compute_encoder, shader_interface, compute_pso_inst);
2295 }
2296
2297 /* Bind buffers.
2298 * NOTE: `ensure_buffer_bindings` must be called after `ensure_texture_bindings` to allow
2299 * for binding of buffer-backed texture's data buffer and metadata. */
2300 this->ensure_buffer_bindings(compute_encoder, shader_interface, compute_pso_inst);
2301
2302 /* Indirect Dispatch compute. */
2303 MTLStorageBuf *mtlssbo = static_cast<MTLStorageBuf *>(indirect_buf);
2304 id<MTLBuffer> mtl_indirect_buf = mtlssbo->get_metal_buffer();
2305 BLI_assert(mtl_indirect_buf != nil);
2306 if (mtl_indirect_buf == nil) {
2307 MTL_LOG_WARNING("Metal Indirect Compute dispatch storage buffer does not exist.");
2308 return;
2309 }
2310
2311 /* Indirect Compute dispatch. */
2312 const MTLComputePipelineStateCommon &compute_state_common =
2313 this->pipeline_state.active_shader->get_compute_common_state();
2314 [compute_encoder
2315 dispatchThreadgroupsWithIndirectBuffer:mtl_indirect_buf
2316 indirectBufferOffset:0
2317 threadsPerThreadgroup:MTLSizeMake(compute_state_common.threadgroup_x_len,
2318 compute_state_common.threadgroup_y_len,
2319 compute_state_common.threadgroup_z_len)];
2320#if MTL_DEBUG_SINGLE_DISPATCH_PER_ENCODER == 1
2321 GPU_flush();
2322#endif
2323}
2324
2326
2327/* -------------------------------------------------------------------- */
2330
2332{
2333 /* Flag visibility buffer as dirty if the buffer being used for visibility has changed --
2334 * This is required by the render pass, and we will break the pass if the results destination
2335 * buffer is modified. */
2336 if (buffer) {
2337 visibility_is_dirty_ = (buffer != visibility_buffer_) || visibility_is_dirty_;
2338 visibility_buffer_ = buffer;
2339 visibility_buffer_->debug_ensure_used();
2340 }
2341 else {
2342 /* If buffer is null, reset visibility state, mark dirty to break render pass if results are no
2343 * longer needed. */
2344 visibility_is_dirty_ = (visibility_buffer_ != nullptr) || visibility_is_dirty_;
2345 visibility_buffer_ = nullptr;
2346 }
2347}
2348
2350{
2351 return visibility_buffer_;
2352}
2353
2355{
2356 visibility_is_dirty_ = false;
2357}
2358
2360{
2361 return visibility_is_dirty_;
2362}
2363
2365
2366/* -------------------------------------------------------------------- */
2369
2370void MTLContext::texture_bind(gpu::MTLTexture *mtl_texture, uint texture_unit, bool is_image)
2371{
2372 BLI_assert(this);
2373 BLI_assert(mtl_texture);
2374
2375 if (texture_unit < 0 || texture_unit >= GPU_max_textures() ||
2376 texture_unit >= MTL_MAX_TEXTURE_SLOTS)
2377 {
2378 MTL_LOG_ERROR("Attempting to bind texture '%s' to invalid texture unit %d",
2379 mtl_texture->get_name(),
2380 texture_unit);
2381 BLI_assert(false);
2382 return;
2383 }
2384
2386 &resource_bind_table)[MTL_MAX_TEXTURE_SLOTS] = (is_image) ?
2387 this->pipeline_state.image_bindings :
2388 this->pipeline_state.texture_bindings;
2389
2390 /* Bind new texture. */
2391 resource_bind_table[texture_unit].texture_resource = mtl_texture;
2392 resource_bind_table[texture_unit].used = true;
2393 mtl_texture->is_bound_ = true;
2394}
2395
2396void MTLContext::sampler_bind(MTLSamplerState sampler_state, uint sampler_unit)
2397{
2398 BLI_assert(this);
2399 if (sampler_unit < 0 || sampler_unit >= GPU_max_textures() ||
2400 sampler_unit >= MTL_MAX_SAMPLER_SLOTS)
2401 {
2402 MTL_LOG_ERROR("Attempting to bind sampler to invalid sampler unit %d", sampler_unit);
2403 BLI_assert(false);
2404 return;
2405 }
2406
2407 /* Apply binding. */
2408 this->pipeline_state.sampler_bindings[sampler_unit] = {true, sampler_state};
2409}
2410
2412 bool is_image,
2414{
2415 BLI_assert(mtl_texture);
2416
2418 &resource_bind_table)[MTL_MAX_TEXTURE_SLOTS] = (is_image) ?
2419 this->pipeline_state.image_bindings :
2420 this->pipeline_state.texture_bindings;
2421
2422 /* Iterate through textures in state and unbind. */
2423 for (int i = 0; i < min_uu(GPU_max_textures(), MTL_MAX_TEXTURE_SLOTS); i++) {
2424 if (resource_bind_table[i].texture_resource == mtl_texture) {
2425 resource_bind_table[i].texture_resource = nullptr;
2426 resource_bind_table[i].used = false;
2427 if (is_image) {
2429 }
2430 }
2431 }
2432
2433 /* Locally unbind texture. */
2434 mtl_texture->is_bound_ = false;
2435}
2436
2438{
2440 &resource_bind_table)[MTL_MAX_TEXTURE_SLOTS] = (is_image) ?
2441 this->pipeline_state.image_bindings :
2442 this->pipeline_state.texture_bindings;
2443
2444 /* Iterate through context's bound textures. */
2445 for (int t = 0; t < min_uu(GPU_max_textures(), MTL_MAX_TEXTURE_SLOTS); t++) {
2446 if (resource_bind_table[t].used && resource_bind_table[t].texture_resource) {
2447 resource_bind_table[t].used = false;
2448 resource_bind_table[t].texture_resource = nullptr;
2449 }
2450 }
2451}
2452
2453id<MTLSamplerState> MTLContext::get_sampler_from_state(MTLSamplerState sampler_state)
2454{
2455 /* Internal sampler states are signal values and do not correspond to actual samplers. */
2457
2458 if (sampler_state.state.type == GPU_SAMPLER_STATE_TYPE_CUSTOM) {
2459 return custom_sampler_state_cache_[sampler_state.state.custom_type];
2460 }
2461
2462 return sampler_state_cache_[sampler_state.state.extend_yz][sampler_state.state.extend_x]
2463 [sampler_state.state.filtering];
2464}
2465
2467static inline MTLSamplerAddressMode to_mtl_type(GPUSamplerExtendMode wrap_mode)
2468{
2469 switch (wrap_mode) {
2471 return MTLSamplerAddressModeClampToEdge;
2473 return MTLSamplerAddressModeRepeat;
2475 return MTLSamplerAddressModeMirrorRepeat;
2477 return MTLSamplerAddressModeClampToBorderColor;
2478 default:
2480 return MTLSamplerAddressModeClampToEdge;
2481 }
2482}
2483
2485{
2486 for (int extend_yz_i = 0; extend_yz_i < GPU_SAMPLER_EXTEND_MODES_COUNT; extend_yz_i++) {
2487 const GPUSamplerExtendMode extend_yz = static_cast<GPUSamplerExtendMode>(extend_yz_i);
2488 const MTLSamplerAddressMode extend_t = to_mtl_type(extend_yz);
2489
2490 for (int extend_x_i = 0; extend_x_i < GPU_SAMPLER_EXTEND_MODES_COUNT; extend_x_i++) {
2491 const GPUSamplerExtendMode extend_x = static_cast<GPUSamplerExtendMode>(extend_x_i);
2492 const MTLSamplerAddressMode extend_s = to_mtl_type(extend_x);
2493
2494 for (int filtering_i = 0; filtering_i < GPU_SAMPLER_FILTERING_TYPES_COUNT; filtering_i++) {
2495 const GPUSamplerFiltering filtering = GPUSamplerFiltering(filtering_i);
2496
2497 MTLSamplerDescriptor *descriptor = [[MTLSamplerDescriptor alloc] init];
2498 descriptor.normalizedCoordinates = true;
2499 descriptor.sAddressMode = extend_s;
2500 descriptor.tAddressMode = extend_t;
2501 descriptor.rAddressMode = extend_t;
2502 descriptor.borderColor = MTLSamplerBorderColorTransparentBlack;
2503 descriptor.minFilter = (filtering & GPU_SAMPLER_FILTERING_LINEAR) ?
2504 MTLSamplerMinMagFilterLinear :
2505 MTLSamplerMinMagFilterNearest;
2506 descriptor.magFilter = (filtering & GPU_SAMPLER_FILTERING_LINEAR) ?
2507 MTLSamplerMinMagFilterLinear :
2508 MTLSamplerMinMagFilterNearest;
2509 descriptor.mipFilter = (filtering & GPU_SAMPLER_FILTERING_MIPMAP) ?
2510 MTLSamplerMipFilterLinear :
2511 MTLSamplerMipFilterNotMipmapped;
2512 descriptor.lodMinClamp = -1000;
2513 descriptor.lodMaxClamp = 1000;
2514 float aniso_filter = max_ff(16, U.anisotropic_filter);
2515 descriptor.maxAnisotropy = (filtering & GPU_SAMPLER_FILTERING_MIPMAP) ? aniso_filter : 1;
2516 descriptor.compareFunction = MTLCompareFunctionAlways;
2517 descriptor.supportArgumentBuffers = true;
2518
2519 id<MTLSamplerState> state = [this->device newSamplerStateWithDescriptor:descriptor];
2520 sampler_state_cache_[extend_yz_i][extend_x_i][filtering_i] = state;
2521
2522 BLI_assert(state != nil);
2523 [descriptor autorelease];
2524 }
2525 }
2526 }
2527
2528 /* Compare sampler for depth textures. */
2529 {
2530 MTLSamplerDescriptor *descriptor = [[MTLSamplerDescriptor alloc] init];
2531 descriptor.minFilter = MTLSamplerMinMagFilterLinear;
2532 descriptor.magFilter = MTLSamplerMinMagFilterLinear;
2533 descriptor.compareFunction = MTLCompareFunctionLessEqual;
2534 descriptor.lodMinClamp = -1000;
2535 descriptor.lodMaxClamp = 1000;
2536 descriptor.supportArgumentBuffers = true;
2537
2538 id<MTLSamplerState> compare_state = [this->device newSamplerStateWithDescriptor:descriptor];
2539 custom_sampler_state_cache_[GPU_SAMPLER_CUSTOM_COMPARE] = compare_state;
2540
2541 BLI_assert(compare_state != nil);
2542 [descriptor autorelease];
2543 }
2544
2545 /* Custom sampler for icons. The icon texture is sampled within the shader using a -0.5f LOD
2546 * bias. */
2547 {
2548 MTLSamplerDescriptor *descriptor = [[MTLSamplerDescriptor alloc] init];
2549 descriptor.minFilter = MTLSamplerMinMagFilterLinear;
2550 descriptor.magFilter = MTLSamplerMinMagFilterLinear;
2551 descriptor.mipFilter = MTLSamplerMipFilterNearest;
2552 descriptor.lodMinClamp = 0;
2553 descriptor.lodMaxClamp = 1;
2554
2555 id<MTLSamplerState> icon_state = [this->device newSamplerStateWithDescriptor:descriptor];
2556 custom_sampler_state_cache_[GPU_SAMPLER_CUSTOM_ICON] = icon_state;
2557
2558 BLI_assert(icon_state != nil);
2559 [descriptor autorelease];
2560 }
2561}
2562
2564{
2565 if (default_sampler_state_ == nil) {
2566 default_sampler_state_ = this->get_sampler_from_state({GPUSamplerState::default_sampler()});
2567 }
2568 return default_sampler_state_;
2569}
2570
2572
2573/* -------------------------------------------------------------------- */
2576
2578{
2579 if (buffer_clear_pso_ != nil) {
2580 return buffer_clear_pso_;
2581 }
2582
2583 /* Fetch active context. */
2584 MTLContext *ctx = MTLContext::get();
2585 BLI_assert(ctx);
2586
2587 @autoreleasepool {
2588 /* Source as NSString. */
2589 const char *src =
2590 "\
2591 struct BufferClearParams {\
2592 uint clear_value;\
2593 };\
2594 kernel void compute_buffer_clear(constant BufferClearParams &params [[buffer(0)]],\
2595 device uint32_t* output_data [[buffer(1)]],\
2596 uint position [[thread_position_in_grid]])\
2597 {\
2598 output_data[position] = params.clear_value;\
2599 }";
2600 NSString *compute_buffer_clear_src = [NSString stringWithUTF8String:src];
2601
2602 /* Prepare shader library for buffer clearing. */
2603 MTLCompileOptions *options = [[[MTLCompileOptions alloc] init] autorelease];
2604 options.languageVersion = MTLLanguageVersion2_2;
2605
2606 NSError *error = nullptr;
2607 id<MTLLibrary> temp_lib = [[ctx->device newLibraryWithSource:compute_buffer_clear_src
2609 error:&error] autorelease];
2610 if (error) {
2611 /* Only exit out if genuine error and not warning. */
2612 if ([[error localizedDescription] rangeOfString:@"Compilation succeeded"].location ==
2613 NSNotFound)
2614 {
2615 NSLog(@"Compile Error - Metal Shader Library error %@ ", error);
2616 BLI_assert(false);
2617 return nil;
2618 }
2619 }
2620
2621 /* Fetch compute function. */
2622 BLI_assert(temp_lib != nil);
2623 id<MTLFunction> temp_compute_function = [[temp_lib newFunctionWithName:@"compute_buffer_clear"]
2624 autorelease];
2625 BLI_assert(temp_compute_function);
2626
2627 /* Compile compute PSO */
2628 buffer_clear_pso_ = [ctx->device newComputePipelineStateWithFunction:temp_compute_function
2629 error:&error];
2630 if (error || buffer_clear_pso_ == nil) {
2631 NSLog(@"Failed to prepare compute_buffer_clear MTLComputePipelineState %@", error);
2632 BLI_assert(false);
2633 return nil;
2634 }
2635
2636 [buffer_clear_pso_ retain];
2637 }
2638
2639 BLI_assert(buffer_clear_pso_ != nil);
2640 return buffer_clear_pso_;
2641}
2642
2644
2645/* -------------------------------------------------------------------- */
2648
2649void present(MTLRenderPassDescriptor *blit_descriptor,
2650 id<MTLRenderPipelineState> blit_pso,
2651 id<MTLTexture> swapchain_texture,
2652 id<CAMetalDrawable> drawable)
2653{
2654
2655 MTLContext *ctx = MTLContext::get();
2656 BLI_assert(ctx);
2657
2658 /* Flush any outstanding work. */
2659 ctx->flush();
2660
2661 /* Always pace CPU to maximum of 3 drawables in flight.
2662 * nextDrawable may have more in flight if backing swapchain
2663 * textures are re-allocate, such as during resize events.
2664 *
2665 * Determine frames in flight based on current latency. If
2666 * we are in a high-latency situation, limit frames in flight
2667 * to increase app responsiveness and keep GPU execution under control.
2668 * If latency improves, increase frames in flight to improve overall
2669 * performance. */
2670 int perf_max_drawables = MTL_MAX_DRAWABLES;
2672 perf_max_drawables = 1;
2673 }
2674 else if (MTLContext::avg_drawable_latency_us > 75000) {
2675 perf_max_drawables = 2;
2676 }
2677
2678 while (MTLContext::max_drawables_in_flight > min_ii(perf_max_drawables, MTL_MAX_DRAWABLES)) {
2680 }
2681
2682 /* Present is submitted in its own CMD Buffer to ensure drawable reference released as early as
2683 * possible. This command buffer is separate as it does not utilize the global state
2684 * for rendering as the main context does. */
2685 id<MTLCommandBuffer> cmdbuf = [ctx->queue commandBuffer];
2687
2688 /* Do Present Call and final Blit to MTLDrawable. */
2689 id<MTLRenderCommandEncoder> enc = [cmdbuf renderCommandEncoderWithDescriptor:blit_descriptor];
2690 [enc setRenderPipelineState:blit_pso];
2691 [enc setFragmentTexture:swapchain_texture atIndex:0];
2692 [enc drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:3];
2693 [enc endEncoding];
2694
2695 /* Present drawable. */
2696 BLI_assert(drawable);
2697 [cmdbuf presentDrawable:drawable];
2698
2699 /* Ensure freed buffers have usage tracked against active CommandBuffer submissions. */
2700 MTLSafeFreeList *cmd_free_buffer_list =
2702 BLI_assert(cmd_free_buffer_list);
2703
2704 /* Increment drawables in flight limiter. */
2706 std::chrono::time_point submission_time = std::chrono::high_resolution_clock::now();
2707
2708 /* Increment free pool reference and decrement upon command buffer completion. */
2709 cmd_free_buffer_list->increment_reference();
2710 [cmdbuf addCompletedHandler:^(id<MTLCommandBuffer> /*cb*/) {
2711 /* Flag freed buffers associated with this CMD buffer as ready to be freed. */
2712 cmd_free_buffer_list->decrement_reference();
2713
2714 /* Decrement count */
2716
2717 MTL_LOG_DEBUG("Active command buffers: %d",
2719
2720 /* Drawable count and latency management. */
2722 std::chrono::time_point completion_time = std::chrono::high_resolution_clock::now();
2723 int64_t microseconds_per_frame = std::chrono::duration_cast<std::chrono::microseconds>(
2724 completion_time - submission_time)
2725 .count();
2726 MTLContext::latency_resolve_average(microseconds_per_frame);
2727
2728 MTL_LOG_DEBUG("Frame Latency: %f ms (Rolling avg: %f ms Drawables: %d)",
2729 ((float)microseconds_per_frame) / 1000.0f,
2730 ((float)MTLContext::avg_drawable_latency_us) / 1000.0f,
2731 perf_max_drawables);
2732 }];
2733
2734 [cmdbuf commit];
2735
2736 /* When debugging, fetch advanced command buffer errors. */
2737 if (G.debug & G_DEBUG_GPU) {
2738 [cmdbuf waitUntilCompleted];
2739 NSError *error = [cmdbuf error];
2740 if (error != nil) {
2741 NSLog(@"%@", error);
2742 BLI_assert(false);
2743 }
2744 }
2745}
2746
2748
2749} // namespace blender::gpu
@ G_DEBUG_GPU
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
void BLI_kdtree_nd_ free(KDTree *tree)
MINLINE uint min_uu(uint a, uint b)
MINLINE float max_ff(float a, float b)
MINLINE int min_ii(int a, int b)
MINLINE uint max_uu(uint a, uint b)
MINLINE int max_ii(int a, int b)
unsigned int uint
Platform independent time functions.
void BLI_time_sleep_ms(int ms)
Definition time.cc:133
#define UNUSED_VARS_NDEBUG(...)
int GPU_max_textures()
@ GPU_COUNTERCLOCKWISE
@ GPU_CLOCKWISE
static constexpr int GPU_MAX_VIEWPORTS
void GPU_flush()
Definition gpu_state.cc:305
@ GPU_CULL_FRONT
Definition GPU_state.hh:137
@ GPU_CULL_NONE
Definition GPU_state.hh:136
@ GPU_CULL_BACK
Definition GPU_state.hh:138
@ GPU_SAMPLER_CUSTOM_ICON
@ GPU_SAMPLER_CUSTOM_COMPARE
blender::gpu::Texture * GPU_texture_create_2d_array(const char *name, int width, int height, int layer_len, int mip_len, blender::gpu::TextureFormat format, eGPUTextureUsage usage, const float *data)
blender::gpu::Texture * GPU_texture_create_cube(const char *name, int width, int mip_len, blender::gpu::TextureFormat format, eGPUTextureUsage usage, const float *data)
@ GPU_SAMPLER_STATE_TYPE_CUSTOM
@ GPU_SAMPLER_STATE_TYPE_INTERNAL
blender::gpu::Texture * GPU_texture_create_1d_array(const char *name, int width, int layer_len, int mip_len, blender::gpu::TextureFormat format, eGPUTextureUsage usage, const float *data)
static const int GPU_SAMPLER_FILTERING_TYPES_COUNT
blender::gpu::Texture * GPU_texture_create_cube_array(const char *name, int width, int layer_len, int mip_len, blender::gpu::TextureFormat format, eGPUTextureUsage usage, const float *data)
eGPUTextureUsage
@ GPU_TEXTURE_USAGE_GENERAL
@ GPU_TEXTURE_USAGE_ATOMIC
GPUSamplerExtendMode
@ GPU_SAMPLER_EXTEND_MODE_MIRRORED_REPEAT
@ GPU_SAMPLER_EXTEND_MODE_REPEAT
@ GPU_SAMPLER_EXTEND_MODE_EXTEND
@ GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER
#define GPU_SAMPLER_CUSTOM_TYPES_COUNT
blender::gpu::Texture * GPU_texture_create_2d(const char *name, int width, int height, int mip_len, blender::gpu::TextureFormat format, eGPUTextureUsage usage, const float *data)
void GPU_texture_unbind_all()
blender::gpu::Texture * GPU_texture_create_from_vertbuf(const char *name, blender::gpu::VertBuf *vertex_buf)
GPUSamplerFiltering
@ GPU_SAMPLER_FILTERING_MIPMAP
@ GPU_SAMPLER_FILTERING_LINEAR
void GPU_texture_free(blender::gpu::Texture *texture)
#define GPU_SAMPLER_EXTEND_MODES_COUNT
blender::gpu::Texture * GPU_texture_create_3d(const char *name, int width, int height, int depth, int mip_len, blender::gpu::TextureFormat format, eGPUTextureUsage usage, const void *data)
blender::gpu::Texture * GPU_texture_create_1d(const char *name, int width, int mip_len, blender::gpu::TextureFormat format, eGPUTextureUsage usage, const float *data)
void GPU_uniformbuf_unbind(blender::gpu::UniformBuf *ubo)
blender::gpu::VertBuf * GPU_vertbuf_create_with_format_ex(const GPUVertFormat &format, GPUUsageType usage)
void GPU_vertbuf_data_alloc(blender::gpu::VertBuf &verts, uint v_len)
void GPU_vertbuf_discard(blender::gpu::VertBuf *)
@ GPU_USAGE_STATIC
@ GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY
void GPU_vertformat_clear(GPUVertFormat *)
uint GPU_vertformat_attr_add(GPUVertFormat *format, blender::StringRef name, blender::gpu::VertAttrType type)
#define U
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const BMVert * v
long long int int64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
id< MTLTexture > metalOverlayTexture()
GHOST_Context * getContext()
static MTLBackend * get()
void init(id< MTLDevice > device)
Definition mtl_memory.mm:35
MTLSafeFreeList * get_current_safe_list()
gpu::MTLBuffer * allocate(uint64_t size, bool cpu_visible)
id< MTLBuffer > get_metal_buffer() const
static volatile std::atomic< int > num_active_cmd_bufs_in_system
void bind_compute_bytes(const void *bytes, uint64_t length, uint index)
void bind_compute_sampler(MTLSamplerBinding &sampler_binding, bool use_argument_buffer_for_samplers, uint slot)
void bind_compute_texture(id< MTLTexture > tex, uint slot)
void bind_compute_buffer(id< MTLBuffer > buffer, uint64_t buffer_offset, uint index)
void bind_pso(id< MTLComputePipelineState > pso)
id< MTLComputePipelineState > get_buffer_clear_pso()
bool ensure_render_pipeline_state(MTLPrimitiveType prim_type)
MTLFrameBuffer * get_current_framebuffer()
MTLFrameBuffer * get_default_framebuffer()
static std::atomic< int64_t > avg_drawable_latency_us
MTLContextComputeUtils & get_compute_utils()
const MTLComputePipelineStateInstance * ensure_compute_pipeline_state()
id< MTLSamplerState > get_sampler_from_state(MTLSamplerState state)
MTLContext(void *ghost_window, void *ghost_context)
shader::SpecializationConstants constants_state
void deactivate() override
static void latency_resolve_average(int64_t frame_latency_us)
gpu::MTLBuffer * get_visibility_buffer() const
void set_visibility_buffer(gpu::MTLBuffer *buffer)
void set_scissor_enabled(bool scissor_enabled)
void framebuffer_bind(MTLFrameBuffer *framebuffer)
id< MTLRenderCommandEncoder > ensure_begin_render_pass()
static MTLContext * get()
id< MTLBuffer > get_null_buffer()
friend class MTLRenderPassState
id< MTLBuffer > get_null_attribute_buffer()
static void global_memory_manager_release_ref()
void activate() override
MTLScratchBufferManager memory_manager
MTLContextGlobalShaderPipelineState pipeline_state
void ensure_depth_stencil_state(MTLPrimitiveType prim_type)
static MTLBufferPool * global_memory_manager
void end_frame() override
static int global_memory_manager_refcount
MTLShader * get_active_shader()
void texture_unbind(gpu::MTLTexture *mtl_texture, bool is_image, StateManager *state_manager)
void set_viewport(int origin_x, int origin_y, int width, int height)
static int64_t frame_latency[MTL_FRAME_AVERAGE_COUNT]
static std::mutex global_memory_manager_reflock
void sampler_bind(MTLSamplerState, uint sampler_unit)
void texture_bind(gpu::MTLTexture *mtl_texture, uint texture_unit, bool is_image)
void specialization_constants_set(const shader::SpecializationConstants *constants_state)
void begin_frame() override
void set_viewports(int count, const int(&viewports)[GPU_MAX_VIEWPORTS][4])
void ensure_texture_bindings(id< MTLRenderCommandEncoder > rec, MTLShaderInterface *shader_interface, const MTLRenderPipelineStateInstance *pipeline_state_instance)
void compute_dispatch_indirect(StorageBuf *indirect_buf)
static void check_error(const char *info)
void set_scissor(int scissor_x, int scissor_y, int scissor_width, int scissor_height)
MTLCommandBufferManager main_command_buffer
id< MTLSamplerState > get_default_sampler_state()
bool is_visibility_dirty() const
void texture_unbind_all(bool is_image)
static void global_memory_manager_acquire_ref()
void memory_statistics_get(int *r_total_mem, int *r_free_mem) override
bool ensure_buffer_bindings(id< MTLRenderCommandEncoder > rec, const MTLShaderInterface *shader_interface, const MTLRenderPipelineStateInstance *pipeline_state_instance)
MTLScratchBufferManager & get_scratchbuffer_manager()
id< MTLCommandQueue > queue
gpu::MTLTexture * get_dummy_texture(GPUTextureType type, GPUSamplerFormat sampler_format)
void compute_dispatch(int groups_x_len, int groups_y_len, int groups_z_len)
MTLContextTextureUtils & get_texture_utils()
static MTLBufferPool * get_global_memory_manager()
static std::atomic< int > max_drawables_in_flight
bool add_color_attachment(gpu::MTLTexture *texture, uint slot, int miplevel, int layer)
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)
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
BufferBindingCached cached_fragment_buffer_bindings[MTL_MAX_BUFFER_BINDINGS]
id< MTLRenderPipelineState > bound_pso
BufferBindingCached cached_vertex_buffer_bindings[MTL_MAX_BUFFER_BINDINGS]
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)
const MTLShaderBufferBlock & get_push_constant_block() const
const MTLShaderTexture & get_texture(uint index) const
void insert_argument_encoder(int buffer_index, id encoder)
const MTLShaderBufferBlock & get_uniform_block(uint index) const
const char * get_name_at_offset(uint32_t offset) const
int get_argument_buffer_bind_index(ShaderStage stage) const
const MTLShaderBufferBlock & get_storage_block(uint index) const
id< MTLArgumentEncoder > find_argument_encoder(int buffer_index) const
MTLComputePipelineStateInstance * bake_compute_pipeline_state(MTLContext *ctx, MTLComputePipelineStateDescriptor &compute_pipeline_descriptor)
void uniform_int(int location, int comp_len, int array_size, const int *data) override
void bind(int slot) override
MTLStorageBuf * get_storagebuf()
const int * get_texture_metadata_ptr() const
eGPUTextureUsage usage_get() const
CCL_NAMESPACE_BEGIN struct Options options
void immDeactivate()
void immActivate()
#define assert(assertion)
constexpr T clamp(T, U, U) RET
float length(VecOp< float, D >) RET
TEX_TEMPLATE DataVec texture(T, FltCoord, float=0.0f) RET
BLI_INLINE float fb(float length, float L)
int count
format
descriptor
static ulong state[N]
#define G(x, y, z)
static void error(const char *str)
#define MTL_MAX_SAMPLER_SLOTS
#define MTL_MAX_BUFFER_BINDINGS
#define MTL_MAX_TEXTURE_SLOTS
#define MTL_MAX_DRAWABLES
Definition mtl_common.hh:10
#define MTL_FRAME_AVERAGE_COUNT
Definition mtl_common.hh:9
#define MTL_LOG_SSBO_ERROR
#define MTL_LOG_UBO_ERROR
#define MTL_LOG_WARNING(info,...)
Definition mtl_debug.hh:42
#define MTL_LOG_DEBUG(info,...)
Definition mtl_debug.hh:49
#define MTL_LOG_ERROR(info,...)
Definition mtl_debug.hh:34
void present(MTLRenderPassDescriptor *blit_descriptor, id< MTLRenderPipelineState > blit_pso, id< MTLTexture > swapchain_texture, id< CAMetalDrawable > drawable)
static MTLPrimitiveTopologyClass mtl_prim_type_to_topology_class(MTLPrimitiveType prim_type)
const MTLSamplerState DEFAULT_SAMPLER_STATE
@ MTL_PIPELINE_STATE_CULLMODE_FLAG
@ MTL_PIPELINE_STATE_SCISSOR_FLAG
@ MTL_PIPELINE_STATE_FRONT_FACING_FLAG
@ MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG
@ MTL_PIPELINE_STATE_VIEWPORT_FLAG
@ MTL_PIPELINE_STATE_ALL_FLAG
static MTLSamplerAddressMode to_mtl_type(GPUSamplerExtendMode wrap_mode)
static void init(bNodeTree *, bNode *node)
GPUSamplerCustomType custom_type
GPUSamplerExtendMode extend_yz
static constexpr GPUSamplerState default_sampler()
GPUSamplerFiltering filtering
GPUSamplerExtendMode extend_x
GPUSamplerStateType type
void set(MTLShader *shader, uint pso_index)
blender::Vector< MTLBufferArgumentData > buffer_bindings_reflection_data_frag
Definition mtl_shader.hh:98
blender::Vector< MTLBufferArgumentData > buffer_bindings_reflection_data_vert
Definition mtl_shader.hh:97
i
Definition text_draw.cc:230