SDL_shadercross: Refactor parameters to info structs and add debug support (#71)

From 93876db239273006df362eca5af8b6533a699d78 Mon Sep 17 00:00:00 2001
From: Evan Hemsley <[EMAIL REDACTED]>
Date: Thu, 19 Dec 2024 13:49:56 -0800
Subject: [PATCH] Refactor parameters to info structs and add debug support
 (#71)

---
 include/SDL3_shadercross/SDL_shadercross.h | 230 +++-----
 src/SDL_shadercross.c                      | 618 ++++++++++-----------
 src/cli.c                                  | 188 ++++---
 3 files changed, 491 insertions(+), 545 deletions(-)

diff --git a/include/SDL3_shadercross/SDL_shadercross.h b/include/SDL3_shadercross/SDL_shadercross.h
index 08cc9e0..493e6a4 100644
--- a/include/SDL3_shadercross/SDL_shadercross.h
+++ b/include/SDL3_shadercross/SDL_shadercross.h
@@ -45,35 +45,66 @@ typedef enum SDL_ShaderCross_ShaderStage
    SDL_SHADERCROSS_SHADERSTAGE_COMPUTE
 } SDL_ShaderCross_ShaderStage;
 
-typedef struct SDL_ShaderCross_GraphicsShaderInfo
+typedef struct SDL_ShaderCross_GraphicsShaderMetadata
 {
-    Uint32 numSamplers;         /**< The number of samplers defined in the shader. */
-    Uint32 numStorageTextures;  /**< The number of storage textures defined in the shader. */
-    Uint32 numStorageBuffers;   /**< The number of storage buffers defined in the shader. */
-    Uint32 numUniformBuffers;   /**< The number of uniform buffers defined in the shader. */
-} SDL_ShaderCross_GraphicsShaderInfo;
+    Uint32 num_samplers;          /**< The number of samplers defined in the shader. */
+    Uint32 num_storage_textures;  /**< The number of storage textures defined in the shader. */
+    Uint32 num_storage_buffers;   /**< The number of storage buffers defined in the shader. */
+    Uint32 num_uniform_buffers;   /**< The number of uniform buffers defined in the shader. */
+} SDL_ShaderCross_GraphicsShaderMetadata;
 
-typedef struct SDL_ShaderCross_ComputePipelineInfo
+typedef struct SDL_ShaderCross_ComputePipelineMetadata
 {
-    Uint32 numSamplers;                  /**< The number of samplers defined in the shader. */
-    Uint32 numReadOnlyStorageTextures;   /**< The number of readonly storage textures defined in the shader. */
-    Uint32 numReadOnlyStorageBuffers;    /**< The number of readonly storage buffers defined in the shader. */
-    Uint32 numReadWriteStorageTextures;  /**< The number of read-write storage textures defined in the shader. */
-    Uint32 numReadWriteStorageBuffers;   /**< The number of read-write storage buffers defined in the shader. */
-    Uint32 numUniformBuffers;            /**< The number of uniform buffers defined in the shader. */
-    Uint32 threadCountX;                 /**< The number of threads in the X dimension. */
-    Uint32 threadCountY;                 /**< The number of threads in the Y dimension. */
-    Uint32 threadCountZ;                 /**< The number of threads in the Z dimension. */
-} SDL_ShaderCross_ComputePipelineInfo;
+    Uint32 num_samplers;                    /**< The number of samplers defined in the shader. */
+    Uint32 num_readonly_storage_textures;   /**< The number of readonly storage textures defined in the shader. */
+    Uint32 num_readonly_storage_buffers;    /**< The number of readonly storage buffers defined in the shader. */
+    Uint32 num_readwrite_storage_textures;  /**< The number of read-write storage textures defined in the shader. */
+    Uint32 num_readwrite_storage_buffers;   /**< The number of read-write storage buffers defined in the shader. */
+    Uint32 num_uniform_buffers;             /**< The number of uniform buffers defined in the shader. */
+    Uint32 threadcount_x;                   /**< The number of threads in the X dimension. */
+    Uint32 threadcount_y;                   /**< The number of threads in the Y dimension. */
+    Uint32 threadcount_z;                   /**< The number of threads in the Z dimension. */
+} SDL_ShaderCross_ComputePipelineMetadata;
+
+typedef struct SDL_ShaderCross_SPIRV_Info
+{
+    const Uint8 *bytecode;                     /**< The SPIRV bytecode. */
+    size_t bytecode_size;                      /**< The length of the SPIRV bytecode. */
+    const char *entrypoint;                    /**< The entry point function name for the shader in UTF-8. */
+    SDL_ShaderCross_ShaderStage shader_stage;  /**< The shader stage to transpile the shader with. */
+    bool enable_debug;                         /**< Allows debug info to be emitted when relevant. Can be useful for graphics debuggers like RenderDoc. */
+    const char *name;                          /**< A UTF-8 name to associate with the shader. Optional, can be NULL. */
+
+    SDL_PropertiesID props;                    /**< A properties ID for extensions. Should be 0 if no extensions are needed. */
+} SDL_ShaderCross_SPIRV_Info;
+
+typedef struct SDL_ShaderCross_HLSL_Define
+{
+    char *name;   /**< The define name. */
+    char *value;  /**< An optional value for the define. Can be NULL. */
+} SDL_ShaderCross_HLSL_Define;
+
+typedef struct SDL_ShaderCross_HLSL_Info
+{
+    const char *source;                        /**< The HLSL source code for the shader. */
+    const char *entrypoint;                    /**< The entry point function name for the shader in UTF-8. */
+    const char *include_dir;                   /**< The include directory for shader code. Optional, can be NULL. */
+    SDL_ShaderCross_HLSL_Define *defines;      /**< An array of defines. Optional, can be NULL. If not NULL, must be terminated with a fully NULL define struct. */
+    SDL_ShaderCross_ShaderStage shader_stage;  /**< The shader stage to compile the shader with. */
+    bool enable_debug;                         /**< Allows debug info to be emitted when relevant. Can be useful for graphics debuggers like RenderDoc. */
+    const char *name;                          /**< A UTF-8 name to associate with the shader. Optional, can be NULL. */
+
+    SDL_PropertiesID props;                    /**< A properties ID for extensions. Should be 0 if no extensions are needed. */
+} SDL_ShaderCross_HLSL_Info;
 
 /**
- * Initializes SDL_gpu_shadercross
+ * Initializes SDL_shadercross
  *
  * \threadsafety This should only be called once, from a single thread.
  */
 extern SDL_DECLSPEC bool SDLCALL SDL_ShaderCross_Init(void);
 /**
- * De-initializes SDL_gpu_shadercross
+ * De-initializes SDL_shadercross
  *
  * \threadsafety This should only be called once, from a single thread.
  */
@@ -91,52 +122,34 @@ extern SDL_DECLSPEC SDL_GPUShaderFormat SDLCALL SDL_ShaderCross_GetSPIRVShaderFo
  *
  * 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 shaderStage the shader stage to transpile the shader with.
+ * \param info a struct describing the shader to transpile.
  * \returns an SDL_malloc'd string containing MSL code.
  */
 extern SDL_DECLSPEC void * SDLCALL SDL_ShaderCross_TranspileMSLFromSPIRV(
-    const Uint8 *bytecode,
-    size_t bytecodeSize,
-    const char *entrypoint,
-    SDL_ShaderCross_ShaderStage shaderStage);
+    const SDL_ShaderCross_SPIRV_Info *info);
 
 /**
  * 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 shaderStage the shader stage to transpile the shader with.
- * \param shaderModel the shader model to transpile the shader with.
+ * \param info a struct describing the shader to transpile.
  * \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,
-    SDL_ShaderCross_ShaderStage shaderStage);
+    const SDL_ShaderCross_SPIRV_Info *info);
 
 /**
  * Compile DXBC bytecode from SPIRV code.
  *
  * You must SDL_free the returned buffer 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 shaderStage the shader stage to compile the shader with.
+ * \param info a struct describing the shader to transpile.
  * \param size filled in with the bytecode buffer size.
+ * \returns an SDL_malloc'd buffer containing DXBC bytecode.
  */
 extern SDL_DECLSPEC void * SDLCALL SDL_ShaderCross_CompileDXBCFromSPIRV(
-    const Uint8 *bytecode,
-    size_t bytecodeSize,
-    const char *entrypoint,
-    SDL_ShaderCross_ShaderStage shaderStage,
+    const SDL_ShaderCross_SPIRV_Info *info,
     size_t *size);
 
 /**
@@ -144,86 +157,71 @@ extern SDL_DECLSPEC void * SDLCALL SDL_ShaderCross_CompileDXBCFromSPIRV(
  *
  * You must SDL_free the returned buffer 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 shaderStage the shader stage to compile the shader with.
+ * \param info a struct describing the shader to transpile.
  * \param size filled in with the bytecode buffer size.
+ * \returns an SDL_malloc'd buffer containing DXIL bytecode.
  */
 extern SDL_DECLSPEC void * SDLCALL SDL_ShaderCross_CompileDXILFromSPIRV(
-    const Uint8 *bytecode,
-    size_t bytecodeSize,
-    const char *entrypoint,
-    SDL_ShaderCross_ShaderStage shaderStage,
+    const SDL_ShaderCross_SPIRV_Info *info,
     size_t *size);
 
 /**
  * Compile an SDL GPU shader from SPIRV code.
  *
  * \param device the SDL GPU device.
- * \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 compile the shader with.
- * \param info a pointer filled in with shader metadata.
+ * \param info a struct describing the shader to transpile.
+ * \param metadata a pointer filled in with shader metadata.
  * \returns a compiled SDL_GPUShader
  *
  * \threadsafety It is safe to call this function from any thread.
  */
 extern SDL_DECLSPEC SDL_GPUShader * SDLCALL SDL_ShaderCross_CompileGraphicsShaderFromSPIRV(
     SDL_GPUDevice *device,
-    const Uint8 *bytecode,
-    size_t bytecodeSize,
-    const char *entrypoint,
-    SDL_GPUShaderStage shaderStage,
-    SDL_ShaderCross_GraphicsShaderInfo *info);
+    const SDL_ShaderCross_SPIRV_Info *info,
+    SDL_ShaderCross_GraphicsShaderMetadata *metadata);
 
 /**
  * Compile an SDL GPU compute pipeline from SPIRV code.
  *
  * \param device the SDL GPU device.
- * \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 info a pointer filled in with compute pipeline metadata.
+ * \param info a struct describing the shader to transpile.
+ * \param metadata a pointer filled in with compute pipeline metadata.
  * \returns a compiled SDL_GPUComputePipeline
  *
  * \threadsafety It is safe to call this function from any thread.
  */
 extern SDL_DECLSPEC SDL_GPUComputePipeline * SDLCALL SDL_ShaderCross_CompileComputePipelineFromSPIRV(
     SDL_GPUDevice *device,
-    const Uint8 *bytecode,
-    size_t bytecodeSize,
-    const char *entrypoint,
-    SDL_ShaderCross_ComputePipelineInfo *info);
+    const SDL_ShaderCross_SPIRV_Info *info,
+    SDL_ShaderCross_ComputePipelineMetadata *metadata);
 
 /**
  * Reflect graphics shader info from SPIRV code.
  *
  * \param bytecode the SPIRV bytecode.
- * \param bytecodeSize the length of the SPIRV bytecode.
- * \param info a pointer filled in with shader metadata.
+ * \param bytecode_size the length of the SPIRV bytecode.
+ * \param metadata a pointer filled in with shader metadata.
  *
  * \threadsafety It is safe to call this function from any thread.
  */
 extern SDL_DECLSPEC bool SDLCALL SDL_ShaderCross_ReflectGraphicsSPIRV(
     const Uint8 *bytecode,
-    size_t bytecodeSize,
-    SDL_ShaderCross_GraphicsShaderInfo *info);
+    size_t bytecode_size,
+    SDL_ShaderCross_GraphicsShaderMetadata *metadata);
 
 /**
  * Reflect compute pipeline info from SPIRV code.
  *
  * \param bytecode the SPIRV bytecode.
- * \param bytecodeSize the length of the SPIRV bytecode.
- * \param info a pointer filled in with compute pipeline metadata.
+ * \param bytecode_size the length of the SPIRV bytecode.
+ * \param metadata a pointer filled in with compute pipeline metadata.
  *
  * \threadsafety It is safe to call this function from any thread.
  */
 extern SDL_DECLSPEC bool SDLCALL SDL_ShaderCross_ReflectComputeSPIRV(
     const Uint8 *bytecode,
-    size_t bytecodeSize,
-    SDL_ShaderCross_ComputePipelineInfo *info);
+    size_t bytecode_size,
+    SDL_ShaderCross_ComputePipelineMetadata *metadata);
 
 /**
  * Get the supported shader formats that HLSL cross-compilation can output
@@ -237,24 +235,14 @@ extern SDL_DECLSPEC SDL_GPUShaderFormat SDLCALL SDL_ShaderCross_GetHLSLShaderFor
  *
  * You must SDL_free the returned buffer once you are done with it.
  *
- * \param hlslSource the HLSL source code for the shader.
- * \param entrypoint the entry point function name for the shader in UTF-8.
- * \param includeDir the include directory for shader code. Optional, can be NULL.
- * \param defines an array of define strings. Optional, can be NULL.
- * \param numDefines the number of strings in the defines array.
- * \param shaderStage the shader stage to compile the shader with.
+ * \param info a struct describing the shader to transpile.
  * \param size filled in with the bytecode buffer size.
  * \returns an SDL_malloc'd buffer containing DXBC bytecode.
  *
  * \threadsafety It is safe to call this function from any thread.
  */
 extern SDL_DECLSPEC void * SDLCALL SDL_ShaderCross_CompileDXBCFromHLSL(
-    const char *hlslSource,
-    const char *entrypoint,
-    const char *includeDir,
-    char **defines,
-    Uint32 numDefines,
-    SDL_ShaderCross_ShaderStage shaderStage,
+    const SDL_ShaderCross_HLSL_Info *info,
     size_t *size);
 
 /**
@@ -262,24 +250,14 @@ extern SDL_DECLSPEC void * SDLCALL SDL_ShaderCross_CompileDXBCFromHLSL(
  *
  * You must SDL_free the returned buffer once you are done with it.
  *
- * \param hlslSource the HLSL source code for the shader.
- * \param entrypoint the entry point function name for the shader in UTF-8.
- * \param includeDir the include directory for shader code. Optional, can be NULL.
- * \param defines an array of define strings. Optional, can be NULL.
- * \param numDefines the number of strings in the defines array.
- * \param shaderStage the shader stage to compile the shader with.
+ * \param info a struct describing the shader to transpile.
  * \param size filled in with the bytecode buffer size.
  * \returns an SDL_malloc'd buffer containing DXIL bytecode.
  *
  * \threadsafety It is safe to call this function from any thread.
  */
 extern SDL_DECLSPEC void * SDLCALL SDL_ShaderCross_CompileDXILFromHLSL(
-    const char *hlslSource,
-    const char *entrypoint,
-    const char *includeDir,
-    char **defines,
-    Uint32 numDefines,
-    SDL_ShaderCross_ShaderStage shaderStage,
+    const SDL_ShaderCross_HLSL_Info *info,
     size_t *size);
 
 /**
@@ -287,73 +265,45 @@ extern SDL_DECLSPEC void * SDLCALL SDL_ShaderCross_CompileDXILFromHLSL(
  *
  * You must SDL_free the returned buffer once you are done with it.
  *
- * \param hlslSource the HLSL source code for the shader.
- * \param entrypoint the entry point function name for the shader in UTF-8.
- * \param includeDir the include directory for shader code. Optional, can be NULL.
- * \param defines an array of define strings. Optional, can be NULL.
- * \param numDefines the number of strings in the defines array.
- * \param shaderStage the shader stage to compile the shader with.
+ * \param info a struct describing the shader to transpile.
  * \param size filled in with the bytecode buffer size.
  * \returns an SDL_malloc'd buffer containing SPIRV bytecode.
  *
  * \threadsafety It is safe to call this function from any thread.
  */
 extern SDL_DECLSPEC void * SDLCALL SDL_ShaderCross_CompileSPIRVFromHLSL(
-    const char *hlslSource,
-    const char *entrypoint,
-    const char *includeDir,
-    char **defines,
-    Uint32 numDefines,
-    SDL_ShaderCross_ShaderStage shaderStage,
+    const SDL_ShaderCross_HLSL_Info *info,
     size_t *size);
 
 /**
  * Compile an SDL GPU shader from HLSL code.
  *
  * \param device the SDL GPU device.
- * \param hlslSource the HLSL source code for the shader.
- * \param entrypoint the entry point function name for the shader in UTF-8.
- * \param includeDir the include directory for shader code. Optional, can be NULL.
- * \param defines an array of define strings. Optional, can be NULL.
- * \param numDefines the number of strings in the defines array.
- * \param graphicsShaderStage the shader stage to compile the shader with.
- * \param info a pointer filled in with shader metadata.
+ * \param info a struct describing the shader to transpile.
+ * \param metadata a pointer filled in with shader metadata.
  * \returns a compiled SDL_GPUShader
  *
  * \threadsafety It is safe to call this function from any thread.
  */
 extern SDL_DECLSPEC SDL_GPUShader * SDLCALL SDL_ShaderCross_CompileGraphicsShaderFromHLSL(
     SDL_GPUDevice *device,
-    const char *hlslSource,
-    const char *entrypoint,
-    const char *includeDir,
-    char **defines,
-    Uint32 numDefines,
-    SDL_GPUShaderStage graphicsShaderStage,
-    SDL_ShaderCross_GraphicsShaderInfo *info);
+    const SDL_ShaderCross_HLSL_Info *info,
+    SDL_ShaderCross_GraphicsShaderMetadata *metadata);
 
 /**
  * Compile an SDL GPU compute pipeline from code.
  *
  * \param device the SDL GPU device.
- * \param hlslSource the HLSL source code for the shader.
- * \param entrypoint the entry point function name for the shader in UTF-8.
- * \param includeDir the include directory for shader code. Optional, can be NULL.
- * \param defines an array of define strings. Optional, can be NULL.
- * \param numDefines the number of strings in the defines array.
- * \param info a pointer filled in with compute pipeline metadata.
+ * \param info a struct describing the shader to transpile.
+ * \param metadata a pointer filled in with compute pipeline metadata.
  * \returns a compiled SDL_GPUComputePipeline
  *
  * \threadsafety It is safe to call this function from any thread.
  */
 extern SDL_DECLSPEC SDL_GPUComputePipeline * SDLCALL SDL_ShaderCross_CompileComputePipelineFromHLSL(
     SDL_GPUDevice *device,
-    const char *hlslSource,
-    const char *entrypoint,
-    const char *includeDir,
-    char **defines,
-    Uint32 numDefines,
-    SDL_ShaderCross_ComputePipelineInfo *info);
+    const SDL_ShaderCross_HLSL_Info *info,
+    SDL_ShaderCross_ComputePipelineMetadata *metadata);
 
 #ifdef __cplusplus
 }
diff --git a/src/SDL_shadercross.c b/src/SDL_shadercross.c
index aaa6510..32db3e8 100644
--- a/src/SDL_shadercross.c
+++ b/src/SDL_shadercross.c
@@ -23,6 +23,10 @@
 #include <SDL3/SDL_loadso.h>
 #include <SDL3/SDL_log.h>
 
+/* Constants */
+#define MAX_DEFINES 64
+#define MAX_DEFINE_STRING_LENGTH 256
+
 /* Win32 Type Definitions */
 
 typedef int HRESULT;
@@ -329,12 +333,7 @@ HRESULT DxcCreateInstance(REFCLSID rclsid, REFIID riid, LPVOID *ppv);
 #endif /* SDL_SHADERCROSS_DXC */
 
 static void *SDL_ShaderCross_INTERNAL_CompileUsingDXC(
-    const char *hlslSource,
-    const char *entrypoint,
-    const char *includeDir,
-    char **defines,
-    Uint32 numDefines,
-    SDL_ShaderCross_ShaderStage shaderStage,
+    const SDL_ShaderCross_HLSL_Info *info,
     bool spirv,
     size_t *size) // filled in with number of bytes of returned buffer
 {
@@ -343,10 +342,13 @@ static void *SDL_ShaderCross_INTERNAL_CompileUsingDXC(
     IDxcResult *dxcResult;
     IDxcBlob *blob;
     IDxcBlobUtf8 *errors;
-    size_t entryPointLength = SDL_utf8strlen(entrypoint) + 1;
+    size_t entryPointLength = SDL_utf8strlen(info->entrypoint) + 1;
     wchar_t *entryPointUtf16 = NULL;
     size_t includeDirLength = 0;
     wchar_t *includeDirUtf16 = NULL;
+    wchar_t *nameUtf16 = NULL;
+    wchar_t **defineStringsUtf16 = NULL;
+    size_t numDefineStrings = 0;
     HRESULT ret;
 
     /* Non-static DxcInstance, since the functions we call on it are not thread-safe */
@@ -399,7 +401,7 @@ static void *SDL_ShaderCross_INTERNAL_CompileUsingDXC(
         return NULL;
     }
 
-    entryPointUtf16 = (wchar_t *)SDL_iconv_string("WCHAR_T", "UTF-8", entrypoint, entryPointLength);
+    entryPointUtf16 = (wchar_t *)SDL_iconv_string("WCHAR_T", "UTF-8", info->entrypoint, entryPointLength);
     if (entryPointUtf16 == NULL) {
         SDL_SetError("%s", "Failed to convert entrypoint to WCHAR_T!");
         dxcInstance->lpVtbl->Release(dxcInstance);
@@ -407,26 +409,38 @@ static void *SDL_ShaderCross_INTERNAL_CompileUsingDXC(
         return NULL;
     }
 
-    LPCWSTR *args = SDL_malloc(sizeof(LPCWSTR) * (numDefines + 8));
-    Uint32 argCount = 0;
+    for (Uint32 i = 0; i < MAX_DEFINES; i += 1) {
+        if (info->defines[i].name == NULL) {
+            break;
+        }
+        numDefineStrings += 1;
+    }
 
-    for (Uint32 i = 0; i < numDefines; i += 1) {
-        args[argCount++] = (wchar_t *)SDL_iconv_string("WCHAR_T", "UTF-8", defines[i], SDL_utf8strlen(defines[i]) + 1);
-        if (args[argCount - 1] == NULL) {
-            SDL_SetError("%s", "Failed to convert define argument to WCHAR_T!");
-            SDL_free(args);
-            dxcInstance->lpVtbl->Release(dxcInstance);
-            utils->lpVtbl->Release(utils);
-            return NULL;
+    char defineString[MAX_DEFINE_STRING_LENGTH];
+    defineStringsUtf16 = SDL_malloc(sizeof(LPCWSTR) * numDefineStrings);
+    for (Uint32 i = 0; i < numDefineStrings; i += 1) {
+        if (info->defines[i].value == NULL) {
+            SDL_snprintf(defineString, MAX_DEFINE_STRING_LENGTH, "-D%s=%s", info->defines[i].name, "1");
+        } else {
+            SDL_snprintf(defineString, MAX_DEFINE_STRING_LENGTH, "-D%s=%s", info->defines[i].name, info->defines[i].value);
         }
+
+        defineStringsUtf16[i] = (wchar_t *)SDL_iconv_string("WCHAR_T", "UTF-8", defineString, MAX_DEFINE_STRING_LENGTH);
+    }
+
+    LPCWSTR *args = SDL_malloc(sizeof(LPCWSTR) * (numDefineStrings + 10));
+    Uint32 argCount = 0;
+
+    for (Uint32 i = 0; i < numDefineStrings; i += 1) {
+        args[argCount++] = defineStringsUtf16[i];
     }
 
     args[argCount++] = (LPCWSTR)L"-E";
     args[argCount++] = (LPCWSTR)entryPointUtf16;
 
-    if (includeDir != NULL) {
-        includeDirLength = SDL_utf8strlen(includeDir) + 1;
-        includeDirUtf16 = (wchar_t *)SDL_iconv_string("WCHAR_T", "UTF-8", includeDir, includeDirLength);
+    if (info->include_dir != NULL) {
+        includeDirLength = SDL_utf8strlen(info->include_dir) + 1;
+        includeDirUtf16 = (wchar_t *)SDL_iconv_string("WCHAR_T", "UTF-8", info->include_dir, includeDirLength);
 
         if (includeDirUtf16 == NULL) {
             SDL_SetError("%s", "Failed to convert include dir to WCHAR_T!");
@@ -440,14 +454,14 @@ static void *SDL_ShaderCross_INTERNAL_CompileUsingDXC(
         argCount += 2;
     }
 
-    source.Ptr = hlslSource;
-    source.Size = SDL_strlen(hlslSource) + 1;
+    source.Ptr = info->source;
+    source.Size = SDL_strlen(info->source) + 1;
     source.Encoding = DXC_CP_ACP;
 
-    if (shaderStage == SDL_SHADERCROSS_SHADERSTAGE_VERTEX) {
+    if (info->shader_stage == SDL_SHADERCROSS_SHADERSTAGE_VERTEX) {
         args[argCount++] = (LPCWSTR)L"-T";
         args[argCount++] = (LPCWSTR)L"vs_6_0";
-    } else if (shaderStage == SDL_SHADERCROSS_SHADERSTAGE_FRAGMENT) {
+    } else if (info->shader_stage == SDL_SHADERCROSS_SHADERSTAGE_FRAGMENT) {
         args[argCount++] = (LPCWSTR)L"-T";
         args[argCount++] = (LPCWSTR)L"ps_6_0";
     } else { // compute
@@ -459,6 +473,23 @@ static void *SDL_ShaderCross_INTERNAL_CompileUsingDXC(
         args[argCount++] = (LPCWSTR)L"-spirv";
     }
 
+    if (info->enable_debug) {
+        if (spirv) {
+            // https://github.com/microsoft/DirectXShaderCompiler/blob/main/docs/SPIR-V.rst#debugging
+            args[argCount++] = (LPCWSTR)L"-fspv-debug=vulkan-with-source";
+        } else {
+            // https://github.com/microsoft/DirectXShaderCompiler/blob/main/docs/SourceLevelDebuggingHLSL.rst#command-line-options
+            args[argCount++] = (LPCWSTR)L"-Zi";
+        }
+    }
+
+    if (info->name) {
+        nameUtf16 = (wchar_t *)SDL_iconv_string("WCHAR_T", "UTF-8", info->name, SDL_utf8strlen(info->name) + 1);
+        if (nameUtf16 != NULL) {
+            args[argCount++] = nameUtf16; // a bare string inserted into the arguments is treated as the source file name
+        }
+    }
+
 #if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
     args[argCount++] = L"-D__XBOX_DISABLE_PRECOMPILE=1";
 #endif
@@ -476,6 +507,9 @@ static void *SDL_ShaderCross_INTERNAL_CompileUsingDXC(
     if (includeDirUtf16 != NULL) {
         SDL_free(includeDirUtf16);
     }
+    if (nameUtf16 != NULL) {
+        SDL_free(nameUtf16);
+    }
 
     if (ret < 0) {
         SDL_SetError("IDxcShaderCompiler3::Compile failed: %X", ret);
@@ -528,8 +562,8 @@ static void *SDL_ShaderCross_INTERNAL_CompileUsingDXC(
     dxcInstance->lpVtbl->Release(dxcInstance);
     utils->lpVtbl->Release(utils);
 
-    for (Uint32 i = 0; i < numDefines; i += 1) {
-        SDL_free(args[i]);
+    for (Uint32 i = 0; i < numDefineStrings; i += 1) {
+        SDL_free(defineStringsUtf16[i]);
     }
     SDL_free(args);
 
@@ -541,67 +575,52 @@ static void *SDL_ShaderCross_INTERNAL_CompileUsingDXC(
 }
 
 void *SDL_ShaderCross_CompileDXILFromHLSL(
-    const char *hlslSource,
-    const char *entrypoint,
-    const char *includeDir,
-    char **defines,
-    Uint32 numDefines,
-    SDL_ShaderCross_ShaderStage shaderStage,
+    const SDL_ShaderCross_HLSL_Info *info,
     size_t *size)
 {
     // Roundtrip to SPIR-V to support things like Structured Buffers.
     size_t spirvSize;
     void *spirv = SDL_ShaderCross_CompileSPIRVFromHLSL(
-        hlslSource,
-        entrypoint,
-        includeDir,
-        defines,
-        numDefines,
-        shaderStage,
+        info,
         &spirvSize);
 
     if (spirv == NULL) {
         return NULL;
     }
 
+    SDL_ShaderCross_SPIRV_Info spirvInfo;
+    spirvInfo.bytecode = spirv;
+    spirvInfo.bytecode_size = spirvSize;
+    spirvInfo.entrypoint = info->entrypoint;
+    spirvInfo.shader_stage = info->shader_stage;
+    spirvInfo.enable_debug = info->enable_debug;
+    spirvInfo.name = info->name;
+    spirvInfo.props = 0;
+
     void *translatedSource = SDL_ShaderCross_TranspileHLSLFromSPIRV(
-        spirv,
-        spirvSize,
-        entrypoint,
-        shaderStage);
+        &spirvInfo);
 
     SDL_free(spirv);
     if (translatedSource == NULL) {
         return NULL;
     }
 
+    SDL_ShaderCross_HLSL_Info translatedHlslInfo;
+    SDL_memcpy(&translatedHlslInfo, info, sizeof(SDL_ShaderCross_HLSL_Info));
+    translatedHlslInfo.source = translatedSource;
+
     return SDL_ShaderCross_INTERNAL_CompileUsingDXC(
-        translatedSource,
-        entrypoint,
-        includeDir,
-        defines,
-        numDefines,
-        shaderStage,
+        &translatedHlslInfo,
         false,
         size);
 }
 
 void *SDL_ShaderCross_CompileSPIRVFromHLSL(
-    const char *hlslSource,
-    const char *entrypoint,
-    const char *includeDir,
-    char **defines,
-    Uint32 numDefines,
-    SDL_ShaderCross_ShaderStage shaderStage,
+    const SDL_ShaderCross_HLSL_Info *info,
     size_t *size)
 {
     return SDL_ShaderCross_INTERNAL_CompileUsingDXC(
-        hlslSource,
-        entrypoint,
-        includeDir,
-        defines,
-        numDefines,
-        shaderStage,
+        info,
         true,
         size);
 }
@@ -679,10 +698,12 @@ typedef HRESULT(__stdcall *pfn_D3DCompile)(
 
 static pfn_D3DCompile SDL_D3DCompile = NULL;
 
+// FIXME: includes and defines
 static ID3DBlob *SDL_ShaderCross_INTERNAL_CompileDXBC(
     const char *hlslSource,
     const char *entrypoint,
-    const char *shaderProfile)
+    const char *shaderProfile,
+    bool enableDebug)
 {
     ID3DBlob *blob;
     ID3DBlob *errorBlob;
@@ -701,7 +722,7 @@ static ID3DBlob *SDL_ShaderCross_INTERNAL_CompileDXBC(
         NULL,
         entrypoint,
         shaderProfile,
-        0,
+        enableDebug ? 1 : 0, // D3DCOMPILE_DEBUG = 1
         0,
         &blob,
         &errorBlob);
@@ -721,12 +742,7 @@ static ID3DBlob *SDL_ShaderCross_INTERNAL_CompileDXBC(
 }
 
 void *SDL_ShaderCross_INTERNAL_CompileDXBCFromHLSL(
-    const char *hlslSource,
-    const char *entrypoint,
-    const char *includeDir,
-    char **defines,
-    Uint32 numDefines,
-    SDL_ShaderCross_ShaderStage shaderStage,
+    const SDL_ShaderCross_HLSL_Info *info,
     bool enableRoundtrip,
     size_t *size) // filled in with number of bytes of returned buffer
 {
@@ -736,23 +752,24 @@ void *SDL_ShaderCross_INTERNAL_CompileDXBCFromHLSL(
         // Need to roundtrip to SM 5.1
         size_t spirv_size;
         void *spirv = SDL_ShaderCross_CompileSPIRVFromHLSL(
-            hlslSource,
-            entrypoint,
-            includeDir,
-            defines,
-            numDefines,
-            shaderStage,
+            info,
             &spirv_size);
 
         if (spirv == NULL) {
             return NULL;
         }
 
+        SDL_ShaderCross_SPIRV_Info spirvInfo;
+        spirvInfo.bytecode = spirv;
+        spirvInfo.bytecode_size = spirv_size;
+        spirvInfo.entrypoint = info->entrypoint;
+        spirvInfo.shader_stage = info->shader_stage;
+        spirvInfo.enable_debug = info->enable_debug;
+        spirvInfo.name = info->name;
+        spirvInfo.props = 0;
+
         transpiledSource = SDL_ShaderCross_TranspileHLSLFromSPIRV(
-            spirv,
-            spirv_size,
-            entrypoint,
-            shaderStage);
+            &spirvInfo);
         SDL_free(spirv);
 
         if (transpiledSource == NULL) {
@@ -761,18 +778,19 @@ void *SDL_ShaderCross_INTERNAL_CompileDXBCFromHLSL(
     }
 
     const char *shaderProfile;
-    if (shaderStage == SDL_SHADERCROSS_SHADERSTAGE_VERTEX) {
+    if (info->shader_stage == SDL_SHADERCROSS_SHADERSTAGE_VERTEX) {
         shaderProfile = "vs_5_1";
-    } else if (shaderStage == SDL_SHADERCROSS_SHADERSTAGE_FRAGMENT) {
+    } else if (info->shader_stage == SDL_SHADERCROSS_SHADERSTAGE_FRAGMENT) {
         shaderProfile = "ps_5_1";
     } else { // compute
         shaderProfile = "cs_5_1";
     }
 
     ID3DBlob *blob = SDL_ShaderCross_INTERNAL_CompileDXBC(
-        transpiledSource != NULL ? transpiledSource : hlslSource,
-        entrypoint,
-        shaderProfile);
+        transpiledSource != NULL ? transpiledSource : info->source,
+        info->entrypoint,
+        shaderProfile,
+        info->enable_debug);
 
     if (blob == NULL) {
         *size = 0;
@@ -793,45 +811,25 @@ void *SDL_ShaderCross_INTERNAL_CompileDXBCFromHLSL(
 
 // Returns raw byte buffer
 void *SDL_ShaderCross_CompileDXBCFromHLSL(
-    const char *hlslSource,
-    const char *entrypoint,
-    const char *includeDir,
-    char **defines,
-    Uint32 numDefines,
-    SDL_ShaderCross_ShaderStage shaderStage,
+    const SDL_ShaderCross_HLSL_Info *info,
     size_t *size) // filled in with number of bytes of returned

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