SDL_gpu_shadercross: MSL: Remap resources based on descriptor set bindings (#30)

From b5fa7ebc8e414fe21b42fceef4ab8a9721ee6270 Mon Sep 17 00:00:00 2001
From: Evan Hemsley <[EMAIL REDACTED]>
Date: Mon, 28 Oct 2024 15:26:48 -0700
Subject: [PATCH] MSL: Remap resources based on descriptor set bindings (#30)

---
 .../SDL_gpu_shadercross.h                     |   3 +-
 src/SDL_gpu_shadercross.c                     | 484 +++++++++++++++++-
 src/cli.c                                     |  15 +-
 3 files changed, 488 insertions(+), 14 deletions(-)

diff --git a/include/SDL3_gpu_shadercross/SDL_gpu_shadercross.h b/include/SDL3_gpu_shadercross/SDL_gpu_shadercross.h
index dd857e5..5d29bd5 100644
--- a/include/SDL3_gpu_shadercross/SDL_gpu_shadercross.h
+++ b/include/SDL3_gpu_shadercross/SDL_gpu_shadercross.h
@@ -88,7 +88,8 @@ extern SDL_DECLSPEC SDL_GPUShaderFormat SDLCALL SDL_ShaderCross_GetSPIRVShaderFo
 extern SDL_DECLSPEC void * SDLCALL SDL_ShaderCross_TranspileMSLFromSPIRV(
     const Uint8 *bytecode,
     size_t bytecodeSize,
-    const char *entrypoint);
+    const char *entrypoint,
+    SDL_ShaderCross_ShaderStage shaderStage);
 
 /**
  * Transpile to HLSL code from SPIRV code.
diff --git a/src/SDL_gpu_shadercross.c b/src/SDL_gpu_shadercross.c
index 4d3e25e..d69e3b0 100644
--- a/src/SDL_gpu_shadercross.c
+++ b/src/SDL_gpu_shadercross.c
@@ -292,12 +292,17 @@ static void *SDL_ShaderCross_INTERNAL_CompileUsingDXC(
     bool spirv,
     size_t *size) // filled in with number of bytes of returned buffer
 {
+    if (SDL_strstr(shaderProfile, "5_0")) {
+        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", "Cannot use Shader Model 5 with DXC!");
+        return NULL;
+    }
+
     DxcBuffer source;
     IDxcResult *dxcResult;
     IDxcBlob *blob;
     IDxcBlobUtf8 *errors;
     size_t entryPointLength = SDL_utf8strlen(entrypoint) + 1;
-    const char *entryPointUtf16 = SDL_iconv_string("UTF-16", "UTF-8", entrypoint, entryPointLength);
+    wchar_t *entryPointUtf16 = (wchar_t *)SDL_iconv_string("WCHAR_T", "UTF-8", entrypoint, entryPointLength);
     LPCWSTR args[] = {
         (LPCWSTR)L"-E",
         (LPCWSTR)entryPointUtf16,
@@ -351,6 +356,8 @@ static void *SDL_ShaderCross_INTERNAL_CompileUsingDXC(
         IID_IDxcResult,
         (void **)&dxcResult);
 
+    SDL_free(entryPointUtf16);
+
     if (ret < 0) {
         SDL_LogError(SDL_LOG_CATEGORY_GPU,
                      "IDxcShaderCompiler3::Compile failed: %X",
@@ -712,8 +719,13 @@ typedef spvc_result (*pfn_spvc_context_parse_spirv)(spvc_context, const SpvId *,
 typedef spvc_result (*pfn_spvc_context_create_compiler)(spvc_context, spvc_backend, spvc_parsed_ir, spvc_capture_mode, spvc_compiler *);
 typedef spvc_result (*pfn_spvc_compiler_create_compiler_options)(spvc_compiler, spvc_compiler_options *);
 typedef spvc_result (*pfn_spvc_compiler_options_set_uint)(spvc_compiler_options, spvc_compiler_option, unsigned);
+typedef spvc_result (*pfn_spvc_compiler_create_shader_resources)(spvc_compiler, spvc_resources *);
+typedef spvc_result (*pfn_spvc_compiler_msl_add_resource_binding)(spvc_compiler, const spvc_msl_resource_binding *);
+typedef spvc_result (*pfn_spvc_compiler_has_decoration)(spvc_compiler, SpvId, SpvDecoration);
+typedef spvc_result (*pfn_spvc_compiler_get_decoration)(spvc_compiler, SpvId, SpvDecoration);
 typedef spvc_result (*pfn_spvc_compiler_install_compiler_options)(spvc_compiler, spvc_compiler_options);
 typedef spvc_result (*pfn_spvc_compiler_compile)(spvc_compiler, const char **);
+typedef spvc_result (*pfn_spvc_resources_get_resource_list_for_type)(spvc_resources, spvc_resource_type, const spvc_reflected_resource **, size_t *);
 typedef const char *(*pfn_spvc_context_get_last_error_string)(spvc_context);
 typedef SpvExecutionModel (*pfn_spvc_compiler_get_execution_model)(spvc_compiler compiler);
 typedef const char *(*pfn_spvc_compiler_get_cleansed_entry_point_name)(spvc_compiler compiler, const char *name, SpvExecutionModel model);
@@ -724,8 +736,13 @@ static pfn_spvc_context_parse_spirv SDL_spvc_context_parse_spirv = NULL;
 static pfn_spvc_context_create_compiler SDL_spvc_context_create_compiler = NULL;
 static pfn_spvc_compiler_create_compiler_options SDL_spvc_compiler_create_compiler_options = NULL;
 static pfn_spvc_compiler_options_set_uint SDL_spvc_compiler_options_set_uint = NULL;
+static pfn_spvc_compiler_create_shader_resources SDL_spvc_compiler_create_shader_resources = NULL;
+static pfn_spvc_compiler_msl_add_resource_binding SDL_spvc_compiler_msl_add_resource_binding = NULL;
+static pfn_spvc_compiler_has_decoration SDL_spvc_compiler_has_decoration = NULL;
+static pfn_spvc_compiler_get_decoration SDL_spvc_compiler_get_decoration = NULL;
 static pfn_spvc_compiler_install_compiler_options SDL_spvc_compiler_install_compiler_options = NULL;
 static pfn_spvc_compiler_compile SDL_spvc_compiler_compile = NULL;
+static pfn_spvc_resources_get_resource_list_for_type SDL_spvc_resources_get_resource_list_for_type = NULL;
 static pfn_spvc_context_get_last_error_string SDL_spvc_context_get_last_error_string = NULL;
 static pfn_spvc_compiler_get_execution_model SDL_spvc_compiler_get_execution_model = NULL;
 static pfn_spvc_compiler_get_cleansed_entry_point_name SDL_spvc_compiler_get_cleansed_entry_point_name = NULL;
@@ -738,8 +755,13 @@ static pfn_spvc_compiler_get_cleansed_entry_point_name SDL_spvc_compiler_get_cle
 #define SDL_spvc_context_create_compiler                spvc_context_create_compiler
 #define SDL_spvc_compiler_create_compiler_options       spvc_compiler_create_compiler_options
 #define SDL_spvc_compiler_options_set_uint              spvc_compiler_options_set_uint
+#define SDL_spvc_compiler_create_shader_resources       spvc_compiler_create_shader_resources
+#define SDL_spvc_compiler_msl_add_resource_binding      spvc_compiler_msl_add_resource_binding
+#define SDL_spvc_compiler_has_decoration                spvc_compiler_has_decoration
+#define SDL_spvc_compiler_get_decoration                spvc_compiler_get_decoration
 #define SDL_spvc_compiler_install_compiler_options      spvc_compiler_install_compiler_options
 #define SDL_spvc_compiler_compile                       spvc_compiler_compile
+#define SDL_spvc_resources_get_resource_list_for_type   spvc_resources_get_resource_list_for_type
 #define SDL_spvc_context_get_last_error_string          spvc_context_get_last_error_string
 #define SDL_spvc_compiler_get_execution_model           spvc_compiler_get_execution_model
 #define SDL_spvc_compiler_get_cleansed_entry_point_name spvc_compiler_get_cleansed_entry_point_name
@@ -764,7 +786,8 @@ static void SDL_ShaderCross_INTERNAL_DestroyTranspileContext(
 
 static SPIRVTranspileContext *SDL_ShaderCross_INTERNAL_TranspileFromSPIRV(
     spvc_backend backend,
-    unsigned shadermodel,
+    unsigned shadermodel, // only used for HLSL
+    SDL_ShaderCross_ShaderStage shaderStage, // only used for MSL
     const Uint8 *code,
     size_t codeSize,
     const char *entrypoint
@@ -815,6 +838,436 @@ static SPIRVTranspileContext *SDL_ShaderCross_INTERNAL_TranspileFromSPIRV(
         SDL_spvc_compiler_options_set_uint(options, SPVC_COMPILER_OPTION_HLSL_FLATTEN_MATRIX_VERTEX_INPUT_SEMANTICS, 1);
     }
 
+    SpvExecutionModel executionModel;
+    if (shaderStage == SDL_SHADERCROSS_SHADERSTAGE_VERTEX) {
+        executionModel = SpvExecutionModelVertex;
+    } else if (shaderStage == SDL_SHADERCROSS_SHADERSTAGE_FRAGMENT) {
+        executionModel = SpvExecutionModelFragment;
+    } else { // compute
+        executionModel = SpvExecutionModelKernel;
+    }
+
+    // MSL doesn't have descriptor sets, so we have to set up index remapping
+    if (backend == SPVC_BACKEND_MSL && shaderStage != SDL_SHADERCROSS_SHADERSTAGE_COMPUTE) {
+        spvc_resources resources;
+        spvc_reflected_resource *reflected_resources;
+        size_t num_texture_samplers;
+        size_t num_storage_textures;
+        size_t num_storage_buffers;
+        size_t num_uniform_buffers;
+
+        result = SDL_spvc_compiler_create_shader_resources(compiler, &resources);
+        if (result < 0) {
+            SPVC_ERROR(spvc_compiler_create_shader_resources);
+            SDL_spvc_context_destroy(context);
+            return NULL;
+        }
+
+        // Combined texture-samplers
+        result = SDL_spvc_resources_get_resource_list_for_type(
+            resources,
+            SPVC_RESOURCE_TYPE_SAMPLED_IMAGE,
+            (const spvc_reflected_resource **)&reflected_resources,
+            &num_texture_samplers);
+        if (result < 0) {
+            SPVC_ERROR(spvc_resources_get_resource_list_for_type);
+            SDL_spvc_context_destroy(context);
+            return NULL;
+        }
+
+        spvc_msl_resource_binding binding;
+        for (size_t i = 0; i < num_texture_samplers; i += 1) {
+            if (!SDL_spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet) || !SDL_spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding)) {
+                SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", "Shader resources must have descriptor set and binding index!");
+                SDL_spvc_context_destroy(context);
+                return NULL;
+            }
+
+            unsigned int descriptor_set_index = SDL_spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet);
+            if (!(descriptor_set_index == 0 || descriptor_set_index == 2)) {
+                SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", "Descriptor set index for graphics texture-sampler must be 0 or 2!");
+                SDL_spvc_context_destroy(context);
+                return NULL;
+            }
+
+            unsigned int binding_index = SDL_spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding);
+
+            binding.stage = executionModel;
+            binding.desc_set = descriptor_set_index;
+            binding.binding = binding_index;
+            binding.msl_texture = binding_index;
+            binding.msl_sampler = binding_index;
+            result = SDL_spvc_compiler_msl_add_resource_binding(compiler, &binding);
+            if (result < 0) {
+                SPVC_ERROR(spvc_compiler_msl_add_resource_binding);
+                SDL_spvc_context_destroy(context);
+                return NULL;
+            }
+        }
+
+        // Storage textures
+        result = SDL_spvc_resources_get_resource_list_for_type(
+            resources,
+            SPVC_RESOURCE_TYPE_STORAGE_IMAGE,
+            (const spvc_reflected_resource **)&reflected_resources,
+            &num_storage_textures);
+        if (result < 0) {
+            SPVC_ERROR(spvc_resources_get_resource_list_for_type);
+            SDL_spvc_context_destroy(context);
+            return NULL;
+        }
+
+        for (size_t i = 0; i < num_storage_textures; i += 1) {
+            if (!SDL_spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet) || !SDL_spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding)) {
+                SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", "Shader resources must have descriptor set and binding index!");
+                SDL_spvc_context_destroy(context);
+                return NULL;
+            }
+
+            unsigned int descriptor_set_index = SDL_spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet);
+            if (!(descriptor_set_index == 0 || descriptor_set_index == 2)) {
+                SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", "Descriptor set index for graphics storage texture must be 0 or 2!");
+                SDL_spvc_context_destroy(context);
+                return NULL;
+            }
+
+            unsigned int binding_index = SDL_spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding);
+
+            binding.stage = executionModel;
+            binding.desc_set = descriptor_set_index;
+            binding.binding = binding_index;
+            binding.msl_texture = num_texture_samplers + binding_index;
+            SDL_spvc_compiler_msl_add_resource_binding(compiler, &binding);
+            if (result < 0) {
+                SPVC_ERROR(spvc_compiler_msl_add_resource_binding);
+                SDL_spvc_context_destroy(context);
+                return NULL;
+            }
+        }
+
+        // Storage buffers
+        result = SDL_spvc_resources_get_resource_list_for_type(
+            resources,
+            SPVC_RESOURCE_TYPE_STORAGE_BUFFER,
+            (const spvc_reflected_resource **)&reflected_resources,
+            &num_storage_buffers);
+        if (result < 0) {
+            SPVC_ERROR(spvc_resources_get_resource_list_for_type);
+            SDL_spvc_context_destroy(context);
+            return NULL;
+        }
+
+        for (size_t i = 0; i < num_storage_buffers; i += 1) {
+            if (!SDL_spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet) || !SDL_spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding)) {
+                SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", "Shader resources must have descriptor set and binding index!");
+                SDL_spvc_context_destroy(context);
+                return NULL;
+            }
+
+            unsigned int descriptor_set_index = SDL_spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet);
+            if (!(descriptor_set_index == 0 || descriptor_set_index == 2)) {
+                SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", "Descriptor set index for graphics storage buffer must be 0 or 2!");
+                SDL_spvc_context_destroy(context);
+                return NULL;
+            }
+
+            unsigned int binding_index = SDL_spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding);
+
+            binding.stage = executionModel;
+            binding.desc_set = descriptor_set_index;
+            binding.binding = binding_index;
+            binding.msl_buffer = binding_index;
+            SDL_spvc_compiler_msl_add_resource_binding(compiler, &binding);
+            if (result < 0) {
+                SPVC_ERROR(spvc_compiler_msl_add_resource_binding);
+                SDL_spvc_context_destroy(context);
+                return NULL;
+            }
+        }
+
+        // Uniform buffers
+        result = SDL_spvc_resources_get_resource_list_for_type(
+            resources,
+            SPVC_RESOURCE_TYPE_UNIFORM_BUFFER,
+            (const spvc_reflected_resource **)&reflected_resources,
+            &num_uniform_buffers);
+        if (result < 0) {
+            SPVC_ERROR(spvc_resources_get_resource_list_for_type);
+            SDL_spvc_context_destroy(context);
+            return NULL;
+        }
+
+        for (size_t i = 0; i< num_uniform_buffers; i += 1) {
+            if (!SDL_spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet) || !SDL_spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding)) {
+                SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", "Shader resources must have descriptor set and binding index!");
+                SDL_spvc_context_destroy(context);
+                return NULL;
+            }
+
+            unsigned int descriptor_set_index = SDL_spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet);
+            if (!(descriptor_set_index == 1 || descriptor_set_index == 3)) {
+                SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", "Descriptor set index for graphics uniform buffer must be 1 or 3!");
+                SDL_spvc_context_destroy(context);
+                return NULL;
+            }
+
+            unsigned int binding_index = SDL_spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding);
+
+            binding.stage = executionModel;
+            binding.desc_set = descriptor_set_index;
+            binding.binding = binding_index;
+            binding.msl_buffer = num_storage_buffers + binding_index;
+            SDL_spvc_compiler_msl_add_resource_binding(compiler, &binding);
+            if (result < 0) {
+                SPVC_ERROR(spvc_compiler_msl_add_resource_binding);
+                SDL_spvc_context_destroy(context);
+                return NULL;
+            }
+        }
+    }
+
+    if (backend == SPVC_BACKEND_MSL && shaderStage == SDL_SHADERCROSS_SHADERSTAGE_COMPUTE) {
+        spvc_resources resources;
+        spvc_reflected_resource *reflected_resources;
+        size_t num_texture_samplers;
+        size_t num_storage_textures; // total storage textures
+        size_t num_storage_buffers; // total storage buffers
+        size_t num_uniform_buffers;
+
+        result = SDL_spvc_compiler_create_shader_resources(compiler, &resources);
+        if (result < 0) {
+            SPVC_ERROR(spvc_compiler_create_shader_resources);
+            SDL_spvc_context_destroy(context);
+            return NULL;
+        }
+
+        spvc_msl_resource_binding binding;
+        unsigned int num_textures = 0;
+        unsigned int num_buffers = 0;
+
+        // Combined texture-samplers
+        result = SDL_spvc_resources_get_resource_list_for_type(
+            resources,
+            SPVC_RESOURCE_TYPE_SAMPLED_IMAGE,
+            (const spvc_reflected_resource **)&reflected_resources,
+            &num_texture_samplers);
+        if (result < 0) {
+            SPVC_ERROR(spvc_resources_get_resource_list_for_type);
+            SDL_spvc_context_destroy(context);
+            return NULL;
+        }
+
+        for (size_t i = 0; i < num_texture_samplers; i += 1) {
+            if (!SDL_spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet) || !SDL_spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding)) {
+                SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", "Shader resources must have descriptor set and binding index!");
+                SDL_spvc_context_destroy(context);
+                return NULL;
+            }
+
+            unsigned int descriptor_set_index = SDL_spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet);
+            if (descriptor_set_index != 0) {
+                SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", "Descriptor set index for compute texture-sampler must be 0!");
+                SDL_spvc_context_destroy(context);
+                return NULL;
+            }
+
+            unsigned int binding_index = SDL_spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding);
+
+            binding.stage = executionModel;
+            binding.desc_set = descriptor_set_index;
+            binding.binding = binding_index;
+            binding.msl_texture = num_textures;
+            binding.msl_sampler = num_textures;
+            result = SDL_spvc_compiler_msl_add_resource_binding(compiler, &binding);
+            if (result < 0) {
+                SPVC_ERROR(spvc_compiler_msl_add_resource_binding);
+                SDL_spvc_context_destroy(context);
+                return NULL;
+            }
+            num_textures += 1;
+        }
+
+        // Storage textures
+        result = SDL_spvc_resources_get_resource_list_for_type(
+            resources,
+            SPVC_RESOURCE_TYPE_STORAGE_IMAGE,
+            (const spvc_reflected_resource **)&reflected_resources,
+            &num_storage_textures);
+        if (result < 0) {
+            SPVC_ERROR(spvc_resources_get_resource_list_for_type);
+            SDL_spvc_context_destroy(context);
+            return NULL;
+        }
+
+        // Readonly storage textures
+        for (size_t i = 0; i < num_storage_textures; i += 1) {
+            if (!SDL_spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet) || !SDL_spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding)) {
+                SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", "Shader resources must have descriptor set and binding index!");
+                SDL_spvc_context_destroy(context);
+                return NULL;
+            }
+
+            unsigned int descriptor_set_index = SDL_spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet);
+            if (!(descriptor_set_index == 0 || descriptor_set_index == 1)) {
+                SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", "Descriptor set index for compute storage texture must be 0 or 1!");
+                SDL_spvc_context_destroy(context);
+                return NULL;
+            }
+
+            // Skip readwrite textures
+            if (descriptor_set_index != 0) { continue; }
+
+            unsigned int binding_index = SDL_spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding);
+
+            binding.stage = executionModel;
+            binding.desc_set = descriptor_set_index;
+            binding.binding = binding_index;
+            binding.msl_texture = num_textures + binding_index;
+            SDL_spvc_compiler_msl_add_resource_binding(compiler, &binding);
+            if (result < 0) {
+                SPVC_ERROR(spvc_compiler_msl_add_resource_binding);
+                SDL_spvc_context_destroy(context);
+                return NULL;
+            }
+
+            num_textures += 1;
+        }
+
+        // Readwrite storage textures
+        for (size_t i = 0; i < num_storage_textures; i += 1) {
+            unsigned int descriptor_set_index = SDL_spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet);
+
+            // Skip readonly textures
+            if (descriptor_set_index != 1) { continue; }
+
+            unsigned int binding_index = SDL_spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding);
+
+            binding.stage = executionModel;
+            binding.desc_set = descriptor_set_index;
+            binding.binding = binding_index;
+            binding.msl_texture = num_textures + binding_index;
+            SDL_spvc_compiler_msl_add_resource_binding(compiler, &binding);
+            if (result < 0) {
+                SPVC_ERROR(spvc_compiler_msl_add_resource_binding);
+                SDL_spvc_context_destroy(context);
+                return NULL;
+            }
+
+            num_textures += 1;
+        }
+
+        // Storage buffers
+        result = SDL_spvc_resources_get_resource_list_for_type(
+            resources,
+            SPVC_RESOURCE_TYPE_STORAGE_BUFFER,
+            (const spvc_reflected_resource **)&reflected_resources,
+            &num_storage_buffers);
+        if (result < 0) {
+            SPVC_ERROR(spvc_resources_get_resource_list_for_type);
+            SDL_spvc_context_destroy(context);
+            return NULL;
+        }
+
+        // Readonly storage buffers
+        for (size_t i = 0; i < num_storage_buffers; i += 1) {
+            if (!SDL_spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet) || !SDL_spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding)) {
+                SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", "Shader resources must have descriptor set and binding index!");
+                SDL_spvc_context_destroy(context);
+                return NULL;
+            }
+
+            unsigned int descriptor_set_index = SDL_spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet);
+            if (!(descriptor_set_index == 0 || descriptor_set_index == 1)) {
+                SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", "Descriptor set index for compute storage buffer must be 0 or 1!");
+                SDL_spvc_context_destroy(context);
+                return NULL;
+            }
+
+            // Skip readwrite buffers
+            if (descriptor_set_index != 0) { continue; }
+
+            unsigned int binding_index = SDL_spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding);
+
+            binding.stage = executionModel;
+            binding.desc_set = descriptor_set_index;
+            binding.binding = binding_index;
+            binding.msl_buffer = binding_index;
+            SDL_spvc_compiler_msl_add_resource_binding(compiler, &binding);
+            if (result < 0) {
+                SPVC_ERROR(spvc_compiler_msl_add_resource_binding);
+                SDL_spvc_context_destroy(context);
+                return NULL;
+            }
+
+            num_buffers += 1;
+        }
+
+        // Readonly storage buffers
+        for (size_t i = 0; i < num_storage_buffers; i += 1) {
+            unsigned int descriptor_set_index = SDL_spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet);
+
+            // Skip readonly buffers
+            if (descriptor_set_index != 1) { continue; }
+
+            unsigned int binding_index = SDL_spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding);
+
+            binding.stage = executionModel;
+            binding.desc_set = descriptor_set_index;
+            binding.binding = binding_index;
+            binding.msl_buffer = num_buffers + binding_index;
+            SDL_spvc_compiler_msl_add_resource_binding(compiler, &binding);
+            if (result < 0) {
+                SPVC_ERROR(spvc_compiler_msl_add_resource_binding);
+                SDL_spvc_context_destroy(context);
+                return NULL;
+            }
+
+            num_buffers += 1;
+        }
+
+        // Uniform buffers
+        result = SDL_spvc_resources_get_resource_list_for_type(
+            resources,
+            SPVC_RESOURCE_TYPE_UNIFORM_BUFFER,
+            (const spvc_reflected_resource **)&reflected_resources,
+            &num_uniform_buffers);
+        if (result < 0) {
+            SPVC_ERROR(spvc_resources_get_resource_list_for_type);
+            SDL_spvc_context_destroy(context);
+            return NULL;
+        }
+
+        for (size_t i = 0; i < num_uniform_buffers; i += 1) {
+            if (!SDL_spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet) || !SDL_spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding)) {
+                SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", "Shader resources must have descriptor set and binding index!");
+                SDL_spvc_context_destroy(context);
+                return NULL;
+            }
+
+            unsigned int descriptor_set_index = SDL_spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet);
+            if (descriptor_set_index != 2) {
+                SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", "Descriptor set index for compute uniform buffer must be 2!");
+                SDL_spvc_context_destroy(context);
+                return NULL;
+            }
+
+            unsigned int binding_index = SDL_spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding);
+
+            binding.stage = executionModel;
+            binding.desc_set = descriptor_set_index;
+            binding.binding = binding_index;
+            binding.msl_buffer = num_buffers + binding_index;
+            SDL_spvc_compiler_msl_add_resource_binding(compiler, &binding);
+            if (result < 0) {
+                SPVC_ERROR(spvc_compiler_msl_add_resource_binding);
+                SDL_spvc_context_destroy(context);
+                return NULL;
+            }
+
+            num_buffers += 1;
+        }
+    }
+
     result = SDL_spvc_compiler_install_compiler_options(compiler, options);
     if (result < 0) {
         SPVC_ERROR(spvc_compiler_install_compiler_options);
@@ -847,7 +1300,7 @@ static void *SDL_ShaderCross_INTERNAL_CompileFromSPIRV(
     SDL_GPUDevice *device,
     SDL_GPUShaderFormat shaderFormat,
     const void *originalCreateInfo,
-    bool isCompute
+    SDL_ShaderCross_ShaderStage shaderStage
 ) {
     const SDL_GPUShaderCreateInfo *createInfo;
     spvc_backend backend;
@@ -875,6 +1328,7 @@ static void *SDL_ShaderCross_INTERNAL_CompileFromSPIRV(
     SPIRVTranspileContext *transpileContext = SDL_ShaderCross_INTERNAL_TranspileFromSPIRV(
         backend,
         shadermodel,
+        shaderStage,
         createInfo->code,
         createInfo->code_size,
         createInfo->entrypoint);
@@ -882,7 +1336,7 @@ static void *SDL_ShaderCross_INTERNAL_CompileFromSPIRV(
     void *shaderObject = NULL;
 
     /* Copy the original create info, but with the new source code */
-    if (isCompute) {
+    if (shaderStage == SDL_SHADERCROSS_SHADERSTAGE_COMPUTE) {
         SDL_GPUComputePipelineCreateInfo newCreateInfo;
         newCreateInfo = *(const SDL_GPUComputePipelineCreateInfo *)createInfo;
 
@@ -948,11 +1402,13 @@ static void *SDL_ShaderCross_INTERNAL_CompileFromSPIRV(
 void *SDL_ShaderCross_TranspileMSLFromSPIRV(
     const Uint8 *bytecode,
     size_t bytecodeSize,
-    const char *entrypoint)
+    const char *entrypoint,
+    SDL_ShaderCross_ShaderStage shaderStage)
 {
     SPIRVTranspileContext *context = SDL_ShaderCross_INTERNAL_TranspileFromSPIRV(
         SPVC_BACKEND_MSL,
         0,
+        shaderStage,
         bytecode,
         bytecodeSize,
         entrypoint
@@ -986,6 +1442,7 @@ void *SDL_ShaderCross_TranspileHLSLFromSPIRV(
     SPIRVTranspileContext *context = SDL_ShaderCross_INTERNAL_TranspileFromSPIRV(
         SPVC_BACKEND_HLSL,
         shadermodel,
+        0, // unused for HLSL
         bytecode,
         bytecodeSize,
         entrypoint
@@ -1009,6 +1466,7 @@ void *SDL_ShaderCross_CompileDXBCFromSPIRV(
     SPIRVTranspileContext *context = SDL_ShaderCross_INTERNAL_TranspileFromSPIRV(
         SPVC_BACKEND_HLSL,
         50,
+        0, // unused for HLSL
         bytecode,
         bytecodeSize,
         entrypoint);
@@ -1042,6 +1500,7 @@ void *SDL_ShaderCross_CompileDXILFromSPIRV(
     SPIRVTranspileContext *context = SDL_ShaderCross_INTERNAL_TranspileFromSPIRV(
         SPVC_BACKEND_HLSL,
         60,
+        0, // unused for HLSL
         bytecode,
         bytecodeSize,
         entrypoint);
@@ -1068,14 +1527,14 @@ void *SDL_ShaderCross_CompileDXILFromSPIRV(
 static void *SDL_ShaderCross_INTERNAL_CreateShaderFromSPIRV(
     SDL_GPUDevice *device,
     const void *originalCreateInfo,
-    bool isCompute)
+    SDL_ShaderCross_ShaderStage shaderStage)
 {
     SDL_GPUShaderFormat format;
 
     SDL_GPUShaderFormat shader_formats = SDL_GetGPUShaderFormats(device);
 
     if (shader_formats & SDL_GPU_SHADERFORMAT_SPIRV) {
-        if (isCompute) {
+        if (shaderStage == SDL_SHADERCROSS_SHADERSTAGE_COMPUTE) {
             return SDL_CreateGPUComputePipeline(device, (const SDL_GPUComputePipelineCreateInfo *)originalCreateInfo);
         } else {
             return SDL_CreateGPUShader(device, (const SDL_GPUShaderCreateInfo *)originalCreateInfo);
@@ -1095,21 +1554,21 @@ static void *SDL_ShaderCross_INTERNAL_CreateShaderFromSPIRV(
         device,
         format,
         originalCreateInfo,
-        isCompute);
+        shaderStage);
 }
 
 SDL_GPUShader *SDL_ShaderCross_CompileGraphicsShaderFromSPIRV(
     SDL_GPUDevice *device,
     const SDL_GPUShaderCreateInfo *createInfo)
 {
-    return (SDL_GPUShader *)SDL_ShaderCross_INTERNAL_CreateShaderFromSPIRV(device, createInfo, false);
+    return (SDL_GPUShader *)SDL_ShaderCross_INTERNAL_Cre

(Patch may be truncated, please check the link at the top of this post.)