SDL_shadercross: shadercross: add build option to track memory allocations (#149)

From 7bdb6c2ebb290b3ce7dfdc76b4b67e14c5a4d7ed Mon Sep 17 00:00:00 2001
From: Anonymous Maarten <[EMAIL REDACTED]>
Date: Sat, 7 Jun 2025 09:47:45 +0200
Subject: [PATCH] shadercross: add build option to track memory allocations
 (#149)

* shadercross: add build option to track memory allocations

* Plug shadercross memory leaks

Tested by compiling hlsl -> msl, hlsl -> spirv and hlsl -> dxil

* ci: update Steam Linux Runtime docker container
---
 .github/workflows/main.yml |  2 +-
 CMakeLists.txt             |  5 +++++
 src/SDL_shadercross.c      |  5 ++++-
 src/cli.c                  | 13 +++++++++++++
 4 files changed, 23 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index c189bcd..bcd1855 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -19,7 +19,7 @@ jobs:
           - { name: Windows (MSVC),                os: windows-2019,   build-spirv-cross: true,  vendored: false, shell: sh, msvc: true, artifact: 'SDL3_shadercross-VC-x64' }
           - { name: Windows (mingw64),             os: windows-latest, build-spirv-cross: false, vendored: false, shell: 'msys2 {0}', msystem: mingw64, msys-env: mingw-w64-x86_64, artifact: 'SDL3_shadercross-mingw64' }
           - { name: Ubuntu 22.04,                  os: ubuntu-22.04,   build-spirv-cross: true,  vendored: false, shell: sh, artifact: 'SDL3_shadercross-linux-x64' }
-          - { name: Steam Linux Runtime (Sniper),  os: ubuntu-latest,  build-spirv-cross: false,  vendored: true, shell: sh, artifact: 'SDL3_shadercross-slrsniper', container: 'registry.gitlab.steamos.cloud/steamrt/sniper/sdk:beta' }
+          - { name: Steam Linux Runtime (Sniper),  os: ubuntu-latest,  build-spirv-cross: false,  vendored: true, shell: sh, artifact: 'SDL3_shadercross-slrsniper', container: 'registry.gitlab.steamos.cloud/steamrt/sniper/sdk:latest' }
           - { name: macOS,                         os: macos-latest,   build-spirv-cross: true,  vendored: true,  shell: sh, artifact: 'SDL3_shadercross-macos-arm64', cmake-arguments: '-DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 -DCMAKE_C_FLAGS="-mmacosx-version-min=11.0" -DCMAKE_CXX_FLAGS="-mmacosx-version-min=11.0"' }
 
     steps:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8b754f2..6cfab37 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -42,6 +42,7 @@ option(SDLSHADERCROSS_SPIRVCROSS_SHARED "Link to shared library variants of depe
 option(SDLSHADERCROSS_VENDORED "Use vendored dependencies" OFF)
 option(SDLSHADERCROSS_CLI "Build command line executable" ON)
 cmake_dependent_option(SDLSHADERCROSS_CLI_STATIC "Link CLI with static libraries" OFF "SDLSHADERCROSS_CLI;SDLSHADERCROSS_STATIC;TARGET SDL3::SDL3-static" OFF)
+cmake_dependent_option(SDLSHADERCROSS_CLI_LEAKCHECK "Check shadercross for memory leaks" OFF "SDLSHADERCROSS_CLI;TARGET SDL3::SDL3_test" OFF)
 option(SDLSHADERCROSS_WERROR "Enable Werror" OFF)
 option(SDLSHADERCROSS_INSTALL "Enable installation" OFF)
 cmake_dependent_option(SDLSHADERCROSS_INSTALL_CPACK "Enable CPack installation" OFF "SDLSHADERCROSS_INSTALL" OFF)
@@ -269,6 +270,10 @@ if(SDLSHADERCROSS_CLI)
 	sdl_add_warning_options(shadercross WARNING_AS_ERROR ${SDLSHADERCROSS_WERROR})
 	sdl_target_link_options_no_undefined(shadercross)
 
+	if(SDLSHADERCROSS_CLI_LEAKCHECK)
+		target_link_libraries(shadercross PRIVATE SDL3::SDL3_test)
+		target_compile_definitions(shadercross PRIVATE LEAKCHECK)
+	endif()
 	if(SDLSHADERCROSS_CLI_STATIC)
 		target_link_libraries(shadercross PRIVATE SDL3_shadercross::SDL3_shadercross-static)
 		target_link_libraries(shadercross PRIVATE SDL3::SDL3-static)
diff --git a/src/SDL_shadercross.c b/src/SDL_shadercross.c
index 06549b6..66270c8 100644
--- a/src/SDL_shadercross.c
+++ b/src/SDL_shadercross.c
@@ -551,6 +551,7 @@ static void *SDL_ShaderCross_INTERNAL_CompileUsingDXC(
     for (Uint32 i = 0; i < numDefineStrings; i += 1) {
         SDL_free(defineStringsUtf16[i]);
     }
+    SDL_free(defineStringsUtf16);
     SDL_free(args);
 
     return buffer;
@@ -603,10 +604,12 @@ void *SDL_ShaderCross_CompileDXILFromHLSL(
     SDL_memcpy(&translatedHlslInfo, info, sizeof(SDL_ShaderCross_HLSL_Info));
     translatedHlslInfo.source = translatedSource;
 
-    return SDL_ShaderCross_INTERNAL_CompileUsingDXC(
+    void *result = SDL_ShaderCross_INTERNAL_CompileUsingDXC(
         &translatedHlslInfo,
         false,
         size);
+    SDL_free(translatedSource);
+    return result;
 #endif
 }
 
diff --git a/src/cli.c b/src/cli.c
index a0198dc..300bcbf 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -22,6 +22,9 @@
 #include <SDL3_shadercross/SDL_shadercross.h>
 #include <SDL3/SDL_log.h>
 #include <SDL3/SDL_iostream.h>
+#ifdef LEAKCHECK
+#include <SDL3/SDL_test_memory.h>
+#endif
 
 // We can emit HLSL and JSON as a destination, so let's redefine the shader format enum.
 typedef enum ShaderCross_DestinationFormat {
@@ -245,6 +248,10 @@ int main(int argc, char *argv[])
 
     bool psslCompat = false;
 
+#ifdef LEAKCHECK
+    SDLTest_TrackAllocations();
+#endif
+
     for (int i = 1; i < argc; i += 1) {
         char *arg = argv[i];
 
@@ -778,5 +785,11 @@ int main(int argc, char *argv[])
     }
     SDL_free(defines);
     SDL_ShaderCross_Quit();
+    SDL_Quit();
+
+#ifdef LEAKCHECK
+    SDLTest_LogAllocations();
+#endif
+
     return result;
 }