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