SDL_shadercross: Reflection API and CLI JSON output

From 48bdd8561e2c770082748ef0694bb19c432fe080 Mon Sep 17 00:00:00 2001
From: cosmonaut <[EMAIL REDACTED]>
Date: Wed, 20 Nov 2024 14:51:22 -0800
Subject: [PATCH] Reflection API and CLI JSON output

---
 include/SDL3_shadercross/SDL_shadercross.h |  28 ++++
 src/SDL_shadercross.c                      | 105 +++++++------
 src/SDL_shadercross.sym                    |   2 +
 src/cli.c                                  | 172 ++++++++++++++++++++-
 4 files changed, 260 insertions(+), 47 deletions(-)

diff --git a/include/SDL3_shadercross/SDL_shadercross.h b/include/SDL3_shadercross/SDL_shadercross.h
index c1154df..08cc9e0 100644
--- a/include/SDL3_shadercross/SDL_shadercross.h
+++ b/include/SDL3_shadercross/SDL_shadercross.h
@@ -197,6 +197,34 @@ extern SDL_DECLSPEC SDL_GPUComputePipeline * SDLCALL SDL_ShaderCross_CompileComp
     const char *entrypoint,
     SDL_ShaderCross_ComputePipelineInfo *info);
 
+/**
+ * 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.
+ *
+ * \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);
+
+/**
+ * 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.
+ *
+ * \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);
+
 /**
  * Get the supported shader formats that HLSL cross-compilation can output
  *
diff --git a/src/SDL_shadercross.c b/src/SDL_shadercross.c
index b1b03ae..6045689 100644
--- a/src/SDL_shadercross.c
+++ b/src/SDL_shadercross.c
@@ -1459,12 +1459,12 @@ static SPIRVTranspileContext *SDL_ShaderCross_INTERNAL_TranspileFromSPIRV(
     return transpileContext;
 }
 
-// Acquire CreateInfo metadata from SPIRV bytecode.
+// Acquire metadata from SPIRV bytecode.
 // TODO: validate descriptor sets
-static bool SDL_ShaderCross_INTERNAL_ReflectGraphicsSPIRV(
+bool SDL_ShaderCross_ReflectGraphicsSPIRV(
     const Uint8 *code,
     size_t codeSize,
-    SDL_GPUShaderCreateInfo *createInfo // filled in with reflected data
+    SDL_ShaderCross_GraphicsShaderInfo *info // filled in with reflected data
 ) {
     spvc_result result;
     spvc_context context = NULL;
@@ -1572,17 +1572,17 @@ static bool SDL_ShaderCross_INTERNAL_ReflectGraphicsSPIRV(
 
     spvc_context_destroy(context);
 
-    createInfo->num_samplers = num_texture_samplers;
-    createInfo->num_storage_textures = num_storage_textures;
-    createInfo->num_storage_buffers = num_storage_buffers;
-    createInfo->num_uniform_buffers = num_uniform_buffers;
+    info->numSamplers = num_texture_samplers;
+    info->numStorageTextures = num_storage_textures;
+    info->numStorageBuffers = num_storage_buffers;
+    info->numUniformBuffers = num_uniform_buffers;
     return true;
 }
 
-static bool SDL_ShaderCross_INTERNAL_ReflectComputeSPIRV(
-    const Uint8 *code,
-    size_t codeSize,
-    SDL_GPUComputePipelineCreateInfo *createInfo // filled in with reflected data
+bool SDL_ShaderCross_ReflectComputeSPIRV(
+    const Uint8 *bytecode,
+    size_t bytecodeSize,
+    SDL_ShaderCross_ComputePipelineInfo *info // filled in with reflected data
 ) {
     spvc_result result;
     spvc_context context = NULL;
@@ -1606,7 +1606,7 @@ static bool SDL_ShaderCross_INTERNAL_ReflectComputeSPIRV(
     }
 
     /* Parse the SPIR-V into IR */
-    result = spvc_context_parse_spirv(context, (const SpvId *)code, codeSize / sizeof(SpvId), &ir);
+    result = spvc_context_parse_spirv(context, (const SpvId *)bytecode, bytecodeSize / sizeof(SpvId), &ir);
     if (result < 0) {
         SPVC_ERROR(spvc_context_parse_spirv);
         spvc_context_destroy(context);
@@ -1740,18 +1740,18 @@ static bool SDL_ShaderCross_INTERNAL_ReflectComputeSPIRV(
     }
 
     // Threadcount
-    createInfo->threadcount_x = spvc_compiler_get_execution_mode_argument_by_index(compiler, SpvExecutionModeLocalSize, 0);
-    createInfo->threadcount_y = spvc_compiler_get_execution_mode_argument_by_index(compiler, SpvExecutionModeLocalSize, 1);
-    createInfo->threadcount_z = spvc_compiler_get_execution_mode_argument_by_index(compiler, SpvExecutionModeLocalSize, 2);
+    info->threadCountX = spvc_compiler_get_execution_mode_argument_by_index(compiler, SpvExecutionModeLocalSize, 0);
+    info->threadCountY = spvc_compiler_get_execution_mode_argument_by_index(compiler, SpvExecutionModeLocalSize, 1);
+    info->threadCountZ = spvc_compiler_get_execution_mode_argument_by_index(compiler, SpvExecutionModeLocalSize, 2);
 
     spvc_context_destroy(context);
 
-    createInfo->num_samplers = num_texture_samplers;
-    createInfo->num_readonly_storage_textures = num_readonly_storage_textures;
-    createInfo->num_readonly_storage_buffers = num_readonly_storage_buffers;
-    createInfo->num_readwrite_storage_textures = num_readwrite_storage_textures;
-    createInfo->num_readwrite_storage_buffers = num_readwrite_storage_buffers;
-    createInfo->num_uniform_buffers = num_uniform_buffers;
+    info->numSamplers = num_texture_samplers;
+    info->numReadOnlyStorageTextures = num_readonly_storage_textures;
+    info->numReadOnlyStorageBuffers = num_readonly_storage_buffers;
+    info->numReadWriteStorageTextures = num_readwrite_storage_textures;
+    info->numReadWriteStorageBuffers = num_readwrite_storage_buffers;
+    info->numUniformBuffers = num_uniform_buffers;
     return true;
 }
 
@@ -1761,7 +1761,8 @@ static void *SDL_ShaderCross_INTERNAL_CompileFromSPIRV(
     size_t bytecodeSize,
     const char *entrypoint,
     SDL_ShaderCross_ShaderStage shaderStage,
-    SDL_GPUShaderFormat targetFormat
+    SDL_GPUShaderFormat targetFormat,
+    void *info
 ) {
     spvc_backend backend;
     unsigned shadermodel = 0;
@@ -1795,13 +1796,23 @@ static void *SDL_ShaderCross_INTERNAL_CompileFromSPIRV(
 
     if (shaderStage == SDL_SHADERCROSS_SHADERSTAGE_COMPUTE) {
         SDL_GPUComputePipelineCreateInfo createInfo;
-        SDL_ShaderCross_INTERNAL_ReflectComputeSPIRV(
+        SDL_ShaderCross_ComputePipelineInfo *pipelineInfo = (SDL_ShaderCross_ComputePipelineInfo *)info;
+        SDL_ShaderCross_ReflectComputeSPIRV(
             bytecode,
             bytecodeSize,
-            &createInfo);
+            pipelineInfo);
         createInfo.entrypoint = transpileContext->cleansed_entrypoint;
         createInfo.format = targetFormat;
         createInfo.props = 0;
+        createInfo.num_samplers = pipelineInfo->numSamplers;
+        createInfo.num_readonly_storage_textures = pipelineInfo->numReadOnlyStorageTextures;
+        createInfo.num_readonly_storage_buffers = pipelineInfo->numReadOnlyStorageBuffers;
+        createInfo.num_readwrite_storage_textures = pipelineInfo->numReadWriteStorageTextures;
+        createInfo.num_readwrite_storage_buffers = pipelineInfo->numReadWriteStorageBuffers;
+        createInfo.num_uniform_buffers = pipelineInfo->numUniformBuffers;
+        createInfo.threadcount_x = pipelineInfo->threadCountX;
+        createInfo.threadcount_y = pipelineInfo->threadCountY;
+        createInfo.threadcount_z = pipelineInfo->threadCountZ;
 
         if (targetFormat == SDL_GPU_SHADERFORMAT_DXBC) {
             createInfo.code = SDL_ShaderCross_INTERNAL_CompileDXBCFromHLSL(
@@ -1830,14 +1841,19 @@ static void *SDL_ShaderCross_INTERNAL_CompileFromSPIRV(
         shaderObject = SDL_CreateGPUComputePipeline(device, &createInfo);
     } else {
         SDL_GPUShaderCreateInfo createInfo;
-        SDL_ShaderCross_INTERNAL_ReflectGraphicsSPIRV(
+        SDL_ShaderCross_GraphicsShaderInfo *shaderInfo = (SDL_ShaderCross_GraphicsShaderInfo *)info;
+        SDL_ShaderCross_ReflectGraphicsSPIRV(
             bytecode,
             bytecodeSize,
-            &createInfo);
+            shaderInfo);
         createInfo.entrypoint = transpileContext->cleansed_entrypoint;
         createInfo.format = targetFormat;
         createInfo.stage = (SDL_GPUShaderStage)shaderStage;
         createInfo.props = 0;
+        createInfo.num_samplers = shaderInfo->numSamplers;
+        createInfo.num_storage_textures = shaderInfo->numStorageTextures;
+        createInfo.num_storage_buffers = shaderInfo->numStorageBuffers;
+        createInfo.num_uniform_buffers = shaderInfo->numUniformBuffers;
 
         if (targetFormat == SDL_GPU_SHADERFORMAT_DXBC) {
             createInfo.code = SDL_ShaderCross_INTERNAL_CompileDXBCFromHLSL(
@@ -2010,42 +2026,42 @@ static void *SDL_ShaderCross_INTERNAL_CreateShaderFromSPIRV(
         if (shaderStage == SDL_SHADERCROSS_SHADERSTAGE_COMPUTE) {
             SDL_GPUComputePipelineCreateInfo createInfo;
             SDL_ShaderCross_ComputePipelineInfo *pipelineInfo = (SDL_ShaderCross_ComputePipelineInfo *)info;
-            SDL_ShaderCross_INTERNAL_ReflectComputeSPIRV(
+            SDL_ShaderCross_ReflectComputeSPIRV(
                 bytecode,
                 bytecodeSize,
-                &createInfo);
+                pipelineInfo);
             createInfo.code = bytecode;
             createInfo.code_size = bytecodeSize;
             createInfo.entrypoint = entrypoint;
             createInfo.format = SDL_GPU_SHADERFORMAT_SPIRV;
             createInfo.props = 0;
-            pipelineInfo->numSamplers = createInfo.num_samplers;
-            pipelineInfo->numReadOnlyStorageTextures = createInfo.num_readonly_storage_textures;
-            pipelineInfo->numReadOnlyStorageBuffers = createInfo.num_readonly_storage_buffers;
-            pipelineInfo->numReadWriteStorageTextures = createInfo.num_readwrite_storage_textures;
-            pipelineInfo->numReadWriteStorageBuffers = createInfo.num_readwrite_storage_buffers;
-            pipelineInfo->numUniformBuffers = createInfo.num_uniform_buffers;
-            pipelineInfo->threadCountX = createInfo.threadcount_x;
-            pipelineInfo->threadCountY = createInfo.threadcount_y;
-            pipelineInfo->threadCountZ = createInfo.threadcount_z;
+            createInfo.num_samplers = pipelineInfo->numSamplers;
+            createInfo.num_readonly_storage_textures = pipelineInfo->numReadOnlyStorageTextures;
+            createInfo.num_readonly_storage_buffers = pipelineInfo->numReadOnlyStorageBuffers;
+            createInfo.num_readwrite_storage_textures = pipelineInfo->numReadWriteStorageTextures;
+            createInfo.num_readwrite_storage_buffers = pipelineInfo->numReadWriteStorageBuffers;
+            createInfo.num_uniform_buffers = pipelineInfo->numUniformBuffers;
+            createInfo.threadcount_x = pipelineInfo->threadCountX;
+            createInfo.threadcount_y = pipelineInfo->threadCountY;
+            createInfo.threadcount_z = pipelineInfo->threadCountZ;
             return SDL_CreateGPUComputePipeline(device, &createInfo);
         } else {
             SDL_GPUShaderCreateInfo createInfo;
             SDL_ShaderCross_GraphicsShaderInfo *shaderInfo = (SDL_ShaderCross_GraphicsShaderInfo *)info;
-            SDL_ShaderCross_INTERNAL_ReflectGraphicsSPIRV(
+            SDL_ShaderCross_ReflectGraphicsSPIRV(
                 bytecode,
                 bytecodeSize,
-                &createInfo);
+                shaderInfo);
             createInfo.code = bytecode;
             createInfo.code_size = bytecodeSize;
             createInfo.entrypoint = entrypoint;
             createInfo.format = SDL_GPU_SHADERFORMAT_SPIRV;
             createInfo.stage = (SDL_GPUShaderStage)shaderStage;
             createInfo.props = 0;
-            shaderInfo->numSamplers = createInfo.num_samplers;
-            shaderInfo->numStorageTextures = createInfo.num_storage_textures;
-            shaderInfo->numStorageBuffers = createInfo.num_storage_buffers;
-            shaderInfo->numUniformBuffers = createInfo.num_uniform_buffers;
+            createInfo.num_samplers = shaderInfo->numSamplers;
+            createInfo.num_storage_textures = shaderInfo->numStorageTextures;
+            createInfo.num_storage_buffers = shaderInfo->numStorageBuffers;
+            createInfo.num_uniform_buffers = shaderInfo->numUniformBuffers;
             return SDL_CreateGPUShader(device, &createInfo);
         }
     } else if (shader_formats & SDL_GPU_SHADERFORMAT_MSL) {
@@ -2071,7 +2087,8 @@ static void *SDL_ShaderCross_INTERNAL_CreateShaderFromSPIRV(
         bytecodeSize,
         entrypoint,
         shaderStage,
-        format);
+        format,
+        info);
 }
 
 SDL_GPUShader *SDL_ShaderCross_CompileGraphicsShaderFromSPIRV(
diff --git a/src/SDL_shadercross.sym b/src/SDL_shadercross.sym
index d660c0e..619b922 100644
--- a/src/SDL_shadercross.sym
+++ b/src/SDL_shadercross.sym
@@ -15,5 +15,7 @@ SDL3_shadercross_0.0.0 {
     SDL_ShaderCross_CompileSPIRVFromHLSL;
     SDL_ShaderCross_CompileGraphicsShaderFromHLSL;
     SDL_ShaderCross_CompileComputePipelineFromHLSL;
+    SDL_ShaderCross_ReflectGraphicsSPIRV;
+    SDL_ShaderCross_ReflectComputeSPIRV;
   local: *;
 };
diff --git a/src/cli.c b/src/cli.c
index 1525888..3a89fa1 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -23,14 +23,15 @@
 #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.
+// We can emit HLSL and JSON 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
+    SHADERFORMAT_HLSL,
+    SHADERFORMAT_JSON
 } ShaderCross_ShaderFormat;
 
 void print_help(void)
@@ -39,7 +40,7 @@ void print_help(void)
     SDL_Log("Usage: shadercross <input> [options]");
     SDL_Log("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, HLSL]");
+    SDL_Log("  %-*s %s", column_width, "-d | --dest <value>", "Destination format. May be inferred from the filename. Values: [DXBC, DXIL, MSL, SPIRV, HLSL, JSON]");
     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, "-I | --include <value>", "HLSL include directory. Only used with HLSL source. Optional.");
@@ -47,6 +48,90 @@ void print_help(void)
     SDL_Log("  %-*s %s", column_width, "-o | --output <value>", "Output file.");
 }
 
+void write_string(SDL_IOStream *outputIO, const char *str)
+{
+    SDL_WriteIO(outputIO, str, SDL_strlen(str));
+}
+
+void write_graphics_reflect_json(SDL_IOStream *outputIO, SDL_ShaderCross_GraphicsShaderInfo *info)
+{
+    char buffer[16];
+    write_string(outputIO, "{ ");
+
+    write_string(outputIO, "\"samplers\": ");
+    SDL_itoa(info->numSamplers, buffer, 10);
+    write_string(outputIO, buffer);
+    write_string(outputIO, ", ");
+
+    write_string(outputIO, "\"storageTextures\": ");
+    SDL_itoa(info->numStorageTextures, buffer, 10);
+    write_string(outputIO, buffer);
+    write_string(outputIO, ", ");
+
+    write_string(outputIO, "\"storageBuffers\": ");
+    SDL_itoa(info->numStorageBuffers, buffer, 10);
+    write_string(outputIO, buffer);
+    write_string(outputIO, ", ");
+
+    write_string(outputIO, "\"uniformBuffers\": ");
+    SDL_itoa(info->numUniformBuffers, buffer, 10);
+    write_string(outputIO, buffer);
+
+    write_string(outputIO, " }\n");
+}
+
+void write_compute_reflect_json(SDL_IOStream *outputIO, SDL_ShaderCross_ComputePipelineInfo *info)
+{
+    char buffer[16];
+    write_string(outputIO, "{ ");
+
+    write_string(outputIO, "\"samplers\": ");
+    SDL_itoa(info->numSamplers, buffer, 10);
+    write_string(outputIO, buffer);
+    write_string(outputIO, ", ");
+
+    write_string(outputIO, "\"readOnlyStorageTextures\": ");
+    SDL_itoa(info->numReadOnlyStorageTextures, buffer, 10);
+    write_string(outputIO, buffer);
+    write_string(outputIO, ", ");
+
+    write_string(outputIO, "\"readOnlyStorageBuffers\": ");
+    SDL_itoa(info->numReadOnlyStorageBuffers, buffer, 10);
+    write_string(outputIO, buffer);
+    write_string(outputIO, ", ");
+
+    write_string(outputIO, "\"readWriteStorageTextures\": ");
+    SDL_itoa(info->numReadWriteStorageTextures, buffer, 10);
+    write_string(outputIO, buffer);
+    write_string(outputIO, ", ");
+
+    write_string(outputIO, "\"readWriteStorageBuffers\": ");
+    SDL_itoa(info->numReadWriteStorageBuffers, buffer, 10);
+    write_string(outputIO, buffer);
+    write_string(outputIO, ", ");
+
+    write_string(outputIO, "\"uniformBuffers\": ");
+    SDL_itoa(info->numUniformBuffers, buffer, 10);
+    write_string(outputIO, buffer);
+    write_string(outputIO, ", ");
+
+    write_string(outputIO, "\"threadCountX\": ");
+    SDL_itoa(info->threadCountX, buffer, 10);
+    write_string(outputIO, buffer);
+    write_string(outputIO, ", ");
+
+    write_string(outputIO, "\"threadCountY\": ");
+    SDL_itoa(info->threadCountY, buffer, 10);
+    write_string(outputIO, buffer);
+    write_string(outputIO, ", ");
+
+    write_string(outputIO, "\"threadCountZ\": ");
+    SDL_itoa(info->threadCountZ, buffer, 10);
+    write_string(outputIO, buffer);
+
+    write_string(outputIO, " }\n");
+}
+
 int main(int argc, char *argv[])
 {
     bool sourceValid = false;
@@ -115,6 +200,9 @@ int main(int argc, char *argv[])
                 } else if (SDL_strcasecmp(argv[i], "HLSL") == 0) {
                     destinationFormat = SHADERFORMAT_HLSL;
                     destinationValid = true;
+                } else if (SDL_strcasecmp(argv[i], "JSON") == 0) {
+                    destinationFormat = SHADERFORMAT_JSON;
+                    destinationValid = true;
                 } else {
                     SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unrecognized destination input %s, destination must be DXBC, DXIL, MSL or SPIRV!", argv[i]);
                     print_help();
@@ -234,6 +322,8 @@ int main(int argc, char *argv[])
             destinationFormat = SHADERFORMAT_SPIRV;
         } else if (SDL_strstr(outputFilename, ".hlsl")) {
             destinationFormat = SHADERFORMAT_HLSL;
+        } else if (SDL_strstr(outputFilename, ".json")) {
+            destinationFormat = SHADERFORMAT_JSON;
         } else {
             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", "Could not infer destination format!");
             print_help();
@@ -339,6 +429,33 @@ int main(int argc, char *argv[])
                 break;
             }
 
+            case SHADERFORMAT_JSON: {
+                if (shaderStage == SDL_SHADERCROSS_SHADERSTAGE_COMPUTE) {
+                    SDL_ShaderCross_ComputePipelineInfo info;
+                    if (SDL_ShaderCross_ReflectComputeSPIRV(
+                        fileData,
+                        fileSize,
+                        &info)) {
+                        write_compute_reflect_json(outputIO, &info);
+                    } else {
+                        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to reflect SPIRV: %s", SDL_GetError());
+                        result = 1;
+                    }
+                } else {
+                    SDL_ShaderCross_GraphicsShaderInfo info;
+                    if (SDL_ShaderCross_ReflectGraphicsSPIRV(
+                        fileData,
+                        fileSize,
+                        &info)) {
+                        write_graphics_reflect_json(outputIO, &info);
+                    } else {
+                        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to reflect SPIRV: %s", SDL_GetError());
+                        result = 1;
+                    }
+                }
+                break;
+            }
+
             case SHADERFORMAT_INVALID: {
                 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Destination format not provided!");
                 result = 1;
@@ -469,6 +586,55 @@ int main(int argc, char *argv[])
                 break;
             }
 
+            case SHADERFORMAT_JSON: {
+                void *spirv = SDL_ShaderCross_CompileSPIRVFromHLSL(
+                    fileData,
+                    entrypointName,
+                    includeDir,
+                    defines,
+                    numDefines,
+                    shaderStage,
+                    &bytecodeSize);
+
+                if (spirv == NULL) {
+                    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to compile HLSL to SPIRV: %s", SDL_GetError());
+                    result = 1;
+                    break;
+                }
+
+                if (shaderStage == SDL_SHADERCROSS_SHADERSTAGE_COMPUTE) {
+                    SDL_ShaderCross_ComputePipelineInfo info;
+                    bool result = SDL_ShaderCross_ReflectComputeSPIRV(
+                        spirv,
+                        bytecodeSize,
+                        &info);
+                    SDL_free(spirv);
+
+                    if (result) {
+                        write_compute_reflect_json(outputIO, &info);
+                    } else {
+                        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to reflect SPIRV: %s", SDL_GetError());
+                        result = 1;
+                    }
+                } else {
+                    SDL_ShaderCross_GraphicsShaderInfo info;
+                    bool result = !SDL_ShaderCross_ReflectGraphicsSPIRV(
+                        spirv,
+                        bytecodeSize,
+                        &info);
+                    SDL_free(spirv);
+
+                    if (result) {
+                        write_graphics_reflect_json(outputIO, &info);
+                    } else {
+                        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to reflect SPIRV: %s", SDL_GetError());
+                        result = 1;
+                    }
+                }
+
+                break;
+            }
+
             case SHADERFORMAT_INVALID: {
                 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Destination format not provided!");
                 result = 1;