Blender V4.3
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 * Enabled by default. TODO: Ensure all required UBO bindings are present. */
50#define DEBUG_BIND_NULL_BUFFER_FOR_MISSING_UBO 1
51
52/* Debug option to bind null buffer for missing SSBOs. NOTE: This is unsafe if replacing a
53 * write-enabled SSBO and should only be used for debugging to identify binding-related issues. */
54#define DEBUG_BIND_NULL_BUFFER_FOR_MISSING_SSBO 0
55
56/* Error or warning depending on debug flag. */
57#if DEBUG_BIND_NULL_BUFFER_FOR_MISSING_UBO == 1
58# define MTL_LOG_UBO_ERROR MTL_LOG_WARNING
59#else
60# define MTL_LOG_UBO_ERROR MTL_LOG_ERROR
61#endif
62
63#if DEBUG_BIND_NULL_BUFFER_FOR_MISSING_SSBO == 1
64# define MTL_LOG_SSBO_ERROR MTL_LOG_WARNING
65#else
66# define MTL_LOG_SSBO_ERROR MTL_LOG_ERROR
67#endif
68
69namespace blender::gpu {
70
71/* Global memory manager. */
75
76/* Swap-chain and latency management. */
77std::atomic<int> MTLContext::max_drawables_in_flight = 0;
78std::atomic<int64_t> MTLContext::avg_drawable_latency_us = 0;
80
81/* -------------------------------------------------------------------- */
85void MTLContext::set_ghost_context(GHOST_ContextHandle ghostCtxHandle)
86{
87 GHOST_Context *ghost_ctx = reinterpret_cast<GHOST_Context *>(ghostCtxHandle);
88 BLI_assert(ghost_ctx != nullptr);
89
90 /* Release old MTLTexture handle */
91 if (default_fbo_mtltexture_) {
92 [default_fbo_mtltexture_ release];
93 default_fbo_mtltexture_ = nil;
94 }
95
96 /* Release Framebuffer attachments */
97 MTLFrameBuffer *mtl_front_left = static_cast<MTLFrameBuffer *>(this->front_left);
98 MTLFrameBuffer *mtl_back_left = static_cast<MTLFrameBuffer *>(this->back_left);
99 mtl_front_left->remove_all_attachments();
100 mtl_back_left->remove_all_attachments();
101
102 GHOST_ContextCGL *ghost_cgl_ctx = dynamic_cast<GHOST_ContextCGL *>(ghost_ctx);
103 if (ghost_cgl_ctx != NULL) {
104 default_fbo_mtltexture_ = ghost_cgl_ctx->metalOverlayTexture();
105
107 "Binding GHOST context CGL %p to GPU context %p. (Device: %p, queue: %p, texture: %p)",
108 ghost_cgl_ctx,
109 this,
110 this->device,
111 this->queue,
112 default_fbo_gputexture_);
113
114 /* Check if the GHOST Context provides a default framebuffer: */
115 if (default_fbo_mtltexture_) {
116
117 /* Release old GPUTexture handle */
118 if (default_fbo_gputexture_) {
119 GPU_texture_free(wrap(static_cast<Texture *>(default_fbo_gputexture_)));
120 default_fbo_gputexture_ = nullptr;
121 }
122
123 /* Retain handle */
124 [default_fbo_mtltexture_ retain];
125
126 /*** Create front and back-buffers ***/
127 /* Create gpu::MTLTexture objects */
128 default_fbo_gputexture_ = new gpu::MTLTexture(
129 "MTL_BACKBUFFER", GPU_RGBA16F, GPU_TEXTURE_2D, default_fbo_mtltexture_);
130
131 /* Update frame-buffers with new texture attachments. */
132 mtl_front_left->add_color_attachment(default_fbo_gputexture_, 0, 0, 0);
133 mtl_back_left->add_color_attachment(default_fbo_gputexture_, 0, 0, 0);
134#ifndef NDEBUG
135 this->label = default_fbo_mtltexture_.label;
136#endif
137 }
138 else {
139
140 /* Add default texture for cases where no other framebuffer is bound */
141 if (!default_fbo_gputexture_) {
142 default_fbo_gputexture_ = static_cast<gpu::MTLTexture *>(unwrap(GPU_texture_create_2d(
143 __func__, 16, 16, 1, GPU_RGBA16F, GPU_TEXTURE_USAGE_GENERAL, nullptr)));
144 }
145 mtl_back_left->add_color_attachment(default_fbo_gputexture_, 0, 0, 0);
146
148 "-- Bound context %p for GPU context: %p is offscreen and does not have a default "
149 "framebuffer",
150 ghost_cgl_ctx,
151 this);
152#ifndef NDEBUG
153 this->label = @"Offscreen Metal Context";
154#endif
155 }
156 }
157 else {
159 " Failed to bind GHOST context to MTLContext -- GHOST_ContextCGL is null "
160 "(GhostContext: %p, GhostContext_CGL: %p)",
161 ghost_ctx,
162 ghost_cgl_ctx);
163 BLI_assert(false);
164 }
165}
166
167void MTLContext::set_ghost_window(GHOST_WindowHandle ghostWinHandle)
168{
169 GHOST_Window *ghostWin = reinterpret_cast<GHOST_Window *>(ghostWinHandle);
170 this->set_ghost_context((GHOST_ContextHandle)(ghostWin ? ghostWin->getContext() : NULL));
171}
172
175/* -------------------------------------------------------------------- */
179/* Placeholder functions */
180MTLContext::MTLContext(void *ghost_window, void *ghost_context)
181 : memory_manager(*this), main_command_buffer(*this)
182{
183 /* Init debug. */
185
186 /* Initialize Render-pass and Frame-buffer State. */
187 this->back_left = nullptr;
188
189 /* Initialize command buffer state. */
191
192 /* Initialize IMM and pipeline state */
193 this->pipeline_state.initialised = false;
194
195 /* Frame management. */
196 is_inside_frame_ = false;
197 current_frame_index_ = 0;
198
199 /* Prepare null data buffer. */
200 null_buffer_ = nil;
201 null_attribute_buffer_ = nil;
202
203 /* Zero-initialize MTL textures. */
204 default_fbo_mtltexture_ = nil;
205 default_fbo_gputexture_ = nullptr;
206
208 ghost_window_ = ghost_window;
209 if (ghost_window_ && ghost_context == NULL) {
210 /* NOTE(Metal): Fetch ghost_context from ghost_window if it is not provided.
211 * Regardless of whether windowed or not, we need access to the GhostContext
212 * for presentation, and device/queue access. */
213 GHOST_Window *ghostWin = reinterpret_cast<GHOST_Window *>(ghost_window_);
214 ghost_context = (ghostWin ? ghostWin->getContext() : NULL);
215 }
216 BLI_assert(ghost_context);
217 this->ghost_context_ = static_cast<GHOST_ContextCGL *>(ghost_context);
218 this->queue = (id<MTLCommandQueue>)this->ghost_context_->metalCommandQueue();
219 this->device = (id<MTLDevice>)this->ghost_context_->metalDevice();
220 BLI_assert(this->queue);
221 BLI_assert(this->device);
222 [this->queue retain];
223 [this->device retain];
224
225#pragma clang diagnostic push
226#pragma clang diagnostic ignored "-Wobjc-method-access"
227 /* Enable increased concurrent shader compiler limit.
228 * NOTE: Disable warning for missing method when building on older OS's, as compiled code will
229 * still work correctly when run on a system with the API available. */
230 if (@available(macOS 13.3, *)) {
231 [this->device setShouldMaximizeConcurrentCompilation:YES];
232 }
233#pragma clang diagnostic pop
234
235 /* Register present callback. */
236 this->ghost_context_->metalRegisterPresentCallback(&present);
237
238 /* Create FrameBuffer handles. */
239 MTLFrameBuffer *mtl_front_left = new MTLFrameBuffer(this, "front_left");
240 MTLFrameBuffer *mtl_back_left = new MTLFrameBuffer(this, "back_left");
241 this->front_left = mtl_front_left;
242 this->back_left = mtl_back_left;
243 this->active_fb = this->back_left;
244
245 /* Prepare platform and capabilities. (NOTE: With METAL, this needs to be done after CTX
246 * initialization). */
247 MTLBackend::platform_init(this);
248 MTLBackend::capabilities_init(this);
249
250 /* Initialize Metal modules. */
251 this->memory_manager.init();
252 this->state_manager = new MTLStateManager(this);
253 this->imm = new MTLImmediate(this);
254
255 /* Ensure global memory manager is initialized. */
258
259 /* Initialize texture read/update structures. */
260 this->get_texture_utils().init();
261
262 /* Bound Samplers struct. */
263 for (int i = 0; i < MTL_MAX_TEXTURE_SLOTS; i++) {
264 samplers_.mtl_sampler[i] = nil;
266 }
267
268 /* Initialize samplers. */
270
273 }
274 else {
276 }
277}
278
280{
281 BLI_assert(this == MTLContext::get());
282 /* Ensure rendering is complete command encoders/command buffers are freed. */
283 if (MTLBackend::get()->is_inside_render_boundary()) {
284 this->finish();
285
286 /* End frame. */
287 if (this->get_inside_frame()) {
288 this->end_frame();
289 }
290 }
291
292 /* Release context textures. */
293 if (default_fbo_gputexture_) {
294 GPU_texture_free(wrap(static_cast<Texture *>(default_fbo_gputexture_)));
295 default_fbo_gputexture_ = nullptr;
296 }
297 if (default_fbo_mtltexture_) {
298 [default_fbo_mtltexture_ release];
299 default_fbo_mtltexture_ = nil;
300 }
301
302 /* Release Memory Manager */
304
305 /* Release update/blit shaders. */
306 this->get_texture_utils().cleanup();
307 this->get_compute_utils().cleanup();
308
309 /* Detach resource references. */
311
312 /* Unbind UBOs. */
313 for (int i = 0; i < MTL_MAX_BUFFER_BINDINGS; i++) {
314 if (this->pipeline_state.ubo_bindings[i].bound &&
315 this->pipeline_state.ubo_bindings[i].ubo != nullptr)
316 {
317 GPUUniformBuf *ubo = wrap(
318 static_cast<UniformBuf *>(this->pipeline_state.ubo_bindings[i].ubo));
320 }
321 }
322
323 /* Unbind SSBOs. */
324 for (int i = 0; i < MTL_MAX_BUFFER_BINDINGS; i++) {
325 if (this->pipeline_state.ssbo_bindings[i].bound &&
326 this->pipeline_state.ssbo_bindings[i].ssbo != nullptr)
327 {
329 }
330 }
331
332 /* Release Dummy resources. */
333 this->free_dummy_resources();
334
335 /* Release Sampler States. */
336 for (int extend_yz_i = 0; extend_yz_i < GPU_SAMPLER_EXTEND_MODES_COUNT; extend_yz_i++) {
337 for (int extend_x_i = 0; extend_x_i < GPU_SAMPLER_EXTEND_MODES_COUNT; extend_x_i++) {
338 for (int filtering_i = 0; filtering_i < GPU_SAMPLER_FILTERING_TYPES_COUNT; filtering_i++) {
339 if (sampler_state_cache_[extend_yz_i][extend_x_i][filtering_i] != nil) {
340 [sampler_state_cache_[extend_yz_i][extend_x_i][filtering_i] release];
341 sampler_state_cache_[extend_yz_i][extend_x_i][filtering_i] = nil;
342 }
343 }
344 }
345 }
346
347 /* Release Custom Sampler States. */
348 for (int i = 0; i < GPU_SAMPLER_CUSTOM_TYPES_COUNT; i++) {
349 if (custom_sampler_state_cache_[i] != nil) {
350 [custom_sampler_state_cache_[i] release];
351 custom_sampler_state_cache_[i] = nil;
352 }
353 }
354
355 /* Empty cached sampler argument buffers. */
356 for (auto entry : cached_sampler_buffers_.values()) {
357 entry->free();
358 }
359 cached_sampler_buffers_.clear();
360
361 /* Free null buffers. */
362 if (null_buffer_) {
363 [null_buffer_ release];
364 }
365 if (null_attribute_buffer_) {
366 [null_attribute_buffer_ release];
367 }
368
369 /* Release memory manager reference. */
371
372 /* Free Metal objects. */
373 if (this->queue) {
374 [this->queue release];
375 }
376 if (this->device) {
377 [this->device release];
378 }
379
380 delete compiler;
381}
382
384{
385 BLI_assert(MTLBackend::get()->is_inside_render_boundary());
386 if (this->get_inside_frame()) {
387 return;
388 }
389
390 /* Begin Command buffer for next frame. */
391 is_inside_frame_ = true;
392}
393
395{
397
398 /* Ensure pre-present work is committed. */
399 this->flush();
400
401 /* Increment frame counter. */
402 is_inside_frame_ = false;
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!");
502 }
503
504 /* Ensure command buffer workload submissions are optimal --
505 * Though do not split a batch mid-IMM recording. */
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). */
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 =
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 }
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
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 else {
614 /* Determine format for dummy texture. */
616 switch (sampler_format) {
619 break;
622 break;
625 break;
628 break;
629 default:
631 }
632
633 /* Create dummy texture based on desired type. */
634 GPUTexture *tex = nullptr;
636 switch (type) {
637 case GPU_TEXTURE_1D:
638 tex = GPU_texture_create_1d("Dummy 1D", 128, 1, format, usage, nullptr);
639 break;
641 tex = GPU_texture_create_1d_array("Dummy 1DArray", 128, 1, 1, format, usage, nullptr);
642 break;
643 case GPU_TEXTURE_2D:
644 tex = GPU_texture_create_2d("Dummy 2D", 128, 128, 1, format, usage, nullptr);
645 break;
647 tex = GPU_texture_create_2d_array("Dummy 2DArray", 128, 128, 1, 1, format, usage, nullptr);
648 break;
649 case GPU_TEXTURE_3D:
650 tex = GPU_texture_create_3d("Dummy 3D", 128, 128, 1, 1, format, usage, nullptr);
651 break;
652 case GPU_TEXTURE_CUBE:
653 tex = GPU_texture_create_cube("Dummy Cube", 128, 1, format, usage, nullptr);
654 break;
656 tex = GPU_texture_create_cube_array("Dummy CubeArray", 128, 1, 1, format, usage, nullptr);
657 break;
659 if (!dummy_verts_[sampler_format]) {
660 GPU_vertformat_clear(&dummy_vertformat_[sampler_format]);
661
662 GPUVertCompType comp_type = GPU_COMP_F32;
664
665 switch (sampler_format) {
668 comp_type = GPU_COMP_F32;
669 fetch_mode = GPU_FETCH_FLOAT;
670 break;
672 comp_type = GPU_COMP_I32;
673 fetch_mode = GPU_FETCH_INT;
674 break;
676 comp_type = GPU_COMP_U32;
677 fetch_mode = GPU_FETCH_INT;
678 break;
679 default:
681 }
682
684 &dummy_vertformat_[sampler_format], "dummy", comp_type, 4, fetch_mode);
685 dummy_verts_[sampler_format] = GPU_vertbuf_create_with_format_ex(
686 dummy_vertformat_[sampler_format],
688 GPU_vertbuf_data_alloc(*dummy_verts_[sampler_format], 64);
689 }
690 tex = GPU_texture_create_from_vertbuf("Dummy TextureBuffer", dummy_verts_[sampler_format]);
691 break;
692 default:
693 BLI_assert_msg(false, "Unrecognised texture type");
694 return nullptr;
695 }
696 gpu::MTLTexture *metal_tex = static_cast<gpu::MTLTexture *>(reinterpret_cast<Texture *>(tex));
697 dummy_textures_[sampler_format][type - 1] = metal_tex;
698 return metal_tex;
699 }
700 return nullptr;
701}
702
704{
705 for (int format = 0; format < GPU_SAMPLER_TYPE_MAX; format++) {
706 for (int tex = 0; tex < GPU_TEXTURE_BUFFER; tex++) {
707 if (dummy_textures_[format][tex]) {
709 reinterpret_cast<GPUTexture *>(static_cast<Texture *>(dummy_textures_[format][tex])));
710 dummy_textures_[format][tex] = nullptr;
711 }
712 }
713 if (dummy_verts_[format]) {
714 GPU_vertbuf_discard(dummy_verts_[format]);
715 }
716 }
717}
718
721/* -------------------------------------------------------------------- */
725/* Metal Context Pipeline State. */
727{
728 /*** Initialize state only once. ***/
729 if (!this->pipeline_state.initialised) {
730 this->pipeline_state.initialised = true;
731 this->pipeline_state.active_shader = nullptr;
732
733 /* Clear bindings state. */
734 for (int t = 0; t < GPU_max_textures(); t++) {
735 /* Textures. */
736 this->pipeline_state.texture_bindings[t].used = false;
738
739 /* Images. */
740 this->pipeline_state.image_bindings[t].used = false;
742 }
743 for (int s = 0; s < MTL_MAX_SAMPLER_SLOTS; s++) {
744 this->pipeline_state.sampler_bindings[s].used = false;
745 }
746 for (int u = 0; u < MTL_MAX_BUFFER_BINDINGS; u++) {
747 this->pipeline_state.ubo_bindings[u].bound = false;
748 this->pipeline_state.ubo_bindings[u].ubo = nullptr;
749 }
750 for (int u = 0; u < MTL_MAX_BUFFER_BINDINGS; u++) {
751 this->pipeline_state.ssbo_bindings[u].bound = false;
752 this->pipeline_state.ssbo_bindings[u].ssbo = nullptr;
753 }
754 }
755
756 /*** State defaults -- restored by GPU_state_init. ***/
757 /* Clear blending State. */
758 this->pipeline_state.color_write_mask = MTLColorWriteMaskRed | MTLColorWriteMaskGreen |
759 MTLColorWriteMaskBlue | MTLColorWriteMaskAlpha;
760 this->pipeline_state.blending_enabled = false;
761 this->pipeline_state.alpha_blend_op = MTLBlendOperationAdd;
762 this->pipeline_state.rgb_blend_op = MTLBlendOperationAdd;
763 this->pipeline_state.dest_alpha_blend_factor = MTLBlendFactorZero;
764 this->pipeline_state.dest_rgb_blend_factor = MTLBlendFactorZero;
765 this->pipeline_state.src_alpha_blend_factor = MTLBlendFactorOne;
766 this->pipeline_state.src_rgb_blend_factor = MTLBlendFactorOne;
767
768 /* Viewport and scissor. */
769 for (int v = 0; v < GPU_MAX_VIEWPORTS; v++) {
774 }
775 this->pipeline_state.scissor_x = 0;
776 this->pipeline_state.scissor_y = 0;
779 this->pipeline_state.scissor_enabled = false;
780
781 /* Culling State. */
782 this->pipeline_state.culling_enabled = false;
785
786 /* DATA and IMAGE access state. */
788
789 /* Depth State. */
794 this->pipeline_state.depth_stencil_state.depth_function = MTLCompareFunctionAlways;
800
801 /* Stencil State. */
806 this->pipeline_state.depth_stencil_state.stencil_func = MTLCompareFunctionAlways;
808 this->pipeline_state.depth_stencil_state.stencil_op_front_depth_fail = MTLStencilOperationKeep;
810 MTLStencilOperationKeep;
812 this->pipeline_state.depth_stencil_state.stencil_op_back_depth_fail = MTLStencilOperationKeep;
814 MTLStencilOperationKeep;
815}
816
817void MTLContext::set_viewport(int origin_x, int origin_y, int width, int height)
818{
819 BLI_assert(this);
820 BLI_assert(width > 0);
821 BLI_assert(height > 0);
822 BLI_assert(origin_x >= 0);
823 BLI_assert(origin_y >= 0);
824 bool changed = (this->pipeline_state.viewport_offset_x[0] != origin_x) ||
825 (this->pipeline_state.viewport_offset_y[0] != origin_y) ||
826 (this->pipeline_state.viewport_width[0] != width) ||
827 (this->pipeline_state.viewport_height[0] != height) ||
829 this->pipeline_state.viewport_offset_x[0] = origin_x;
830 this->pipeline_state.viewport_offset_y[0] = origin_y;
831 this->pipeline_state.viewport_width[0] = width;
832 this->pipeline_state.viewport_height[0] = height;
834
835 if (changed) {
838 }
839}
840
841void MTLContext::set_viewports(int count, const int (&viewports)[GPU_MAX_VIEWPORTS][4])
842{
843 BLI_assert(this);
844 bool changed = (this->pipeline_state.num_active_viewports != count);
845 for (int v = 0; v < count; v++) {
846 const int(&viewport_info)[4] = viewports[v];
847
848 BLI_assert(viewport_info[0] >= 0);
849 BLI_assert(viewport_info[1] >= 0);
850 BLI_assert(viewport_info[2] > 0);
851 BLI_assert(viewport_info[3] > 0);
852
853 changed = changed || (this->pipeline_state.viewport_offset_x[v] != viewport_info[0]) ||
854 (this->pipeline_state.viewport_offset_y[v] != viewport_info[1]) ||
855 (this->pipeline_state.viewport_width[v] != viewport_info[2]) ||
856 (this->pipeline_state.viewport_height[v] != viewport_info[3]);
857 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 }
863
864 if (changed) {
867 }
868}
869
870void MTLContext::set_scissor(int scissor_x, int scissor_y, int scissor_width, int scissor_height)
871{
872 BLI_assert(this);
873 bool changed = (this->pipeline_state.scissor_x != scissor_x) ||
874 (this->pipeline_state.scissor_y != scissor_y) ||
875 (this->pipeline_state.scissor_width != scissor_width) ||
876 (this->pipeline_state.scissor_height != scissor_height) ||
877 (this->pipeline_state.scissor_enabled != true);
878 this->pipeline_state.scissor_x = scissor_x;
879 this->pipeline_state.scissor_y = scissor_y;
880 this->pipeline_state.scissor_width = scissor_width;
881 this->pipeline_state.scissor_height = scissor_height;
882 this->pipeline_state.scissor_enabled = (scissor_width > 0 && scissor_height > 0);
883
884 if (changed) {
887 }
888}
889
890void MTLContext::set_scissor_enabled(bool scissor_enabled)
891{
892 /* Only turn on Scissor if requested scissor region is valid */
893 scissor_enabled = scissor_enabled && (this->pipeline_state.scissor_width > 0 &&
895
896 bool changed = (this->pipeline_state.scissor_enabled != scissor_enabled);
897 this->pipeline_state.scissor_enabled = scissor_enabled;
898 if (changed) {
901 }
902}
903
906/* -------------------------------------------------------------------- */
917bool MTLContext::ensure_render_pipeline_state(MTLPrimitiveType mtl_prim_type)
918{
920
921 /* Check if an active shader is bound. */
922 if (!this->pipeline_state.active_shader) {
923 MTL_LOG_WARNING("No Metal shader for bound GL shader");
924 return false;
925 }
926
927 /* Also ensure active shader is valid. */
928 if (!this->pipeline_state.active_shader->is_valid()) {
930 "Bound active shader is not valid (Missing/invalid implementation for Metal).", );
931 return false;
932 }
933
934 /* Apply global state. */
935 this->state_manager->apply_state();
936
937 /* Main command buffer tracks the current state of the render pass, based on bound
938 * MTLFrameBuffer. */
940
941 /* Debug Check: Ensure Framebuffer instance is not dirty. */
943
944 /* Fetch shader interface. */
946 if (shader_interface == nullptr) {
947 MTL_LOG_WARNING("Bound active shader does not have a valid shader interface!", );
948 return false;
949 }
950
951 /* Fetch shader and bake valid PipelineStateObject (PSO) based on current
952 * shader and state combination. This PSO represents the final GPU-executable
953 * permutation of the shader. */
954 MTLRenderPipelineStateInstance *pipeline_state_instance =
956 this, mtl_prim_type_to_topology_class(mtl_prim_type));
957 if (!pipeline_state_instance) {
958 MTL_LOG_ERROR("Failed to bake Metal pipeline state for shader: %s",
959 shader_interface->get_name());
960 return false;
961 }
962
963 bool result = false;
964 if (pipeline_state_instance->pso) {
965
966 /* Fetch render command encoder. A render pass should already be active.
967 * This will be NULL if invalid. */
968 id<MTLRenderCommandEncoder> rec =
970 BLI_assert(rec);
971 if (rec == nil) {
972 MTL_LOG_ERROR("ensure_render_pipeline_state called while render pass is not active.");
973 return false;
974 }
975
976 /* Bind Render Pipeline State. */
977 BLI_assert(pipeline_state_instance->pso);
978 if (rps.bound_pso != pipeline_state_instance->pso) {
979 [rec setRenderPipelineState:pipeline_state_instance->pso];
980 rps.bound_pso = pipeline_state_instance->pso;
981 }
982
984 /* Texture Bindings. */
985 /* We will iterate through all texture bindings on the context and determine if any of the
986 * active slots match those in our shader interface. If so, textures will be bound. */
987 if (shader_interface->get_total_textures() > 0) {
988 this->ensure_texture_bindings(rec, shader_interface, pipeline_state_instance);
989 }
990
991 /* Transform feedback buffer binding. */
993 if (tf_vbo != nullptr && pipeline_state_instance->transform_feedback_buffer_index >= 0) {
994
995 /* Ensure primitive type is either GPU_LINES, GPU_TRIANGLES or GPU_POINT */
996 BLI_assert(mtl_prim_type == MTLPrimitiveTypeLine ||
997 mtl_prim_type == MTLPrimitiveTypeTriangle ||
998 mtl_prim_type == MTLPrimitiveTypePoint);
999
1000 /* Fetch active transform feedback buffer from vertbuf */
1001 MTLVertBuf *tf_vbo_mtl = static_cast<MTLVertBuf *>(reinterpret_cast<VertBuf *>(tf_vbo));
1002 /* Ensure TF buffer is ready. */
1003 tf_vbo_mtl->bind();
1004 id<MTLBuffer> tf_buffer_mtl = tf_vbo_mtl->get_metal_buffer();
1005 BLI_assert(tf_buffer_mtl != nil);
1006
1007 if (tf_buffer_mtl != nil) {
1008 [rec setVertexBuffer:tf_buffer_mtl
1009 offset:0
1010 atIndex:pipeline_state_instance->transform_feedback_buffer_index];
1011 MTL_LOG_INFO("Successfully bound VBO: %p for transform feedback (MTL Buffer: %p)",
1012 tf_vbo_mtl,
1013 tf_buffer_mtl);
1014 }
1015 }
1016
1017 /* Matrix Bindings. */
1018 /* This is now called upon shader bind. We may need to re-evaluate this though,
1019 * as was done here to ensure uniform changes between draws were tracked.
1020 * NOTE(Metal): We may be able to remove this. */
1021 GPU_matrix_bind(reinterpret_cast<struct GPUShader *>(
1022 static_cast<Shader *>(this->pipeline_state.active_shader)));
1023
1024 /* Bind buffers.
1025 * NOTE: `ensure_buffer_bindings` must be called after `ensure_texture_bindings` to allow
1026 * for binding of buffer-backed texture's data buffer and metadata. */
1027 this->ensure_buffer_bindings(rec, shader_interface, pipeline_state_instance);
1028
1029 /* Bind Null attribute buffer, if needed. */
1030 if (pipeline_state_instance->null_attribute_buffer_index >= 0) {
1031 if (G.debug & G_DEBUG_GPU) {
1032 MTL_LOG_INFO("Binding null attribute buffer at index: %d",
1033 pipeline_state_instance->null_attribute_buffer_index);
1034 }
1036 0,
1037 pipeline_state_instance->null_attribute_buffer_index);
1038 }
1039
1041 /* State: Viewport. */
1042 if (this->pipeline_state.num_active_viewports > 1) {
1043 /* Multiple Viewports. */
1044 MTLViewport viewports[GPU_MAX_VIEWPORTS];
1045 for (int v = 0; v < this->pipeline_state.num_active_viewports; v++) {
1046 MTLViewport &viewport = viewports[v];
1047 viewport.originX = (double)this->pipeline_state.viewport_offset_x[v];
1048 viewport.originY = (double)this->pipeline_state.viewport_offset_y[v];
1049 viewport.width = (double)this->pipeline_state.viewport_width[v];
1050 viewport.height = (double)this->pipeline_state.viewport_height[v];
1053 }
1054 [rec setViewports:viewports count:this->pipeline_state.num_active_viewports];
1055 }
1056 else {
1057 /* Single Viewport. */
1058 MTLViewport viewport;
1059 viewport.originX = (double)this->pipeline_state.viewport_offset_x[0];
1060 viewport.originY = (double)this->pipeline_state.viewport_offset_y[0];
1061 viewport.width = (double)this->pipeline_state.viewport_width[0];
1062 viewport.height = (double)this->pipeline_state.viewport_height[0];
1065 [rec setViewport:viewport];
1066 }
1067
1068 /* State: Scissor. */
1070
1071 /* Get FrameBuffer associated with active RenderCommandEncoder. */
1073
1074 MTLScissorRect scissor;
1075 if (this->pipeline_state.scissor_enabled) {
1076 scissor.x = this->pipeline_state.scissor_x;
1077 scissor.y = this->pipeline_state.scissor_y;
1078 scissor.width = this->pipeline_state.scissor_width;
1079 scissor.height = this->pipeline_state.scissor_height;
1080
1081 /* Some scissor assignments exceed the bounds of the viewport due to implicitly added
1082 * padding to the width/height - Clamp width/height. */
1083 BLI_assert(scissor.x >= 0 && scissor.x < render_fb->get_default_width());
1084 BLI_assert(scissor.y >= 0 && scissor.y < render_fb->get_default_height());
1085 scissor.width = (uint)min_ii(scissor.width,
1086 max_ii(render_fb->get_default_width() - (int)(scissor.x), 0));
1087 scissor.height = (uint)min_ii(
1088 scissor.height, max_ii(render_fb->get_default_height() - (int)(scissor.y), 0));
1089 BLI_assert(scissor.width > 0 &&
1090 (scissor.x + scissor.width <= render_fb->get_default_width()));
1091 BLI_assert(scissor.height > 0 && (scissor.height <= render_fb->get_default_height()));
1092 }
1093 else {
1094 /* Scissor is disabled, reset to default size as scissor state may have been previously
1095 * assigned on this encoder.
1096 * NOTE: If an attachment-less framebuffer is used, fetch specified width/height rather
1097 * than active attachment width/height as provided by get_default_w/h(). */
1098 uint default_w = render_fb->get_default_width();
1099 uint default_h = render_fb->get_default_height();
1100 bool is_attachmentless = (default_w == 0) && (default_h == 0);
1101 scissor.x = 0;
1102 scissor.y = 0;
1103 scissor.width = (is_attachmentless) ? render_fb->get_width() : default_w;
1104 scissor.height = (is_attachmentless) ? render_fb->get_height() : default_h;
1105 }
1106
1107 /* Scissor state can still be flagged as changed if it is toggled on and off, without
1108 * parameters changing between draws. */
1109 if (memcmp(&scissor, &rps.last_scissor_rect, sizeof(MTLScissorRect))) {
1110 [rec setScissorRect:scissor];
1111 rps.last_scissor_rect = scissor;
1112 }
1114 ~MTL_PIPELINE_STATE_SCISSOR_FLAG);
1115 }
1116
1117 /* State: Face winding. */
1119 /* We need to invert the face winding in Metal, to account for the inverted-Y coordinate
1120 * system. */
1121 MTLWinding winding = (this->pipeline_state.front_face == GPU_CLOCKWISE) ?
1122 MTLWindingClockwise :
1123 MTLWindingCounterClockwise;
1124 [rec setFrontFacingWinding:winding];
1126 ~MTL_PIPELINE_STATE_FRONT_FACING_FLAG);
1127 }
1128
1129 /* State: cull-mode. */
1131
1132 MTLCullMode mode = MTLCullModeNone;
1133 if (this->pipeline_state.culling_enabled) {
1134 switch (this->pipeline_state.cull_mode) {
1135 case GPU_CULL_NONE:
1136 mode = MTLCullModeNone;
1137 break;
1138 case GPU_CULL_FRONT:
1139 mode = MTLCullModeFront;
1140 break;
1141 case GPU_CULL_BACK:
1142 mode = MTLCullModeBack;
1143 break;
1144 default:
1146 break;
1147 }
1148 }
1149 [rec setCullMode:mode];
1151 ~MTL_PIPELINE_STATE_CULLMODE_FLAG);
1152 }
1153
1154 /* Pipeline state is now good. */
1155 result = true;
1156 }
1157 return result;
1158}
1159
1160/* Bind UBOs and SSBOs to an active render command encoder using the rendering state of the
1161 * current context -> Active shader, Bound UBOs). */
1163 id<MTLRenderCommandEncoder> /*rec*/,
1164 const MTLShaderInterface *shader_interface,
1165 const MTLRenderPipelineStateInstance *pipeline_state_instance)
1166{
1167 /* Fetch Render Pass state. */
1169
1170 /* Shader owned push constant block for uniforms.. */
1171 bool active_shader_changed = (rps.last_bound_shader_state.shader_ !=
1173 rps.last_bound_shader_state.shader_ == nullptr ||
1175 pipeline_state_instance->shader_pso_index);
1176
1177 const MTLShaderBufferBlock &push_constant_block = shader_interface->get_push_constant_block();
1178 if (push_constant_block.size > 0) {
1179
1180 /* Fetch uniform buffer base binding index from pipeline_state_instance - There buffer index
1181 * will be offset by the number of bound VBOs. */
1182 uint32_t block_size = push_constant_block.size;
1183 uint32_t buffer_index = pipeline_state_instance->base_uniform_buffer_index +
1184 push_constant_block.buffer_index;
1185 BLI_assert(buffer_index >= 0 && buffer_index < MTL_MAX_BUFFER_BINDINGS);
1186
1187 /* Only need to rebind block if push constants have been modified -- or if no data is bound for
1188 * the current RenderCommandEncoder. */
1190 active_shader_changed || !rps.cached_vertex_buffer_bindings[buffer_index].is_bytes ||
1191 !rps.cached_fragment_buffer_bindings[buffer_index].is_bytes || true)
1192 {
1193
1194 /* Bind push constant data. */
1197 this->pipeline_state.active_shader->get_push_constant_data(), block_size, buffer_index);
1199 this->pipeline_state.active_shader->get_push_constant_data(), block_size, buffer_index);
1200
1201 /* Only need to rebind block if it has been modified. */
1203 }
1204 }
1206 pipeline_state_instance->shader_pso_index);
1207
1208 /* Bind Global GPUUniformBuffers */
1209 /* Iterate through expected UBOs in the shader interface, and check if the globally bound ones
1210 * match. This is used to support the gpu_uniformbuffer module, where the uniform data is global,
1211 * and not owned by the shader instance. */
1212 for (const uint ubo_index : IndexRange(shader_interface->get_total_uniform_blocks())) {
1213 const MTLShaderBufferBlock &ubo = shader_interface->get_uniform_block(ubo_index);
1214
1215 if (ubo.buffer_index >= 0 && ubo.location >= 0) {
1216 /* Explicit lookup location for UBO in bind table. */
1217 const uint32_t ubo_location = ubo.location;
1218 /* buffer(N) index of where to bind the UBO. */
1219 const uint32_t buffer_index = ubo.buffer_index;
1220 id<MTLBuffer> ubo_buffer = nil;
1221 size_t ubo_size = 0;
1222
1223 bool bind_dummy_buffer = false;
1224 if (this->pipeline_state.ubo_bindings[ubo_location].bound) {
1225
1226 /* Fetch UBO global-binding properties from slot. */
1227 ubo_buffer = this->pipeline_state.ubo_bindings[ubo_location].ubo->get_metal_buffer();
1228 ubo_size = this->pipeline_state.ubo_bindings[ubo_location].ubo->get_size();
1229
1230 /* Use dummy zero buffer if no buffer assigned -- this is an optimization to avoid
1231 * allocating zero buffers. */
1232 if (ubo_buffer == nil) {
1233 bind_dummy_buffer = true;
1234 }
1235 else {
1236 BLI_assert(ubo_buffer != nil);
1237 BLI_assert(ubo_size > 0);
1238
1239 if (pipeline_state_instance->reflection_data_available) {
1240 /* NOTE: While the vertex and fragment stages have different UBOs, the indices in each
1241 * case will be the same for the same UBO.
1242 * We also determine expected size and then ensure buffer of the correct size
1243 * exists in one of the vertex/fragment shader binding tables. This path is used
1244 * to verify that the size of the bound UBO matches what is expected in the shader. */
1245 uint32_t expected_size =
1246 (buffer_index <
1247 pipeline_state_instance->buffer_bindings_reflection_data_vert.size()) ?
1248 pipeline_state_instance->buffer_bindings_reflection_data_vert[buffer_index]
1249 .size :
1250 0;
1251 if (expected_size == 0) {
1252 expected_size =
1253 (buffer_index <
1254 pipeline_state_instance->buffer_bindings_reflection_data_frag.size()) ?
1255 pipeline_state_instance->buffer_bindings_reflection_data_frag[buffer_index]
1256 .size :
1257 0;
1258 }
1260 expected_size > 0,
1261 "Shader interface expects UBO, but shader reflection data reports that it "
1262 "is not present");
1263
1264 /* If ubo size is smaller than the size expected by the shader, we need to bind the
1265 * dummy buffer, which will be big enough, to avoid an OOB error. */
1266 if (ubo_size < expected_size) {
1268 "[UBO] UBO (UBO Name: %s) bound at location: %d (buffer[[%d]]) with size "
1269 "%lu (Expected size "
1270 "%d) (Shader Name: %s) is too small -- binding NULL buffer. This is likely an "
1271 "over-binding, which is not used, but we need this to avoid validation "
1272 "issues",
1273 shader_interface->get_name_at_offset(ubo.name_offset),
1274 ubo_location,
1275 pipeline_state_instance->base_uniform_buffer_index + buffer_index,
1276 ubo_size,
1277 expected_size,
1278 shader_interface->get_name());
1279 bind_dummy_buffer = true;
1280 }
1281 }
1282 }
1283 }
1284 else {
1286 "[UBO] Shader '%s' expected UBO '%s' to be bound at buffer slot: %d "
1287 "(buffer[[%d]])-- but "
1288 "nothing was bound -- binding dummy buffer",
1289 shader_interface->get_name(),
1290 shader_interface->get_name_at_offset(ubo.name_offset),
1291 ubo_location,
1292 pipeline_state_instance->base_uniform_buffer_index + buffer_index);
1293 bind_dummy_buffer = true;
1294 }
1295
1296 if (bind_dummy_buffer) {
1297 /* Perform Dummy binding. */
1298 ubo_buffer = this->get_null_buffer();
1299 ubo_size = [ubo_buffer length];
1300 }
1301
1302 if (ubo_buffer != nil) {
1303
1304 uint32_t buffer_bind_index = pipeline_state_instance->base_uniform_buffer_index +
1305 buffer_index;
1306
1307 /* Bind Vertex UBO. */
1308 if (bool(ubo.stage_mask & ShaderStage::VERTEX)) {
1309 BLI_assert(buffer_bind_index >= 0 && buffer_bind_index < MTL_MAX_BUFFER_BINDINGS);
1310 rps.bind_vertex_buffer(ubo_buffer, 0, buffer_bind_index);
1311 }
1312
1313 /* Bind Fragment UBOs. */
1314 if (bool(ubo.stage_mask & ShaderStage::FRAGMENT)) {
1315 BLI_assert(buffer_bind_index >= 0 && buffer_bind_index < MTL_MAX_BUFFER_BINDINGS);
1316 rps.bind_fragment_buffer(ubo_buffer, 0, buffer_bind_index);
1317 }
1318 }
1319 else {
1321 "[UBO] Shader '%s' has UBO '%s' bound at buffer index: %d -- but MTLBuffer "
1322 "is NULL!",
1323 shader_interface->get_name(),
1324 shader_interface->get_name_at_offset(ubo.name_offset),
1325 buffer_index);
1326 }
1327 }
1328 }
1329
1330 /* Bind Global GPUStorageBuf's */
1331 /* Iterate through expected SSBOs in the shader interface, and check if the globally bound ones
1332 * match. This is used to support the gpu_uniformbuffer module, where the uniform data is global,
1333 * and not owned by the shader instance. */
1334 for (const uint ssbo_index : IndexRange(shader_interface->get_total_storage_blocks())) {
1335 const MTLShaderBufferBlock &ssbo = shader_interface->get_storage_block(ssbo_index);
1336
1337 if (ssbo.buffer_index >= 0 && ssbo.location >= 0) {
1338 /* Explicit lookup location for SSBO in bind table. */
1339 const uint32_t ssbo_location = ssbo.location;
1340 /* buffer(N) index of where to bind the SSBO. */
1341 const uint32_t buffer_index = ssbo.buffer_index;
1342 id<MTLBuffer> ssbo_buffer = nil;
1343 size_t ssbo_size = 0;
1344 UNUSED_VARS_NDEBUG(ssbo_size);
1345
1346 if (this->pipeline_state.ssbo_bindings[ssbo_location].bound) {
1347
1348 /* Fetch SSBO global-binding properties from slot. */
1349 ssbo_buffer = this->pipeline_state.ssbo_bindings[ssbo_location].ssbo->get_metal_buffer();
1350 ssbo_size = this->pipeline_state.ssbo_bindings[ssbo_location].ssbo->get_size();
1351
1352 /* For SSBOs, we always need to ensure the buffer exists, as it may be written to. */
1353 BLI_assert(ssbo_buffer != nil);
1354 BLI_assert(ssbo_size > 0);
1355 }
1356 else {
1358 "[SSBO] Shader '%s' expected SSBO '%s' to be bound at buffer location: %d "
1359 "(buffer[[%d]]) -- "
1360 "but "
1361 "nothing was bound.",
1362 shader_interface->get_name(),
1363 shader_interface->get_name_at_offset(ssbo.name_offset),
1364 ssbo_location,
1365 pipeline_state_instance->base_storage_buffer_index + buffer_index);
1366
1367#if DEBUG_BIND_NULL_BUFFER_FOR_MISSING_SSBO == 1
1368 ssbo_buffer = this->get_null_buffer();
1369 ssbo_size = [ssbo_buffer length];
1370#endif
1371 }
1372
1373 if (ssbo_buffer != nil) {
1374 uint32_t buffer_bind_index = pipeline_state_instance->base_storage_buffer_index +
1375 buffer_index;
1376
1377 /* Bind Vertex SSBO. */
1378 if (bool(ssbo.stage_mask & ShaderStage::VERTEX)) {
1379 BLI_assert(buffer_bind_index >= 0 && buffer_bind_index < MTL_MAX_BUFFER_BINDINGS);
1380 rps.bind_vertex_buffer(ssbo_buffer, 0, buffer_bind_index);
1381 }
1382
1383 /* Bind Fragment SSBOs. */
1384 if (bool(ssbo.stage_mask & ShaderStage::FRAGMENT)) {
1385 BLI_assert(buffer_bind_index >= 0 && buffer_bind_index < MTL_MAX_BUFFER_BINDINGS);
1386 rps.bind_fragment_buffer(ssbo_buffer, 0, buffer_bind_index);
1387 }
1388 }
1389 else {
1391 "[SSBO] Shader '%s' had SSBO '%s' bound at SSBO location: %d "
1392 "(buffer[["
1393 "%d]]) -- but bound MTLStorageBuf was nil.",
1394 shader_interface->get_name(),
1395 shader_interface->get_name_at_offset(ssbo.name_offset),
1396 ssbo_location,
1397 pipeline_state_instance->base_storage_buffer_index + buffer_index);
1398 }
1399 }
1400 }
1401
1402 return true;
1403}
1404
1405/* Variant for compute. Bind UBOs and SSBOs to an active compute command encoder using the
1406 * rendering state of the current context -> Active shader, Bound UBOs). */
1408 id<MTLComputeCommandEncoder> /*rec*/,
1409 const MTLShaderInterface *shader_interface,
1410 const MTLComputePipelineStateInstance *pipeline_state_instance)
1411{
1412 /* Fetch Compute Pass state. */
1414
1415 /* Fetch push constant block and bind. */
1416 const MTLShaderBufferBlock &push_constant_block = shader_interface->get_push_constant_block();
1417 if (push_constant_block.size > 0) {
1418
1419 /* Fetch uniform buffer base binding index from pipeline_state_instance - There buffer index
1420 * will be offset by the number of bound VBOs. */
1421 uint32_t block_size = push_constant_block.size;
1422 uint32_t buffer_index = pipeline_state_instance->base_uniform_buffer_index +
1423 push_constant_block.buffer_index;
1424 BLI_assert(buffer_index >= 0 && buffer_index < MTL_MAX_BUFFER_BINDINGS);
1425
1426 /* For compute, we must always re-bind the push constant block as other compute
1427 * operations may have assigned resources over the top, outside of the compiled
1428 * compute shader path. */
1429 /* Bind push constant data. */
1432 this->pipeline_state.active_shader->get_push_constant_data(), block_size, buffer_index);
1433
1434 /* Only need to rebind block if it has been modified. */
1436 }
1437
1438 /* Bind Global GPUUniformBuffers */
1439 /* Iterate through expected UBOs in the shader interface, and check if the globally bound ones
1440 * match. This is used to support the gpu_uniformbuffer module, where the uniform data is global,
1441 * and not owned by the shader instance. */
1442 for (const uint ubo_index : IndexRange(shader_interface->get_total_uniform_blocks())) {
1443 const MTLShaderBufferBlock &ubo = shader_interface->get_uniform_block(ubo_index);
1444
1445 if (ubo.buffer_index >= 0) {
1446 /* Explicit lookup location for UBO in bind table. */
1447 const uint32_t ubo_location = ubo.location;
1448 /* buffer(N) index of where to bind the UBO. */
1449 const uint32_t buffer_index = ubo.buffer_index;
1450 id<MTLBuffer> ubo_buffer = nil;
1451 size_t ubo_size = 0;
1452
1453 bool bind_dummy_buffer = false;
1454 if (this->pipeline_state.ubo_bindings[ubo_location].bound) {
1455
1456 /* Fetch UBO global-binding properties from slot. */
1457 ubo_buffer = this->pipeline_state.ubo_bindings[ubo_location].ubo->get_metal_buffer();
1458 ubo_size = this->pipeline_state.ubo_bindings[ubo_location].ubo->get_size();
1459 UNUSED_VARS_NDEBUG(ubo_size);
1460
1461 /* Use dummy zero buffer if no buffer assigned -- this is an optimization to avoid
1462 * allocating zero buffers. */
1463 if (ubo_buffer == nil) {
1464 bind_dummy_buffer = true;
1465 }
1466 else {
1467 BLI_assert(ubo_buffer != nil);
1468 BLI_assert(ubo_size > 0);
1469 }
1470 }
1471 else {
1473 "[UBO] Shader '%s' expected UBO '%s' to be bound at buffer location: %d "
1474 "(buffer[[%d]]) -- but "
1475 "nothing was bound -- binding dummy buffer",
1476 shader_interface->get_name(),
1477 shader_interface->get_name_at_offset(ubo.name_offset),
1478 ubo_location,
1479 pipeline_state_instance->base_uniform_buffer_index + buffer_index);
1480 bind_dummy_buffer = true;
1481 }
1482
1483 if (bind_dummy_buffer) {
1484 /* Perform Dummy binding. */
1485 ubo_buffer = this->get_null_buffer();
1486 ubo_size = [ubo_buffer length];
1487 }
1488
1489 if (ubo_buffer != nil) {
1490 uint32_t buffer_bind_index = pipeline_state_instance->base_uniform_buffer_index +
1491 buffer_index;
1492
1493 /* Bind Compute UBO. */
1494 if (bool(ubo.stage_mask & ShaderStage::COMPUTE)) {
1495 BLI_assert(buffer_bind_index >= 0 && buffer_bind_index < MTL_MAX_BUFFER_BINDINGS);
1496 cs.bind_compute_buffer(ubo_buffer, 0, buffer_bind_index);
1497 }
1498 }
1499 else {
1501 "[UBO] Compute Shader '%s' has UBO '%s' bound at buffer index: %d -- but MTLBuffer "
1502 "is NULL!",
1503 shader_interface->get_name(),
1504 shader_interface->get_name_at_offset(ubo.name_offset),
1505 buffer_index);
1506 }
1507 }
1508 }
1509
1510 /* Bind Global GPUStorageBuffers. */
1511 /* Iterate through expected SSBOs in the shader interface, and check if the globally bound ones
1512 * match. */
1513 for (const uint ssbo_index : IndexRange(shader_interface->get_total_storage_blocks())) {
1514 const MTLShaderBufferBlock &ssbo = shader_interface->get_storage_block(ssbo_index);
1515
1516 if (ssbo.buffer_index >= 0 && ssbo.location >= 0) {
1517 /* Explicit lookup location for SSBO in bind table. */
1518 const uint32_t ssbo_location = ssbo.location;
1519 /* buffer(N) index of where to bind the SSBO. */
1520 const uint32_t buffer_index = ssbo.buffer_index;
1521 id<MTLBuffer> ssbo_buffer = nil;
1522 int ssbo_size = 0;
1523
1524 if (this->pipeline_state.ssbo_bindings[ssbo_location].bound) {
1525
1526 /* Fetch UBO global-binding properties from slot. */
1527 ssbo_buffer = this->pipeline_state.ssbo_bindings[ssbo_location].ssbo->get_metal_buffer();
1528 ssbo_size = this->pipeline_state.ssbo_bindings[ssbo_location].ssbo->get_size();
1529 UNUSED_VARS_NDEBUG(ssbo_size);
1530
1531 /* For SSBOs, we always need to ensure the buffer exists, as it may be written to. */
1532 BLI_assert(ssbo_buffer != nil);
1533 BLI_assert(ssbo_size > 0);
1534 }
1535 else {
1537 "[SSBO] Shader '%s' expected SSBO '%s' to be bound at SSBO location: %d "
1538 "(buffer[["
1539 "%d]]) -- but "
1540 "nothing was bound.",
1541 shader_interface->get_name(),
1542 shader_interface->get_name_at_offset(ssbo.name_offset),
1543 ssbo_location,
1544 pipeline_state_instance->base_storage_buffer_index + buffer_index);
1545
1546#if DEBUG_BIND_NULL_BUFFER_FOR_MISSING_SSBO == 1
1547 ssbo_buffer = this->get_null_buffer();
1548 ssbo_size = [ssbo_buffer length];
1549#endif
1550 }
1551
1552 if (ssbo_buffer != nil) {
1553 uint32_t buffer_bind_index = pipeline_state_instance->base_storage_buffer_index +
1554 buffer_index;
1555
1556 /* Bind Compute SSBO. */
1557 if (bool(ssbo.stage_mask & ShaderStage::COMPUTE)) {
1558 BLI_assert(buffer_bind_index >= 0 && buffer_bind_index < MTL_MAX_BUFFER_BINDINGS);
1559 cs.bind_compute_buffer(ssbo_buffer, 0, buffer_bind_index);
1560 }
1561 }
1562 else {
1564 "[SSBO] Shader '%s' had SSBO '%s' bound at SSBO location: %d "
1565 "(buffer[["
1566 "%d]]) -- but bound MTLStorageBuf was nil.",
1567 shader_interface->get_name(),
1568 shader_interface->get_name_at_offset(ssbo.name_offset),
1569 ssbo_location,
1570 pipeline_state_instance->base_storage_buffer_index + buffer_index);
1571 }
1572 }
1573 }
1574
1575 return true;
1576}
1577
1578/* Ensure texture bindings are correct and up to date for current draw call. */
1580 id<MTLRenderCommandEncoder> rec,
1581 MTLShaderInterface *shader_interface,
1582 const MTLRenderPipelineStateInstance *pipeline_state_instance)
1583{
1584 BLI_assert(shader_interface != nil);
1585 BLI_assert(rec != nil);
1586 UNUSED_VARS_NDEBUG(rec);
1587
1588 /* Fetch Render Pass state. */
1590
1591 @autoreleasepool {
1592 int vertex_arg_buffer_bind_index = -1;
1593 int fragment_arg_buffer_bind_index = -1;
1594
1595 /* Argument buffers are used for samplers, when the limit of 16 is exceeded. */
1596 bool use_argument_buffer_for_samplers = shader_interface->uses_argument_buffer_for_samplers();
1597 vertex_arg_buffer_bind_index = shader_interface->get_argument_buffer_bind_index(
1599 fragment_arg_buffer_bind_index = shader_interface->get_argument_buffer_bind_index(
1601
1602 /* Loop through expected textures in shader interface and resolve bindings with currently
1603 * bound textures.. */
1604 for (const uint t : IndexRange(shader_interface->get_max_texture_index() + 1)) {
1605 /* Ensure the bound texture is compatible with the shader interface. If the
1606 * shader does not expect a texture to be bound for the current slot, we skip
1607 * binding.
1608 * NOTE: Global texture bindings may be left over from prior draw calls. */
1609 const MTLShaderTexture &shader_texture_info = shader_interface->get_texture(t);
1610 if (!shader_texture_info.used) {
1611 /* Skip unused binding points if explicit indices are specified. */
1612 continue;
1613 }
1614
1615 /* Determine bind lookup table depending on whether an image binding or texture.
1616 * NOTE: Images and Texture Samplers share a binding table in Metal. */
1617 bool is_resource_sampler = shader_texture_info.is_texture_sampler;
1618 MTLTextureBinding(&resource_bind_table)[MTL_MAX_TEXTURE_SLOTS] =
1619 (is_resource_sampler) ? this->pipeline_state.texture_bindings :
1621
1622 /* Texture resource bind slot in shader `[[texture(n)]]`. */
1623 int slot = shader_texture_info.slot_index;
1624 /* Explicit bind location for texture. */
1625 int location = shader_texture_info.location;
1626 /* Default sampler. */
1627 MTLSamplerBinding default_binding = {true, DEFAULT_SAMPLER_STATE};
1628
1629 if (slot >= 0 && slot < GPU_max_textures()) {
1630 bool bind_dummy_texture = true;
1631 if (resource_bind_table[location].used) {
1632 gpu::MTLTexture *bound_texture = resource_bind_table[location].texture_resource;
1633 MTLSamplerBinding &bound_sampler = (is_resource_sampler) ?
1634 this->pipeline_state.sampler_bindings[location] :
1635 default_binding;
1636 BLI_assert(bound_texture);
1637 BLI_assert(bound_sampler.used);
1638
1639 if (shader_texture_info.type == bound_texture->type_) {
1640 /* Bind texture and sampler if the bound texture matches the type expected by the
1641 * shader. */
1642 id<MTLTexture> tex = bound_texture->get_metal_handle();
1643
1644 if (bool(shader_texture_info.stage_mask & ShaderStage::VERTEX)) {
1645 rps.bind_vertex_texture(tex, slot);
1646 rps.bind_vertex_sampler(bound_sampler, use_argument_buffer_for_samplers, slot);
1647 }
1648
1649 if (bool(shader_texture_info.stage_mask & ShaderStage::FRAGMENT)) {
1650 rps.bind_fragment_texture(tex, slot);
1651 rps.bind_fragment_sampler(bound_sampler, use_argument_buffer_for_samplers, slot);
1652 }
1653
1654 /* Bind texture buffer to associated SSBO slot. */
1655 if (shader_texture_info.texture_buffer_ssbo_location != -1) {
1656 BLI_assert(bound_texture->usage_get() & GPU_TEXTURE_USAGE_ATOMIC);
1657 MTLStorageBuf *tex_storage_buf = bound_texture->get_storagebuf();
1658 BLI_assert(tex_storage_buf != nullptr);
1659 tex_storage_buf->bind(shader_texture_info.texture_buffer_ssbo_location);
1660 /* Update bound texture metadata.
1661 * components packed int uint4 (sizeX, sizeY, sizeZ/Layers, bytes per row). */
1662 MTLShader *active_shader = this->pipeline_state.active_shader;
1663 const int *metadata = bound_texture->get_texture_metdata_ptr();
1664 BLI_assert(shader_texture_info.buffer_metadata_uniform_loc != -1);
1665 active_shader->uniform_int(
1666 shader_texture_info.buffer_metadata_uniform_loc, 4, 1, metadata);
1667 }
1668
1669 /* Texture state resolved, no need to bind dummy texture */
1670 bind_dummy_texture = false;
1671 }
1672 else {
1673 /* Texture type for bound texture (e.g. Texture2DArray) does not match what was
1674 * expected in the shader interface. This is a problem and we will need to bind
1675 * a dummy texture to ensure correct API usage. */
1677 "(Shader '%s') Texture (%s) %p bound to slot %d is incompatible -- Wrong "
1678 "texture target type. (Expecting type %d, actual type %d) (binding "
1679 "name:'%s')(texture name:'%s')",
1680 shader_interface->get_name(),
1681 is_resource_sampler ? "TextureSampler" : "TextureImage",
1682 bound_texture,
1683 slot,
1684 shader_texture_info.type,
1685 bound_texture->type_,
1686 shader_interface->get_name_at_offset(shader_texture_info.name_offset),
1687 bound_texture->get_name());
1688 }
1689 }
1690 else {
1692 "Shader '%s' expected texture (%s) to be bound to location %d (texture[[%d]]) -- No "
1693 "texture was "
1694 "bound. (name:'%s')",
1695 shader_interface->get_name(),
1696 is_resource_sampler ? "TextureSampler" : "TextureImage",
1697 location,
1698 slot,
1699 shader_interface->get_name_at_offset(shader_texture_info.name_offset));
1700 }
1701
1702 /* Bind Dummy texture -- will temporarily resolve validation issues while incorrect formats
1703 * are provided -- as certain configurations may not need any binding. These issues should
1704 * be fixed in the high-level, if problems crop up. */
1705 if (bind_dummy_texture) {
1706 if (bool(shader_texture_info.stage_mask & ShaderStage::VERTEX)) {
1708 get_dummy_texture(shader_texture_info.type, shader_texture_info.sampler_format)
1709 ->get_metal_handle(),
1710 slot);
1711
1712 /* Bind default sampler state. */
1713 rps.bind_vertex_sampler(default_binding, use_argument_buffer_for_samplers, slot);
1714 }
1715 if (bool(shader_texture_info.stage_mask & ShaderStage::FRAGMENT)) {
1717 get_dummy_texture(shader_texture_info.type, shader_texture_info.sampler_format)
1718 ->get_metal_handle(),
1719 slot);
1720
1721 /* Bind default sampler state. */
1722 rps.bind_fragment_sampler(default_binding, use_argument_buffer_for_samplers, slot);
1723 }
1724 }
1725 }
1726 else {
1728 "Shader %p expected texture (%s) to be bound to slot %d -- Slot exceeds the "
1729 "hardware/API limit of '%d'. (name:'%s')",
1731 is_resource_sampler ? "TextureSampler" : "TextureImage",
1732 slot,
1734 shader_interface->get_name_at_offset(shader_texture_info.name_offset));
1735 }
1736 }
1737
1738 /* Construct and Bind argument buffer.
1739 * NOTE(Metal): Samplers use an argument buffer when the limit of 16 samplers is exceeded. */
1740 if (use_argument_buffer_for_samplers) {
1741#ifndef NDEBUG
1742 /* Debug check to validate each expected texture in the shader interface has a valid
1743 * sampler object bound to the context. We will need all of these to be valid
1744 * when constructing the sampler argument buffer. */
1745 for (const uint i : IndexRange(shader_interface->get_max_texture_index() + 1)) {
1746 const MTLShaderTexture &texture = shader_interface->get_texture(i);
1747 if (texture.used) {
1748 BLI_assert(this->samplers_.mtl_sampler[i] != nil);
1749 }
1750 }
1751#endif
1752
1753 /* Check to ensure the buffer binding index for the argument buffer has been assigned.
1754 * This PSO property will be set if we expect to use argument buffers, and the shader
1755 * uses any amount of textures. */
1756 BLI_assert(vertex_arg_buffer_bind_index >= 0 || fragment_arg_buffer_bind_index >= 0);
1757 if (vertex_arg_buffer_bind_index >= 0 || fragment_arg_buffer_bind_index >= 0) {
1758 /* Offset binding index to be relative to the start of static uniform buffer binding slots.
1759 * The first N slots, prior to `pipeline_state_instance->base_uniform_buffer_index` are
1760 * used by vertex and index buffer bindings, and the number of buffers present will vary
1761 * between PSOs. */
1762 int arg_buffer_idx = (pipeline_state_instance->base_uniform_buffer_index +
1763 vertex_arg_buffer_bind_index);
1764 assert(arg_buffer_idx < 32);
1765 id<MTLArgumentEncoder> argument_encoder = shader_interface->find_argument_encoder(
1766 arg_buffer_idx);
1767 if (argument_encoder == nil) {
1768 argument_encoder = [pipeline_state_instance->vert
1769 newArgumentEncoderWithBufferIndex:arg_buffer_idx];
1770 shader_interface->insert_argument_encoder(arg_buffer_idx, argument_encoder);
1771 }
1772
1773 /* Generate or Fetch argument buffer sampler configuration.
1774 * NOTE(Metal): we need to base sampler counts off of the maximal texture
1775 * index. This is not the most optimal, but in practice, not a use-case
1776 * when argument buffers are required.
1777 * This is because with explicit texture indices, the binding indices
1778 * should match across draws, to allow the high-level to optimize bind-points. */
1779 gpu::MTLBuffer *encoder_buffer = nullptr;
1780 this->samplers_.num_samplers = shader_interface->get_max_texture_index() + 1;
1781
1782 gpu::MTLBuffer **cached_smp_buffer_search = this->cached_sampler_buffers_.lookup_ptr(
1783 this->samplers_);
1784 if (cached_smp_buffer_search != nullptr) {
1785 encoder_buffer = *cached_smp_buffer_search;
1786 }
1787 else {
1788 /* Populate argument buffer with current global sampler bindings. */
1789 size_t size = [argument_encoder encodedLength];
1790 size_t alignment = max_uu([argument_encoder alignment], 256);
1791 size_t size_align_delta = (size % alignment);
1792 size_t aligned_alloc_size = ((alignment > 1) && (size_align_delta > 0)) ?
1793 size + (alignment - (size % alignment)) :
1794 size;
1795
1796 /* Allocate buffer to store encoded sampler arguments. */
1797 encoder_buffer = MTLContext::get_global_memory_manager()->allocate(aligned_alloc_size,
1798 true);
1799 BLI_assert(encoder_buffer);
1800 BLI_assert(encoder_buffer->get_metal_buffer());
1801 [argument_encoder setArgumentBuffer:encoder_buffer->get_metal_buffer() offset:0];
1802 [argument_encoder
1803 setSamplerStates:this->samplers_.mtl_sampler
1804 withRange:NSMakeRange(0, shader_interface->get_max_texture_index() + 1)];
1805 encoder_buffer->flush();
1806
1807 /* Insert into cache. */
1808 this->cached_sampler_buffers_.add_new(this->samplers_, encoder_buffer);
1809 }
1810
1811 BLI_assert(encoder_buffer != nullptr);
1812 int vert_buffer_index = (pipeline_state_instance->base_uniform_buffer_index +
1813 vertex_arg_buffer_bind_index);
1814 rps.bind_vertex_buffer(encoder_buffer->get_metal_buffer(), 0, vert_buffer_index);
1815
1816 /* Fragment shader shares its argument buffer binding with the vertex shader, So no need to
1817 * re-encode. We can use the same argument buffer. */
1818 if (fragment_arg_buffer_bind_index >= 0) {
1819 BLI_assert(fragment_arg_buffer_bind_index);
1820 int frag_buffer_index = (pipeline_state_instance->base_uniform_buffer_index +
1821 fragment_arg_buffer_bind_index);
1822 rps.bind_fragment_buffer(encoder_buffer->get_metal_buffer(), 0, frag_buffer_index);
1823 }
1824 }
1825 }
1826 }
1827}
1828
1829/* Texture binding variant for compute command encoder.
1830 * Ensure bound texture resources are bound to the active MTLComputeCommandEncoder. */
1832 id<MTLComputeCommandEncoder> rec,
1833 MTLShaderInterface *shader_interface,
1834 const MTLComputePipelineStateInstance *pipeline_state_instance)
1835{
1836 BLI_assert(shader_interface != nil);
1837 BLI_assert(rec != nil);
1838 UNUSED_VARS_NDEBUG(rec);
1839
1840 /* Fetch Render Pass state. */
1842
1843 @autoreleasepool {
1844 int compute_arg_buffer_bind_index = -1;
1845
1846 /* Argument buffers are used for samplers, when the limit of 16 is exceeded.
1847 * NOTE: Compute uses vertex argument for arg buffer bind index. */
1848 bool use_argument_buffer_for_samplers = shader_interface->uses_argument_buffer_for_samplers();
1849 compute_arg_buffer_bind_index = shader_interface->get_argument_buffer_bind_index(
1851
1852 /* Loop through expected textures in shader interface and resolve bindings with currently
1853 * bound textures.. */
1854 for (const uint t : IndexRange(shader_interface->get_max_texture_index() + 1)) {
1855 /* Ensure the bound texture is compatible with the shader interface. If the
1856 * shader does not expect a texture to be bound for the current slot, we skip
1857 * binding.
1858 * NOTE: Global texture bindings may be left over from prior draw calls. */
1859 const MTLShaderTexture &shader_texture_info = shader_interface->get_texture(t);
1860 if (!shader_texture_info.used) {
1861 /* Skip unused binding points if explicit indices are specified. */
1862 continue;
1863 }
1864
1865 /* Determine bind lookup table depending on whether an image binding or texture.
1866 * NOTE: Images and Texture Samplers share a binding table in Metal. */
1867 bool is_resource_sampler = shader_texture_info.is_texture_sampler;
1868 MTLTextureBinding(&resource_bind_table)[MTL_MAX_TEXTURE_SLOTS] =
1869 (is_resource_sampler) ? this->pipeline_state.texture_bindings :
1871
1872 /* Texture resource bind slot in shader `[[texture(n)]]`. */
1873 int slot = shader_texture_info.slot_index;
1874 /* Explicit bind location for texture. */
1875 int location = shader_texture_info.location;
1876 /* Default sampler. */
1877 MTLSamplerBinding default_binding = {true, DEFAULT_SAMPLER_STATE};
1878
1879 if (slot >= 0 && slot < GPU_max_textures()) {
1880 bool bind_dummy_texture = true;
1881 if (resource_bind_table[location].used) {
1882 gpu::MTLTexture *bound_texture = resource_bind_table[location].texture_resource;
1883 MTLSamplerBinding &bound_sampler = (is_resource_sampler) ?
1884 this->pipeline_state.sampler_bindings[location] :
1885 default_binding;
1886 BLI_assert(bound_texture);
1887 BLI_assert(bound_sampler.used);
1888
1889 if (shader_texture_info.type == bound_texture->type_) {
1890 /* Bind texture and sampler if the bound texture matches the type expected by the
1891 * shader. */
1892 id<MTLTexture> tex = bound_texture->get_metal_handle();
1893
1894 /* If texture resource is an image binding and has a non-default swizzle mask, we need
1895 * to bind the source texture resource to retain image write access. */
1896 if (!is_resource_sampler && bound_texture->has_custom_swizzle()) {
1897 tex = bound_texture->get_metal_handle_base();
1898 }
1899
1900 if (bool(shader_texture_info.stage_mask & ShaderStage::COMPUTE)) {
1901 cs.bind_compute_texture(tex, slot);
1902 cs.bind_compute_sampler(bound_sampler, use_argument_buffer_for_samplers, slot);
1903 }
1904
1905 /* Bind texture buffer to associated SSBO slot. */
1906 if (shader_texture_info.texture_buffer_ssbo_location != -1) {
1907 BLI_assert(bound_texture->usage_get() & GPU_TEXTURE_USAGE_ATOMIC);
1908 MTLStorageBuf *tex_storage_buf = bound_texture->get_storagebuf();
1909 BLI_assert(tex_storage_buf != nullptr);
1910 tex_storage_buf->bind(shader_texture_info.texture_buffer_ssbo_location);
1911 /* Update bound texture metadata.
1912 * components packed int uint4 (sizeX, sizeY, sizeZ/Layers, bytes per row). */
1913 MTLShader *active_shader = this->pipeline_state.active_shader;
1914 const int *metadata = bound_texture->get_texture_metdata_ptr();
1915 BLI_assert(shader_texture_info.buffer_metadata_uniform_loc != -1);
1916 active_shader->uniform_int(
1917 shader_texture_info.buffer_metadata_uniform_loc, 4, 1, metadata);
1918 }
1919
1920 /* Texture state resolved, no need to bind dummy texture */
1921 bind_dummy_texture = false;
1922 }
1923 else {
1924 /* Texture type for bound texture (e.g. Texture2DArray) does not match what was
1925 * expected in the shader interface. This is a problem and we will need to bind
1926 * a dummy texture to ensure correct API usage. */
1928 "(Shader '%s') Texture (%s) %p bound to slot %d is incompatible -- Wrong "
1929 "texture target type. (Expecting type %d, actual type %d) (binding "
1930 "name:'%s')(texture name:'%s')",
1931 shader_interface->get_name(),
1932 is_resource_sampler ? "TextureSampler" : "TextureImage",
1933 bound_texture,
1934 slot,
1935 shader_texture_info.type,
1936 bound_texture->type_,
1937 shader_interface->get_name_at_offset(shader_texture_info.name_offset),
1938 bound_texture->get_name());
1939 }
1940 }
1941 else {
1943 "Shader '%s' expected texture (%s) to be bound to location %d (texture[[%d]]) -- No "
1944 "texture was "
1945 "bound. (name:'%s')",
1946 shader_interface->get_name(),
1947 is_resource_sampler ? "TextureSampler" : "TextureImage",
1948 location,
1949 slot,
1950 shader_interface->get_name_at_offset(shader_texture_info.name_offset));
1951 }
1952
1953 /* Bind Dummy texture -- will temporarily resolve validation issues while incorrect formats
1954 * are provided -- as certain configurations may not need any binding. These issues should
1955 * be fixed in the high-level, if problems crop up. */
1956 if (bind_dummy_texture) {
1957 if (bool(shader_texture_info.stage_mask & ShaderStage::COMPUTE)) {
1959 get_dummy_texture(shader_texture_info.type, shader_texture_info.sampler_format)
1960 ->get_metal_handle(),
1961 slot);
1962
1963 /* Bind default sampler state. */
1964 MTLSamplerBinding default_binding = {true, DEFAULT_SAMPLER_STATE};
1965 cs.bind_compute_sampler(default_binding, use_argument_buffer_for_samplers, slot);
1966 }
1967 }
1968 }
1969 else {
1971 "Shader %p expected texture (%s) to be bound to slot %d -- Slot exceeds the "
1972 "hardware/API limit of '%d'. (name:'%s')",
1974 is_resource_sampler ? "TextureSampler" : "TextureImage",
1975 slot,
1977 shader_interface->get_name_at_offset(shader_texture_info.name_offset));
1978 }
1979 }
1980
1981 /* Construct and Bind argument buffer.
1982 * NOTE(Metal): Samplers use an argument buffer when the limit of 16 samplers is exceeded. */
1983 if (use_argument_buffer_for_samplers) {
1984#ifndef NDEBUG
1985 /* Debug check to validate each expected texture in the shader interface has a valid
1986 * sampler object bound to the context. We will need all of these to be valid
1987 * when constructing the sampler argument buffer. */
1988 for (const uint i : IndexRange(shader_interface->get_max_texture_index() + 1)) {
1989 const MTLShaderTexture &texture = shader_interface->get_texture(i);
1990 if (texture.used) {
1991 BLI_assert(this->samplers_.mtl_sampler[i] != nil);
1992 }
1993 }
1994#endif
1995
1996 /* Check to ensure the buffer binding index for the argument buffer has been assigned.
1997 * This PSO property will be set if we expect to use argument buffers, and the shader
1998 * uses any amount of textures. */
1999 BLI_assert(compute_arg_buffer_bind_index >= 0);
2000 if (compute_arg_buffer_bind_index >= 0) {
2001 /* Offset binding index to be relative to the start of static uniform buffer binding slots.
2002 * The first N slots, prior to `pipeline_state_instance->base_uniform_buffer_index` are
2003 * used by vertex and index buffer bindings, and the number of buffers present will vary
2004 * between PSOs. */
2005 int arg_buffer_idx = (pipeline_state_instance->base_uniform_buffer_index +
2006 compute_arg_buffer_bind_index);
2007 assert(arg_buffer_idx < 32);
2008 id<MTLArgumentEncoder> argument_encoder = shader_interface->find_argument_encoder(
2009 arg_buffer_idx);
2010 if (argument_encoder == nil) {
2011 argument_encoder = [pipeline_state_instance->compute
2012 newArgumentEncoderWithBufferIndex:arg_buffer_idx];
2013 shader_interface->insert_argument_encoder(arg_buffer_idx, argument_encoder);
2014 }
2015
2016 /* Generate or Fetch argument buffer sampler configuration.
2017 * NOTE(Metal): we need to base sampler counts off of the maximal texture
2018 * index. This is not the most optimal, but in practice, not a use-case
2019 * when argument buffers are required.
2020 * This is because with explicit texture indices, the binding indices
2021 * should match across draws, to allow the high-level to optimize bind-points. */
2022 gpu::MTLBuffer *encoder_buffer = nullptr;
2023 this->samplers_.num_samplers = shader_interface->get_max_texture_index() + 1;
2024
2025 gpu::MTLBuffer **cached_smp_buffer_search = this->cached_sampler_buffers_.lookup_ptr(
2026 this->samplers_);
2027 if (cached_smp_buffer_search != nullptr) {
2028 encoder_buffer = *cached_smp_buffer_search;
2029 }
2030 else {
2031 /* Populate argument buffer with current global sampler bindings. */
2032 size_t size = [argument_encoder encodedLength];
2033 size_t alignment = max_uu([argument_encoder alignment], 256);
2034 size_t size_align_delta = (size % alignment);
2035 size_t aligned_alloc_size = ((alignment > 1) && (size_align_delta > 0)) ?
2036 size + (alignment - (size % alignment)) :
2037 size;
2038
2039 /* Allocate buffer to store encoded sampler arguments. */
2040 encoder_buffer = MTLContext::get_global_memory_manager()->allocate(aligned_alloc_size,
2041 true);
2042 BLI_assert(encoder_buffer);
2043 BLI_assert(encoder_buffer->get_metal_buffer());
2044 [argument_encoder setArgumentBuffer:encoder_buffer->get_metal_buffer() offset:0];
2045 [argument_encoder
2046 setSamplerStates:this->samplers_.mtl_sampler
2047 withRange:NSMakeRange(0, shader_interface->get_max_texture_index() + 1)];
2048 encoder_buffer->flush();
2049
2050 /* Insert into cache. */
2051 this->cached_sampler_buffers_.add_new(this->samplers_, encoder_buffer);
2052 }
2053
2054 BLI_assert(encoder_buffer != nullptr);
2055 int compute_buffer_index = (pipeline_state_instance->base_uniform_buffer_index +
2056 compute_arg_buffer_bind_index);
2057 cs.bind_compute_buffer(encoder_buffer->get_metal_buffer(), 0, compute_buffer_index);
2058 }
2059 }
2060 }
2061}
2062
2063/* Encode latest depth-stencil state. */
2064void MTLContext::ensure_depth_stencil_state(MTLPrimitiveType prim_type)
2065{
2066 /* Check if we need to update state. */
2068 return;
2069 }
2070
2071 /* Fetch render command encoder. */
2072 id<MTLRenderCommandEncoder> rec = this->main_command_buffer.get_active_render_command_encoder();
2073 BLI_assert(rec);
2074
2075 /* Fetch Render Pass state. */
2077
2080 bool hasDepthTarget = fb->has_depth_attachment();
2081 bool hasStencilTarget = fb->has_stencil_attachment();
2082
2083 if (hasDepthTarget || hasStencilTarget) {
2084 /* Update FrameBuffer State. */
2086 this->pipeline_state.depth_stencil_state.has_stencil_target = hasStencilTarget;
2087
2088 /* Check if current MTLContextDepthStencilState maps to an existing state object in
2089 * the Depth-stencil state cache. */
2090 id<MTLDepthStencilState> ds_state = nil;
2091 id<MTLDepthStencilState> *depth_stencil_state_lookup =
2092 this->depth_stencil_state_cache.lookup_ptr(this->pipeline_state.depth_stencil_state);
2093
2094 /* If not, populate DepthStencil state descriptor. */
2095 if (depth_stencil_state_lookup == nullptr) {
2096
2097 MTLDepthStencilDescriptor *ds_state_desc = [[[MTLDepthStencilDescriptor alloc] init]
2098 autorelease];
2099
2100 if (hasDepthTarget) {
2101 ds_state_desc.depthWriteEnabled =
2103 ds_state_desc.depthCompareFunction =
2106 MTLCompareFunctionAlways;
2107 }
2108
2109 if (hasStencilTarget) {
2110 ds_state_desc.backFaceStencil.readMask =
2112 ds_state_desc.backFaceStencil.writeMask =
2114 ds_state_desc.backFaceStencil.stencilFailureOperation =
2116 ds_state_desc.backFaceStencil.depthFailureOperation =
2118 ds_state_desc.backFaceStencil.depthStencilPassOperation =
2120 ds_state_desc.backFaceStencil.stencilCompareFunction =
2123 MTLCompareFunctionAlways;
2124
2125 ds_state_desc.frontFaceStencil.readMask =
2127 ds_state_desc.frontFaceStencil.writeMask =
2129 ds_state_desc.frontFaceStencil.stencilFailureOperation =
2131 ds_state_desc.frontFaceStencil.depthFailureOperation =
2133 ds_state_desc.frontFaceStencil.depthStencilPassOperation =
2135 ds_state_desc.frontFaceStencil.stencilCompareFunction =
2138 MTLCompareFunctionAlways;
2139 }
2140
2141 /* Bake new DS state. */
2142 ds_state = [this->device newDepthStencilStateWithDescriptor:ds_state_desc];
2143
2144 /* Store state in cache. */
2145 BLI_assert(ds_state != nil);
2146 this->depth_stencil_state_cache.add_new(this->pipeline_state.depth_stencil_state, ds_state);
2147 }
2148 else {
2149 ds_state = *depth_stencil_state_lookup;
2150 BLI_assert(ds_state != nil);
2151 }
2152
2153 /* Bind Depth Stencil State to render command encoder. */
2154 BLI_assert(ds_state != nil);
2155 if (ds_state != nil) {
2156 if (rps.bound_ds_state != ds_state) {
2157 [rec setDepthStencilState:ds_state];
2158 rps.bound_ds_state = ds_state;
2159 }
2160 }
2161
2162 /* Apply dynamic depth-stencil state on encoder. */
2163 if (hasStencilTarget) {
2164 uint32_t stencil_ref_value =
2167 0;
2168 if (stencil_ref_value != rps.last_used_stencil_ref_value) {
2169 [rec setStencilReferenceValue:stencil_ref_value];
2170 rps.last_used_stencil_ref_value = stencil_ref_value;
2171 }
2172 }
2173
2174 if (hasDepthTarget) {
2175 bool doBias = false;
2176 switch (prim_type) {
2177 case MTLPrimitiveTypeTriangle:
2178 case MTLPrimitiveTypeTriangleStrip:
2180 break;
2181 case MTLPrimitiveTypeLine:
2182 case MTLPrimitiveTypeLineStrip:
2184 break;
2185 case MTLPrimitiveTypePoint:
2187 break;
2188 }
2189 [rec setDepthBias:(doBias) ? this->pipeline_state.depth_stencil_state.depth_bias : 0
2190 slopeScale:(doBias) ? this->pipeline_state.depth_stencil_state.depth_slope_scale : 0
2191 clamp:0];
2192 }
2193 }
2194}
2195
2198/* -------------------------------------------------------------------- */
2203{
2204 /* Verify if bound shader is valid and fetch MTLComputePipelineStateInstance. */
2205 /* Check if an active shader is bound. */
2206 if (!this->pipeline_state.active_shader) {
2207 MTL_LOG_WARNING("No Metal shader bound!");
2208 return nullptr;
2209 }
2210 /* Also ensure active shader is valid. */
2211 if (!this->pipeline_state.active_shader->is_valid()) {
2213 "Bound active shader is not valid (Missing/invalid implementation for Metal).", );
2214 return nullptr;
2215 }
2216 /* Verify this is a compute shader. */
2217
2218 /* Fetch shader interface. */
2219 MTLShaderInterface *shader_interface = this->pipeline_state.active_shader->get_interface();
2220 if (shader_interface == nullptr) {
2221 MTL_LOG_WARNING("Bound active shader does not have a valid shader interface!", );
2222 return nullptr;
2223 }
2224
2225 MTLShader *active_shader = this->pipeline_state.active_shader;
2226
2227 /* Set descriptor to default shader constants . */
2228 MTLComputePipelineStateDescriptor compute_pipeline_descriptor(active_shader->constants.values);
2229
2230 const MTLComputePipelineStateInstance *compute_pso_inst =
2232 compute_pipeline_descriptor);
2233
2234 if (compute_pso_inst == nullptr || compute_pso_inst->pso == nil) {
2235 MTL_LOG_WARNING("No valid compute PSO for compute dispatch!", );
2236 return nullptr;
2237 }
2238 return compute_pso_inst;
2239}
2240
2241void MTLContext::compute_dispatch(int groups_x_len, int groups_y_len, int groups_z_len)
2242{
2243 /* Ensure all resources required by upcoming compute submission are correctly bound to avoid
2244 * out of bounds reads/writes. */
2245 const MTLComputePipelineStateInstance *compute_pso_inst = this->ensure_compute_pipeline_state();
2246 if (compute_pso_inst == nullptr) {
2247 return;
2248 }
2249
2250#if MTL_DEBUG_SINGLE_DISPATCH_PER_ENCODER == 1
2251 GPU_flush();
2252#endif
2253
2254 /* Shader instance. */
2255 MTLShaderInterface *shader_interface = this->pipeline_state.active_shader->get_interface();
2256 BLI_assert(compute_pso_inst != nullptr);
2257
2258 /* Begin compute encoder. */
2259 id<MTLComputeCommandEncoder> compute_encoder =
2261 BLI_assert(compute_encoder != nil);
2262
2263 /* Bind PSO. */
2265 cs.bind_pso(compute_pso_inst->pso);
2266
2268 /* Texture Bindings. */
2269 /* We will iterate through all texture bindings on the context and determine if any of the
2270 * active slots match those in our shader interface. If so, textures will be bound. */
2271 if (shader_interface->get_total_textures() > 0) {
2272 this->ensure_texture_bindings(compute_encoder, shader_interface, compute_pso_inst);
2273 }
2274
2275 /* Bind buffers.
2276 * NOTE: `ensure_buffer_bindings` must be called after `ensure_texture_bindings` to allow
2277 * for binding of buffer-backed texture's data buffer and metadata. */
2278 this->ensure_buffer_bindings(compute_encoder, shader_interface, compute_pso_inst);
2279
2280 /* Dispatch compute. */
2281 const MTLComputePipelineStateCommon &compute_state_common =
2283 [compute_encoder dispatchThreadgroups:MTLSizeMake(max_ii(groups_x_len, 1),
2284 max_ii(groups_y_len, 1),
2285 max_ii(groups_z_len, 1))
2286 threadsPerThreadgroup:MTLSizeMake(compute_state_common.threadgroup_x_len,
2287 compute_state_common.threadgroup_y_len,
2288 compute_state_common.threadgroup_z_len)];
2289#if MTL_DEBUG_SINGLE_DISPATCH_PER_ENCODER == 1
2290 GPU_flush();
2291#endif
2292}
2293
2295{
2296
2297#if MTL_DEBUG_SINGLE_DISPATCH_PER_ENCODER == 1
2298 GPU_flush();
2299#endif
2300
2301 /* Ensure all resources required by upcoming compute submission are correctly bound. */
2302 const MTLComputePipelineStateInstance *compute_pso_inst = this->ensure_compute_pipeline_state();
2303 BLI_assert(compute_pso_inst != nullptr);
2304
2305 /* Shader instance. */
2306 MTLShaderInterface *shader_interface = this->pipeline_state.active_shader->get_interface();
2307
2308 /* Begin compute encoder. */
2309 id<MTLComputeCommandEncoder> compute_encoder =
2311 BLI_assert(compute_encoder != nil);
2312
2313 /* Bind PSO. */
2315 cs.bind_pso(compute_pso_inst->pso);
2316
2318 /* Texture Bindings. */
2319 /* We will iterate through all texture bindings on the context and determine if any of the
2320 * active slots match those in our shader interface. If so, textures will be bound. */
2321 if (shader_interface->get_total_textures() > 0) {
2322 this->ensure_texture_bindings(compute_encoder, shader_interface, compute_pso_inst);
2323 }
2324
2325 /* Bind buffers.
2326 * NOTE: `ensure_buffer_bindings` must be called after `ensure_texture_bindings` to allow
2327 * for binding of buffer-backed texture's data buffer and metadata. */
2328 this->ensure_buffer_bindings(compute_encoder, shader_interface, compute_pso_inst);
2329
2330 /* Indirect Dispatch compute. */
2331 MTLStorageBuf *mtlssbo = static_cast<MTLStorageBuf *>(indirect_buf);
2332 id<MTLBuffer> mtl_indirect_buf = mtlssbo->get_metal_buffer();
2333 BLI_assert(mtl_indirect_buf != nil);
2334 if (mtl_indirect_buf == nil) {
2335 MTL_LOG_WARNING("Metal Indirect Compute dispatch storage buffer does not exist.");
2336 return;
2337 }
2338
2339 /* Indirect Compute dispatch. */
2340 const MTLComputePipelineStateCommon &compute_state_common =
2342 [compute_encoder
2343 dispatchThreadgroupsWithIndirectBuffer:mtl_indirect_buf
2344 indirectBufferOffset:0
2345 threadsPerThreadgroup:MTLSizeMake(compute_state_common.threadgroup_x_len,
2346 compute_state_common.threadgroup_y_len,
2347 compute_state_common.threadgroup_z_len)];
2348#if MTL_DEBUG_SINGLE_DISPATCH_PER_ENCODER == 1
2349 GPU_flush();
2350#endif
2351}
2352
2355/* -------------------------------------------------------------------- */
2360{
2361 /* Flag visibility buffer as dirty if the buffer being used for visibility has changed --
2362 * This is required by the render pass, and we will break the pass if the results destination
2363 * buffer is modified. */
2364 if (buffer) {
2365 visibility_is_dirty_ = (buffer != visibility_buffer_) || visibility_is_dirty_;
2366 visibility_buffer_ = buffer;
2367 visibility_buffer_->debug_ensure_used();
2368 }
2369 else {
2370 /* If buffer is null, reset visibility state, mark dirty to break render pass if results are no
2371 * longer needed. */
2372 visibility_is_dirty_ = (visibility_buffer_ != nullptr) || visibility_is_dirty_;
2373 visibility_buffer_ = nullptr;
2374 }
2375}
2376
2378{
2379 return visibility_buffer_;
2380}
2381
2383{
2384 visibility_is_dirty_ = false;
2385}
2386
2388{
2389 return visibility_is_dirty_;
2390}
2391
2394/* -------------------------------------------------------------------- */
2398void MTLContext::texture_bind(gpu::MTLTexture *mtl_texture, uint texture_unit, bool is_image)
2399{
2400 BLI_assert(this);
2401 BLI_assert(mtl_texture);
2402
2403 if (texture_unit < 0 || texture_unit >= GPU_max_textures() ||
2404 texture_unit >= MTL_MAX_TEXTURE_SLOTS)
2405 {
2406 MTL_LOG_ERROR("Attempting to bind texture '%s' to invalid texture unit %d",
2407 mtl_texture->get_name(),
2408 texture_unit);
2409 BLI_assert(false);
2410 return;
2411 }
2412
2414 &resource_bind_table)[MTL_MAX_TEXTURE_SLOTS] = (is_image) ?
2417
2418 /* Bind new texture. */
2419 resource_bind_table[texture_unit].texture_resource = mtl_texture;
2420 resource_bind_table[texture_unit].used = true;
2421 mtl_texture->is_bound_ = true;
2422}
2423
2424void MTLContext::sampler_bind(MTLSamplerState sampler_state, uint sampler_unit)
2425{
2426 BLI_assert(this);
2427 if (sampler_unit < 0 || sampler_unit >= GPU_max_textures() ||
2428 sampler_unit >= MTL_MAX_SAMPLER_SLOTS)
2429 {
2430 MTL_LOG_ERROR("Attempting to bind sampler to invalid sampler unit %d", sampler_unit);
2431 BLI_assert(false);
2432 return;
2433 }
2434
2435 /* Apply binding. */
2436 this->pipeline_state.sampler_bindings[sampler_unit] = {true, sampler_state};
2437}
2438
2439void MTLContext::texture_unbind(gpu::MTLTexture *mtl_texture, bool is_image)
2440{
2441 BLI_assert(mtl_texture);
2442
2444 &resource_bind_table)[MTL_MAX_TEXTURE_SLOTS] = (is_image) ?
2447
2448 /* Iterate through textures in state and unbind. */
2449 for (int i = 0; i < min_uu(GPU_max_textures(), MTL_MAX_TEXTURE_SLOTS); i++) {
2450 if (resource_bind_table[i].texture_resource == mtl_texture) {
2451 resource_bind_table[i].texture_resource = nullptr;
2452 resource_bind_table[i].used = false;
2453 }
2454 }
2455
2456 /* Locally unbind texture. */
2457 mtl_texture->is_bound_ = false;
2458}
2459
2461{
2463 &resource_bind_table)[MTL_MAX_TEXTURE_SLOTS] = (is_image) ?
2466
2467 /* Iterate through context's bound textures. */
2468 for (int t = 0; t < min_uu(GPU_max_textures(), MTL_MAX_TEXTURE_SLOTS); t++) {
2469 if (resource_bind_table[t].used && resource_bind_table[t].texture_resource) {
2470 resource_bind_table[t].used = false;
2471 resource_bind_table[t].texture_resource = nullptr;
2472 }
2473 }
2474}
2475
2476id<MTLSamplerState> MTLContext::get_sampler_from_state(MTLSamplerState sampler_state)
2477{
2478 /* Internal sampler states are signal values and do not correspond to actual samplers. */
2480
2481 if (sampler_state.state.type == GPU_SAMPLER_STATE_TYPE_CUSTOM) {
2482 return custom_sampler_state_cache_[sampler_state.state.custom_type];
2483 }
2484
2485 return sampler_state_cache_[sampler_state.state.extend_yz][sampler_state.state.extend_x]
2486 [sampler_state.state.filtering];
2487}
2488
2490static inline MTLSamplerAddressMode to_mtl_type(GPUSamplerExtendMode wrap_mode)
2491{
2492 switch (wrap_mode) {
2494 return MTLSamplerAddressModeClampToEdge;
2496 return MTLSamplerAddressModeRepeat;
2498 return MTLSamplerAddressModeMirrorRepeat;
2500 return MTLSamplerAddressModeClampToBorderColor;
2501 default:
2503 return MTLSamplerAddressModeClampToEdge;
2504 }
2505}
2506
2508{
2509 for (int extend_yz_i = 0; extend_yz_i < GPU_SAMPLER_EXTEND_MODES_COUNT; extend_yz_i++) {
2510 const GPUSamplerExtendMode extend_yz = static_cast<GPUSamplerExtendMode>(extend_yz_i);
2511 const MTLSamplerAddressMode extend_t = to_mtl_type(extend_yz);
2512
2513 for (int extend_x_i = 0; extend_x_i < GPU_SAMPLER_EXTEND_MODES_COUNT; extend_x_i++) {
2514 const GPUSamplerExtendMode extend_x = static_cast<GPUSamplerExtendMode>(extend_x_i);
2515 const MTLSamplerAddressMode extend_s = to_mtl_type(extend_x);
2516
2517 for (int filtering_i = 0; filtering_i < GPU_SAMPLER_FILTERING_TYPES_COUNT; filtering_i++) {
2518 const GPUSamplerFiltering filtering = GPUSamplerFiltering(filtering_i);
2519
2520 MTLSamplerDescriptor *descriptor = [[MTLSamplerDescriptor alloc] init];
2521 descriptor.normalizedCoordinates = true;
2522 descriptor.sAddressMode = extend_s;
2523 descriptor.tAddressMode = extend_t;
2524 descriptor.rAddressMode = extend_t;
2525 descriptor.borderColor = MTLSamplerBorderColorTransparentBlack;
2526 descriptor.minFilter = (filtering & GPU_SAMPLER_FILTERING_LINEAR) ?
2527 MTLSamplerMinMagFilterLinear :
2528 MTLSamplerMinMagFilterNearest;
2529 descriptor.magFilter = (filtering & GPU_SAMPLER_FILTERING_LINEAR) ?
2530 MTLSamplerMinMagFilterLinear :
2531 MTLSamplerMinMagFilterNearest;
2532 descriptor.mipFilter = (filtering & GPU_SAMPLER_FILTERING_MIPMAP) ?
2533 MTLSamplerMipFilterLinear :
2534 MTLSamplerMipFilterNotMipmapped;
2535 descriptor.lodMinClamp = -1000;
2536 descriptor.lodMaxClamp = 1000;
2537 float aniso_filter = max_ff(16, U.anisotropic_filter);
2538 descriptor.maxAnisotropy = (filtering & GPU_SAMPLER_FILTERING_MIPMAP) ? aniso_filter : 1;
2539 descriptor.compareFunction = MTLCompareFunctionAlways;
2540 descriptor.supportArgumentBuffers = true;
2541
2542 id<MTLSamplerState> state = [this->device newSamplerStateWithDescriptor:descriptor];
2543 sampler_state_cache_[extend_yz_i][extend_x_i][filtering_i] = state;
2544
2545 BLI_assert(state != nil);
2546 [descriptor autorelease];
2547 }
2548 }
2549 }
2550
2551 /* Compare sampler for depth textures. */
2552 {
2553 MTLSamplerDescriptor *descriptor = [[MTLSamplerDescriptor alloc] init];
2554 descriptor.minFilter = MTLSamplerMinMagFilterLinear;
2555 descriptor.magFilter = MTLSamplerMinMagFilterLinear;
2556 descriptor.compareFunction = MTLCompareFunctionLessEqual;
2557 descriptor.lodMinClamp = -1000;
2558 descriptor.lodMaxClamp = 1000;
2559 descriptor.supportArgumentBuffers = true;
2560
2561 id<MTLSamplerState> compare_state = [this->device newSamplerStateWithDescriptor:descriptor];
2562 custom_sampler_state_cache_[GPU_SAMPLER_CUSTOM_COMPARE] = compare_state;
2563
2564 BLI_assert(compare_state != nil);
2565 [descriptor autorelease];
2566 }
2567
2568 /* Custom sampler for icons. The icon texture is sampled within the shader using a -0.5f LOD
2569 * bias. */
2570 {
2571 MTLSamplerDescriptor *descriptor = [[MTLSamplerDescriptor alloc] init];
2572 descriptor.minFilter = MTLSamplerMinMagFilterLinear;
2573 descriptor.magFilter = MTLSamplerMinMagFilterLinear;
2574 descriptor.mipFilter = MTLSamplerMipFilterNearest;
2575 descriptor.lodMinClamp = 0;
2576 descriptor.lodMaxClamp = 1;
2577
2578 id<MTLSamplerState> icon_state = [this->device newSamplerStateWithDescriptor:descriptor];
2579 custom_sampler_state_cache_[GPU_SAMPLER_CUSTOM_ICON] = icon_state;
2580
2581 BLI_assert(icon_state != nil);
2582 [descriptor autorelease];
2583 }
2584}
2585
2587{
2588 if (default_sampler_state_ == nil) {
2589 default_sampler_state_ = this->get_sampler_from_state({GPUSamplerState::default_sampler()});
2590 }
2591 return default_sampler_state_;
2592}
2593
2596/* -------------------------------------------------------------------- */
2601{
2602 if (buffer_clear_pso_ != nil) {
2603 return buffer_clear_pso_;
2604 }
2605
2606 /* Fetch active context. */
2607 MTLContext *ctx = MTLContext::get();
2608 BLI_assert(ctx);
2609
2610 @autoreleasepool {
2611 /* Source as NSString. */
2612 const char *src =
2613 "\
2614 struct BufferClearParams {\
2615 uint clear_value;\
2616 };\
2617 kernel void compute_buffer_clear(constant BufferClearParams &params [[buffer(0)]],\
2618 device uint32_t* output_data [[buffer(1)]],\
2619 uint position [[thread_position_in_grid]])\
2620 {\
2621 output_data[position] = params.clear_value;\
2622 }";
2623 NSString *compute_buffer_clear_src = [NSString stringWithUTF8String:src];
2624
2625 /* Prepare shader library for buffer clearing. */
2626 MTLCompileOptions *options = [[[MTLCompileOptions alloc] init] autorelease];
2627 options.languageVersion = MTLLanguageVersion2_2;
2628
2629 NSError *error = nullptr;
2630 id<MTLLibrary> temp_lib = [[ctx->device newLibraryWithSource:compute_buffer_clear_src
2632 error:&error] autorelease];
2633 if (error) {
2634 /* Only exit out if genuine error and not warning. */
2635 if ([[error localizedDescription] rangeOfString:@"Compilation succeeded"].location ==
2636 NSNotFound)
2637 {
2638 NSLog(@"Compile Error - Metal Shader Library error %@ ", error);
2639 BLI_assert(false);
2640 return nil;
2641 }
2642 }
2643
2644 /* Fetch compute function. */
2645 BLI_assert(temp_lib != nil);
2646 id<MTLFunction> temp_compute_function = [[temp_lib newFunctionWithName:@"compute_buffer_clear"]
2647 autorelease];
2648 BLI_assert(temp_compute_function);
2649
2650 /* Compile compute PSO */
2651 buffer_clear_pso_ = [ctx->device newComputePipelineStateWithFunction:temp_compute_function
2652 error:&error];
2653 if (error || buffer_clear_pso_ == nil) {
2654 NSLog(@"Failed to prepare compute_buffer_clear MTLComputePipelineState %@", error);
2655 BLI_assert(false);
2656 return nil;
2657 }
2658
2659 [buffer_clear_pso_ retain];
2660 }
2661
2662 BLI_assert(buffer_clear_pso_ != nil);
2663 return buffer_clear_pso_;
2664}
2665
2668/* -------------------------------------------------------------------- */
2672void present(MTLRenderPassDescriptor *blit_descriptor,
2673 id<MTLRenderPipelineState> blit_pso,
2674 id<MTLTexture> swapchain_texture,
2675 id<CAMetalDrawable> drawable)
2676{
2677
2678 MTLContext *ctx = MTLContext::get();
2679 BLI_assert(ctx);
2680
2681 /* Flush any outstanding work. */
2682 ctx->flush();
2683
2684 /* Always pace CPU to maximum of 3 drawables in flight.
2685 * nextDrawable may have more in flight if backing swapchain
2686 * textures are re-allocate, such as during resize events.
2687 *
2688 * Determine frames in flight based on current latency. If
2689 * we are in a high-latency situation, limit frames in flight
2690 * to increase app responsiveness and keep GPU execution under control.
2691 * If latency improves, increase frames in flight to improve overall
2692 * performance. */
2693 int perf_max_drawables = MTL_MAX_DRAWABLES;
2695 perf_max_drawables = 1;
2696 }
2697 else if (MTLContext::avg_drawable_latency_us > 75000) {
2698 perf_max_drawables = 2;
2699 }
2700
2701 while (MTLContext::max_drawables_in_flight > min_ii(perf_max_drawables, MTL_MAX_DRAWABLES)) {
2703 }
2704
2705 /* Present is submitted in its own CMD Buffer to ensure drawable reference released as early as
2706 * possible. This command buffer is separate as it does not utilize the global state
2707 * for rendering as the main context does. */
2708 id<MTLCommandBuffer> cmdbuf = [ctx->queue commandBuffer];
2710
2711 /* Do Present Call and final Blit to MTLDrawable. */
2712 id<MTLRenderCommandEncoder> enc = [cmdbuf renderCommandEncoderWithDescriptor:blit_descriptor];
2713 [enc setRenderPipelineState:blit_pso];
2714 [enc setFragmentTexture:swapchain_texture atIndex:0];
2715 [enc drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:3];
2716 [enc endEncoding];
2717
2718 /* Present drawable. */
2719 BLI_assert(drawable);
2720 [cmdbuf presentDrawable:drawable];
2721
2722 /* Ensure freed buffers have usage tracked against active CommandBuffer submissions. */
2723 MTLSafeFreeList *cmd_free_buffer_list =
2725 BLI_assert(cmd_free_buffer_list);
2726
2727 /* Increment drawables in flight limiter. */
2729 std::chrono::time_point submission_time = std::chrono::high_resolution_clock::now();
2730
2731 /* Increment free pool reference and decrement upon command buffer completion. */
2732 cmd_free_buffer_list->increment_reference();
2733 [cmdbuf addCompletedHandler:^(id<MTLCommandBuffer> /*cb*/) {
2734 /* Flag freed buffers associated with this CMD buffer as ready to be freed. */
2735 cmd_free_buffer_list->decrement_reference();
2736
2737 /* Decrement count */
2739 MTL_LOG_INFO("Active command buffers: %d", MTLCommandBufferManager::num_active_cmd_bufs);
2740
2741 /* Drawable count and latency management. */
2743 std::chrono::time_point completion_time = std::chrono::high_resolution_clock::now();
2744 int64_t microseconds_per_frame = std::chrono::duration_cast<std::chrono::microseconds>(
2745 completion_time - submission_time)
2746 .count();
2747 MTLContext::latency_resolve_average(microseconds_per_frame);
2748
2749 MTL_LOG_INFO("Frame Latency: %f ms (Rolling avg: %f ms Drawables: %d)",
2750 ((float)microseconds_per_frame) / 1000.0f,
2751 ((float)MTLContext::avg_drawable_latency_us) / 1000.0f,
2752 perf_max_drawables);
2753 }];
2754
2755 [cmdbuf commit];
2756
2757 /* When debugging, fetch advanced command buffer errors. */
2758 if (G.debug & G_DEBUG_GPU) {
2759 [cmdbuf waitUntilCompleted];
2760 NSError *error = [cmdbuf error];
2761 if (error != nil) {
2762 NSLog(@"%@", error);
2763 BLI_assert(false);
2764 }
2765 }
2766}
2767
2770} // namespace blender::gpu
@ G_DEBUG_GPU
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
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.c:85
#define UNUSED_VARS_NDEBUG(...)
typedef double(DMatrix)[4][4]
int GPU_max_textures()
bool GPU_use_parallel_compilation()
@ GPU_COUNTERCLOCKWISE
@ GPU_CLOCKWISE
#define GPU_MAX_VIEWPORTS
void GPU_matrix_bind(GPUShader *shader)
void GPU_flush()
Definition gpu_state.cc:294
@ GPU_CULL_FRONT
Definition GPU_state.hh:134
@ GPU_CULL_NONE
Definition GPU_state.hh:133
@ GPU_CULL_BACK
Definition GPU_state.hh:135
@ 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_RGBA8I
@ GPU_RGBA8UI
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
uint GPU_vertformat_attr_add(GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
void GPU_vertformat_clear(GPUVertFormat *)
GPUVertCompType
@ GPU_COMP_F32
@ GPU_COMP_I32
@ GPU_COMP_U32
struct GPUShader GPUShader
ATTR_WARN_UNUSED_RESULT const BMVert * v
void init()
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
unsigned int U
Definition btGjkEpa3.h:78
SIMD_FORCE_INLINE btScalar length() const
Return the length of the vector.
Definition btVector3.h:257
void metalRegisterPresentCallback(void(*callback)(MTLRenderPassDescriptor *, id< MTLRenderPipelineState >, id< MTLTexture >, id< CAMetalDrawable >))
id< MTLTexture > metalOverlayTexture()
MTLDevice * metalDevice()
MTLCommandQueue * metalCommandQueue()
GHOST_Context * getContext()
static MTLBackend * get()
void init(id< MTLDevice > device)
Definition mtl_memory.mm:28
MTLSafeFreeList * get_current_safe_list()
gpu::MTLBuffer * allocate(uint64_t size, bool cpu_visible)
Definition mtl_memory.mm:96
id< MTLBuffer > get_metal_buffer() const
void prepare(bool supports_render=true)
MTLRenderPassState & get_render_pass_state()
id< MTLRenderCommandEncoder > ensure_begin_render_command_encoder(MTLFrameBuffer *ctx_framebuffer, bool force_begin, bool *r_new_pass)
id< MTLComputeCommandEncoder > ensure_begin_compute_encoder()
id< MTLRenderCommandEncoder > get_active_render_command_encoder()
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)
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()
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 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
const MTLComputePipelineStateCommon & get_compute_common_state()
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
VertBuf * get_transform_feedback_active_buffer()
MTLShaderInterface * get_interface()
MTLRenderPipelineStateInstance * bake_current_pipeline_state(MTLContext *ctx, MTLPrimitiveTopologyClass prim_type)
void push_constant_bindstate_mark_dirty(bool is_dirty)
void bind(int slot) override
const int * get_texture_metdata_ptr() const
MTLStorageBuf * get_storagebuf()
struct blender::gpu::Shader::Constants constants
virtual void apply_state()=0
eGPUTextureUsage usage_get() const
CCL_NAMESPACE_BEGIN struct Options options
#define NULL
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
RAYTRACE_GROUP_SIZE additional_info("eevee_shared", "eevee_gbuffer_data", "eevee_global_ubo", "eevee_sampling_data", "eevee_utility_texture", "eevee_hiz_data", "draw_view") .specialization_constant(Type RAYTRACE_GROUP_SIZE in_sh_0_tx in_sh_2_tx screen_normal_tx GPU_RGBA8
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:11
#define MTL_FRAME_AVERAGE_COUNT
Definition mtl_common.hh:10
#define MTL_LOG_SSBO_ERROR
#define MTL_LOG_UBO_ERROR
#define MTL_LOG_INFO(info,...)
Definition mtl_debug.hh:51
#define MTL_LOG_WARNING(info,...)
Definition mtl_debug.hh:44
#define MTL_LOG_ERROR(info,...)
Definition mtl_debug.hh:36
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)
unsigned int uint32_t
Definition stdint.h:80
__int64 int64_t
Definition stdint.h:89
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)
MTLStencilOperation stencil_op_back_depthstencil_pass
MTLStencilOperation stencil_op_front_stencil_fail
MTLStencilOperation stencil_op_front_depthstencil_pass
MTLStorageBufferBinding ssbo_bindings[MTL_MAX_BUFFER_BINDINGS]
MTLTextureBinding texture_bindings[MTL_MAX_TEXTURE_SLOTS]
MTLUniformBufferBinding ubo_bindings[MTL_MAX_BUFFER_BINDINGS]
MTLTextureBinding image_bindings[MTL_MAX_TEXTURE_SLOTS]
MTLSamplerBinding sampler_bindings[MTL_MAX_SAMPLER_SLOTS]
blender::Vector< MTLBufferArgumentData > buffer_bindings_reflection_data_frag
blender::Vector< MTLBufferArgumentData > buffer_bindings_reflection_data_vert
Definition mtl_shader.hh:99
MTLSamplerState mtl_sampler_flags[MTL_MAX_TEXTURE_SLOTS]
id< MTLSamplerState > mtl_sampler[MTL_MAX_TEXTURE_SLOTS]
gpu::MTLTexture * texture_resource
ccl_device_inline int clamp(int a, int mn, int mx)
Definition util/math.h:379