SDL_gpu_shadercross: Refactor to allow HLSL destination

From 03b1168085549f16c61abbc94569127389918458 Mon Sep 17 00:00:00 2001
From: cosmonaut <[EMAIL REDACTED]>
Date: Fri, 25 Oct 2024 11:34:38 -0700
Subject: [PATCH] Refactor to allow HLSL destination

---
 include/SDL_gpu_shadercross.h |  22 ++-
 src/SDL_gpu_shadercross.c     | 332 +++++++++++++++++++---------------
 src/cli.c                     | 113 +++++++++---
 3 files changed, 287 insertions(+), 180 deletions(-)

diff --git a/include/SDL_gpu_shadercross.h b/include/SDL_gpu_shadercross.h
index a0eb9b6..27cff7e 100644
--- a/include/SDL_gpu_shadercross.h
+++ b/include/SDL_gpu_shadercross.h
@@ -76,6 +76,7 @@ extern SDL_DECLSPEC SDL_GPUShaderFormat SDLCALL SDL_ShaderCross_GetSPIRVShaderFo
  * \param bytecodeSize the length of the SPIRV bytecode.
  * \param entrypoint the entry point function name for the shader in UTF-8.
  * \param shaderStage the shader stage to transpile the shader with.
+ * \returns an SDL_malloc'd string containing MSL code.
  */
 extern SDL_DECLSPEC void * SDLCALL SDL_ShaderCross_TranspileMSLFromSPIRV(
     const Uint8 *bytecode,
@@ -83,6 +84,23 @@ extern SDL_DECLSPEC void * SDLCALL SDL_ShaderCross_TranspileMSLFromSPIRV(
     const char *entrypoint,
     SDL_ShaderCross_ShaderStage shaderStage);
 
+/**
+ * Transpile to HLSL code from SPIRV code.
+ *
+ * You must SDL_free the returned string once you are done with it.
+ *
+ * \param bytecode the SPIRV bytecode.
+ * \param bytecodeSize the length of the SPIRV bytecode.
+ * \param entrypoint the entry point function name for the shader in UTF-8.
+ * \param shaderProfile the HLSL shader profile to transpile with.
+ * \returns an SDL_malloc'd string containing HLSL code.
+ */
+extern SDL_DECLSPEC void * SDLCALL SDL_ShaderCross_TranspileHLSLFromSPIRV(
+    const Uint8 *bytecode,
+    size_t bytecodeSize,
+    const char *entrypoint,
+    const char *shaderProfile);
+
 /**
  * Compile DXBC bytecode from SPIRV code.
  *
@@ -91,7 +109,7 @@ extern SDL_DECLSPEC void * SDLCALL SDL_ShaderCross_TranspileMSLFromSPIRV(
  * \param bytecode the SPIRV bytecode.
  * \param bytecodeSize the length of the SPIRV bytecode.
  * \param entrypoint the entry point function name for the shader in UTF-8.
- * \param shaderStage the shader stage to transpile the shader with.
+ * \param shaderStage the shader stage to compile the shader with.
  * \param size filled in with the bytecode buffer size.
  */
 extern SDL_DECLSPEC void * SDLCALL SDL_ShaderCross_CompileDXBCFromSPIRV(
@@ -109,7 +127,7 @@ extern SDL_DECLSPEC void * SDLCALL SDL_ShaderCross_CompileDXBCFromSPIRV(
  * \param bytecode the SPIRV bytecode.
  * \param bytecodeSize the length of the SPIRV bytecode.
  * \param entrypoint the entry point function name for the shader in UTF-8.
- * \param shaderStage the shader stage to transpile the shader with.
+ * \param shaderStage the shader stage to compile the shader with.
  * \param size filled in with the bytecode buffer size.
  */
 extern SDL_DECLSPEC void * SDLCALL SDL_ShaderCross_CompileDXILFromSPIRV(
diff --git a/src/SDL_gpu_shadercross.c b/src/SDL_gpu_shadercross.c
index c93672e..862da93 100644
--- a/src/SDL_gpu_shadercross.c
+++ b/src/SDL_gpu_shadercross.c
@@ -748,37 +748,35 @@ static pfn_spvc_compiler_get_cleansed_entry_point_name SDL_spvc_compiler_get_cle
 #define SPVC_ERROR(func) \
     SDL_SetError(#func " failed: %s", SDL_spvc_context_get_last_error_string(context))
 
-// Returns a malloc'd CreateInfo struct.
-// It also malloc's the bytecode pointer and entrypoint string.
-static void *SDL_ShaderCross_INTERNAL_TranslateFromSPIRV(
-    SDL_GPUShaderFormat shaderFormat,
-    const void *originalCreateInfo,
-    bool isCompute
+typedef struct SPIRVTranspileContext {
+    spvc_context context;
+    const char *translated_source;
+    const char *cleansed_entrypoint;
+} SPIRVTranspileContext;
+
+static void SDL_ShaderCross_INTERNAL_DestroyTranspileContext(
+    SPIRVTranspileContext *context)
+{
+    SDL_spvc_context_destroy(context->context);
+    SDL_free(context);
+}
+
+static SPIRVTranspileContext *SDL_ShaderCross_INTERNAL_TranspileFromSPIRV(
+    spvc_backend backend,
+    unsigned shadermodel,
+    const Uint8 *code,
+    size_t codeSize,
+    const char *entrypoint
 ) {
-    const SDL_GPUShaderCreateInfo *createInfo;
     spvc_result result;
-    spvc_backend backend;
-    unsigned shadermodel;
     spvc_context context = NULL;
     spvc_parsed_ir ir = NULL;
     spvc_compiler compiler = NULL;
     spvc_compiler_options options = NULL;
+    SPIRVTranspileContext *transpileContext = NULL;
     const char *translated_source;
     const char *cleansed_entrypoint;
 
-    if (shaderFormat == SDL_GPU_SHADERFORMAT_DXBC) {
-        backend = SPVC_BACKEND_HLSL;
-        shadermodel = 50;
-    } else if (shaderFormat == SDL_GPU_SHADERFORMAT_DXIL) {
-        backend = SPVC_BACKEND_HLSL;
-        shadermodel = 60;
-    } else if (shaderFormat == SDL_GPU_SHADERFORMAT_MSL) {
-        backend = SPVC_BACKEND_MSL;
-    } else {
-        SDL_SetError("SDL_ShaderCross_INTERNAL_CreateShaderFromSPIRV: Unexpected SDL_GPUBackend");
-        return NULL;
-    }
-
     /* Create the SPIRV-Cross context */
     result = SDL_spvc_context_create(&context);
     if (result < 0) {
@@ -786,14 +784,8 @@ static void *SDL_ShaderCross_INTERNAL_TranslateFromSPIRV(
         return NULL;
     }
 
-    /* SDL_GPUShaderCreateInfo and SDL_GPUComputePipelineCreateInfo
-     * share the same struct layout for their first 3 members, which
-     * is all we need to transpile them!
-     */
-    createInfo = (const SDL_GPUShaderCreateInfo *)originalCreateInfo;
-
     /* Parse the SPIR-V into IR */
-    result = SDL_spvc_context_parse_spirv(context, (const SpvId *)createInfo->code, createInfo->code_size / sizeof(SpvId), &ir);
+    result = SDL_spvc_context_parse_spirv(context, (const SpvId *)code, codeSize / sizeof(SpvId), &ir);
     if (result < 0) {
         SPVC_ERROR(spvc_context_parse_spirv);
         SDL_spvc_context_destroy(context);
@@ -840,145 +832,171 @@ static void *SDL_ShaderCross_INTERNAL_TranslateFromSPIRV(
     /* Determine the "cleansed" entrypoint name (e.g. main -> main0 on MSL) */
     cleansed_entrypoint = SDL_spvc_compiler_get_cleansed_entry_point_name(
         compiler,
-        createInfo->entrypoint,
+        entrypoint,
         SDL_spvc_compiler_get_execution_model(compiler));
 
-    void *translatedCreateInfo = NULL;
+    transpileContext = SDL_malloc(sizeof(SPIRVTranspileContext));
+    transpileContext->context = context;
+    transpileContext->cleansed_entrypoint = cleansed_entrypoint;
+    transpileContext->translated_source = translated_source;
+    return transpileContext;
+}
+
+static void *SDL_ShaderCross_INTERNAL_CompileFromSPIRV(
+    SDL_GPUDevice *device,
+    SDL_GPUShaderFormat shaderFormat,
+    const void *originalCreateInfo,
+    bool isCompute
+) {
+    const SDL_GPUShaderCreateInfo *createInfo;
+    spvc_backend backend;
+    unsigned shadermodel = 0;
+
+    if (shaderFormat == SDL_GPU_SHADERFORMAT_DXBC) {
+        backend = SPVC_BACKEND_HLSL;
+        shadermodel = 50;
+    } else if (shaderFormat == SDL_GPU_SHADERFORMAT_DXIL) {
+        backend = SPVC_BACKEND_HLSL;
+        shadermodel = 60;
+    } else if (shaderFormat == SDL_GPU_SHADERFORMAT_MSL) {
+        backend = SPVC_BACKEND_MSL;
+    } else {
+        SDL_SetError("SDL_ShaderCross_INTERNAL_CreateShaderFromSPIRV: Unexpected SDL_GPUBackend");
+        return NULL;
+    }
+
+    /* SDL_GPUShaderCreateInfo and SDL_GPUComputePipelineCreateInfo
+     * share the same struct layout for their first 3 members, which
+     * is all we need to transpile them!
+     */
+    createInfo = (const SDL_GPUShaderCreateInfo *)originalCreateInfo;
+
+    SPIRVTranspileContext *transpileContext = SDL_ShaderCross_INTERNAL_TranspileFromSPIRV(
+        backend,
+        shadermodel,
+        createInfo->code,
+        createInfo->code_size,
+        createInfo->entrypoint);
+
+    void *shaderObject = NULL;
 
     /* Copy the original create info, but with the new source code */
     if (isCompute) {
-        SDL_GPUComputePipelineCreateInfo *newCreateInfo = SDL_malloc(sizeof(SDL_GPUComputePipelineCreateInfo));
-        SDL_memcpy(newCreateInfo, createInfo, sizeof(SDL_GPUComputePipelineCreateInfo));
-
-        newCreateInfo->format = shaderFormat;
+        SDL_GPUComputePipelineCreateInfo newCreateInfo;
+        newCreateInfo = *(const SDL_GPUComputePipelineCreateInfo *)createInfo;
 
-        size_t cleansed_entrypoint_length = SDL_utf8strlen(cleansed_entrypoint) + 1;
-        newCreateInfo->entrypoint = SDL_malloc(cleansed_entrypoint_length);
-        SDL_utf8strlcpy((char *)newCreateInfo->entrypoint, cleansed_entrypoint, cleansed_entrypoint_length);
+        newCreateInfo.entrypoint = transpileContext->cleansed_entrypoint;
+        newCreateInfo.format = shaderFormat;
 
         if (shaderFormat == SDL_GPU_SHADERFORMAT_DXBC) {
-            newCreateInfo->code = SDL_ShaderCross_CompileDXBCFromHLSL(
-                translated_source,
-                cleansed_entrypoint,
+            newCreateInfo.code = SDL_ShaderCross_CompileDXBCFromHLSL(
+                transpileContext->translated_source,
+                transpileContext->cleansed_entrypoint,
                 (shadermodel == 50) ? "cs_5_0" : "cs_6_0",
-                &newCreateInfo->code_size);
+                &newCreateInfo.code_size);
         } else if (shaderFormat == SDL_GPU_SHADERFORMAT_DXIL) {
-            newCreateInfo->code = SDL_ShaderCross_CompileDXILFromHLSL(
-                translated_source,
-                cleansed_entrypoint,
+            newCreateInfo.code = SDL_ShaderCross_CompileDXILFromHLSL(
+                transpileContext->translated_source,
+                transpileContext->cleansed_entrypoint,
                 (shadermodel == 50) ? "cs_5_0" : "cs_6_0",
-                &newCreateInfo->code_size);
+                &newCreateInfo.code_size);
         } else { // MSL
-            newCreateInfo->code_size = SDL_strlen(translated_source) + 1;
-            newCreateInfo->code = SDL_malloc(newCreateInfo->code_size);
-            SDL_strlcpy((char *)newCreateInfo->code, translated_source, newCreateInfo->code_size);
+            newCreateInfo.code = (const Uint8 *)transpileContext->translated_source;
+            newCreateInfo.code_size = SDL_strlen(transpileContext->translated_source) + 1;
         }
 
-        translatedCreateInfo = newCreateInfo;
+        shaderObject = SDL_CreateGPUComputePipeline(device, &newCreateInfo);
     } else {
-        SDL_GPUShaderCreateInfo *newCreateInfo = SDL_malloc(sizeof(SDL_GPUShaderCreateInfo));
-        SDL_memcpy(newCreateInfo, createInfo, sizeof(SDL_GPUShaderCreateInfo));
-
-        newCreateInfo->format = shaderFormat;
+        SDL_GPUShaderCreateInfo newCreateInfo;
+        newCreateInfo = *createInfo;
 
-        size_t cleansed_entrypoint_length = SDL_utf8strlen(cleansed_entrypoint) + 1;
-        newCreateInfo->entrypoint = SDL_malloc(cleansed_entrypoint_length);
-        SDL_utf8strlcpy((char *)newCreateInfo->entrypoint, cleansed_entrypoint, cleansed_entrypoint_length);
+        newCreateInfo.format = shaderFormat;
+        newCreateInfo.entrypoint = transpileContext->cleansed_entrypoint;
 
         const char *profile;
-        if (newCreateInfo->stage == SDL_GPU_SHADERSTAGE_VERTEX) {
+        if (newCreateInfo.stage == SDL_GPU_SHADERSTAGE_VERTEX) {
             profile = (shadermodel == 50) ? "vs_5_0" : "vs_6_0";
         } else {
             profile = (shadermodel == 50) ? "ps_5_0" : "ps_6_0";
         }
 
         if (shaderFormat == SDL_GPU_SHADERFORMAT_DXBC) {
-            newCreateInfo->code = SDL_ShaderCross_CompileDXBCFromHLSL(
-                translated_source,
-                cleansed_entrypoint,
+            newCreateInfo.code = SDL_ShaderCross_CompileDXBCFromHLSL(
+                transpileContext->translated_source,
+                transpileContext->cleansed_entrypoint,
                 profile,
-                &newCreateInfo->code_size);
+                &newCreateInfo.code_size);
         } else if (shaderFormat == SDL_GPU_SHADERFORMAT_DXIL) {
-            newCreateInfo->code = SDL_ShaderCross_CompileDXILFromHLSL(
-                translated_source,
-                cleansed_entrypoint,
+            newCreateInfo.code = SDL_ShaderCross_CompileDXILFromHLSL(
+                transpileContext->translated_source,
+                transpileContext->cleansed_entrypoint,
                 profile,
-                &newCreateInfo->code_size);
+                &newCreateInfo.code_size);
         } else { // MSL
-            newCreateInfo->code_size = SDL_strlen(translated_source) + 1;
-            newCreateInfo->code = SDL_malloc(newCreateInfo->code_size);
-            SDL_strlcpy((char *)newCreateInfo->code, translated_source, newCreateInfo->code_size);
+            newCreateInfo.code = (const Uint8 *)transpileContext->translated_source;
+            newCreateInfo.code_size = SDL_strlen(transpileContext->translated_source) + 1;
         }
 
-        translatedCreateInfo = newCreateInfo;
+        shaderObject = SDL_CreateGPUShader(device, &newCreateInfo);
     }
 
-    SDL_spvc_context_destroy(context);
-    return translatedCreateInfo;
+    SDL_ShaderCross_INTERNAL_DestroyTranspileContext(transpileContext);
+    return shaderObject;
 }
 
-static void *SDL_ShaderCross_INTERNAL_CompileFromSPIRV(
-    SDL_GPUShaderFormat format,
+void *SDL_ShaderCross_TranspileMSLFromSPIRV(
     const Uint8 *bytecode,
     size_t bytecodeSize,
     const char *entrypoint,
-    SDL_ShaderCross_ShaderStage shaderStage,
-    size_t *size)
+    SDL_ShaderCross_ShaderStage shaderStage)
 {
-    if (shaderStage == SDL_SHADERCROSS_SHADERSTAGE_COMPUTE) {
-        SDL_GPUComputePipelineCreateInfo createInfo;
-        createInfo.code = bytecode;
-        createInfo.code_size = bytecodeSize;
-        createInfo.entrypoint = entrypoint;
-        createInfo.format = SDL_GPU_SHADERFORMAT_SPIRV;
-
-        SDL_GPUComputePipelineCreateInfo *translatedCreateInfo = (SDL_GPUComputePipelineCreateInfo *)SDL_ShaderCross_INTERNAL_TranslateFromSPIRV(
-            format,
-            &createInfo,
-            true);
-
-        void *result = (void *)translatedCreateInfo->code;
-        *size = translatedCreateInfo->code_size;
-        SDL_free((void *)translatedCreateInfo->entrypoint);
-        SDL_free(translatedCreateInfo);
-
-        return result;
-    } else {
-        SDL_GPUShaderCreateInfo createInfo;
-        createInfo.code = bytecode;
-        createInfo.code_size = bytecodeSize;
-        createInfo.entrypoint = entrypoint;
-        createInfo.format = SDL_GPU_SHADERFORMAT_SPIRV;
-        createInfo.stage = (SDL_GPUShaderStage)shaderStage;
-
-        SDL_GPUShaderCreateInfo *translatedCreateInfo = (SDL_GPUShaderCreateInfo *)SDL_ShaderCross_INTERNAL_TranslateFromSPIRV(
-            format,
-            &createInfo,
-            false);
-
-        void *result = (void *)translatedCreateInfo->code;
-        *size = translatedCreateInfo->code_size;
-        SDL_free((void *)translatedCreateInfo->entrypoint);
-        SDL_free(translatedCreateInfo);
-
-        return result;
-    }
+    SPIRVTranspileContext *context = SDL_ShaderCross_INTERNAL_TranspileFromSPIRV(
+        SPVC_BACKEND_MSL,
+        0,
+        bytecode,
+        bytecodeSize,
+        entrypoint
+    );
+
+    size_t length = SDL_strlen(context->translated_source) + 1;
+    char *result = SDL_malloc(length);
+    SDL_strlcpy(result, context->translated_source, length);
+
+    SDL_ShaderCross_INTERNAL_DestroyTranspileContext(context);
+    return result;
 }
 
-void *SDL_ShaderCross_TranspileMSLFromSPIRV(
+void *SDL_ShaderCross_TranspileHLSLFromSPIRV(
     const Uint8 *bytecode,
     size_t bytecodeSize,
     const char *entrypoint,
-    SDL_ShaderCross_ShaderStage shaderStage)
+    const char *shaderProfile)
 {
-    size_t size;
-    return SDL_ShaderCross_INTERNAL_CompileFromSPIRV(
-        SDL_GPU_SHADERFORMAT_MSL,
+    unsigned int shadermodel = 0;
+
+    if (SDL_strstr(shaderProfile, "5_0")) {
+        shadermodel = 50;
+    } else if (SDL_strstr(shaderProfile, "6_0")) {
+        shadermodel = 60;
+    } else {
+        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", "Invalid shader profile!");
+        return NULL;
+    }
+
+    SPIRVTranspileContext *context = SDL_ShaderCross_INTERNAL_TranspileFromSPIRV(
+        SPVC_BACKEND_HLSL,
+        shadermodel,
         bytecode,
         bytecodeSize,
-        entrypoint,
-        shaderStage,
-        &size);
+        entrypoint
+    );
+
+    size_t length = SDL_strlen(context->translated_source) + 1;
+    char *result = SDL_malloc(length);
+    SDL_strlcpy(result, context->translated_source, length);
+
+    SDL_ShaderCross_INTERNAL_DestroyTranspileContext(context);
+    return result;
 }
 
 void *SDL_ShaderCross_CompileDXBCFromSPIRV(
@@ -988,13 +1006,30 @@ void *SDL_ShaderCross_CompileDXBCFromSPIRV(
     SDL_ShaderCross_ShaderStage shaderStage,
     size_t *size)
 {
-    return SDL_ShaderCross_INTERNAL_CompileFromSPIRV(
-        SDL_GPU_SHADERFORMAT_DXBC,
+    SPIRVTranspileContext *context = SDL_ShaderCross_INTERNAL_TranspileFromSPIRV(
+        SPVC_BACKEND_HLSL,
+        50,
         bytecode,
         bytecodeSize,
-        entrypoint,
-        shaderStage,
+        entrypoint);
+
+    const char *profile;
+    if (shaderStage == SDL_SHADERCROSS_SHADERSTAGE_VERTEX) {
+        profile = "vs_5_0";
+    } else if (shaderStage == SDL_SHADERCROSS_SHADERSTAGE_FRAGMENT) {
+        profile = "ps_5_0";
+    } else { // compute
+        profile = "cs_5_0";
+    }
+
+    void *result = SDL_ShaderCross_CompileDXBCFromHLSL(
+        context->translated_source,
+        context->cleansed_entrypoint,
+        profile,
         size);
+
+    SDL_ShaderCross_INTERNAL_DestroyTranspileContext(context);
+    return result;
 }
 
 void *SDL_ShaderCross_CompileDXILFromSPIRV(
@@ -1004,13 +1039,30 @@ void *SDL_ShaderCross_CompileDXILFromSPIRV(
     SDL_ShaderCross_ShaderStage shaderStage,
     size_t *size)
 {
-    return SDL_ShaderCross_INTERNAL_CompileFromSPIRV(
-        SDL_GPU_SHADERFORMAT_DXIL,
+    SPIRVTranspileContext *context = SDL_ShaderCross_INTERNAL_TranspileFromSPIRV(
+        SPVC_BACKEND_HLSL,
+        60,
         bytecode,
         bytecodeSize,
-        entrypoint,
-        shaderStage,
+        entrypoint);
+
+    const char *profile;
+    if (shaderStage == SDL_SHADERCROSS_SHADERSTAGE_VERTEX) {
+        profile = "vs_6_0";
+    } else if (shaderStage == SDL_SHADERCROSS_SHADERSTAGE_FRAGMENT) {
+        profile = "ps_6_0";
+    } else { // compute
+        profile = "cs_6_0";
+    }
+
+    void *result = SDL_ShaderCross_CompileDXILFromHLSL(
+        context->translated_source,
+        context->cleansed_entrypoint,
+        profile,
         size);
+
+    SDL_ShaderCross_INTERNAL_DestroyTranspileContext(context);
+    return result;
 }
 
 static void *SDL_ShaderCross_INTERNAL_CreateShaderFromSPIRV(
@@ -1019,7 +1071,6 @@ static void *SDL_ShaderCross_INTERNAL_CreateShaderFromSPIRV(
     bool isCompute)
 {
     SDL_GPUShaderFormat format;
-    void *compiledResult;
 
     SDL_GPUShaderFormat shader_formats = SDL_GetGPUShaderFormats(device);
 
@@ -1040,26 +1091,11 @@ static void *SDL_ShaderCross_INTERNAL_CreateShaderFromSPIRV(
         return NULL;
     }
 
-    void *translatedCreateInfo = SDL_ShaderCross_INTERNAL_TranslateFromSPIRV(
+    return SDL_ShaderCross_INTERNAL_CompileFromSPIRV(
+        device,
         format,
         originalCreateInfo,
         isCompute);
-
-    if (isCompute) {
-        SDL_GPUComputePipelineCreateInfo *computeCreateInfo = (SDL_GPUComputePipelineCreateInfo *)translatedCreateInfo;
-        compiledResult = SDL_CreateGPUComputePipeline(device, computeCreateInfo);
-        SDL_free((void *)computeCreateInfo->entrypoint);
-        SDL_free((void *)computeCreateInfo->code);
-        SDL_free(computeCreateInfo);
-    } else {
-        SDL_GPUShaderCreateInfo *shaderCreateInfo = (SDL_GPUShaderCreateInfo *)translatedCreateInfo;
-        compiledResult = SDL_CreateGPUShader(device, shaderCreateInfo);
-        SDL_free((void *)shaderCreateInfo->entrypoint);
-        SDL_free((void *)shaderCreateInfo->code);
-        SDL_free(shaderCreateInfo);
-    }
-
-    return compiledResult;
 }
 
 SDL_GPUShader *SDL_ShaderCross_CompileGraphicsShaderFromSPIRV(
diff --git a/src/cli.c b/src/cli.c
index ac28eea..d9be75a 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -23,16 +23,26 @@
 #include <SDL3/SDL_log.h>
 #include <SDL3/SDL_iostream.h>
 
+// We can emit HLSL as a destination, so let's redefine the shader format enum.
+typedef enum ShaderCross_DestinationFormat {
+    SHADERFORMAT_INVALID,
+    SHADERFORMAT_SPIRV,
+    SHADERFORMAT_DXBC,
+    SHADERFORMAT_DXIL,
+    SHADERFORMAT_MSL,
+    SHADERFORMAT_HLSL
+} ShaderCross_ShaderFormat;
+
 void print_help()
 {
     int column_width = 32;
     SDL_Log("%s", "Usage: shadercross <input> [options]");
     SDL_Log("%s", "Required options:\n");
     SDL_Log("  %-*s %s", column_width, "-s | --source <value>", "Source language format. May be inferred from the filename. Values: [SPIRV, HLSL]");
-    SDL_Log("  %-*s %s", column_width, "-d | --dest <value>", "Destination format. May be inferred from the filename. Values: [DXBC, DXIL, MSL, SPIRV]");
+    SDL_Log("  %-*s %s", column_width, "-d | --dest <value>", "Destination format. May be inferred from the filename. Values: [DXBC, DXIL, MSL, SPIRV, HLSL]");
     SDL_Log("  %-*s %s", column_width, "-t | --stage <value>", "Shader stage. May be inferred from the filename. Values: [vertex, fragment, compute]");
     SDL_Log("  %-*s %s", column_width, "-e | --entrypoint <value>", "Entrypoint function name. Default: \"main\".");
-    SDL_Log("  %-*s %s", column_width, "-m | --shadermodel <value>", "HLSL Shader Model. Only used with HLSL source. Values: [50, 51, 60]");
+    SDL_Log("  %-*s %s", column_width, "-m | --shadermodel <value>", "HLSL Shader Model. Only used with HLSL source or destination. Values: [50, 60]");
     SDL_Log("  %-*s %s", column_width, "-o | --output <value>", "Output file.");
 }
 
@@ -44,7 +54,7 @@ int main(int argc, char *argv[])
     bool stageValid = false;
 
     bool spirvSource = false;
-    SDL_GPUShaderFormat destinationFormat = SDL_GPU_SHADERFORMAT_INVALID;
+    ShaderCross_ShaderFormat destinationFormat = SHADERFORMAT_INVALID;
     SDL_ShaderCross_ShaderStage shaderStage = SDL_SHADERCROSS_SHADERSTAGE_VERTEX;
     char *outputFilename = NULL;
     char *entrypointName = "main";
@@ -88,16 +98,16 @@ int main(int argc, char *argv[])
                 }
                 i += 1;
                 if (SDL_strcasecmp(argv[i], "DXBC") == 0) {
-                    destinationFormat = SDL_GPU_SHADERFORMAT_DXBC;
+                    destinationFormat = SHADERFORMAT_DXBC;
                     destinationValid = true;
                 } else if (SDL_strcasecmp(argv[i], "DXIL") == 0) {
-                    destinationFormat = SDL_GPU_SHADERFORMAT_DXIL;
+                    destinationFormat = SHADERFORMAT_DXIL;
                     destinationValid = true;
                 } else if (SDL_strcasecmp(argv[i], "MSL") == 0) {
-                    destinationFormat = SDL_GPU_SHADERFORMAT_MSL;
+                    destinationFormat = SHADERFORMAT_MSL;
                     destinationValid = true;
                 } else if (SDL_strcasecmp(argv[i], "SPIRV") == 0) {
-                    destinationFormat = SDL_GPU_SHADERFORMAT_SPIRV;
+                    destinationFormat = SHADERFORMAT_SPIRV;
                     destinationValid = true;
                 } else {
                     SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unrecognized destination input %s, destination must be DXBC, DXIL, MSL or SPIRV!", argv[i]);
@@ -153,14 +163,14 @@ int main(int argc, char *argv[])
                 accept_optionals = false;
             } else {
                 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s: Unknown argument: %s", argv[0], arg);
-                print_help(1);
+                print_help();
                 return 1;
             }
         } else if (!filename) {
             filename = arg;
         } else {
             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s: Unknown argument: %s", argv[0], arg);
-            print_help(1);
+            print_help();
             return 1;
         }
     }
@@ -194,13 +204,15 @@ int main(int argc, char *argv[])
 
     if (!destinationValid) {
         if (SDL_strstr(outputFilename, ".dxbc")) {
-            destinationFormat = SDL_GPU_SHADERFORMAT_DXBC;
+            destinationFormat = SHADERFORMAT_DXBC;
         } else if (SDL_strstr(outputFilename, ".dxil")) {
-            destinationFormat = SDL_GPU_SHADERFORMAT_DXIL;
+            destinationFormat = SHADERFORMAT_DXIL;
         } else if (SDL_strstr(outputFilename, ".msl")) {
-            destinationFormat = SDL_GPU_SHADERFORMAT_MSL;
+            destinationFormat = SHADERFORMAT_MSL;
         } else if (SDL_strstr(outputFilename, ".spv")) {
-            destinationFormat = SDL_GPU_SHADERFORMAT_SPIRV;
+            destinationFormat = SHADERFORMAT_SPIRV;
+        } else if (SDL_strstr(outputFilename, ".hlsl")) {
+            destinationFormat = SHADERFORMAT_HLSL;
         } else {
             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", "Could not infer destination format!");
             print_help();
@@ -233,7 +245,7 @@ int main(int argc, char *argv[])
 
     if (spirvSource) {
         switch (destinationFormat) {
-            case SDL_GPU_SHADERFORMAT_DXBC: {
+            case SHADERFORMAT_DXBC: {
                 Uint8 *buffer = SDL_ShaderCross_CompileDXBCFromSPIRV(
                     fileData,
                     fileSize,
@@ -247,7 +259,7 @@ int main(int argc, char *argv[])
                 break;
             }
 
-            case SDL_GPU_SHADERFORMAT_DXIL: {
+            case SHADERFORMAT_DXIL: {
                 Uint8 *buffer = SDL_ShaderCross_CompileDXILFromSPIRV(
                     fileData,
                     fileSize,
@@ -261,21 +273,60 @@ int main(int argc, char *argv[])
                 break;
             }
 
-            case SDL_GPU_SHADERFORMAT_MSL: {
+            case SHADERFORMAT_MSL: {
                 char *buffer = SDL_ShaderCross_TranspileMSLFromSPIRV(
                     fileData,
                     fileSize,
                     entrypointName,
-                    SDL_SHADERCROSS_SHADERSTAGE_VERTEX);
+                    shaderStage);
+                SDL_IOprintf(outputIO, "%s", buffer);
+                SDL_free(buffer);
+                break;
+            }
+
+            case SHADERFORMAT_HLSL: {
+                char *profileName;
+                if (shaderModel == 50) {
+                    if (shaderStage == SDL_SHADERCROSS_SHADERSTAGE_VERTEX) {
+                        profileName = "vs_5_0";
+                    } else if (shaderStage == SDL_SHADERCROSS_SHADERSTAGE_FRAGMENT) {
+                        profileName = "ps_5_0";
+                    } else {
+                        profileName = "cs_5_0";
+                    }
+                } else if (shaderModel == 60) {
+                    if (shaderStage == SDL_SHADERCROSS_SHADERSTAGE_VERTEX) {
+                        profileName = "vs_6_0";
+                    } else if (shaderStage == SDL_SHADERCROSS_SHADERSTAGE_FRAGMENT) {
+                        profileName = "ps_6_0";
+                    } else {
+                        profileName = "cs_6_0";
+                    }
+                } else {
+                    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", "Unrecognized shader model!");
+                    print_help();
+                    return 1;
+                }
+
+                char *buffer = SDL_ShaderCross_TranspileHLSLFromSPIRV(
+                    fileData,
+                    fileSize,
+                    entrypointName,
+                    profileName);
                 SDL_IOprintf(outputIO, "%s", buffer);
                 SDL_free(buffer);
                 break;
             }
 
-            case SDL_GPU_SHADERFORMAT_SPIRV: {
+            case SHADERFORMAT_SPIRV: {
                 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Input and output are both SPIRV. Did you mean to do that?");
                 return 1;
             }
+
+            case SHADERFORMAT_INVALID: {
+                SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Destination format not provided!");
+                return 1;
+            }
         }
     } else {
         char *profileName;
@@ -287,14 +338,6 @@ int main(int argc, char *argv[])
             } else {
                 profileName = "cs_5_0";
             }
-        } else if (shaderModel == 51) {
-            if (shaderStage == SDL_SHADERCROSS_SHADERSTAGE_VERTEX) {
-                profileName = "vs_5_1";
-            } else if (shaderStage == SDL_SHADERCROSS_SHADERSTAGE_FRAGMENT) {
-                profileName = "ps_5_1";
-            } else {
-                profileName = "cs_5_1";
-            }
         } else if (shaderModel == 60) {
             if (shaderStage == SDL_SHADERCROSS_SHADERSTAGE_VERTEX) {
                 profileName = "vs_6_0";
@@ -310,7 +353,7 @@ int main(int argc, char *argv[])
         }
 
         switch (destinationFormat) {
-            case SDL_GPU_SHADERFORMAT_DXBC: {
+            case SHADERFORMAT_DXBC: {
                 Uint8 *buffer = SDL_ShaderCross_CompileDXBCFromHLSL(
                     fileData,
                     entrypointName,
@@ -323,7 +366,7 @@ int main(int argc, char *argv[])
                 break;
             }
 
-            case SDL_GPU_SHADERFORMAT_DXIL: {
+            case SHADERFORMAT_DXIL: {
                 Uint8 *buffer = SDL_ShaderCross_CompileDXILFromHLSL(
                     fileData,
                     entrypointName,
@@ -336,7 +379,7 @@ int main(int argc, char *argv[])
                 break;
             }
 
-            case SDL_GPU_SHADERFORMAT_MSL: {
+            case SHADERFORMAT_MSL: {
                 void *spirv = SDL_ShaderCross_CompileSPIRVFromHLSL(
                     fileData,
                     entrypointName,
@@ -353,7 +396,7 @@ int main(int argc, char *argv[])
                 break;
             }
 
-            case SDL_GPU_SHADERFORMAT_SPIRV: {
+            case SHADERFORMAT_SPIRV: {
                 Uint8 *buffer = SDL_ShaderCross_CompileSPIRVFromHLSL(
                     fileData,
                     entrypointName,
@@ -365,6 +408,16 @@ int main(int argc, char *argv[])
                 SDL_free(buffer);
                 return 0;
             }
+
+            case SHADERFORMAT_HLSL: {
+                SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Input and output are both HLSL. Did you mean to do that?");
+                return 1;
+            }
+
+            case SHADERFORMAT_INVALID: {
+                SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Destination format not provided!");
+                return 1;
+            }
         }
     }
 

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