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_mtl_ctx !=
nullptr) {
106 "Binding GHOST context MTL %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];
128 TextureFormat::SFLOAT_16_16_16_16,
130 default_fbo_mtltexture_);
136 this->
label = default_fbo_mtltexture_.label;
142 if (!default_fbo_gputexture_) {
148 TextureFormat::SFLOAT_16_16_16_16,
155 "-- Bound context %p for GPU context: %p is offscreen and does not have a default "
160 this->
label =
@"Offscreen Metal Context";
166 " Failed to bind GHOST context to MTLContext -- GHOST_ContextMTL is null "
167 "(GhostContext: %p, GhostContext_MTL: %p)",
174void MTLContext::set_ghost_window(GHOST_WindowHandle ghostWinHandle)
177 this->set_ghost_context((GHOST_ContextHandle)(ghostWin ? ghostWin->
getContext() :
nullptr));
203 is_inside_frame_ =
false;
204 current_frame_index_ = 0;
208 null_attribute_buffer_ = nil;
211 default_fbo_mtltexture_ = nil;
212 default_fbo_gputexture_ =
nullptr;
221 ghost_context = (ghostWin ? ghostWin->
getContext() :
nullptr);
225 this->
queue = (id<MTLCommandQueue>)this->ghost_context_->metalCommandQueue();
226 this->
device = (id<MTLDevice>)this->ghost_context_->metalDevice();
229 [this->
queue retain];
232#pragma clang diagnostic push
233#pragma clang diagnostic ignored "-Wobjc-method-access"
237 if (@available(macOS 13.3, *)) {
238 [this->
device setShouldMaximizeConcurrentCompilation:YES];
240#pragma clang diagnostic pop
243 this->ghost_context_->metalRegisterPresentCallback(&
present);
254 MTLBackend::platform_init(
this);
255 MTLBackend::capabilities_init(
this);
271 samplers_.mtl_sampler[
i] = nil;
299 if (default_fbo_gputexture_) {
301 default_fbo_gputexture_ =
nullptr;
303 if (default_fbo_mtltexture_) {
304 [default_fbo_mtltexture_ release];
305 default_fbo_mtltexture_ = nil;
321 this->pipeline_state.ubo_bindings[
i].ubo !=
nullptr)
331 this->pipeline_state.ssbo_bindings[
i].ssbo !=
nullptr)
344 if (sampler_state_cache_[extend_yz_i][extend_x_i][filtering_i] != nil) {
345 [sampler_state_cache_[extend_yz_i][extend_x_i][filtering_i] release];
346 sampler_state_cache_[extend_yz_i][extend_x_i][filtering_i] = nil;
354 if (custom_sampler_state_cache_[
i] != nil) {
355 [custom_sampler_state_cache_[
i] release];
356 custom_sampler_state_cache_[
i] = nil;
361 for (
auto *entry : cached_sampler_buffers_.values()) {
364 cached_sampler_buffers_.clear();
368 [null_buffer_ release];
370 if (null_attribute_buffer_) {
371 [null_attribute_buffer_ release];
379 [this->
queue release];
385 this->process_frame_timings();
396 is_inside_frame_ =
true;
407 is_inside_frame_ =
false;
409 this->process_frame_timings();
428 else if (ghost_context_) {
429 this->set_ghost_context((GHOST_ContextHandle)ghost_context_);
435 this->pipeline_state.ubo_bindings[
i].ubo !=
nullptr)
445 this->pipeline_state.ssbo_bindings[
i].ssbo !=
nullptr)
507 BLI_assert(
false &&
"No framebuffer is bound!");
522 this->active_fb != this->main_command_buffer.get_active_framebuffer() ||
523 this->main_command_buffer.get_active_framebuffer()->get_dirty() ||
524 this->is_visibility_dirty())
529 MTL_LOG_WARNING(
"Framebuffer validation failed, falling back to default framebuffer");
533 MTL_LOG_ERROR(
"CRITICAL: DEFAULT FRAMEBUFFER FAIL VALIDATION!!");
538 bool new_render_pass =
false;
539 id<MTLRenderCommandEncoder> new_enc =
542 if (new_render_pass) {
570 if (null_buffer_ != nil) {
579 static const int null_buffer_size = 20480;
580 null_buffer_ = [this->
device newBufferWithLength:null_buffer_size
581 options:MTLResourceStorageModeManaged];
582 [null_buffer_ retain];
583 uint32_t *null_data = (uint32_t *)calloc(1, null_buffer_size);
584 memcpy([null_buffer_ contents], null_data, null_buffer_size);
585 [null_buffer_ didModifyRange:NSMakeRange(0, null_buffer_size)];
594 if (null_attribute_buffer_ != nil) {
595 return null_attribute_buffer_;
600 static const int null_buffer_size = 256;
601 null_attribute_buffer_ = [this->
device newBufferWithLength:null_buffer_size
602 options:MTLResourceStorageModeManaged];
604 [null_attribute_buffer_ retain];
605 float data[4] = {0.0f, 0.0f, 0.0f, 1.0f};
606 memcpy([null_attribute_buffer_ contents],
data,
sizeof(
float) * 4);
607 [null_attribute_buffer_ didModifyRange:NSMakeRange(0, null_buffer_size)];
609 return null_attribute_buffer_;
616 gpu::MTLTexture *dummy_tex = dummy_textures_[sampler_format][type - 1];
617 if (dummy_tex !=
nullptr) {
622 switch (sampler_format) {
624 format = TextureFormat::UNORM_8_8_8_8;
627 format = TextureFormat::SINT_8_8_8_8;
630 format = TextureFormat::UINT_8_8_8_8;
633 format = TextureFormat::SFLOAT_32_DEPTH_UINT_8;
665 if (!dummy_verts_[sampler_format]) {
668 VertAttrType attr_type = VertAttrType::SFLOAT_32_32_32_32;
670 switch (sampler_format) {
673 attr_type = VertAttrType::SFLOAT_32_32_32_32;
676 attr_type = VertAttrType::SINT_32_32_32_32;
679 attr_type = VertAttrType::UINT_32_32_32_32;
687 dummy_vertformat_[sampler_format],
698 dummy_textures_[sampler_format][type - 1] = metal_tex;
706 if (dummy_textures_[
format][tex]) {
709 dummy_textures_[
format][tex] =
nullptr;
712 if (dummy_verts_[
format]) {
743 this->
pipeline_state.texture_bindings[t].texture_resource =
nullptr;
747 this->
pipeline_state.image_bindings[t].texture_resource =
nullptr;
764 this->
pipeline_state.color_write_mask = MTLColorWriteMaskRed | MTLColorWriteMaskGreen |
765 MTLColorWriteMaskBlue | MTLColorWriteMaskAlpha;
769 this->
pipeline_state.dest_alpha_blend_factor = MTLBlendFactorZero;
796 this->
pipeline_state.depth_stencil_state.depth_write_enable =
false;
797 this->
pipeline_state.depth_stencil_state.depth_test_enabled =
false;
800 this->
pipeline_state.depth_stencil_state.depth_function = MTLCompareFunctionAlways;
803 this->
pipeline_state.depth_stencil_state.depth_bias_enabled_for_points =
false;
804 this->
pipeline_state.depth_stencil_state.depth_bias_enabled_for_lines =
false;
805 this->
pipeline_state.depth_stencil_state.depth_bias_enabled_for_tris =
false;
808 this->
pipeline_state.depth_stencil_state.stencil_test_enabled =
false;
809 this->
pipeline_state.depth_stencil_state.stencil_read_mask = 0xFF;
810 this->
pipeline_state.depth_stencil_state.stencil_write_mask = 0xFF;
812 this->
pipeline_state.depth_stencil_state.stencil_func = MTLCompareFunctionAlways;
813 this->
pipeline_state.depth_stencil_state.stencil_op_front_stencil_fail = MTLStencilOperationKeep;
814 this->
pipeline_state.depth_stencil_state.stencil_op_front_depth_fail = MTLStencilOperationKeep;
815 this->
pipeline_state.depth_stencil_state.stencil_op_front_depthstencil_pass =
816 MTLStencilOperationKeep;
817 this->
pipeline_state.depth_stencil_state.stencil_op_back_stencil_fail = MTLStencilOperationKeep;
818 this->
pipeline_state.depth_stencil_state.stencil_op_back_depth_fail = MTLStencilOperationKeep;
819 this->
pipeline_state.depth_stencil_state.stencil_op_back_depthstencil_pass =
820 MTLStencilOperationKeep;
830 bool changed = (this->
pipeline_state.viewport_offset_x[0] != origin_x) ||
852 const int (&viewport_info)[4] = viewports[
v];
859 changed = changed || (this->
pipeline_state.viewport_offset_x[
v] != viewport_info[0]) ||
888 this->
pipeline_state.scissor_enabled = (scissor_width > 0 && scissor_height > 0);
899 scissor_enabled = scissor_enabled && (this->
pipeline_state.scissor_width > 0 &&
902 bool changed = (this->
pipeline_state.scissor_enabled != scissor_enabled);
936 "Bound active shader is not valid (Missing/invalid implementation for Metal).", );
952 if (shader_interface ==
nullptr) {
953 MTL_LOG_WARNING(
"Bound active shader does not have a valid shader interface!", );
963 if (!pipeline_state_instance) {
964 MTL_LOG_ERROR(
"Failed to bake Metal pipeline state for shader: %s",
970 if (pipeline_state_instance->
pso) {
974 id<MTLRenderCommandEncoder> rec =
978 MTL_LOG_ERROR(
"ensure_render_pipeline_state called while render pass is not active.");
985 [rec setRenderPipelineState:pipeline_state_instance->
pso];
1019 MTLViewport &viewport = viewports[
v];
1024 viewport.znear = this->
pipeline_state.depth_stencil_state.depth_range_near;
1025 viewport.zfar = this->
pipeline_state.depth_stencil_state.depth_range_far;
1031 MTLViewport viewport;
1032 viewport.originX = (double)this->
pipeline_state.viewport_offset_x[0];
1033 viewport.originY = (double)this->
pipeline_state.viewport_offset_y[0];
1035 viewport.height = (double)this->
pipeline_state.viewport_height[0];
1036 viewport.znear = this->
pipeline_state.depth_stencil_state.depth_range_near;
1037 viewport.zfar = this->
pipeline_state.depth_stencil_state.depth_range_far;
1038 [rec setViewport:viewport];
1047 MTLScissorRect scissor;
1073 bool is_attachmentless = (default_w == 0) && (default_h == 0);
1076 scissor.width = (is_attachmentless) ? render_fb->
get_width() : default_w;
1077 scissor.height = (is_attachmentless) ? render_fb->
get_height() : default_h;
1083 [rec setScissorRect:scissor];
1095 MTLWindingClockwise :
1096 MTLWindingCounterClockwise;
1097 [rec setFrontFacingWinding:winding];
1105 MTLCullMode mode = MTLCullModeNone;
1109 mode = MTLCullModeNone;
1112 mode = MTLCullModeFront;
1115 mode = MTLCullModeBack;
1122 [rec setCullMode:mode];
1136 id<MTLRenderCommandEncoder> ,
1151 if (push_constant_block.
size > 0) {
1155 uint32_t block_size = push_constant_block.
size;
1162 if (this->
pipeline_state.active_shader->get_push_constant_is_dirty() ||
1170 this->
pipeline_state.active_shader->get_push_constant_data(), block_size, buffer_index);
1172 this->
pipeline_state.active_shader->get_push_constant_data(), block_size, buffer_index);
1175 this->
pipeline_state.active_shader->push_constant_bindstate_mark_dirty(
false);
1190 const uint32_t ubo_location = ubo.
location;
1193 id<MTLBuffer> ubo_buffer = nil;
1194 size_t ubo_size = 0;
1196 bool bind_dummy_buffer =
false;
1200 ubo_buffer = this->
pipeline_state.ubo_bindings[ubo_location].ubo->get_metal_buffer();
1201 ubo_size = this->
pipeline_state.ubo_bindings[ubo_location].ubo->get_size();
1205 if (ubo_buffer == nil) {
1206 bind_dummy_buffer =
true;
1218 uint32_t expected_size =
1224 if (expected_size == 0) {
1234 "Shader interface expects UBO, but shader reflection data reports that it "
1239 if (ubo_size < expected_size) {
1241 "[UBO] UBO (UBO Name: %s) bound at location: %d (buffer[[%d]]) with size "
1242 "%lu (Expected size "
1243 "%d) (Shader Name: %s) is too small -- binding NULL buffer. This is likely an "
1244 "over-binding, which is not used, but we need this to avoid validation "
1252 bind_dummy_buffer =
true;
1259 "[UBO] Shader '%s' expected UBO '%s' to be bound at buffer slot: %d "
1260 "(buffer[[%d]])-- but "
1261 "nothing was bound -- binding dummy buffer",
1266 bind_dummy_buffer =
true;
1269 if (bind_dummy_buffer) {
1272 ubo_size = [ubo_buffer
length];
1275 if (ubo_buffer != nil) {
1294 "[UBO] Shader '%s' has UBO '%s' bound at buffer index: %d -- but MTLBuffer "
1312 const uint32_t ssbo_location = ssbo.
location;
1315 id<MTLBuffer> ssbo_buffer = nil;
1316 size_t ssbo_size = 0;
1322 ssbo_buffer = this->
pipeline_state.ssbo_bindings[ssbo_location].ssbo->get_metal_buffer();
1323 ssbo_size = this->
pipeline_state.ssbo_bindings[ssbo_location].ssbo->get_size();
1331 "[SSBO] Shader '%s' expected SSBO '%s' to be bound at buffer location: %d "
1332 "(buffer[[%d]]) -- "
1334 "nothing was bound.",
1340#if DEBUG_BIND_NULL_BUFFER_FOR_MISSING_SSBO == 1
1342 ssbo_size = [ssbo_buffer
length];
1346 if (ssbo_buffer != nil) {
1364 "[SSBO] Shader '%s' had SSBO '%s' bound at SSBO location: %d "
1366 "%d]]) -- but bound MTLStorageBuf was nil.",
1381 id<MTLComputeCommandEncoder> ,
1390 if (push_constant_block.
size > 0) {
1394 uint32_t block_size = push_constant_block.
size;
1405 this->
pipeline_state.active_shader->get_push_constant_data(), block_size, buffer_index);
1408 this->
pipeline_state.active_shader->push_constant_bindstate_mark_dirty(
false);
1420 const uint32_t ubo_location = ubo.
location;
1423 id<MTLBuffer> ubo_buffer = nil;
1424 size_t ubo_size = 0;
1426 bool bind_dummy_buffer =
false;
1430 ubo_buffer = this->
pipeline_state.ubo_bindings[ubo_location].ubo->get_metal_buffer();
1431 ubo_size = this->
pipeline_state.ubo_bindings[ubo_location].ubo->get_size();
1436 if (ubo_buffer == nil) {
1437 bind_dummy_buffer =
true;
1446 "[UBO] Shader '%s' expected UBO '%s' to be bound at buffer location: %d "
1447 "(buffer[[%d]]) -- but "
1448 "nothing was bound -- binding dummy buffer",
1453 bind_dummy_buffer =
true;
1456 if (bind_dummy_buffer) {
1459 ubo_size = [ubo_buffer
length];
1462 if (ubo_buffer != nil) {
1474 "[UBO] Compute Shader '%s' has UBO '%s' bound at buffer index: %d -- but MTLBuffer "
1491 const uint32_t ssbo_location = ssbo.
location;
1494 id<MTLBuffer> ssbo_buffer = nil;
1500 ssbo_buffer = this->
pipeline_state.ssbo_bindings[ssbo_location].ssbo->get_metal_buffer();
1501 ssbo_size = this->
pipeline_state.ssbo_bindings[ssbo_location].ssbo->get_size();
1510 "[SSBO] Shader '%s' expected SSBO '%s' to be bound at SSBO location: %d "
1513 "nothing was bound.",
1519#if DEBUG_BIND_NULL_BUFFER_FOR_MISSING_SSBO == 1
1521 ssbo_size = [ssbo_buffer
length];
1525 if (ssbo_buffer != nil) {
1537 "[SSBO] Shader '%s' had SSBO '%s' bound at SSBO location: %d "
1539 "%d]]) -- but bound MTLStorageBuf was nil.",
1553 id<MTLRenderCommandEncoder> rec,
1565 int vertex_arg_buffer_bind_index = -1;
1566 int fragment_arg_buffer_bind_index = -1;
1583 if (!shader_texture_info.
used) {
1598 int location = shader_texture_info.
location;
1603 bool bind_dummy_texture =
true;
1604 if (resource_bind_table[location].used) {
1605 gpu::MTLTexture *bound_texture = resource_bind_table[location].texture_resource;
1612 if (shader_texture_info.
type == bound_texture->
type_) {
1615 id<MTLTexture> tex = bound_texture->get_metal_handle();
1643 bind_dummy_texture =
false;
1650 "(Shader '%s') Texture (%s) %p bound to slot %d is incompatible -- Wrong "
1651 "texture target type. (Expecting type %d, actual type %d) (binding "
1652 "name:'%s')(texture name:'%s')",
1654 is_resource_sampler ?
"TextureSampler" :
"TextureImage",
1657 shader_texture_info.
type,
1658 bound_texture->
type_,
1665 "Shader '%s' expected texture (%s) to be bound to location %d (texture[[%d]]) -- No "
1667 "bound. (name:'%s')",
1669 is_resource_sampler ?
"TextureSampler" :
"TextureImage",
1678 if (bind_dummy_texture) {
1682 ->get_metal_handle(),
1691 ->get_metal_handle(),
1701 "Shader %p expected texture (%s) to be bound to slot %d -- Slot exceeds the "
1702 "hardware/API limit of '%d'. (name:'%s')",
1704 is_resource_sampler ?
"TextureSampler" :
"TextureImage",
1713 if (use_argument_buffer_for_samplers) {
1721 BLI_assert(this->samplers_.mtl_sampler[
i] != nil);
1729 BLI_assert(vertex_arg_buffer_bind_index >= 0 || fragment_arg_buffer_bind_index >= 0);
1730 if (vertex_arg_buffer_bind_index >= 0 || fragment_arg_buffer_bind_index >= 0) {
1736 vertex_arg_buffer_bind_index);
1737 assert(arg_buffer_idx < 32);
1740 if (argument_encoder == nil) {
1741 argument_encoder = [pipeline_state_instance->
vert
1742 newArgumentEncoderWithBufferIndex:arg_buffer_idx];
1755 gpu::MTLBuffer **cached_smp_buffer_search = this->cached_sampler_buffers_.lookup_ptr(
1757 if (cached_smp_buffer_search !=
nullptr) {
1758 encoder_buffer = *cached_smp_buffer_search;
1762 size_t size = [argument_encoder encodedLength];
1763 size_t alignment =
max_uu([argument_encoder alignment], 256);
1764 size_t size_align_delta = (
size % alignment);
1765 size_t aligned_alloc_size = ((alignment > 1) && (size_align_delta > 0)) ?
1766 size + (alignment - (
size % alignment)) :
1774 [argument_encoder setArgumentBuffer:encoder_buffer->
get_metal_buffer() offset:0];
1776 setSamplerStates:this->samplers_.mtl_sampler
1778 encoder_buffer->
flush();
1781 this->cached_sampler_buffers_.add_new(this->samplers_, encoder_buffer);
1786 vertex_arg_buffer_bind_index);
1791 if (fragment_arg_buffer_bind_index >= 0) {
1794 fragment_arg_buffer_bind_index);
1805 id<MTLComputeCommandEncoder> rec,
1817 int compute_arg_buffer_bind_index = -1;
1833 if (!shader_texture_info.
used) {
1848 int location = shader_texture_info.
location;
1853 bool bind_dummy_texture =
true;
1854 if (resource_bind_table[location].used) {
1855 gpu::MTLTexture *bound_texture = resource_bind_table[location].texture_resource;
1862 if (shader_texture_info.
type == bound_texture->
type_) {
1865 id<MTLTexture> tex = bound_texture->get_metal_handle();
1870 tex = bound_texture->get_metal_handle_base();
1894 bind_dummy_texture =
false;
1901 "(Shader '%s') Texture (%s) %p bound to slot %d is incompatible -- Wrong "
1902 "texture target type. (Expecting type %d, actual type %d) (binding "
1903 "name:'%s')(texture name:'%s')",
1905 is_resource_sampler ?
"TextureSampler" :
"TextureImage",
1908 shader_texture_info.
type,
1909 bound_texture->
type_,
1916 "Shader '%s' expected texture (%s) to be bound to location %d (texture[[%d]]) -- No "
1918 "bound. (name:'%s')",
1920 is_resource_sampler ?
"TextureSampler" :
"TextureImage",
1929 if (bind_dummy_texture) {
1933 ->get_metal_handle(),
1944 "Shader %p expected texture (%s) to be bound to slot %d -- Slot exceeds the "
1945 "hardware/API limit of '%d'. (name:'%s')",
1947 is_resource_sampler ?
"TextureSampler" :
"TextureImage",
1956 if (use_argument_buffer_for_samplers) {
1964 BLI_assert(this->samplers_.mtl_sampler[
i] != nil);
1972 BLI_assert(compute_arg_buffer_bind_index >= 0);
1973 if (compute_arg_buffer_bind_index >= 0) {
1979 compute_arg_buffer_bind_index);
1980 assert(arg_buffer_idx < 32);
1983 if (argument_encoder == nil) {
1984 argument_encoder = [pipeline_state_instance->
compute
1985 newArgumentEncoderWithBufferIndex:arg_buffer_idx];
1998 gpu::MTLBuffer **cached_smp_buffer_search = this->cached_sampler_buffers_.lookup_ptr(
2000 if (cached_smp_buffer_search !=
nullptr) {
2001 encoder_buffer = *cached_smp_buffer_search;
2005 size_t size = [argument_encoder encodedLength];
2006 size_t alignment =
max_uu([argument_encoder alignment], 256);
2007 size_t size_align_delta = (
size % alignment);
2008 size_t aligned_alloc_size = ((alignment > 1) && (size_align_delta > 0)) ?
2009 size + (alignment - (
size % alignment)) :
2017 [argument_encoder setArgumentBuffer:encoder_buffer->
get_metal_buffer() offset:0];
2019 setSamplerStates:this->samplers_.mtl_sampler
2021 encoder_buffer->
flush();
2024 this->cached_sampler_buffers_.add_new(this->samplers_, encoder_buffer);
2029 compute_arg_buffer_bind_index);
2045 id<MTLRenderCommandEncoder> rec = this->
main_command_buffer.get_active_render_command_encoder();
2053 bool hasDepthTarget =
fb->has_depth_attachment();
2054 bool hasStencilTarget =
fb->has_stencil_attachment();
2056 if (hasDepthTarget || hasStencilTarget) {
2058 this->
pipeline_state.depth_stencil_state.has_depth_target = hasDepthTarget;
2059 this->
pipeline_state.depth_stencil_state.has_stencil_target = hasStencilTarget;
2063 id<MTLDepthStencilState> ds_state = nil;
2064 id<MTLDepthStencilState> *depth_stencil_state_lookup =
2065 this->depth_stencil_state_cache.lookup_ptr(this->
pipeline_state.depth_stencil_state);
2068 if (depth_stencil_state_lookup ==
nullptr) {
2070 MTLDepthStencilDescriptor *ds_state_desc = [[[MTLDepthStencilDescriptor alloc]
init]
2073 if (hasDepthTarget) {
2074 ds_state_desc.depthWriteEnabled =
2076 ds_state_desc.depthCompareFunction =
2079 MTLCompareFunctionAlways;
2082 if (hasStencilTarget) {
2083 ds_state_desc.backFaceStencil.readMask =
2085 ds_state_desc.backFaceStencil.writeMask =
2087 ds_state_desc.backFaceStencil.stencilFailureOperation =
2088 this->
pipeline_state.depth_stencil_state.stencil_op_back_stencil_fail;
2089 ds_state_desc.backFaceStencil.depthFailureOperation =
2090 this->
pipeline_state.depth_stencil_state.stencil_op_back_depth_fail;
2091 ds_state_desc.backFaceStencil.depthStencilPassOperation =
2092 this->
pipeline_state.depth_stencil_state.stencil_op_back_depthstencil_pass;
2093 ds_state_desc.backFaceStencil.stencilCompareFunction =
2094 (this->
pipeline_state.depth_stencil_state.stencil_test_enabled) ?
2096 MTLCompareFunctionAlways;
2098 ds_state_desc.frontFaceStencil.readMask =
2100 ds_state_desc.frontFaceStencil.writeMask =
2102 ds_state_desc.frontFaceStencil.stencilFailureOperation =
2103 this->
pipeline_state.depth_stencil_state.stencil_op_front_stencil_fail;
2104 ds_state_desc.frontFaceStencil.depthFailureOperation =
2105 this->
pipeline_state.depth_stencil_state.stencil_op_front_depth_fail;
2106 ds_state_desc.frontFaceStencil.depthStencilPassOperation =
2107 this->
pipeline_state.depth_stencil_state.stencil_op_front_depthstencil_pass;
2108 ds_state_desc.frontFaceStencil.stencilCompareFunction =
2109 (this->
pipeline_state.depth_stencil_state.stencil_test_enabled) ?
2111 MTLCompareFunctionAlways;
2115 ds_state = [this->
device newDepthStencilStateWithDescriptor:ds_state_desc];
2119 this->depth_stencil_state_cache.add_new(this->
pipeline_state.depth_stencil_state, ds_state);
2122 ds_state = *depth_stencil_state_lookup;
2128 if (ds_state != nil) {
2130 [rec setDepthStencilState:ds_state];
2136 if (hasStencilTarget) {
2137 uint32_t stencil_ref_value =
2138 (this->
pipeline_state.depth_stencil_state.stencil_test_enabled) ?
2142 [rec setStencilReferenceValue:stencil_ref_value];
2147 if (hasDepthTarget) {
2148 bool doBias =
false;
2149 switch (prim_type) {
2150 case MTLPrimitiveTypeTriangle:
2151 case MTLPrimitiveTypeTriangleStrip:
2152 doBias = this->
pipeline_state.depth_stencil_state.depth_bias_enabled_for_tris;
2154 case MTLPrimitiveTypeLine:
2155 case MTLPrimitiveTypeLineStrip:
2156 doBias = this->
pipeline_state.depth_stencil_state.depth_bias_enabled_for_lines;
2158 case MTLPrimitiveTypePoint:
2159 doBias = this->
pipeline_state.depth_stencil_state.depth_bias_enabled_for_points;
2162 [rec setDepthBias:(doBias) ? this->
pipeline_state.depth_stencil_state.depth_bias : 0
2163 slopeScale:(doBias) ? this->
pipeline_state.depth_stencil_state.depth_slope_scale : 0
2186 "Bound active shader is not valid (Missing/invalid implementation for Metal).", );
2193 if (shader_interface ==
nullptr) {
2194 MTL_LOG_WARNING(
"Bound active shader does not have a valid shader interface!", );
2206 if (compute_pso_inst ==
nullptr || compute_pso_inst->
pso == nil) {
2210 return compute_pso_inst;
2218 if (compute_pso_inst ==
nullptr) {
2222#if MTL_DEBUG_SINGLE_DISPATCH_PER_ENCODER == 1
2231 id<MTLComputeCommandEncoder> compute_encoder =
2255 [compute_encoder dispatchThreadgroups:MTLSizeMake(
max_ii(groups_x_len, 1),
2261#if MTL_DEBUG_SINGLE_DISPATCH_PER_ENCODER == 1
2269#if MTL_DEBUG_SINGLE_DISPATCH_PER_ENCODER == 1
2281 id<MTLComputeCommandEncoder> compute_encoder =
2306 if (mtl_indirect_buf == nil) {
2307 MTL_LOG_WARNING(
"Metal Indirect Compute dispatch storage buffer does not exist.");
2315 dispatchThreadgroupsWithIndirectBuffer:mtl_indirect_buf
2316 indirectBufferOffset:0
2320#if MTL_DEBUG_SINGLE_DISPATCH_PER_ENCODER == 1
2337 visibility_is_dirty_ = (buffer != visibility_buffer_) || visibility_is_dirty_;
2338 visibility_buffer_ = buffer;
2344 visibility_is_dirty_ = (visibility_buffer_ !=
nullptr) || visibility_is_dirty_;
2345 visibility_buffer_ =
nullptr;
2351 return visibility_buffer_;
2356 visibility_is_dirty_ =
false;
2361 return visibility_is_dirty_;
2378 MTL_LOG_ERROR(
"Attempting to bind texture '%s' to invalid texture unit %d",
2391 resource_bind_table[texture_unit].texture_resource = mtl_texture;
2392 resource_bind_table[texture_unit].used =
true;
2393 mtl_texture->is_bound_ =
true;
2402 MTL_LOG_ERROR(
"Attempting to bind sampler to invalid sampler unit %d", sampler_unit);
2408 this->
pipeline_state.sampler_bindings[sampler_unit] = {
true, sampler_state};
2424 if (resource_bind_table[
i].texture_resource == mtl_texture) {
2425 resource_bind_table[
i].texture_resource =
nullptr;
2426 resource_bind_table[
i].used =
false;
2434 mtl_texture->is_bound_ =
false;
2446 if (resource_bind_table[t].used && resource_bind_table[t].texture_resource) {
2447 resource_bind_table[t].used =
false;
2448 resource_bind_table[t].texture_resource =
nullptr;
2469 switch (wrap_mode) {
2471 return MTLSamplerAddressModeClampToEdge;
2473 return MTLSamplerAddressModeRepeat;
2475 return MTLSamplerAddressModeMirrorRepeat;
2477 return MTLSamplerAddressModeClampToBorderColor;
2480 return MTLSamplerAddressModeClampToEdge;
2488 const MTLSamplerAddressMode extend_t =
to_mtl_type(extend_yz);
2492 const MTLSamplerAddressMode extend_s =
to_mtl_type(extend_x);
2497 MTLSamplerDescriptor *
descriptor = [[MTLSamplerDescriptor alloc]
init];
2502 descriptor.borderColor = MTLSamplerBorderColorTransparentBlack;
2504 MTLSamplerMinMagFilterLinear :
2505 MTLSamplerMinMagFilterNearest;
2507 MTLSamplerMinMagFilterLinear :
2508 MTLSamplerMinMagFilterNearest;
2510 MTLSamplerMipFilterLinear :
2511 MTLSamplerMipFilterNotMipmapped;
2514 float aniso_filter =
max_ff(16,
U.anisotropic_filter);
2516 descriptor.compareFunction = MTLCompareFunctionAlways;
2520 sampler_state_cache_[extend_yz_i][extend_x_i][filtering_i] =
state;
2530 MTLSamplerDescriptor *
descriptor = [[MTLSamplerDescriptor alloc]
init];
2531 descriptor.minFilter = MTLSamplerMinMagFilterLinear;
2532 descriptor.magFilter = MTLSamplerMinMagFilterLinear;
2533 descriptor.compareFunction = MTLCompareFunctionLessEqual;
2538 id<MTLSamplerState> compare_state = [this->
device newSamplerStateWithDescriptor:
descriptor];
2548 MTLSamplerDescriptor *
descriptor = [[MTLSamplerDescriptor alloc]
init];
2549 descriptor.minFilter = MTLSamplerMinMagFilterLinear;
2550 descriptor.magFilter = MTLSamplerMinMagFilterLinear;
2551 descriptor.mipFilter = MTLSamplerMipFilterNearest;
2555 id<MTLSamplerState> icon_state = [this->
device newSamplerStateWithDescriptor:
descriptor];
2565 if (default_sampler_state_ == nil) {
2568 return default_sampler_state_;
2579 if (buffer_clear_pso_ != nil) {
2580 return buffer_clear_pso_;
2591 struct BufferClearParams {\
2594 kernel void compute_buffer_clear(constant BufferClearParams ¶ms [[buffer(0)]],\
2595 device uint32_t* output_data [[buffer(1)]],\
2596 uint position [[thread_position_in_grid]])\
2598 output_data[position] = params.clear_value;\
2600 NSString *compute_buffer_clear_src = [NSString stringWithUTF8String:src];
2603 MTLCompileOptions *
options = [[[MTLCompileOptions alloc]
init] autorelease];
2604 options.languageVersion = MTLLanguageVersion2_2;
2606 NSError *
error =
nullptr;
2607 id<MTLLibrary> temp_lib = [[ctx->
device newLibraryWithSource:compute_buffer_clear_src
2612 if ([[
error localizedDescription] rangeOfString:
@"Compilation succeeded"].location ==
2615 NSLog(
@"Compile Error - Metal Shader Library error %@ ",
error);
2623 id<MTLFunction> temp_compute_function = [[temp_lib newFunctionWithName:
@"compute_buffer_clear"]
2628 buffer_clear_pso_ = [ctx->
device newComputePipelineStateWithFunction:temp_compute_function
2630 if (
error || buffer_clear_pso_ == nil) {
2631 NSLog(
@"Failed to prepare compute_buffer_clear MTLComputePipelineState %@",
error);
2636 [buffer_clear_pso_ retain];
2640 return buffer_clear_pso_;
2649void present(MTLRenderPassDescriptor *blit_descriptor,
2650 id<MTLRenderPipelineState> blit_pso,
2651 id<MTLTexture> swapchain_texture,
2652 id<CAMetalDrawable> drawable)
2672 perf_max_drawables = 1;
2675 perf_max_drawables = 2;
2685 id<MTLCommandBuffer> cmdbuf = [ctx->
queue commandBuffer];
2689 id<MTLRenderCommandEncoder> enc = [cmdbuf renderCommandEncoderWithDescriptor:blit_descriptor];
2690 [enc setRenderPipelineState:blit_pso];
2691 [enc setFragmentTexture:swapchain_texture atIndex:0];
2692 [enc drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:3];
2697 [cmdbuf presentDrawable:drawable];
2706 std::chrono::time_point submission_time = std::chrono::high_resolution_clock::now();
2710 [cmdbuf addCompletedHandler:^(id<MTLCommandBuffer> ) {
2722 std::chrono::time_point completion_time = std::chrono::high_resolution_clock::now();
2723 int64_t microseconds_per_frame = std::chrono::duration_cast<std::chrono::microseconds>(
2724 completion_time - submission_time)
2728 MTL_LOG_DEBUG(
"Frame Latency: %f ms (Rolling avg: %f ms Drawables: %d)",
2729 ((
float)microseconds_per_frame) / 1000.0f,
2731 perf_max_drawables);
2738 [cmdbuf waitUntilCompleted];
2741 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
blender::gpu::Texture * GPU_texture_create_2d_array(const char *name, int width, int height, int layer_len, int mip_len, blender::gpu::TextureFormat format, eGPUTextureUsage usage, const float *data)
blender::gpu::Texture * GPU_texture_create_cube(const char *name, int width, int mip_len, blender::gpu::TextureFormat format, eGPUTextureUsage usage, const float *data)
@ GPU_SAMPLER_STATE_TYPE_CUSTOM
@ GPU_SAMPLER_STATE_TYPE_INTERNAL
blender::gpu::Texture * GPU_texture_create_1d_array(const char *name, int width, int layer_len, int mip_len, blender::gpu::TextureFormat format, eGPUTextureUsage usage, const float *data)
static const int GPU_SAMPLER_FILTERING_TYPES_COUNT
blender::gpu::Texture * GPU_texture_create_cube_array(const char *name, int width, int layer_len, int mip_len, blender::gpu::TextureFormat format, eGPUTextureUsage usage, const float *data)
@ 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
#define GPU_SAMPLER_CUSTOM_TYPES_COUNT
blender::gpu::Texture * GPU_texture_create_2d(const char *name, int width, int height, int mip_len, blender::gpu::TextureFormat format, eGPUTextureUsage usage, const float *data)
void GPU_texture_unbind_all()
blender::gpu::Texture * GPU_texture_create_from_vertbuf(const char *name, blender::gpu::VertBuf *vertex_buf)
@ GPU_SAMPLER_FILTERING_MIPMAP
@ GPU_SAMPLER_FILTERING_LINEAR
void GPU_texture_free(blender::gpu::Texture *texture)
#define GPU_SAMPLER_EXTEND_MODES_COUNT
blender::gpu::Texture * GPU_texture_create_3d(const char *name, int width, int height, int depth, int mip_len, blender::gpu::TextureFormat format, eGPUTextureUsage usage, const void *data)
blender::gpu::Texture * GPU_texture_create_1d(const char *name, int width, int mip_len, blender::gpu::TextureFormat format, eGPUTextureUsage usage, const float *data)
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 texture_unbind(gpu::MTLTexture *mtl_texture, bool is_image, StateManager *state_manager)
void set_viewport(int origin_x, int origin_y, int width, int height)
static int64_t frame_latency[MTL_FRAME_AVERAGE_COUNT]
static std::mutex global_memory_manager_reflock
void sampler_bind(MTLSamplerState, uint sampler_unit)
void texture_bind(gpu::MTLTexture *mtl_texture, uint texture_unit, bool is_image)
void specialization_constants_set(const shader::SpecializationConstants *constants_state)
void begin_frame() override
void 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 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
gpu::MTLTexture * get_dummy_texture(GPUTextureType type, GPUSamplerFormat sampler_format)
void compute_dispatch(int groups_x_len, int groups_y_len, int groups_z_len)
MTLContextTextureUtils & get_texture_utils()
static MTLBufferPool * get_global_memory_manager()
static std::atomic< int > max_drawables_in_flight
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
#define assert(assertion)
constexpr T clamp(T, U, U) RET
float length(VecOp< float, D >) RET
TEX_TEMPLATE DataVec texture(T, FltCoord, float=0.0f) RET
BLI_INLINE float fb(float length, float L)
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_WARNING(info,...)
#define MTL_LOG_DEBUG(info,...)
#define MTL_LOG_ERROR(info,...)
void present(MTLRenderPassDescriptor *blit_descriptor, id< MTLRenderPipelineState > blit_pso, id< MTLTexture > swapchain_texture, id< CAMetalDrawable > drawable)
static MTLPrimitiveTopologyClass mtl_prim_type_to_topology_class(MTLPrimitiveType prim_type)
const MTLSamplerState DEFAULT_SAMPLER_STATE
@ MTL_PIPELINE_STATE_CULLMODE_FLAG
@ MTL_PIPELINE_STATE_SCISSOR_FLAG
@ MTL_PIPELINE_STATE_FRONT_FACING_FLAG
@ MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG
@ MTL_PIPELINE_STATE_VIEWPORT_FLAG
@ MTL_PIPELINE_STATE_ALL_FLAG
static MTLSamplerAddressMode to_mtl_type(GPUSamplerExtendMode wrap_mode)
static void init(bNodeTree *, bNode *node)
GPUSamplerCustomType custom_type
GPUSamplerExtendMode extend_yz
static constexpr GPUSamplerState default_sampler()
GPUSamplerFiltering filtering
GPUSamplerExtendMode extend_x
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
GPUSamplerFormat sampler_format