From eb9e254b8c771a8722e25c360dc0009d19cf8e84 Mon Sep 17 00:00:00 2001
From: Giovanni Mascellani <gmascellani@codeweavers.com>
Date: Fri, 7 Mar 2025 23:53:12 +0100
Subject: [PATCH 1/6] tests/shader_runner_metal: Handle multisampled 2D texture
 arrays properly.

They're not supported by the shader runner anyway, but there's no
reason to make the code subtly wrong.
---
 tests/shader_runner_metal.m | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/tests/shader_runner_metal.m b/tests/shader_runner_metal.m
index 8b0f646d1..dd12dc6ef 100644
--- a/tests/shader_runner_metal.m
+++ b/tests/shader_runner_metal.m
@@ -199,9 +199,10 @@ static void init_resource_texture(struct metal_runner *runner,
 
     desc = [[MTLTextureDescriptor alloc] init];
     if (params->desc.sample_count > 1)
-        desc.textureType = MTLTextureType2DMultisample;
-    else if (params->desc.depth > 1)
-        desc.textureType = MTLTextureType2DArray;
+        desc.textureType = params->desc.depth > 1 ? MTLTextureType2DMultisampleArray
+                : MTLTextureType2DMultisample;
+    else
+        desc.textureType = params->desc.depth > 1 ? MTLTextureType2DArray : MTLTextureType2D;
     desc.pixelFormat = get_metal_pixel_format(params->desc.format);
     ok(desc.pixelFormat != MTLPixelFormatInvalid, "Unhandled pixel format %#x.\n", params->desc.format);
     desc.width = params->desc.width;
-- 
GitLab


From e5bb3a52335c130d85e41e7a2e2d3bc5dd26857b Mon Sep 17 00:00:00 2001
From: Feifan He <feifan@codeweavers.com>
Date: Mon, 9 Dec 2024 11:30:13 +0800
Subject: [PATCH 2/6] tests/shader_runner_metal: Introduce a helper to encode
 the argument buffer.

Co-authored-by: Giovanni Mascellani <gmascellani@codeweavers.com>
---
 tests/shader_runner_metal.m | 71 ++++++++++++++++++++++++-------------
 1 file changed, 46 insertions(+), 25 deletions(-)

diff --git a/tests/shader_runner_metal.m b/tests/shader_runner_metal.m
index dd12dc6ef..9e0038e75 100644
--- a/tests/shader_runner_metal.m
+++ b/tests/shader_runner_metal.m
@@ -319,6 +319,51 @@ static id<MTLFunction> compile_stage(struct metal_runner *runner, enum shader_ty
     return [function autorelease];
 }
 
+static void encode_argument_buffer(struct metal_runner *runner,
+        id<MTLRenderCommandEncoder> command_encoder)
+{
+    NSMutableArray<MTLArgumentDescriptor *> *argument_descriptors;
+    id<MTLDevice> device = runner->device;
+    MTLArgumentDescriptor *arg_desc;
+    id<MTLArgumentEncoder> encoder;
+    id<MTLBuffer> argument_buffer;
+
+    argument_descriptors = [[[NSMutableArray alloc] init] autorelease];
+
+    if (runner->r.uniform_count)
+    {
+        arg_desc = [MTLArgumentDescriptor argumentDescriptor];
+        arg_desc.dataType = MTLDataTypePointer;
+        arg_desc.index = 0;
+        arg_desc.access = MTLBindingAccessReadOnly;
+        [argument_descriptors addObject:arg_desc];
+    }
+
+    if (![argument_descriptors count])
+        return;
+
+    encoder = [[device newArgumentEncoderWithArguments:argument_descriptors] autorelease];
+    argument_buffer = [[device newBufferWithLength:encoder.encodedLength
+            options:DEFAULT_BUFFER_RESOURCE_OPTIONS] autorelease];
+    [encoder setArgumentBuffer:argument_buffer offset:0];
+
+    if (runner->r.uniform_count)
+    {
+        id<MTLBuffer> cb;
+
+        cb = [[device newBufferWithBytes:runner->r.uniforms
+                length:runner->r.uniform_count * sizeof(*runner->r.uniforms)
+                options:DEFAULT_BUFFER_RESOURCE_OPTIONS] autorelease];
+        [encoder setBuffer:cb offset:0 atIndex:0];
+        [command_encoder useResource:cb
+                usage:MTLResourceUsageRead
+                stages:MTLRenderStageVertex | MTLRenderStageFragment];
+    }
+
+    [command_encoder setVertexBuffer:argument_buffer offset:0 atIndex:0];
+    [command_encoder setFragmentBuffer:argument_buffer offset:0 atIndex:0];
+}
+
 static bool metal_runner_dispatch(struct shader_runner *r, unsigned int x, unsigned int y, unsigned int z)
 {
     return false;
@@ -380,13 +425,9 @@ static bool metal_runner_draw(struct shader_runner *r, D3D_PRIMITIVE_TOPOLOGY to
     id<MTLCommandBuffer> command_buffer;
     MTLDepthStencilDescriptor *ds_desc;
     MTLRenderPassDescriptor *pass_desc;
-    id<MTLArgumentEncoder> descriptors;
     MTLVertexDescriptor *vertex_desc;
     struct metal_resource *resource;
-    MTLArgumentDescriptor *cbv_desc;
     id<MTLRenderPipelineState> pso;
-    id<MTLBuffer> argument_buffer;
-    id<MTLBuffer> cb;
     bool ret = false;
     NSError *err;
 
@@ -482,27 +523,7 @@ static bool metal_runner_draw(struct shader_runner *r, D3D_PRIMITIVE_TOPOLOGY to
         command_buffer = [runner->queue commandBuffer];
         encoder = [command_buffer renderCommandEncoderWithDescriptor:pass_desc];
 
-        if (r->uniform_count)
-        {
-            cb = [[device newBufferWithBytes:r->uniforms
-                    length:runner->r.uniform_count * sizeof(*runner->r.uniforms)
-                    options:DEFAULT_BUFFER_RESOURCE_OPTIONS] autorelease];
-
-            cbv_desc = [MTLArgumentDescriptor argumentDescriptor];
-            cbv_desc.dataType = MTLDataTypePointer;
-            cbv_desc.index = 0;
-            cbv_desc.access = MTLBindingAccessReadOnly;
-
-            descriptors = [[device newArgumentEncoderWithArguments:@[cbv_desc]] autorelease];
-            argument_buffer = [[device newBufferWithLength:descriptors.encodedLength
-                    options:DEFAULT_BUFFER_RESOURCE_OPTIONS] autorelease];
-            [descriptors setArgumentBuffer:argument_buffer offset:0];
-            [descriptors setBuffer:cb offset:0 atIndex:0];
-
-            [encoder setVertexBuffer:argument_buffer offset:0 atIndex:0];
-            [encoder setFragmentBuffer:argument_buffer offset:0 atIndex:0];
-            [encoder useResource:cb usage:MTLResourceUsageRead stages:MTLRenderStageVertex | MTLRenderStageFragment];
-        }
+        encode_argument_buffer(runner, encoder);
 
         if (runner->r.input_element_count > 32)
             fatal_error("Unsupported input element count %zu.\n", runner->r.input_element_count);
-- 
GitLab


From ed677a8f328eb5803320e46c64ef524354512d77 Mon Sep 17 00:00:00 2001
From: Giovanni Mascellani <gmascellani@codeweavers.com>
Date: Sat, 12 Apr 2025 22:54:23 +0200
Subject: [PATCH 3/6] tests/shader_runner_metal: Do not use shared buffers.

They are not supported on non-Apple discrete GPUs. Instead make
them managed (if they are short lived) or private.
---
 tests/shader_runner_metal.m | 35 +++++++++++++++++++++++++++--------
 1 file changed, 27 insertions(+), 8 deletions(-)

diff --git a/tests/shader_runner_metal.m b/tests/shader_runner_metal.m
index 9e0038e75..b3ca9590a 100644
--- a/tests/shader_runner_metal.m
+++ b/tests/shader_runner_metal.m
@@ -27,7 +27,6 @@
 #undef BOOL
 
 static const MTLResourceOptions DEFAULT_BUFFER_RESOURCE_OPTIONS = MTLResourceCPUCacheModeDefaultCache
-        | MTLResourceStorageModeShared
         | MTLResourceHazardTrackingModeDefault;
 
 struct metal_resource
@@ -164,12 +163,29 @@ static void init_resource_buffer(struct metal_runner *runner,
 {
     id<MTLDevice> device = runner->device;
 
+    resource->buffer = [device newBufferWithLength:params->data_size
+            options:DEFAULT_BUFFER_RESOURCE_OPTIONS | MTLResourceStorageModePrivate];
+
     if (params->data)
-        resource->buffer = [device newBufferWithBytes:params->data
+    {
+        id<MTLCommandBuffer> command_buffer;
+        id<MTLBlitCommandEncoder> blit;
+        id<MTLBuffer> upload_buffer;
+
+        upload_buffer = [[device newBufferWithBytes:params->data
                 length:params->data_size
-                options:DEFAULT_BUFFER_RESOURCE_OPTIONS];
-    else
-        resource->buffer = [device newBufferWithLength:params->data_size options:DEFAULT_BUFFER_RESOURCE_OPTIONS];
+                options:DEFAULT_BUFFER_RESOURCE_OPTIONS | MTLResourceStorageModeManaged] autorelease];
+
+        command_buffer = [runner->queue commandBuffer];
+
+        blit = [command_buffer blitCommandEncoder];
+        [blit copyFromBuffer:upload_buffer sourceOffset:0 toBuffer:resource->buffer
+                destinationOffset:0 size:params->data_size];
+        [blit endEncoding];
+
+        [command_buffer commit];
+        [command_buffer waitUntilCompleted];
+    }
 }
 
 static void init_resource_texture(struct metal_runner *runner,
@@ -344,7 +360,7 @@ static void encode_argument_buffer(struct metal_runner *runner,
 
     encoder = [[device newArgumentEncoderWithArguments:argument_descriptors] autorelease];
     argument_buffer = [[device newBufferWithLength:encoder.encodedLength
-            options:DEFAULT_BUFFER_RESOURCE_OPTIONS] autorelease];
+            options:DEFAULT_BUFFER_RESOURCE_OPTIONS | MTLResourceStorageModeManaged] autorelease];
     [encoder setArgumentBuffer:argument_buffer offset:0];
 
     if (runner->r.uniform_count)
@@ -353,13 +369,15 @@ static void encode_argument_buffer(struct metal_runner *runner,
 
         cb = [[device newBufferWithBytes:runner->r.uniforms
                 length:runner->r.uniform_count * sizeof(*runner->r.uniforms)
-                options:DEFAULT_BUFFER_RESOURCE_OPTIONS] autorelease];
+                options:DEFAULT_BUFFER_RESOURCE_OPTIONS | MTLResourceStorageModeManaged] autorelease];
         [encoder setBuffer:cb offset:0 atIndex:0];
         [command_encoder useResource:cb
                 usage:MTLResourceUsageRead
                 stages:MTLRenderStageVertex | MTLRenderStageFragment];
     }
 
+    [argument_buffer didModifyRange:NSMakeRange(0, encoder.encodedLength)];
+
     [command_encoder setVertexBuffer:argument_buffer offset:0 atIndex:0];
     [command_encoder setFragmentBuffer:argument_buffer offset:0 atIndex:0];
 }
@@ -618,7 +636,7 @@ static struct resource_readback *metal_runner_get_resource_readback(struct shade
     rb->rb.depth = 1;
     rb->rb.row_pitch = rb->rb.width * resource->r.desc.texel_size;
     rb->buffer = [runner->device newBufferWithLength:rb->rb.row_pitch * rb->rb.height
-            options:DEFAULT_BUFFER_RESOURCE_OPTIONS];
+            options:DEFAULT_BUFFER_RESOURCE_OPTIONS | MTLResourceStorageModeManaged];
 
     level = sub_resource_idx % resource->r.desc.level_count;
     layer = sub_resource_idx / resource->r.desc.level_count;
@@ -637,6 +655,7 @@ static struct resource_readback *metal_runner_get_resource_readback(struct shade
                 destinationOffset:0
                 destinationBytesPerRow:rb->rb.row_pitch
                 destinationBytesPerImage:0];
+        [blit synchronizeResource:rb->buffer];
         [blit endEncoding];
 
         [command_buffer commit];
-- 
GitLab


From 3493688a4d688530d4c0e33fca198673dc5d07d5 Mon Sep 17 00:00:00 2001
From: Giovanni Mascellani <gmascellani@codeweavers.com>
Date: Tue, 11 Mar 2025 21:53:10 +0100
Subject: [PATCH 4/6] vkd3d-shader/msl: Access descriptors with a type-erased
 array.

Currently a descriptor set is represented with a structure
whose fields are the various descriptors that the shader is
going to use, each of them qualified with its type. This model
doesn't match very well what happens with D3D12 and with Vulkan
and descriptor indexing and mutable descriptors, where many
descriptor of different types can overlap, and only at shader
runtime it is decided which of them must be used.

Therefore with this patch a descriptor is represented as a
structure whose fields carry no intrinsic typing information,
but are reinterpreted according to the shader behavior on
each access. For the moment there is just one field, but more
will be added to account for fancier descriptor types and
to workaround Metal limitations.

When completed, this design will be similar in spirit to what
the Metal shader converter does, with little technical
differences.

This choice has a couple of drawbacks:

 1. shader debugging with Xcode is less comfortable, because
    the shader doesn't carry static descriptor typing
    information and Xcode is therefore less able to provide
    useful context;

 2. it requires tier 2 argument buffer support and macOS
    version >= 13, which includes guarantees on the runtime
    layout for descriptors.

Supporting descriptor indexing and mutable descriptors is
considered to be worthy of those drawbacks, though, is
effectively required by many applications and appears to be
the first goal we should aim for. If needed, in the future
we might also add support for older hardware, at least for
shaders that do not make use of advanced features.
---
 libs/vkd3d-shader/msl.c | 262 ++++++++++++++++------------------------
 1 file changed, 107 insertions(+), 155 deletions(-)

diff --git a/libs/vkd3d-shader/msl.c b/libs/vkd3d-shader/msl.c
index d477bfa1c..7bb8307b1 100644
--- a/libs/vkd3d-shader/msl.c
+++ b/libs/vkd3d-shader/msl.c
@@ -106,6 +106,68 @@ static void msl_print_register_datatype(struct vkd3d_string_buffer *buffer,
     }
 }
 
+static bool msl_check_shader_visibility(const struct msl_generator *gen,
+        enum vkd3d_shader_visibility visibility)
+{
+    enum vkd3d_shader_type t = gen->program->shader_version.type;
+
+    switch (visibility)
+    {
+        case VKD3D_SHADER_VISIBILITY_ALL:
+            return true;
+        case VKD3D_SHADER_VISIBILITY_VERTEX:
+            return t == VKD3D_SHADER_TYPE_VERTEX;
+        case VKD3D_SHADER_VISIBILITY_HULL:
+            return t == VKD3D_SHADER_TYPE_HULL;
+        case VKD3D_SHADER_VISIBILITY_DOMAIN:
+            return t == VKD3D_SHADER_TYPE_DOMAIN;
+        case VKD3D_SHADER_VISIBILITY_GEOMETRY:
+            return t == VKD3D_SHADER_TYPE_GEOMETRY;
+        case VKD3D_SHADER_VISIBILITY_PIXEL:
+            return t == VKD3D_SHADER_TYPE_PIXEL;
+        case VKD3D_SHADER_VISIBILITY_COMPUTE:
+            return t == VKD3D_SHADER_TYPE_COMPUTE;
+        default:
+            WARN("Invalid shader visibility %#x.\n", visibility);
+            return false;
+    }
+}
+
+static const struct vkd3d_shader_descriptor_binding *msl_get_cbv_binding(const struct msl_generator *gen,
+        unsigned int register_space, unsigned int register_idx)
+{
+    const struct vkd3d_shader_interface_info *interface_info = gen->interface_info;
+    unsigned int i;
+
+    if (!interface_info)
+        return NULL;
+
+    for (i = 0; i < interface_info->binding_count; ++i)
+    {
+        const struct vkd3d_shader_resource_binding *binding = &interface_info->bindings[i];
+
+        if (binding->type != VKD3D_SHADER_DESCRIPTOR_TYPE_CBV)
+            continue;
+        if (binding->register_space != register_space)
+            continue;
+        if (binding->register_index != register_idx)
+            continue;
+        if (!msl_check_shader_visibility(gen, binding->shader_visibility))
+            continue;
+        if (!(binding->flags & VKD3D_SHADER_BINDING_FLAG_BUFFER))
+            continue;
+
+        return &binding->binding;
+    }
+
+    return NULL;
+}
+
+static void msl_print_cbv_name(struct vkd3d_string_buffer *buffer, unsigned int binding)
+{
+    vkd3d_string_buffer_printf(buffer, "descriptors[%u].buf<vkd3d_vec4>()", binding);
+}
+
 static void msl_print_register_name(struct vkd3d_string_buffer *buffer,
         struct msl_generator *gen, const struct vkd3d_shader_register *reg)
 {
@@ -220,23 +282,36 @@ static void msl_print_register_name(struct vkd3d_string_buffer *buffer,
             break;
 
         case VKD3DSPR_CONSTBUFFER:
-            if (reg->idx_count != 3)
-            {
-                msl_compiler_error(gen, VKD3D_SHADER_ERROR_MSL_INTERNAL,
-                        "Internal compiler error: Unhandled constant buffer register index count %u.", reg->idx_count);
-                vkd3d_string_buffer_printf(buffer, "<unhandled register %#x>", reg->type);
-                break;
-            }
-            if (reg->idx[0].rel_addr || reg->idx[2].rel_addr)
             {
-                msl_compiler_error(gen, VKD3D_SHADER_ERROR_MSL_INTERNAL,
-                        "Internal compiler error: Unhandled constant buffer register indirect addressing.");
-                vkd3d_string_buffer_printf(buffer, "<unhandled register %#x>", reg->type);
+                const struct vkd3d_shader_descriptor_binding *binding;
+
+                if (reg->idx_count != 3)
+                {
+                    msl_compiler_error(gen, VKD3D_SHADER_ERROR_MSL_INTERNAL,
+                            "Internal compiler error: Unhandled constant buffer register index count %u.",
+                            reg->idx_count);
+                    vkd3d_string_buffer_printf(buffer, "<unhandled register %#x>", reg->type);
+                    break;
+                }
+                if (reg->idx[0].rel_addr || reg->idx[1].rel_addr || reg->idx[2].rel_addr)
+                {
+                    msl_compiler_error(gen, VKD3D_SHADER_ERROR_MSL_INTERNAL,
+                            "Internal compiler error: Unhandled constant buffer register indirect addressing.");
+                    vkd3d_string_buffer_printf(buffer, "<unhandled register %#x>", reg->type);
+                    break;
+                }
+                if (!(binding = msl_get_cbv_binding(gen, 0, reg->idx[1].offset)))
+                {
+                    msl_compiler_error(gen, VKD3D_SHADER_ERROR_MSL_BINDING_NOT_FOUND,
+                            "Cannot finding binding for CBV register %u.", reg->idx[0].offset);
+                    vkd3d_string_buffer_printf(buffer, "<unhandled register %#x>", reg->type);
+                    break;
+                }
+                msl_print_cbv_name(buffer, binding->binding);
+                vkd3d_string_buffer_printf(buffer, "[%u]", reg->idx[2].offset);
+                msl_print_register_datatype(buffer, gen, reg->data_type);
                 break;
             }
-            vkd3d_string_buffer_printf(buffer, "descriptors.cb_%u[%u]", reg->idx[0].offset, reg->idx[2].offset);
-            msl_print_register_datatype(buffer, gen, reg->data_type);
-            break;
 
         default:
             msl_compiler_error(gen, VKD3D_SHADER_ERROR_MSL_INTERNAL,
@@ -715,144 +790,6 @@ static void msl_handle_instruction(struct msl_generator *gen, const struct vkd3d
     }
 }
 
-static bool msl_check_shader_visibility(const struct msl_generator *gen,
-        enum vkd3d_shader_visibility visibility)
-{
-    enum vkd3d_shader_type t = gen->program->shader_version.type;
-
-    switch (visibility)
-    {
-        case VKD3D_SHADER_VISIBILITY_ALL:
-            return true;
-        case VKD3D_SHADER_VISIBILITY_VERTEX:
-            return t == VKD3D_SHADER_TYPE_VERTEX;
-        case VKD3D_SHADER_VISIBILITY_HULL:
-            return t == VKD3D_SHADER_TYPE_HULL;
-        case VKD3D_SHADER_VISIBILITY_DOMAIN:
-            return t == VKD3D_SHADER_TYPE_DOMAIN;
-        case VKD3D_SHADER_VISIBILITY_GEOMETRY:
-            return t == VKD3D_SHADER_TYPE_GEOMETRY;
-        case VKD3D_SHADER_VISIBILITY_PIXEL:
-            return t == VKD3D_SHADER_TYPE_PIXEL;
-        case VKD3D_SHADER_VISIBILITY_COMPUTE:
-            return t == VKD3D_SHADER_TYPE_COMPUTE;
-        default:
-            WARN("Invalid shader visibility %#x.\n", visibility);
-            return false;
-    }
-}
-
-static bool msl_get_cbv_binding(const struct msl_generator *gen,
-        unsigned int register_space, unsigned int register_idx, unsigned int *binding_idx)
-{
-    const struct vkd3d_shader_interface_info *interface_info = gen->interface_info;
-    const struct vkd3d_shader_resource_binding *binding;
-    unsigned int i;
-
-    if (!interface_info)
-        return false;
-
-    for (i = 0; i < interface_info->binding_count; ++i)
-    {
-        binding = &interface_info->bindings[i];
-
-        if (binding->type != VKD3D_SHADER_DESCRIPTOR_TYPE_CBV)
-            continue;
-        if (binding->register_space != register_space)
-            continue;
-        if (binding->register_index != register_idx)
-            continue;
-        if (!msl_check_shader_visibility(gen, binding->shader_visibility))
-            continue;
-        if (!(binding->flags & VKD3D_SHADER_BINDING_FLAG_BUFFER))
-            continue;
-        *binding_idx = i;
-        return true;
-    }
-
-    return false;
-}
-
-static void msl_generate_cbv_declaration(struct msl_generator *gen,
-        const struct vkd3d_shader_descriptor_info1 *cbv)
-{
-    const struct vkd3d_shader_descriptor_binding *binding;
-    struct vkd3d_string_buffer *buffer = gen->buffer;
-    unsigned int binding_idx;
-    size_t size;
-
-    if (cbv->count != 1)
-    {
-        msl_compiler_error(gen, VKD3D_SHADER_ERROR_MSL_BINDING_NOT_FOUND,
-                "Constant buffer %u has unsupported descriptor array size %u.", cbv->register_id, cbv->count);
-        return;
-    }
-
-    if (!msl_get_cbv_binding(gen, cbv->register_space, cbv->register_index, &binding_idx))
-    {
-        msl_compiler_error(gen, VKD3D_SHADER_ERROR_MSL_BINDING_NOT_FOUND,
-                "No descriptor binding specified for constant buffer %u.", cbv->register_id);
-        return;
-    }
-
-    binding = &gen->interface_info->bindings[binding_idx].binding;
-
-    if (binding->set != 0)
-    {
-        msl_compiler_error(gen, VKD3D_SHADER_ERROR_MSL_BINDING_NOT_FOUND,
-                "Unsupported binding set %u specified for constant buffer %u.", binding->set, cbv->register_id);
-        return;
-    }
-
-    if (binding->count != 1)
-    {
-        msl_compiler_error(gen, VKD3D_SHADER_ERROR_MSL_BINDING_NOT_FOUND,
-                "Unsupported binding count %u specified for constant buffer %u.", binding->count, cbv->register_id);
-        return;
-    }
-
-    size = align(cbv->buffer_size, VKD3D_VEC4_SIZE * sizeof(uint32_t));
-    size /= VKD3D_VEC4_SIZE * sizeof(uint32_t);
-
-    vkd3d_string_buffer_printf(buffer,
-            "constant vkd3d_vec4 *cb_%u [[id(%u)]];", cbv->register_id, binding->binding);
-};
-
-static void msl_generate_descriptor_struct_declarations(struct msl_generator *gen)
-{
-    const struct vkd3d_shader_scan_descriptor_info1 *info = &gen->program->descriptors;
-    const struct vkd3d_shader_descriptor_info1 *descriptor;
-    struct vkd3d_string_buffer *buffer = gen->buffer;
-    unsigned int i;
-
-    if (!info->descriptor_count)
-        return;
-
-    vkd3d_string_buffer_printf(buffer, "struct vkd3d_%s_descriptors\n{\n", gen->prefix);
-
-    for (i = 0; i < info->descriptor_count; ++i)
-    {
-        descriptor = &info->descriptors[i];
-
-        msl_print_indent(buffer, 1);
-        switch (descriptor->type)
-        {
-            case VKD3D_SHADER_DESCRIPTOR_TYPE_CBV:
-                msl_generate_cbv_declaration(gen, descriptor);
-                break;
-
-            default:
-                vkd3d_string_buffer_printf(buffer, "/* <unhandled descriptor type %#x> */", descriptor->type);
-                msl_compiler_error(gen, VKD3D_SHADER_ERROR_MSL_INTERNAL,
-                        "Internal compiler error: Unhandled descriptor type %#x.", descriptor->type);
-                break;
-        }
-        vkd3d_string_buffer_printf(buffer, "\n");
-    }
-
-    vkd3d_string_buffer_printf(buffer, "};\n\n");
-}
-
 static void msl_generate_input_struct_declarations(struct msl_generator *gen)
 {
     const struct shader_signature *signature = &gen->program->input_signature;
@@ -1175,7 +1112,7 @@ static void msl_generate_entrypoint(struct msl_generator *gen)
         msl_print_indent(gen->buffer, 2);
         /* TODO: Configurable argument buffer binding location. */
         vkd3d_string_buffer_printf(gen->buffer,
-                "constant vkd3d_%s_descriptors& descriptors [[buffer(0)]],\n", gen->prefix);
+                "constant descriptor *descriptors [[buffer(0)]],\n");
     }
 
     msl_print_indent(gen->buffer, 2);
@@ -1223,7 +1160,22 @@ static int msl_generator_generate(struct msl_generator *gen, struct vkd3d_shader
     vkd3d_string_buffer_printf(gen->buffer, "    int4 i;\n");
     vkd3d_string_buffer_printf(gen->buffer, "    float4 f;\n};\n\n");
 
-    msl_generate_descriptor_struct_declarations(gen);
+    if (gen->program->descriptors.descriptor_count > 0)
+    {
+        vkd3d_string_buffer_printf(gen->buffer,
+                "struct descriptor\n"
+                "{\n"
+                "    const device void *ptr;\n"
+                "\n"
+                "    template<typename T>\n"
+                "    const device T * constant &buf() constant\n"
+                "    {\n"
+                "        return reinterpret_cast<const device T * constant &>(this->ptr);\n"
+                "    }\n"
+                "};\n"
+                "\n");
+    }
+
     msl_generate_input_struct_declarations(gen);
     msl_generate_output_struct_declarations(gen);
 
@@ -1234,7 +1186,7 @@ static int msl_generator_generate(struct msl_generator *gen, struct vkd3d_shader
     if (gen->write_depth)
         vkd3d_string_buffer_printf(gen->buffer, ", thread float& o_depth");
     if (gen->program->descriptors.descriptor_count)
-        vkd3d_string_buffer_printf(gen->buffer, ", constant vkd3d_%s_descriptors& descriptors", gen->prefix);
+        vkd3d_string_buffer_printf(gen->buffer, ", constant descriptor *descriptors");
     vkd3d_string_buffer_printf(gen->buffer, ")\n{\n");
 
     ++gen->indent;
-- 
GitLab


From 015a751ea047a3991d81d90d216dace6fe2d2cb9 Mon Sep 17 00:00:00 2001
From: Feifan He <feifan@codeweavers.com>
Date: Thu, 12 Dec 2024 13:57:18 +0800
Subject: [PATCH 5/6] tests/shader_runner_metal: Add texture support.

Co-authored-by: Giovanni Mascellani <gmascellani@codeweavers.com>
---
 tests/shader_runner_metal.m | 182 +++++++++++++++++++++++++++++++++---
 1 file changed, 168 insertions(+), 14 deletions(-)

diff --git a/tests/shader_runner_metal.m b/tests/shader_runner_metal.m
index b3ca9590a..92175eab6 100644
--- a/tests/shader_runner_metal.m
+++ b/tests/shader_runner_metal.m
@@ -65,8 +65,13 @@ static MTLPixelFormat get_metal_pixel_format(DXGI_FORMAT format)
             return MTLPixelFormatRGBA32Uint;
         case DXGI_FORMAT_R32G32B32A32_SINT:
             return MTLPixelFormatRGBA32Sint;
+        case DXGI_FORMAT_R32G32_UINT:
+            return MTLPixelFormatRG32Uint;
         case DXGI_FORMAT_R32_FLOAT:
             return MTLPixelFormatR32Float;
+        case DXGI_FORMAT_R32_SINT:
+            return MTLPixelFormatR32Sint;
+        case DXGI_FORMAT_R32_TYPELESS:
         case DXGI_FORMAT_R32_UINT:
             return MTLPixelFormatR32Uint;
         case DXGI_FORMAT_D32_FLOAT:
@@ -194,8 +199,17 @@ static void init_resource_texture(struct metal_runner *runner,
     id<MTLDevice> device = runner->device;
     MTLTextureDescriptor *desc;
 
-    if (params->desc.type != RESOURCE_TYPE_RENDER_TARGET && params->desc.type != RESOURCE_TYPE_DEPTH_STENCIL)
-        return;
+    switch (params->desc.type)
+    {
+        case RESOURCE_TYPE_RENDER_TARGET:
+        case RESOURCE_TYPE_DEPTH_STENCIL:
+        case RESOURCE_TYPE_TEXTURE:
+            break;
+
+        case RESOURCE_TYPE_UAV:
+        case RESOURCE_TYPE_VERTEX_BUFFER:
+            return;
+    }
 
     if (params->desc.sample_count > 1)
     {
@@ -210,9 +224,6 @@ static void init_resource_texture(struct metal_runner *runner,
         }
     }
 
-    if (params->data)
-        fatal_error("Initial texture resource data not implemented.\n");
-
     desc = [[MTLTextureDescriptor alloc] init];
     if (params->desc.sample_count > 1)
         desc.textureType = params->desc.depth > 1 ? MTLTextureType2DMultisampleArray
@@ -227,10 +238,64 @@ static void init_resource_texture(struct metal_runner *runner,
     desc.mipmapLevelCount = params->desc.level_count;
     desc.sampleCount = max(params->desc.sample_count, 1);
     desc.storageMode = MTLStorageModePrivate;
-    desc.usage = MTLTextureUsageRenderTarget;
+
+    switch (params->desc.type)
+    {
+        case RESOURCE_TYPE_RENDER_TARGET:
+        case RESOURCE_TYPE_DEPTH_STENCIL:
+            desc.usage = MTLTextureUsageRenderTarget;
+            break;
+
+        case RESOURCE_TYPE_TEXTURE:
+            desc.usage = MTLTextureUsageShaderRead;
+            break;
+
+        case RESOURCE_TYPE_UAV:
+        case RESOURCE_TYPE_VERTEX_BUFFER:
+            break;
+    }
 
     resource->texture = [device newTextureWithDescriptor:desc];
     ok(resource->texture, "Failed to create texture.\n");
+
+    if (params->data)
+    {
+        unsigned int buffer_offset = 0, level, level_width, level_height;
+        id<MTLCommandBuffer> command_buffer;
+        id<MTLBlitCommandEncoder> blit;
+        id<MTLTexture> upload_texture;
+
+        if (params->desc.sample_count > 1)
+            fatal_error("Cannot upload data to a multisampled texture.\n");
+        if (params->desc.depth > 1)
+            fatal_error("Uploading data to a texture array is not supported.\n");
+
+        desc.storageMode = MTLStorageModeManaged;
+        upload_texture = [[device newTextureWithDescriptor:desc] autorelease];
+
+        for (level = 0; level < params->desc.level_count; ++level)
+        {
+            level_width  = get_level_dimension(params->desc.width, level);
+            level_height = get_level_dimension(params->desc.height, level);
+            [upload_texture replaceRegion:MTLRegionMake2D(0, 0, level_width, level_height)
+                    mipmapLevel:level
+                    slice:0
+                    withBytes:&params->data[buffer_offset]
+                    bytesPerRow:level_width * params->desc.texel_size
+                    bytesPerImage:level_height * level_width * params->desc.texel_size];
+            buffer_offset += level_height * level_width * params->desc.texel_size;
+        }
+
+        command_buffer = [runner->queue commandBuffer];
+
+        blit = [command_buffer blitCommandEncoder];
+        [blit copyFromTexture:upload_texture toTexture:resource->texture];
+        [blit endEncoding];
+
+        [command_buffer commit];
+        [command_buffer waitUntilCompleted];
+    }
+
     [desc release];
 }
 
@@ -263,9 +328,9 @@ static bool compile_shader(struct metal_runner *runner, enum shader_type type, s
 {
     struct vkd3d_shader_interface_info interface_info = {.type = VKD3D_SHADER_STRUCTURE_TYPE_INTERFACE_INFO};
     struct vkd3d_shader_compile_info info = {.type = VKD3D_SHADER_STRUCTURE_TYPE_COMPILE_INFO};
-    struct vkd3d_shader_resource_binding bindings[MAX_RESOURCES + MAX_SAMPLERS];
+    struct vkd3d_shader_resource_binding bindings[MAX_RESOURCES + MAX_SAMPLERS + 1 /* CBV */];
     struct vkd3d_shader_resource_binding *binding;
-    unsigned int descriptor_binding = 0;
+    unsigned int i;
     char *messages;
     int ret;
 
@@ -289,15 +354,47 @@ static bool compile_shader(struct metal_runner *runner, enum shader_type type, s
 
     if (runner->r.uniform_count)
     {
-        binding = &bindings[interface_info.binding_count++];
+        binding = &bindings[interface_info.binding_count];
         binding->type = VKD3D_SHADER_DESCRIPTOR_TYPE_CBV;
         binding->register_space = 0;
         binding->register_index = 0;
         binding->shader_visibility = VKD3D_SHADER_VISIBILITY_ALL;
         binding->flags = VKD3D_SHADER_BINDING_FLAG_BUFFER;
         binding->binding.set = 0;
-        binding->binding.binding = descriptor_binding++;
+        binding->binding.binding = interface_info.binding_count;
         binding->binding.count = 1;
+        ++interface_info.binding_count;
+    }
+
+    for (i = 0; i < runner->r.resource_count; ++i)
+    {
+        const struct metal_resource *resource = metal_resource(runner->r.resources[i]);
+
+        switch (resource->r.desc.type)
+        {
+            case RESOURCE_TYPE_TEXTURE:
+                binding = &bindings[interface_info.binding_count];
+                binding->type = VKD3D_SHADER_DESCRIPTOR_TYPE_SRV;
+                binding->register_space = 0;
+                binding->register_index = resource->r.desc.slot;
+                binding->shader_visibility = VKD3D_SHADER_VISIBILITY_ALL;
+                if (resource->r.desc.dimension == RESOURCE_DIMENSION_BUFFER)
+                    binding->flags = VKD3D_SHADER_BINDING_FLAG_BUFFER;
+                else
+                    binding->flags = VKD3D_SHADER_BINDING_FLAG_IMAGE;
+                binding->binding.set = 0;
+                binding->binding.binding = interface_info.binding_count;
+                binding->binding.count = 1;
+                ++interface_info.binding_count;
+                break;
+
+            case RESOURCE_TYPE_RENDER_TARGET:
+            case RESOURCE_TYPE_DEPTH_STENCIL:
+            case RESOURCE_TYPE_UAV:
+            case RESOURCE_TYPE_VERTEX_BUFFER:
+                break;
+
+        }
     }
 
     interface_info.bindings = bindings;
@@ -335,7 +432,7 @@ static id<MTLFunction> compile_stage(struct metal_runner *runner, enum shader_ty
     return [function autorelease];
 }
 
-static void encode_argument_buffer(struct metal_runner *runner,
+static bool encode_argument_buffer(struct metal_runner *runner,
         id<MTLRenderCommandEncoder> command_encoder)
 {
     NSMutableArray<MTLArgumentDescriptor *> *argument_descriptors;
@@ -343,6 +440,7 @@ static void encode_argument_buffer(struct metal_runner *runner,
     MTLArgumentDescriptor *arg_desc;
     id<MTLArgumentEncoder> encoder;
     id<MTLBuffer> argument_buffer;
+    unsigned int i, index = 0;
 
     argument_descriptors = [[[NSMutableArray alloc] init] autorelease];
 
@@ -355,8 +453,31 @@ static void encode_argument_buffer(struct metal_runner *runner,
         [argument_descriptors addObject:arg_desc];
     }
 
+    for (i = 0; i < runner->r.resource_count; ++i)
+    {
+        struct metal_resource *resource = metal_resource(runner->r.resources[i]);
+
+        switch (resource->r.desc.type)
+        {
+            case RESOURCE_TYPE_TEXTURE:
+                arg_desc = [MTLArgumentDescriptor argumentDescriptor];
+                arg_desc.dataType = MTLDataTypeTexture;
+                arg_desc.index = [argument_descriptors count];
+                arg_desc.access = MTLBindingAccessReadOnly;
+                arg_desc.textureType = [resource->texture textureType];
+                [argument_descriptors addObject:arg_desc];
+                break;
+
+            case RESOURCE_TYPE_RENDER_TARGET:
+            case RESOURCE_TYPE_DEPTH_STENCIL:
+            case RESOURCE_TYPE_UAV:
+            case RESOURCE_TYPE_VERTEX_BUFFER:
+                break;
+        }
+    }
+
     if (![argument_descriptors count])
-        return;
+        return true;
 
     encoder = [[device newArgumentEncoderWithArguments:argument_descriptors] autorelease];
     argument_buffer = [[device newBufferWithLength:encoder.encodedLength
@@ -370,16 +491,44 @@ static void encode_argument_buffer(struct metal_runner *runner,
         cb = [[device newBufferWithBytes:runner->r.uniforms
                 length:runner->r.uniform_count * sizeof(*runner->r.uniforms)
                 options:DEFAULT_BUFFER_RESOURCE_OPTIONS | MTLResourceStorageModeManaged] autorelease];
-        [encoder setBuffer:cb offset:0 atIndex:0];
+        [encoder setBuffer:cb offset:0 atIndex:index++];
         [command_encoder useResource:cb
                 usage:MTLResourceUsageRead
                 stages:MTLRenderStageVertex | MTLRenderStageFragment];
     }
 
+    for (i = 0; i < runner->r.resource_count; ++i)
+    {
+        struct metal_resource *resource = metal_resource(runner->r.resources[i]);
+
+        switch (resource->r.desc.type)
+        {
+            case RESOURCE_TYPE_TEXTURE:
+                [encoder setTexture:resource->texture atIndex:index++];
+                if (!resource->texture)
+                {
+                    trace("Unsupported buffer texture\n");
+                    return false;
+                }
+                [command_encoder useResource:resource->texture
+                        usage:MTLResourceUsageRead
+                        stages:MTLRenderStageVertex | MTLRenderStageFragment];
+                break;
+
+            case RESOURCE_TYPE_RENDER_TARGET:
+            case RESOURCE_TYPE_DEPTH_STENCIL:
+            case RESOURCE_TYPE_UAV:
+            case RESOURCE_TYPE_VERTEX_BUFFER:
+                break;
+        }
+    }
+
     [argument_buffer didModifyRange:NSMakeRange(0, encoder.encodedLength)];
 
     [command_encoder setVertexBuffer:argument_buffer offset:0 atIndex:0];
     [command_encoder setFragmentBuffer:argument_buffer offset:0 atIndex:0];
+
+    return true;
 }
 
 static bool metal_runner_dispatch(struct shader_runner *r, unsigned int x, unsigned int y, unsigned int z)
@@ -541,7 +690,12 @@ static bool metal_runner_draw(struct shader_runner *r, D3D_PRIMITIVE_TOPOLOGY to
         command_buffer = [runner->queue commandBuffer];
         encoder = [command_buffer renderCommandEncoderWithDescriptor:pass_desc];
 
-        encode_argument_buffer(runner, encoder);
+        if (!encode_argument_buffer(runner, encoder))
+        {
+            [encoder endEncoding];
+            ret = false;
+            goto done;
+        }
 
         if (runner->r.input_element_count > 32)
             fatal_error("Unsupported input element count %zu.\n", runner->r.input_element_count);
-- 
GitLab


From ba7a9a0b290de49d39c5c58fab550ce0478b10f9 Mon Sep 17 00:00:00 2001
From: Feifan He <feifan@codeweavers.com>
Date: Thu, 12 Dec 2024 15:02:51 +0800
Subject: [PATCH 6/6] vkd3d-shader/msl: Implement VKD3DSIH_LD.

Co-authored-by: Giovanni Mascellani <gmascellani@codeweavers.com>
---
 libs/vkd3d-shader/msl.c                       | 202 +++++++++++++++++-
 libs/vkd3d-shader/vkd3d_shader_private.h      |   1 +
 tests/hlsl/cbuffer.shader_test                |   4 +-
 tests/hlsl/initializer-objects.shader_test    |   4 +-
 tests/hlsl/load-level.shader_test             |   6 +-
 tests/hlsl/object-references.shader_test      |   4 +-
 ...egister-reservations-resources.shader_test |  20 +-
 tests/hlsl/texture-load-typed.shader_test     |   4 +-
 8 files changed, 223 insertions(+), 22 deletions(-)

diff --git a/libs/vkd3d-shader/msl.c b/libs/vkd3d-shader/msl.c
index 7bb8307b1..55e5aa2b9 100644
--- a/libs/vkd3d-shader/msl.c
+++ b/libs/vkd3d-shader/msl.c
@@ -46,6 +46,14 @@ struct msl_generator
     const struct vkd3d_shader_interface_info *interface_info;
 };
 
+struct msl_resource_type_info
+{
+    size_t read_coord_size;
+    bool array;
+    bool lod;
+    const char *type_suffix;
+};
+
 static void VKD3D_PRINTF_FUNC(3, 4) msl_compiler_error(struct msl_generator *gen,
         enum vkd3d_shader_error error, const char *fmt, ...)
 {
@@ -57,6 +65,30 @@ static void VKD3D_PRINTF_FUNC(3, 4) msl_compiler_error(struct msl_generator *gen
     gen->failed = true;
 }
 
+static const struct msl_resource_type_info *msl_get_resource_type_info(enum vkd3d_shader_resource_type t)
+{
+    static const struct msl_resource_type_info info[] =
+    {
+        [VKD3D_SHADER_RESOURCE_NONE]              = {0, false, false, "none"},
+        [VKD3D_SHADER_RESOURCE_BUFFER]            = {1, false, false, "_buffer"},
+        [VKD3D_SHADER_RESOURCE_TEXTURE_1D]        = {1, false, false, "1d"},
+        [VKD3D_SHADER_RESOURCE_TEXTURE_2D]        = {2, false, true,  "2d"},
+        [VKD3D_SHADER_RESOURCE_TEXTURE_2DMS]      = {2, false, false, "2d_ms"},
+        [VKD3D_SHADER_RESOURCE_TEXTURE_3D]        = {3, false, true,  "3d"},
+        [VKD3D_SHADER_RESOURCE_TEXTURE_CUBE]      = {2, false, true,  "cube"},
+        [VKD3D_SHADER_RESOURCE_TEXTURE_1DARRAY]   = {1, true,  false, "1d_array"},
+        [VKD3D_SHADER_RESOURCE_TEXTURE_2DARRAY]   = {2, true,  true,  "2d_array"},
+        [VKD3D_SHADER_RESOURCE_TEXTURE_2DMSARRAY] = {2, true,  false, "2d_ms_array"},
+        [VKD3D_SHADER_RESOURCE_TEXTURE_CUBEARRAY] = {2, true,  true,  "cube_array"},
+    };
+
+    if (!t || t >= ARRAY_SIZE(info))
+        return NULL;
+
+    return &info[t];
+}
+
+
 static const char *msl_get_prefix(enum vkd3d_shader_type type)
 {
     switch (type)
@@ -83,6 +115,30 @@ static void msl_print_indent(struct vkd3d_string_buffer *buffer, unsigned int in
     vkd3d_string_buffer_printf(buffer, "%*s", 4 * indent, "");
 }
 
+static void msl_print_resource_datatype(struct msl_generator *gen,
+        struct vkd3d_string_buffer *buffer, enum vkd3d_shader_resource_data_type data_type)
+{
+    switch (data_type)
+    {
+        case VKD3D_SHADER_RESOURCE_DATA_FLOAT:
+        case VKD3D_SHADER_RESOURCE_DATA_UNORM:
+        case VKD3D_SHADER_RESOURCE_DATA_SNORM:
+            vkd3d_string_buffer_printf(buffer, "float");
+            break;
+        case VKD3D_SHADER_RESOURCE_DATA_INT:
+            vkd3d_string_buffer_printf(buffer, "int");
+            break;
+        case VKD3D_SHADER_RESOURCE_DATA_UINT:
+            vkd3d_string_buffer_printf(buffer, "uint");
+            break;
+        default:
+            msl_compiler_error(gen, VKD3D_SHADER_ERROR_MSL_INTERNAL,
+                    "Internal compiler error: Unhandled resource datatype %#x.", data_type);
+            vkd3d_string_buffer_printf(buffer, "<unrecognised resource datatype %#x>", data_type);
+            break;
+    }
+}
+
 static void msl_print_register_datatype(struct vkd3d_string_buffer *buffer,
         struct msl_generator *gen, enum vkd3d_data_type data_type)
 {
@@ -163,11 +219,55 @@ static const struct vkd3d_shader_descriptor_binding *msl_get_cbv_binding(const s
     return NULL;
 }
 
+static const struct vkd3d_shader_descriptor_binding *msl_get_srv_binding(const struct msl_generator *gen,
+        unsigned int register_space, unsigned int register_idx, enum vkd3d_shader_resource_type resource_type)
+{
+    const struct vkd3d_shader_interface_info *interface_info = gen->interface_info;
+    enum vkd3d_shader_binding_flag resource_type_flag;
+    unsigned int i;
+
+    if (!interface_info)
+        return NULL;
+
+    resource_type_flag = resource_type == VKD3D_SHADER_RESOURCE_BUFFER
+            ? VKD3D_SHADER_BINDING_FLAG_BUFFER : VKD3D_SHADER_BINDING_FLAG_IMAGE;
+
+    for (i = 0; i < interface_info->binding_count; ++i)
+    {
+        const struct vkd3d_shader_resource_binding *binding = &interface_info->bindings[i];
+
+        if (binding->type != VKD3D_SHADER_DESCRIPTOR_TYPE_SRV)
+            continue;
+        if (binding->register_space != register_space)
+            continue;
+        if (binding->register_index != register_idx)
+            continue;
+        if (!msl_check_shader_visibility(gen, binding->shader_visibility))
+            continue;
+        if (!(binding->flags & resource_type_flag))
+            continue;
+
+        return &binding->binding;
+    }
+
+    return NULL;
+}
+
 static void msl_print_cbv_name(struct vkd3d_string_buffer *buffer, unsigned int binding)
 {
     vkd3d_string_buffer_printf(buffer, "descriptors[%u].buf<vkd3d_vec4>()", binding);
 }
 
+static void msl_print_srv_name(struct vkd3d_string_buffer *buffer, struct msl_generator *gen,
+        unsigned int binding, const struct msl_resource_type_info *resource_type_info,
+        enum vkd3d_shader_resource_data_type resource_data_type)
+{
+    vkd3d_string_buffer_printf(buffer, "descriptors[%u].tex<texture%s<",
+            binding, resource_type_info->type_suffix);
+    msl_print_resource_datatype(gen, buffer, resource_data_type);
+    vkd3d_string_buffer_printf(buffer, ">>()");
+}
+
 static void msl_print_register_name(struct vkd3d_string_buffer *buffer,
         struct msl_generator *gen, const struct vkd3d_shader_register *reg)
 {
@@ -604,6 +704,96 @@ static void msl_else(struct msl_generator *gen)
     msl_begin_block(gen);
 }
 
+static void msl_ld(struct msl_generator *gen, const struct vkd3d_shader_instruction *ins)
+{
+    const struct msl_resource_type_info *resource_type_info;
+    enum vkd3d_shader_resource_data_type resource_data_type;
+    unsigned int resource_id, resource_idx, resource_space;
+    const struct vkd3d_shader_descriptor_info1 *descriptor;
+    const struct vkd3d_shader_descriptor_binding *binding;
+    enum vkd3d_shader_resource_type resource_type;
+    struct msl_src coord, array_index, lod;
+    struct vkd3d_string_buffer *read;
+    uint32_t coord_mask;
+    struct msl_dst dst;
+
+    if (vkd3d_shader_instruction_has_texel_offset(ins))
+        msl_compiler_error(gen, VKD3D_SHADER_ERROR_MSL_INTERNAL,
+                "Internal compiler error: Unhandled texel fetch offset.");
+
+    if (ins->src[1].reg.idx[0].rel_addr || ins->src[1].reg.idx[1].rel_addr)
+        msl_compiler_error(gen, VKD3D_SHADER_ERROR_MSL_UNSUPPORTED,
+                "Descriptor indexing is not supported.");
+
+    resource_id = ins->src[1].reg.idx[0].offset;
+    resource_idx = ins->src[1].reg.idx[1].offset;
+    if ((descriptor = vkd3d_shader_find_descriptor(&gen->program->descriptors,
+            VKD3D_SHADER_DESCRIPTOR_TYPE_SRV, resource_id)))
+    {
+        resource_type = descriptor->resource_type;
+        resource_space = descriptor->register_space;
+        resource_data_type = descriptor->resource_data_type;
+    }
+    else
+    {
+        msl_compiler_error(gen, VKD3D_SHADER_ERROR_MSL_INTERNAL,
+                "Internal compiler error: Undeclared resource descriptor %u.", resource_id);
+        resource_space = 0;
+        resource_type = VKD3D_SHADER_RESOURCE_TEXTURE_2D;
+        resource_data_type = VKD3D_SHADER_RESOURCE_DATA_FLOAT;
+    }
+
+    if ((resource_type_info = msl_get_resource_type_info(resource_type)))
+    {
+        coord_mask = vkd3d_write_mask_from_component_count(resource_type_info->read_coord_size);
+    }
+    else
+    {
+        msl_compiler_error(gen, VKD3D_SHADER_ERROR_MSL_INTERNAL,
+                "Internal compiler error: Unhandled resource type %#x.", resource_type);
+        coord_mask = vkd3d_write_mask_from_component_count(2);
+    }
+
+    if (!(binding = msl_get_srv_binding(gen, resource_space, resource_idx, resource_type)))
+    {
+        msl_compiler_error(gen, VKD3D_SHADER_ERROR_MSL_BINDING_NOT_FOUND,
+                "Cannot finding binding for SRV register %u index %u space %u.",
+                resource_id, resource_idx, resource_space);
+        return;
+    }
+
+    msl_dst_init(&dst, gen, ins, &ins->dst[0]);
+    msl_src_init(&coord, gen, &ins->src[0], coord_mask);
+    /* `coord_mask + 1` gives exactly the array index component mask if it is an array resource */
+    /* Or it's simply unused, saving some branches */
+    msl_src_init(&array_index, gen, &ins->src[0], coord_mask + 1);
+    msl_src_init(&lod, gen, &ins->src[0], VKD3DSP_WRITEMASK_3);
+    read = vkd3d_string_buffer_get(&gen->string_buffers);
+
+    vkd3d_string_buffer_printf(read, "as_type<uint4>(");
+    msl_print_srv_name(read, gen, binding->binding, resource_type_info, resource_data_type);
+    vkd3d_string_buffer_printf(read, ".read(");
+    if (resource_type_info->read_coord_size > 1)
+        vkd3d_string_buffer_printf(read, "as_type<uint%zu>(%s)",
+                resource_type_info->read_coord_size, coord.str->buffer);
+    else
+        vkd3d_string_buffer_printf(read, "as_type<uint>(%s)", coord.str->buffer);
+    if (resource_type_info->array)
+        vkd3d_string_buffer_printf(read, ", as_type<uint>(%s)", array_index.str->buffer);
+    if (resource_type_info->lod)
+        vkd3d_string_buffer_printf(read, ", as_type<uint>(%s)", lod.str->buffer);
+    vkd3d_string_buffer_printf(read, "))");
+    msl_print_swizzle(read, ins->src[1].swizzle, ins->dst[0].write_mask);
+
+    msl_print_assignment(gen, &dst, "%s", read->buffer);
+
+    vkd3d_string_buffer_release(&gen->string_buffers, read);
+    msl_src_cleanup(&lod, &gen->string_buffers);
+    msl_src_cleanup(&array_index, &gen->string_buffers);
+    msl_src_cleanup(&coord, &gen->string_buffers);
+    msl_dst_cleanup(&dst, &gen->string_buffers);
+}
+
 static void msl_unary_op(struct msl_generator *gen, const struct vkd3d_shader_instruction *ins, const char *op)
 {
     struct msl_src src;
@@ -745,6 +935,9 @@ static void msl_handle_instruction(struct msl_generator *gen, const struct vkd3d
         case VKD3DSIH_UTOF:
             msl_cast(gen, ins, "float");
             break;
+        case VKD3DSIH_LD:
+            msl_ld(gen, ins);
+            break;
         case VKD3DSIH_LOG:
             msl_intrinsic(gen, ins, "log2");
             break;
@@ -1148,7 +1341,8 @@ static int msl_generator_generate(struct msl_generator *gen, struct vkd3d_shader
     MESSAGE("Generating a MSL shader. This is unsupported; you get to keep all the pieces if it breaks.\n");
 
     vkd3d_string_buffer_printf(gen->buffer, "/* Generated by %s. */\n\n", vkd3d_shader_get_version(NULL, NULL));
-    vkd3d_string_buffer_printf(gen->buffer, "#include <metal_common>\n\n");
+    vkd3d_string_buffer_printf(gen->buffer, "#include <metal_common>\n");
+    vkd3d_string_buffer_printf(gen->buffer, "#include <metal_texture>\n\n");
     vkd3d_string_buffer_printf(gen->buffer, "using namespace metal;\n\n");
 
     if (gen->program->global_flags)
@@ -1168,6 +1362,12 @@ static int msl_generator_generate(struct msl_generator *gen, struct vkd3d_shader
                 "    const device void *ptr;\n"
                 "\n"
                 "    template<typename T>\n"
+                "    constant T &tex() constant\n"
+                "    {\n"
+                "        return reinterpret_cast<constant T &>(this->ptr);\n"
+                "    }\n"
+                "\n"
+                "    template<typename T>\n"
                 "    const device T * constant &buf() constant\n"
                 "    {\n"
                 "        return reinterpret_cast<const device T * constant &>(this->ptr);\n"
diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h
index 168c40bd3..03dc02174 100644
--- a/libs/vkd3d-shader/vkd3d_shader_private.h
+++ b/libs/vkd3d-shader/vkd3d_shader_private.h
@@ -262,6 +262,7 @@ enum vkd3d_shader_error
 
     VKD3D_SHADER_ERROR_MSL_INTERNAL                     = 10000,
     VKD3D_SHADER_ERROR_MSL_BINDING_NOT_FOUND            = 10001,
+    VKD3D_SHADER_ERROR_MSL_UNSUPPORTED                  = 10002,
 
     VKD3D_SHADER_ERROR_FX_NOT_IMPLEMENTED               = 11000,
     VKD3D_SHADER_ERROR_FX_INVALID_VERSION               = 11001,
diff --git a/tests/hlsl/cbuffer.shader_test b/tests/hlsl/cbuffer.shader_test
index cdba5e282..c0caeefd9 100644
--- a/tests/hlsl/cbuffer.shader_test
+++ b/tests/hlsl/cbuffer.shader_test
@@ -619,7 +619,7 @@ float4 main() : sv_target
 }
 
 [test]
-todo(msl) draw quad
+draw quad
 probe (0, 0) rgba (1.0, 1.0, 1.0, 1.0)
 
 
@@ -850,7 +850,7 @@ uniform 4 float4 4.0 5.0 6.0 7.0
 uniform 8 float4 8.0 9.0 10.0 11.0
 uniform 12 float4 12.0 13.0 14.0 15.0
 uniform 16 float4 16.0 17.0 18.0 19.0
-todo(msl) draw quad
+draw quad
 probe (0, 0) rgba (124.0, 135.0, 146.0, 150.5)
 
 [require]
diff --git a/tests/hlsl/initializer-objects.shader_test b/tests/hlsl/initializer-objects.shader_test
index aea171549..6156233da 100644
--- a/tests/hlsl/initializer-objects.shader_test
+++ b/tests/hlsl/initializer-objects.shader_test
@@ -25,7 +25,7 @@ float4 main() : sv_target
 }
 
 [test]
-todo(msl) draw quad
+draw quad
 probe (0, 0) rgba (0.2, 0.2, 0.2, 0.1)
 
 
@@ -48,7 +48,7 @@ float4 main() : sv_target
 }
 
 [test]
-todo(msl) draw quad
+draw quad
 probe (0, 0) rgba (31.1, 41.1, 51.1, 61.1) 1
 
 
diff --git a/tests/hlsl/load-level.shader_test b/tests/hlsl/load-level.shader_test
index 99a86d71d..668fe4e33 100644
--- a/tests/hlsl/load-level.shader_test
+++ b/tests/hlsl/load-level.shader_test
@@ -22,10 +22,10 @@ float4 main() : sv_target
 
 [test]
 uniform 0 uint 0
-todo(msl) draw quad
+draw quad
 probe (0, 0) rgba (1.0, 0.0, 1.0, 0.0)
 uniform 0 uint 1
-todo(msl) draw quad
+draw quad
 probe (0, 0) rgba (0.0, 0.0, 1.0, 0.0)
 
 [pixel shader fail]
@@ -47,5 +47,5 @@ float4 main() : sv_target
 }
 
 [test]
-todo(msl) draw quad
+draw quad
 probe (0, 0) rgba (1.0, 0.0, 1.0, 0.0)
diff --git a/tests/hlsl/object-references.shader_test b/tests/hlsl/object-references.shader_test
index 79355c7d2..ef126b611 100644
--- a/tests/hlsl/object-references.shader_test
+++ b/tests/hlsl/object-references.shader_test
@@ -111,7 +111,7 @@ float4 main() : sv_target
 }
 
 [test]
-todo(msl) draw quad
+draw quad
 probe (0, 0) rgba (312, 312, 312, 111)
 
 
@@ -134,7 +134,7 @@ float4 main() : sv_target
 }
 
 [test]
-todo(msl) draw quad
+draw quad
 probe (0, 0) rgba (2132, 2132, 2132, 1111)
 
 
diff --git a/tests/hlsl/register-reservations-resources.shader_test b/tests/hlsl/register-reservations-resources.shader_test
index fa4968d28..b94eee4bd 100644
--- a/tests/hlsl/register-reservations-resources.shader_test
+++ b/tests/hlsl/register-reservations-resources.shader_test
@@ -34,7 +34,7 @@ float4 main() : sv_target
 }
 
 [test]
-todo(msl) draw quad
+draw quad
 probe (0, 0) rgba (41.0, 41.0, 41.0, 1089.0)
 
 
@@ -50,7 +50,7 @@ float4 main() : sv_target
 }
 
 [test]
-todo(msl) draw quad
+draw quad
 probe (0, 0) rgba (0.0, 0.0, 0.0, 99.0)
 
 
@@ -65,7 +65,7 @@ float4 main() : sv_target
 }
 
 [test]
-todo(msl) draw quad
+draw quad
 probe (0, 0) rgba (1.0, 1.0, 1.0, 99.0)
 
 
@@ -84,7 +84,7 @@ shader model >= 4.0
 shader model < 6.0
 
 [test]
-todo(msl) draw quad
+draw quad
 probe (0, 0) rgba (0.0, 0.0, 0.0, 99.0)
 
 [require]
@@ -110,7 +110,7 @@ float4 main() : sv_target
 
 
 [test]
-todo(msl) draw quad
+draw quad
 probe (0, 0) rgba (0.0, 0.0, 0.0, 99.0)
 
 [pixel shader]
@@ -123,7 +123,7 @@ float4 main() : sv_target
 }
 
 [test]
-todo(msl) draw quad
+draw quad
 probe (0, 0) rgba (4.0, 4.0, 4.0, 99.0)
 
 
@@ -139,7 +139,7 @@ float4 main() : sv_target
 }
 
 [test]
-todo(msl) draw quad
+draw quad
 probe (0, 0) rgba (1.0, 1.0, 1.0, 99.0)
 
 
@@ -154,7 +154,7 @@ float4 main() : sv_target
 }
 
 [test]
-todo(msl) draw quad
+draw quad
 probe (0, 0) rgba (2.0, 2.0, 2.0, 99.0)
 
 
@@ -168,7 +168,7 @@ float4 main() : sv_target
 }
 
 [test]
-todo(msl) draw quad
+draw quad
 probe (0, 0) rgba (2.0, 2.0, 2.0, 99.0)
 
 
@@ -241,5 +241,5 @@ float4 main() : sv_target
 }
 
 [test]
-todo(msl) draw quad
+draw quad
 probe (0, 0) rgba (1.0, 1.0, 1.0, 99.0)
diff --git a/tests/hlsl/texture-load-typed.shader_test b/tests/hlsl/texture-load-typed.shader_test
index d08783944..8ca4a915e 100644
--- a/tests/hlsl/texture-load-typed.shader_test
+++ b/tests/hlsl/texture-load-typed.shader_test
@@ -48,7 +48,7 @@ float4 main() : sv_target
 }
 
 [test]
-todo(msl) draw quad
+draw quad
 probe (0, 0) rgba (0.8, -3.0, 4294967295.0, 123.0)
 
 % lowercase 'texture2D'
@@ -65,7 +65,7 @@ float4 main() : sv_target
 }
 
 [test]
-todo(msl) draw quad
+draw quad
 probe (0, 0) rgba (0.8, -3.0, 4294967295.0, 123.0)
 
 [require]
-- 
GitLab