43# define MTL_DEBUG_SINGLE_DISPATCH_PER_ENCODER 0
45# define MTL_DEBUG_SINGLE_DISPATCH_PER_ENCODER 1
49#define DEBUG_BIND_NULL_BUFFER_FOR_MISSING_UBO 0
53#define DEBUG_BIND_NULL_BUFFER_FOR_MISSING_SSBO 0
56#if DEBUG_BIND_NULL_BUFFER_FOR_MISSING_UBO == 1
57# define MTL_LOG_UBO_ERROR MTL_LOG_WARNING
59# define MTL_LOG_UBO_ERROR MTL_LOG_ERROR
62#if DEBUG_BIND_NULL_BUFFER_FOR_MISSING_SSBO == 1
63# define MTL_LOG_SSBO_ERROR MTL_LOG_WARNING
65# define MTL_LOG_SSBO_ERROR MTL_LOG_ERROR
84void MTLContext::set_ghost_context(GHOST_ContextHandle ghostCtxHandle)
90 if (default_fbo_mtltexture_) {
91 [default_fbo_mtltexture_ release];
92 default_fbo_mtltexture_ = nil;
102 if (ghost_cgl_ctx !=
nullptr) {
106 "Binding GHOST context CGL %p to GPU context %p. (Device: %p, queue: %p, texture: %p)",
111 default_fbo_gputexture_);
114 if (default_fbo_mtltexture_) {
117 if (default_fbo_gputexture_) {
119 default_fbo_gputexture_ =
nullptr;
123 [default_fbo_mtltexture_ retain];
134 this->
label = default_fbo_mtltexture_.label;
140 if (!default_fbo_gputexture_) {
147 "-- Bound context %p for GPU context: %p is offscreen and does not have a default "
152 this->
label =
@"Offscreen Metal Context";
158 " Failed to bind GHOST context to MTLContext -- GHOST_ContextCGL is null "
159 "(GhostContext: %p, GhostContext_CGL: %p)",
166void MTLContext::set_ghost_window(GHOST_WindowHandle ghostWinHandle)
169 this->set_ghost_context((GHOST_ContextHandle)(ghostWin ? ghostWin->
getContext() :
nullptr));
195 is_inside_frame_ =
false;
196 current_frame_index_ = 0;
200 null_attribute_buffer_ = nil;
203 default_fbo_mtltexture_ = nil;
204 default_fbo_gputexture_ =
nullptr;
213 ghost_context = (ghostWin ? ghostWin->
getContext() :
nullptr);
217 this->
queue = (id<MTLCommandQueue>)this->ghost_context_->metalCommandQueue();
218 this->
device = (id<MTLDevice>)this->ghost_context_->metalDevice();
221 [this->
queue retain];
224#pragma clang diagnostic push
225#pragma clang diagnostic ignored "-Wobjc-method-access"
229 if (@available(macOS 13.3, *)) {
230 [this->
device setShouldMaximizeConcurrentCompilation:YES];
232#pragma clang diagnostic pop
235 this->ghost_context_->metalRegisterPresentCallback(&
present);
246 MTLBackend::platform_init(
this);
247 MTLBackend::capabilities_init(
this);
263 samplers_.mtl_sampler[
i] = nil;
291 if (default_fbo_gputexture_) {
293 default_fbo_gputexture_ =
nullptr;
295 if (default_fbo_mtltexture_) {
296 [default_fbo_mtltexture_ release];
297 default_fbo_mtltexture_ = nil;
313 this->pipeline_state.ubo_bindings[
i].ubo !=
nullptr)
315 GPUUniformBuf *ubo =
wrap(
324 this->pipeline_state.ssbo_bindings[
i].ssbo !=
nullptr)
337 if (sampler_state_cache_[extend_yz_i][extend_x_i][filtering_i] != nil) {
338 [sampler_state_cache_[extend_yz_i][extend_x_i][filtering_i] release];
339 sampler_state_cache_[extend_yz_i][extend_x_i][filtering_i] = nil;
347 if (custom_sampler_state_cache_[
i] != nil) {
348 [custom_sampler_state_cache_[
i] release];
349 custom_sampler_state_cache_[
i] = nil;
354 for (
auto *entry : cached_sampler_buffers_.values()) {
357 cached_sampler_buffers_.clear();
361 [null_buffer_ release];
363 if (null_attribute_buffer_) {
364 [null_attribute_buffer_ release];
372 [this->
queue release];
378 this->process_frame_timings();
389 is_inside_frame_ =
true;
400 is_inside_frame_ =
false;
402 this->process_frame_timings();
421 else if (ghost_context_) {
422 this->set_ghost_context((GHOST_ContextHandle)ghost_context_);
428 this->pipeline_state.ubo_bindings[
i].ubo !=
nullptr)
438 this->pipeline_state.ssbo_bindings[
i].ssbo !=
nullptr)
500 BLI_assert(
false &&
"No framebuffer is bound!");
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())
522 MTL_LOG_WARNING(
"Framebuffer validation failed, falling back to default framebuffer");
526 MTL_LOG_ERROR(
"CRITICAL: DEFAULT FRAMEBUFFER FAIL VALIDATION!!");
531 bool new_render_pass =
false;
532 id<MTLRenderCommandEncoder> new_enc =
535 if (new_render_pass) {
563 if (null_buffer_ != nil) {
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)];
587 if (null_attribute_buffer_ != nil) {
588 return null_attribute_buffer_;
593 static const int null_buffer_size = 256;
594 null_attribute_buffer_ = [this->
device newBufferWithLength:null_buffer_size
595 options:MTLResourceStorageModeManaged];
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)];
602 return null_attribute_buffer_;
609 gpu::MTLTexture *dummy_tex = dummy_textures_[sampler_format][type - 1];
610 if (dummy_tex !=
nullptr) {
615 switch (sampler_format) {
633 GPUTexture *tex =
nullptr;
658 if (!dummy_verts_[sampler_format]) {
664 switch (sampler_format) {
683 &dummy_vertformat_[sampler_format],
"dummy", comp_type, 4, fetch_mode);
685 dummy_vertformat_[sampler_format],
696 dummy_textures_[sampler_format][type - 1] = metal_tex;
704 if (dummy_textures_[
format][tex]) {
706 reinterpret_cast<GPUTexture *
>(
static_cast<Texture *
>(dummy_textures_[
format][tex])));
707 dummy_textures_[
format][tex] =
nullptr;
710 if (dummy_verts_[
format]) {
741 this->
pipeline_state.texture_bindings[t].texture_resource =
nullptr;
745 this->
pipeline_state.image_bindings[t].texture_resource =
nullptr;
762 this->
pipeline_state.color_write_mask = MTLColorWriteMaskRed | MTLColorWriteMaskGreen |
763 MTLColorWriteMaskBlue | MTLColorWriteMaskAlpha;
767 this->
pipeline_state.dest_alpha_blend_factor = MTLBlendFactorZero;
794 this->
pipeline_state.depth_stencil_state.depth_write_enable =
false;
795 this->
pipeline_state.depth_stencil_state.depth_test_enabled =
false;
798 this->
pipeline_state.depth_stencil_state.depth_function = MTLCompareFunctionAlways;
801 this->
pipeline_state.depth_stencil_state.depth_bias_enabled_for_points =
false;
802 this->
pipeline_state.depth_stencil_state.depth_bias_enabled_for_lines =
false;
803 this->
pipeline_state.depth_stencil_state.depth_bias_enabled_for_tris =
false;
806 this->
pipeline_state.depth_stencil_state.stencil_test_enabled =
false;
807 this->
pipeline_state.depth_stencil_state.stencil_read_mask = 0xFF;
808 this->
pipeline_state.depth_stencil_state.stencil_write_mask = 0xFF;
810 this->
pipeline_state.depth_stencil_state.stencil_func = MTLCompareFunctionAlways;
811 this->
pipeline_state.depth_stencil_state.stencil_op_front_stencil_fail = MTLStencilOperationKeep;
812 this->
pipeline_state.depth_stencil_state.stencil_op_front_depth_fail = MTLStencilOperationKeep;
813 this->
pipeline_state.depth_stencil_state.stencil_op_front_depthstencil_pass =
814 MTLStencilOperationKeep;
815 this->
pipeline_state.depth_stencil_state.stencil_op_back_stencil_fail = MTLStencilOperationKeep;
816 this->
pipeline_state.depth_stencil_state.stencil_op_back_depth_fail = MTLStencilOperationKeep;
817 this->
pipeline_state.depth_stencil_state.stencil_op_back_depthstencil_pass =
818 MTLStencilOperationKeep;
828 bool changed = (this->
pipeline_state.viewport_offset_x[0] != origin_x) ||
850 const int(&viewport_info)[4] = viewports[
v];
857 changed = changed || (this->
pipeline_state.viewport_offset_x[
v] != viewport_info[0]) ||
886 this->
pipeline_state.scissor_enabled = (scissor_width > 0 && scissor_height > 0);
897 scissor_enabled = scissor_enabled && (this->
pipeline_state.scissor_width > 0 &&
900 bool changed = (this->
pipeline_state.scissor_enabled != scissor_enabled);
934 "Bound active shader is not valid (Missing/invalid implementation for Metal).", );
950 if (shader_interface ==
nullptr) {
951 MTL_LOG_WARNING(
"Bound active shader does not have a valid shader interface!", );
961 if (!pipeline_state_instance) {
962 MTL_LOG_ERROR(
"Failed to bake Metal pipeline state for shader: %s",
968 if (pipeline_state_instance->
pso) {
972 id<MTLRenderCommandEncoder> rec =
976 MTL_LOG_ERROR(
"ensure_render_pipeline_state called while render pass is not active.");
983 [rec setRenderPipelineState:pipeline_state_instance->
pso];
1003 MTL_LOG_INFO(
"Binding null attribute buffer at index: %d",
1017 MTLViewport &viewport = viewports[
v];
1022 viewport.znear = this->
pipeline_state.depth_stencil_state.depth_range_near;
1023 viewport.zfar = this->
pipeline_state.depth_stencil_state.depth_range_far;
1029 MTLViewport viewport;
1030 viewport.originX = (double)this->
pipeline_state.viewport_offset_x[0];
1031 viewport.originY = (double)this->
pipeline_state.viewport_offset_y[0];
1033 viewport.height = (double)this->
pipeline_state.viewport_height[0];
1034 viewport.znear = this->
pipeline_state.depth_stencil_state.depth_range_near;
1035 viewport.zfar = this->
pipeline_state.depth_stencil_state.depth_range_far;
1036 [rec setViewport:viewport];
1045 MTLScissorRect scissor;
1071 bool is_attachmentless = (default_w == 0) && (default_h == 0);
1074 scissor.width = (is_attachmentless) ? render_fb->
get_width() : default_w;
1075 scissor.height = (is_attachmentless) ? render_fb->
get_height() : default_h;
1081 [rec setScissorRect:scissor];
1093 MTLWindingClockwise :
1094 MTLWindingCounterClockwise;
1095 [rec setFrontFacingWinding:winding];
1103 MTLCullMode mode = MTLCullModeNone;
1107 mode = MTLCullModeNone;
1110 mode = MTLCullModeFront;
1113 mode = MTLCullModeBack;
1120 [rec setCullMode:mode];
1134 id<MTLRenderCommandEncoder> ,
1149 if (push_constant_block.
size > 0) {
1153 uint32_t block_size = push_constant_block.
size;
1160 if (this->
pipeline_state.active_shader->get_push_constant_is_dirty() ||
1168 this->
pipeline_state.active_shader->get_push_constant_data(), block_size, buffer_index);
1170 this->
pipeline_state.active_shader->get_push_constant_data(), block_size, buffer_index);
1173 this->
pipeline_state.active_shader->push_constant_bindstate_mark_dirty(
false);
1188 const uint32_t ubo_location = ubo.
location;
1191 id<MTLBuffer> ubo_buffer = nil;
1192 size_t ubo_size = 0;
1194 bool bind_dummy_buffer =
false;
1198 ubo_buffer = this->
pipeline_state.ubo_bindings[ubo_location].ubo->get_metal_buffer();
1199 ubo_size = this->
pipeline_state.ubo_bindings[ubo_location].ubo->get_size();
1203 if (ubo_buffer == nil) {
1204 bind_dummy_buffer =
true;
1216 uint32_t expected_size =
1222 if (expected_size == 0) {
1232 "Shader interface expects UBO, but shader reflection data reports that it "
1237 if (ubo_size < expected_size) {
1239 "[UBO] UBO (UBO Name: %s) bound at location: %d (buffer[[%d]]) with size "
1240 "%lu (Expected size "
1241 "%d) (Shader Name: %s) is too small -- binding NULL buffer. This is likely an "
1242 "over-binding, which is not used, but we need this to avoid validation "
1250 bind_dummy_buffer =
true;
1257 "[UBO] Shader '%s' expected UBO '%s' to be bound at buffer slot: %d "
1258 "(buffer[[%d]])-- but "
1259 "nothing was bound -- binding dummy buffer",
1264 bind_dummy_buffer =
true;
1267 if (bind_dummy_buffer) {
1270 ubo_size = [ubo_buffer
length];
1273 if (ubo_buffer != nil) {
1292 "[UBO] Shader '%s' has UBO '%s' bound at buffer index: %d -- but MTLBuffer "
1310 const uint32_t ssbo_location = ssbo.
location;
1313 id<MTLBuffer> ssbo_buffer = nil;
1314 size_t ssbo_size = 0;
1320 ssbo_buffer = this->
pipeline_state.ssbo_bindings[ssbo_location].ssbo->get_metal_buffer();
1321 ssbo_size = this->
pipeline_state.ssbo_bindings[ssbo_location].ssbo->get_size();
1329 "[SSBO] Shader '%s' expected SSBO '%s' to be bound at buffer location: %d "
1330 "(buffer[[%d]]) -- "
1332 "nothing was bound.",
1338#if DEBUG_BIND_NULL_BUFFER_FOR_MISSING_SSBO == 1
1340 ssbo_size = [ssbo_buffer
length];
1344 if (ssbo_buffer != nil) {
1362 "[SSBO] Shader '%s' had SSBO '%s' bound at SSBO location: %d "
1364 "%d]]) -- but bound MTLStorageBuf was nil.",
1379 id<MTLComputeCommandEncoder> ,
1388 if (push_constant_block.
size > 0) {
1392 uint32_t block_size = push_constant_block.
size;
1403 this->
pipeline_state.active_shader->get_push_constant_data(), block_size, buffer_index);
1406 this->
pipeline_state.active_shader->push_constant_bindstate_mark_dirty(
false);
1418 const uint32_t ubo_location = ubo.
location;
1421 id<MTLBuffer> ubo_buffer = nil;
1422 size_t ubo_size = 0;
1424 bool bind_dummy_buffer =
false;
1428 ubo_buffer = this->
pipeline_state.ubo_bindings[ubo_location].ubo->get_metal_buffer();
1429 ubo_size = this->
pipeline_state.ubo_bindings[ubo_location].ubo->get_size();
1434 if (ubo_buffer == nil) {
1435 bind_dummy_buffer =
true;
1444 "[UBO] Shader '%s' expected UBO '%s' to be bound at buffer location: %d "
1445 "(buffer[[%d]]) -- but "
1446 "nothing was bound -- binding dummy buffer",
1451 bind_dummy_buffer =
true;
1454 if (bind_dummy_buffer) {
1457 ubo_size = [ubo_buffer
length];
1460 if (ubo_buffer != nil) {
1472 "[UBO] Compute Shader '%s' has UBO '%s' bound at buffer index: %d -- but MTLBuffer "
1489 const uint32_t ssbo_location = ssbo.
location;
1492 id<MTLBuffer> ssbo_buffer = nil;
1498 ssbo_buffer = this->
pipeline_state.ssbo_bindings[ssbo_location].ssbo->get_metal_buffer();
1499 ssbo_size = this->
pipeline_state.ssbo_bindings[ssbo_location].ssbo->get_size();
1508 "[SSBO] Shader '%s' expected SSBO '%s' to be bound at SSBO location: %d "
1511 "nothing was bound.",
1517#if DEBUG_BIND_NULL_BUFFER_FOR_MISSING_SSBO == 1
1519 ssbo_size = [ssbo_buffer
length];
1523 if (ssbo_buffer != nil) {
1535 "[SSBO] Shader '%s' had SSBO '%s' bound at SSBO location: %d "
1537 "%d]]) -- but bound MTLStorageBuf was nil.",
1551 id<MTLRenderCommandEncoder> rec,
1563 int vertex_arg_buffer_bind_index = -1;
1564 int fragment_arg_buffer_bind_index = -1;
1581 if (!shader_texture_info.
used) {
1596 int location = shader_texture_info.
location;
1601 bool bind_dummy_texture =
true;
1602 if (resource_bind_table[location].used) {
1603 gpu::MTLTexture *bound_texture = resource_bind_table[location].texture_resource;
1610 if (shader_texture_info.
type == bound_texture->
type_) {
1613 id<MTLTexture> tex = bound_texture->get_metal_handle();
1641 bind_dummy_texture =
false;
1648 "(Shader '%s') Texture (%s) %p bound to slot %d is incompatible -- Wrong "
1649 "texture target type. (Expecting type %d, actual type %d) (binding "
1650 "name:'%s')(texture name:'%s')",
1652 is_resource_sampler ?
"TextureSampler" :
"TextureImage",
1655 shader_texture_info.
type,
1656 bound_texture->
type_,
1663 "Shader '%s' expected texture (%s) to be bound to location %d (texture[[%d]]) -- No "
1665 "bound. (name:'%s')",
1667 is_resource_sampler ?
"TextureSampler" :
"TextureImage",
1676 if (bind_dummy_texture) {
1680 ->get_metal_handle(),
1689 ->get_metal_handle(),
1699 "Shader %p expected texture (%s) to be bound to slot %d -- Slot exceeds the "
1700 "hardware/API limit of '%d'. (name:'%s')",
1702 is_resource_sampler ?
"TextureSampler" :
"TextureImage",
1711 if (use_argument_buffer_for_samplers) {
1719 BLI_assert(this->samplers_.mtl_sampler[
i] != nil);
1727 BLI_assert(vertex_arg_buffer_bind_index >= 0 || fragment_arg_buffer_bind_index >= 0);
1728 if (vertex_arg_buffer_bind_index >= 0 || fragment_arg_buffer_bind_index >= 0) {
1734 vertex_arg_buffer_bind_index);
1735 assert(arg_buffer_idx < 32);
1738 if (argument_encoder == nil) {
1739 argument_encoder = [pipeline_state_instance->
vert
1740 newArgumentEncoderWithBufferIndex:arg_buffer_idx];
1753 gpu::MTLBuffer **cached_smp_buffer_search = this->cached_sampler_buffers_.lookup_ptr(
1755 if (cached_smp_buffer_search !=
nullptr) {
1756 encoder_buffer = *cached_smp_buffer_search;
1760 size_t size = [argument_encoder encodedLength];
1761 size_t alignment =
max_uu([argument_encoder alignment], 256);
1762 size_t size_align_delta = (
size % alignment);
1763 size_t aligned_alloc_size = ((alignment > 1) && (size_align_delta > 0)) ?
1764 size + (alignment - (
size % alignment)) :
1772 [argument_encoder setArgumentBuffer:encoder_buffer->
get_metal_buffer() offset:0];
1774 setSamplerStates:this->samplers_.mtl_sampler
1776 encoder_buffer->
flush();
1779 this->cached_sampler_buffers_.add_new(this->samplers_, encoder_buffer);
1784 vertex_arg_buffer_bind_index);
1789 if (fragment_arg_buffer_bind_index >= 0) {
1792 fragment_arg_buffer_bind_index);
1803 id<MTLComputeCommandEncoder> rec,
1815 int compute_arg_buffer_bind_index = -1;
1831 if (!shader_texture_info.
used) {
1846 int location = shader_texture_info.
location;
1851 bool bind_dummy_texture =
true;
1852 if (resource_bind_table[location].used) {
1853 gpu::MTLTexture *bound_texture = resource_bind_table[location].texture_resource;
1860 if (shader_texture_info.
type == bound_texture->
type_) {
1863 id<MTLTexture> tex = bound_texture->get_metal_handle();
1868 tex = bound_texture->get_metal_handle_base();
1892 bind_dummy_texture =
false;
1899 "(Shader '%s') Texture (%s) %p bound to slot %d is incompatible -- Wrong "
1900 "texture target type. (Expecting type %d, actual type %d) (binding "
1901 "name:'%s')(texture name:'%s')",
1903 is_resource_sampler ?
"TextureSampler" :
"TextureImage",
1906 shader_texture_info.
type,
1907 bound_texture->
type_,
1914 "Shader '%s' expected texture (%s) to be bound to location %d (texture[[%d]]) -- No "
1916 "bound. (name:'%s')",
1918 is_resource_sampler ?
"TextureSampler" :
"TextureImage",
1927 if (bind_dummy_texture) {
1931 ->get_metal_handle(),
1942 "Shader %p expected texture (%s) to be bound to slot %d -- Slot exceeds the "
1943 "hardware/API limit of '%d'. (name:'%s')",
1945 is_resource_sampler ?
"TextureSampler" :
"TextureImage",
1954 if (use_argument_buffer_for_samplers) {
1962 BLI_assert(this->samplers_.mtl_sampler[
i] != nil);
1970 BLI_assert(compute_arg_buffer_bind_index >= 0);
1971 if (compute_arg_buffer_bind_index >= 0) {
1977 compute_arg_buffer_bind_index);
1978 assert(arg_buffer_idx < 32);
1981 if (argument_encoder == nil) {
1982 argument_encoder = [pipeline_state_instance->
compute
1983 newArgumentEncoderWithBufferIndex:arg_buffer_idx];
1996 gpu::MTLBuffer **cached_smp_buffer_search = this->cached_sampler_buffers_.lookup_ptr(
1998 if (cached_smp_buffer_search !=
nullptr) {
1999 encoder_buffer = *cached_smp_buffer_search;
2003 size_t size = [argument_encoder encodedLength];
2004 size_t alignment =
max_uu([argument_encoder alignment], 256);
2005 size_t size_align_delta = (
size % alignment);
2006 size_t aligned_alloc_size = ((alignment > 1) && (size_align_delta > 0)) ?
2007 size + (alignment - (
size % alignment)) :
2015 [argument_encoder setArgumentBuffer:encoder_buffer->
get_metal_buffer() offset:0];
2017 setSamplerStates:this->samplers_.mtl_sampler
2019 encoder_buffer->
flush();
2022 this->cached_sampler_buffers_.add_new(this->samplers_, encoder_buffer);
2027 compute_arg_buffer_bind_index);
2043 id<MTLRenderCommandEncoder> rec = this->
main_command_buffer.get_active_render_command_encoder();
2051 bool hasDepthTarget =
fb->has_depth_attachment();
2052 bool hasStencilTarget =
fb->has_stencil_attachment();
2054 if (hasDepthTarget || hasStencilTarget) {
2056 this->
pipeline_state.depth_stencil_state.has_depth_target = hasDepthTarget;
2057 this->
pipeline_state.depth_stencil_state.has_stencil_target = hasStencilTarget;
2061 id<MTLDepthStencilState> ds_state = nil;
2062 id<MTLDepthStencilState> *depth_stencil_state_lookup =
2063 this->depth_stencil_state_cache.lookup_ptr(this->
pipeline_state.depth_stencil_state);
2066 if (depth_stencil_state_lookup ==
nullptr) {
2068 MTLDepthStencilDescriptor *ds_state_desc = [[[MTLDepthStencilDescriptor alloc]
init]
2071 if (hasDepthTarget) {
2072 ds_state_desc.depthWriteEnabled =
2074 ds_state_desc.depthCompareFunction =
2077 MTLCompareFunctionAlways;
2080 if (hasStencilTarget) {
2081 ds_state_desc.backFaceStencil.readMask =
2083 ds_state_desc.backFaceStencil.writeMask =
2085 ds_state_desc.backFaceStencil.stencilFailureOperation =
2086 this->
pipeline_state.depth_stencil_state.stencil_op_back_stencil_fail;
2087 ds_state_desc.backFaceStencil.depthFailureOperation =
2088 this->
pipeline_state.depth_stencil_state.stencil_op_back_depth_fail;
2089 ds_state_desc.backFaceStencil.depthStencilPassOperation =
2090 this->
pipeline_state.depth_stencil_state.stencil_op_back_depthstencil_pass;
2091 ds_state_desc.backFaceStencil.stencilCompareFunction =
2092 (this->
pipeline_state.depth_stencil_state.stencil_test_enabled) ?
2094 MTLCompareFunctionAlways;
2096 ds_state_desc.frontFaceStencil.readMask =
2098 ds_state_desc.frontFaceStencil.writeMask =
2100 ds_state_desc.frontFaceStencil.stencilFailureOperation =
2101 this->
pipeline_state.depth_stencil_state.stencil_op_front_stencil_fail;
2102 ds_state_desc.frontFaceStencil.depthFailureOperation =
2103 this->
pipeline_state.depth_stencil_state.stencil_op_front_depth_fail;
2104 ds_state_desc.frontFaceStencil.depthStencilPassOperation =
2105 this->
pipeline_state.depth_stencil_state.stencil_op_front_depthstencil_pass;
2106 ds_state_desc.frontFaceStencil.stencilCompareFunction =
2107 (this->
pipeline_state.depth_stencil_state.stencil_test_enabled) ?
2109 MTLCompareFunctionAlways;
2113 ds_state = [this->
device newDepthStencilStateWithDescriptor:ds_state_desc];
2117 this->depth_stencil_state_cache.add_new(this->
pipeline_state.depth_stencil_state, ds_state);
2120 ds_state = *depth_stencil_state_lookup;
2126 if (ds_state != nil) {
2128 [rec setDepthStencilState:ds_state];
2134 if (hasStencilTarget) {
2135 uint32_t stencil_ref_value =
2136 (this->
pipeline_state.depth_stencil_state.stencil_test_enabled) ?
2140 [rec setStencilReferenceValue:stencil_ref_value];
2145 if (hasDepthTarget) {
2146 bool doBias =
false;
2147 switch (prim_type) {
2148 case MTLPrimitiveTypeTriangle:
2149 case MTLPrimitiveTypeTriangleStrip:
2150 doBias = this->
pipeline_state.depth_stencil_state.depth_bias_enabled_for_tris;
2152 case MTLPrimitiveTypeLine:
2153 case MTLPrimitiveTypeLineStrip:
2154 doBias = this->
pipeline_state.depth_stencil_state.depth_bias_enabled_for_lines;
2156 case MTLPrimitiveTypePoint:
2157 doBias = this->
pipeline_state.depth_stencil_state.depth_bias_enabled_for_points;
2160 [rec setDepthBias:(doBias) ? this->
pipeline_state.depth_stencil_state.depth_bias : 0
2161 slopeScale:(doBias) ? this->
pipeline_state.depth_stencil_state.depth_slope_scale : 0
2184 "Bound active shader is not valid (Missing/invalid implementation for Metal).", );
2191 if (shader_interface ==
nullptr) {
2192 MTL_LOG_WARNING(
"Bound active shader does not have a valid shader interface!", );
2204 if (compute_pso_inst ==
nullptr || compute_pso_inst->
pso == nil) {
2208 return compute_pso_inst;
2216 if (compute_pso_inst ==
nullptr) {
2220#if MTL_DEBUG_SINGLE_DISPATCH_PER_ENCODER == 1
2229 id<MTLComputeCommandEncoder> compute_encoder =
2253 [compute_encoder dispatchThreadgroups:MTLSizeMake(
max_ii(groups_x_len, 1),
2259#if MTL_DEBUG_SINGLE_DISPATCH_PER_ENCODER == 1
2267#if MTL_DEBUG_SINGLE_DISPATCH_PER_ENCODER == 1
2279 id<MTLComputeCommandEncoder> compute_encoder =
2304 if (mtl_indirect_buf == nil) {
2305 MTL_LOG_WARNING(
"Metal Indirect Compute dispatch storage buffer does not exist.");
2313 dispatchThreadgroupsWithIndirectBuffer:mtl_indirect_buf
2314 indirectBufferOffset:0
2318#if MTL_DEBUG_SINGLE_DISPATCH_PER_ENCODER == 1
2335 visibility_is_dirty_ = (buffer != visibility_buffer_) || visibility_is_dirty_;
2336 visibility_buffer_ = buffer;
2342 visibility_is_dirty_ = (visibility_buffer_ !=
nullptr) || visibility_is_dirty_;
2343 visibility_buffer_ =
nullptr;
2349 return visibility_buffer_;
2354 visibility_is_dirty_ =
false;
2359 return visibility_is_dirty_;
2376 MTL_LOG_ERROR(
"Attempting to bind texture '%s' to invalid texture unit %d",
2389 resource_bind_table[texture_unit].texture_resource = mtl_texture;
2390 resource_bind_table[texture_unit].used =
true;
2391 mtl_texture->is_bound_ =
true;
2400 MTL_LOG_ERROR(
"Attempting to bind sampler to invalid sampler unit %d", sampler_unit);
2406 this->
pipeline_state.sampler_bindings[sampler_unit] = {
true, sampler_state};
2420 if (resource_bind_table[
i].texture_resource == mtl_texture) {
2421 resource_bind_table[
i].texture_resource =
nullptr;
2422 resource_bind_table[
i].used =
false;
2427 mtl_texture->is_bound_ =
false;
2439 if (resource_bind_table[t].used && resource_bind_table[t].texture_resource) {
2440 resource_bind_table[t].used =
false;
2441 resource_bind_table[t].texture_resource =
nullptr;
2462 switch (wrap_mode) {
2464 return MTLSamplerAddressModeClampToEdge;
2466 return MTLSamplerAddressModeRepeat;
2468 return MTLSamplerAddressModeMirrorRepeat;
2470 return MTLSamplerAddressModeClampToBorderColor;
2473 return MTLSamplerAddressModeClampToEdge;
2481 const MTLSamplerAddressMode extend_t =
to_mtl_type(extend_yz);
2485 const MTLSamplerAddressMode extend_s =
to_mtl_type(extend_x);
2490 MTLSamplerDescriptor *
descriptor = [[MTLSamplerDescriptor alloc]
init];
2495 descriptor.borderColor = MTLSamplerBorderColorTransparentBlack;
2497 MTLSamplerMinMagFilterLinear :
2498 MTLSamplerMinMagFilterNearest;
2500 MTLSamplerMinMagFilterLinear :
2501 MTLSamplerMinMagFilterNearest;
2503 MTLSamplerMipFilterLinear :
2504 MTLSamplerMipFilterNotMipmapped;
2507 float aniso_filter =
max_ff(16,
U.anisotropic_filter);
2509 descriptor.compareFunction = MTLCompareFunctionAlways;
2513 sampler_state_cache_[extend_yz_i][extend_x_i][filtering_i] =
state;
2523 MTLSamplerDescriptor *
descriptor = [[MTLSamplerDescriptor alloc]
init];
2524 descriptor.minFilter = MTLSamplerMinMagFilterLinear;
2525 descriptor.magFilter = MTLSamplerMinMagFilterLinear;
2526 descriptor.compareFunction = MTLCompareFunctionLessEqual;
2531 id<MTLSamplerState> compare_state = [this->
device newSamplerStateWithDescriptor:
descriptor];
2541 MTLSamplerDescriptor *
descriptor = [[MTLSamplerDescriptor alloc]
init];
2542 descriptor.minFilter = MTLSamplerMinMagFilterLinear;
2543 descriptor.magFilter = MTLSamplerMinMagFilterLinear;
2544 descriptor.mipFilter = MTLSamplerMipFilterNearest;
2548 id<MTLSamplerState> icon_state = [this->
device newSamplerStateWithDescriptor:
descriptor];
2558 if (default_sampler_state_ == nil) {
2561 return default_sampler_state_;
2572 if (buffer_clear_pso_ != nil) {
2573 return buffer_clear_pso_;
2584 struct BufferClearParams {\
2587 kernel void compute_buffer_clear(constant BufferClearParams ¶ms [[buffer(0)]],\
2588 device uint32_t* output_data [[buffer(1)]],\
2589 uint position [[thread_position_in_grid]])\
2591 output_data[position] = params.clear_value;\
2593 NSString *compute_buffer_clear_src = [NSString stringWithUTF8String:src];
2596 MTLCompileOptions *
options = [[[MTLCompileOptions alloc]
init] autorelease];
2597 options.languageVersion = MTLLanguageVersion2_2;
2599 NSError *
error =
nullptr;
2600 id<MTLLibrary> temp_lib = [[ctx->
device newLibraryWithSource:compute_buffer_clear_src
2605 if ([[
error localizedDescription] rangeOfString:
@"Compilation succeeded"].location ==
2608 NSLog(
@"Compile Error - Metal Shader Library error %@ ",
error);
2616 id<MTLFunction> temp_compute_function = [[temp_lib newFunctionWithName:
@"compute_buffer_clear"]
2621 buffer_clear_pso_ = [ctx->
device newComputePipelineStateWithFunction:temp_compute_function
2623 if (
error || buffer_clear_pso_ == nil) {
2624 NSLog(
@"Failed to prepare compute_buffer_clear MTLComputePipelineState %@",
error);
2629 [buffer_clear_pso_ retain];
2633 return buffer_clear_pso_;
2642void present(MTLRenderPassDescriptor *blit_descriptor,
2643 id<MTLRenderPipelineState> blit_pso,
2644 id<MTLTexture> swapchain_texture,
2645 id<CAMetalDrawable> drawable)
2665 perf_max_drawables = 1;
2668 perf_max_drawables = 2;
2678 id<MTLCommandBuffer> cmdbuf = [ctx->
queue commandBuffer];
2682 id<MTLRenderCommandEncoder> enc = [cmdbuf renderCommandEncoderWithDescriptor:blit_descriptor];
2683 [enc setRenderPipelineState:blit_pso];
2684 [enc setFragmentTexture:swapchain_texture atIndex:0];
2685 [enc drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:3];
2690 [cmdbuf presentDrawable:drawable];
2699 std::chrono::time_point submission_time = std::chrono::high_resolution_clock::now();
2703 [cmdbuf addCompletedHandler:^(id<MTLCommandBuffer> ) {
2715 std::chrono::time_point completion_time = std::chrono::high_resolution_clock::now();
2716 int64_t microseconds_per_frame = std::chrono::duration_cast<std::chrono::microseconds>(
2717 completion_time - submission_time)
2721 MTL_LOG_INFO(
"Frame Latency: %f ms (Rolling avg: %f ms Drawables: %d)",
2722 ((
float)microseconds_per_frame) / 1000.0f,
2724 perf_max_drawables);
2731 [cmdbuf waitUntilCompleted];
2734 NSLog(
@"%@",
error);
#define BLI_assert_unreachable()
#define BLI_assert_msg(a, msg)
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)
Platform independent time functions.
void BLI_time_sleep_ms(int ms)
#define UNUSED_VARS_NDEBUG(...)
static constexpr int GPU_MAX_VIEWPORTS
@ 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)
@ GPU_TEXTURE_USAGE_GENERAL
@ GPU_TEXTURE_USAGE_ATOMIC
@ 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
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)
@ GPU_SAMPLER_FILTERING_MIPMAP
@ GPU_SAMPLER_FILTERING_LINEAR
#define GPU_SAMPLER_EXTEND_MODES_COUNT
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_FLAG_BUFFER_TEXTURE_ONLY
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const BMVert * v
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
id< MTLTexture > metalOverlayTexture()
GHOST_Context * getContext()
StateManager * state_manager
bool is_active_on_thread()
static MTLBackend * get()
void init(id< MTLDevice > device)
MTLSafeFreeList * get_current_safe_list()
gpu::MTLBuffer * allocate(uint64_t size, bool cpu_visible)
id< MTLBuffer > get_metal_buffer() const
void inc_active_command_buffer_count()
void dec_active_command_buffer_count()
static volatile std::atomic< int > num_active_cmd_bufs_in_system
void bind_compute_bytes(const void *bytes, uint64_t length, uint index)
void bind_compute_sampler(MTLSamplerBinding &sampler_binding, bool use_argument_buffer_for_samplers, uint slot)
void bind_compute_texture(id< MTLTexture > tex, uint slot)
void bind_compute_buffer(id< MTLBuffer > buffer, uint64_t buffer_offset, uint index)
void bind_pso(id< MTLComputePipelineState > pso)
id< MTLComputePipelineState > get_buffer_clear_pso()
bool ensure_render_pipeline_state(MTLPrimitiveType prim_type)
MTLFrameBuffer * get_current_framebuffer()
MTLFrameBuffer * get_default_framebuffer()
static std::atomic< int64_t > avg_drawable_latency_us
MTLContextComputeUtils & get_compute_utils()
const MTLComputePipelineStateInstance * ensure_compute_pipeline_state()
friend class MTLComputeState
void clear_visibility_dirty()
id< MTLSamplerState > get_sampler_from_state(MTLSamplerState state)
MTLContext(void *ghost_window, void *ghost_context)
shader::SpecializationConstants constants_state
void deactivate() override
static void latency_resolve_average(int64_t frame_latency_us)
gpu::MTLBuffer * get_visibility_buffer() const
void set_visibility_buffer(gpu::MTLBuffer *buffer)
void set_scissor_enabled(bool scissor_enabled)
void framebuffer_bind(MTLFrameBuffer *framebuffer)
id< MTLRenderCommandEncoder > ensure_begin_render_pass()
static MTLContext * get()
id< MTLBuffer > get_null_buffer()
friend class MTLRenderPassState
id< MTLBuffer > get_null_attribute_buffer()
static void global_memory_manager_release_ref()
MTLScratchBufferManager memory_manager
void pipeline_state_init()
MTLContextGlobalShaderPipelineState pipeline_state
void ensure_depth_stencil_state(MTLPrimitiveType prim_type)
static MTLBufferPool * global_memory_manager
void end_frame() override
static int global_memory_manager_refcount
MTLShader * get_active_shader()
void set_viewport(int origin_x, int origin_y, int width, int height)
static int64_t frame_latency[MTL_FRAME_AVERAGE_COUNT]
static std::mutex global_memory_manager_reflock
void sampler_bind(MTLSamplerState, uint sampler_unit)
void texture_bind(gpu::MTLTexture *mtl_texture, uint texture_unit, bool is_image)
gpu::MTLTexture * get_dummy_texture(eGPUTextureType type, eGPUSamplerFormat sampler_format)
void specialization_constants_set(const shader::SpecializationConstants *constants_state)
void begin_frame() override
void framebuffer_restore()
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)
void sampler_state_cache_init()
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
void free_dummy_resources()
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
void remove_all_attachments()
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
MTLScissorRect last_scissor_rect
uint last_used_stencil_ref_value
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)
void increment_reference()
void decrement_reference()
const char * get_name() const
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
uint32_t get_total_textures() const
const char * get_name_at_offset(uint32_t offset) const
bool uses_argument_buffer_for_samplers() 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
uint32_t get_total_uniform_blocks() const
uint32_t get_total_storage_blocks() const
uint32_t get_max_texture_index() const
MTLComputePipelineStateInstance * bake_compute_pipeline_state(MTLContext *ctx, MTLComputePipelineStateDescriptor &compute_pipeline_descriptor)
void uniform_int(int location, int comp_len, int array_size, const int *data) override
id< MTLBuffer > get_metal_buffer()
void bind(int slot) override
bool has_custom_swizzle()
MTLStorageBuf * get_storagebuf()
const int * get_texture_metadata_ptr() const
eGPUTextureUsage usage_get() const
CCL_NAMESPACE_BEGIN struct Options options
TEX_TEMPLATE DataVec texture(T, FltCoord, float=0.0f) RET
#define assert(assertion)
constexpr T clamp(T, U, U) RET
float length(VecOp< float, D >) RET
BLI_INLINE float fb(float length, float L)
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
#define MTL_FRAME_AVERAGE_COUNT
#define MTL_LOG_SSBO_ERROR
#define MTL_LOG_UBO_ERROR
#define MTL_LOG_INFO(info,...)
#define MTL_LOG_WARNING(info,...)
#define MTL_LOG_ERROR(info,...)
void present(MTLRenderPassDescriptor *blit_descriptor, id< MTLRenderPipelineState > blit_pso, id< MTLTexture > swapchain_texture, id< CAMetalDrawable > drawable)
static Context * unwrap(GPUContext *ctx)
static GPUContext * wrap(Context *ctx)
static MTLPrimitiveTopologyClass mtl_prim_type_to_topology_class(MTLPrimitiveType prim_type)
const MTLSamplerState DEFAULT_SAMPLER_STATE
@ MTL_PIPELINE_STATE_CULLMODE_FLAG
@ MTL_PIPELINE_STATE_SCISSOR_FLAG
@ MTL_PIPELINE_STATE_FRONT_FACING_FLAG
@ MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG
@ MTL_PIPELINE_STATE_VIEWPORT_FLAG
@ MTL_PIPELINE_STATE_ALL_FLAG
static MTLSamplerAddressMode to_mtl_type(GPUSamplerExtendMode wrap_mode)
static void init(bNodeTree *, bNode *node)
GPUSamplerCustomType custom_type
GPUSamplerExtendMode extend_yz
static constexpr GPUSamplerState default_sampler()
GPUSamplerFiltering filtering
GPUSamplerExtendMode extend_x
void set(MTLShader *shader, uint pso_index)
id< MTLFunction > compute
id< MTLComputePipelineState > pso
int base_uniform_buffer_index
int base_storage_buffer_index
bool reflection_data_available
int null_attribute_buffer_index
id< MTLRenderPipelineState > pso
int base_uniform_buffer_index
int base_storage_buffer_index
blender::Vector< MTLBufferArgumentData > buffer_bindings_reflection_data_frag
uint32_t shader_pso_index
blender::Vector< MTLBufferArgumentData > buffer_bindings_reflection_data_vert
int texture_buffer_ssbo_location
int buffer_metadata_uniform_loc
eGPUSamplerFormat sampler_format