diff --git a/libs/vkd3d-shader/msl.c b/libs/vkd3d-shader/msl.c
index d477bfa1c1bed72b0ba2d4637aa5d8c4747cd376..55e5aa2b96c497729fe9f58e169127549a5bac11 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)
 {
@@ -106,6 +162,112 @@ 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 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)
 {
@@ -220,23 +382,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,
@@ -529,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;
@@ -670,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;
@@ -715,144 +983,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 +1305,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);
@@ -1211,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)
@@ -1223,7 +1354,28 @@ 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"
+                "    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"
+                "    }\n"
+                "};\n"
+                "\n");
+    }
+
     msl_generate_input_struct_declarations(gen);
     msl_generate_output_struct_declarations(gen);
 
@@ -1234,7 +1386,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;
diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h
index 168c40bd302736f91c7a53a0e866884931f2f647..03dc0217474d327d324cf32b0a6c6de949f81559 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 cdba5e2820960290db7ca07ceffcac2e727f41e0..c0caeefd9b379c892d18b35066a86855e21d20c8 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 aea171549e84d27e1b44e2d2b4220a20c5dd2ad7..6156233da2f8e01fd38156037223c27049465f63 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 99a86d71d770265ca1ae05ae70f80c9223bf0d11..668fe4e3397e052e5f380b5656b8711738fd7451 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 79355c7d257ca70f040c67e18e2d60c57166ede4..ef126b61102749d919996d8d10016d4dfd4f7427 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 fa4968d28a0456183dc1782bceb89d8ef681d9a7..b94eee4bd81f9a8f55edde40f4a806f5a212804a 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 d08783944e9069ddfa4b33d4faccdf6af442faa6..8ca4a915edc81b8ea562893ca36e13a245ee9395 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]
diff --git a/tests/shader_runner_metal.m b/tests/shader_runner_metal.m
index 8b0f646d1a17f31217667f765da851bc16921aa1..92175eab6efcc5e736bce11264d0605de57d80c3 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
@@ -66,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:
@@ -164,12 +168,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,
@@ -178,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)
     {
@@ -194,14 +224,12 @@ 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 = 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;
@@ -210,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];
 }
 
@@ -246,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;
 
@@ -272,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;
@@ -318,6 +432,105 @@ static id<MTLFunction> compile_stage(struct metal_runner *runner, enum shader_ty
     return [function autorelease];
 }
 
+static bool 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;
+    unsigned int i, index = 0;
+
+    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];
+    }
+
+    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 true;
+
+    encoder = [[device newArgumentEncoderWithArguments:argument_descriptors] autorelease];
+    argument_buffer = [[device newBufferWithLength:encoder.encodedLength
+            options:DEFAULT_BUFFER_RESOURCE_OPTIONS | MTLResourceStorageModeManaged] 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 | MTLResourceStorageModeManaged] autorelease];
+        [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)
 {
     return false;
@@ -379,13 +592,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;
 
@@ -481,26 +690,11 @@ 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)
+        if (!encode_argument_buffer(runner, encoder))
         {
-            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];
+            [encoder endEncoding];
+            ret = false;
+            goto done;
         }
 
         if (runner->r.input_element_count > 32)
@@ -596,7 +790,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;
@@ -615,6 +809,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];