diff options
| author | skal <pascal.massimino@gmail.com> | 2026-02-03 10:59:08 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-02-03 10:59:08 +0100 |
| commit | 124899f27b6c1ec02bfa16a57a4e43ea2b7ebac0 (patch) | |
| tree | f96af35a888baee7be5b1a01849325653c5f7af1 /src | |
| parent | 4660ce3eec7c91a20d6d93fa3e142c1fd157e869 (diff) | |
test(shader): Add ShaderComposer and WGSL asset validation tests (Task #26)
Implemented comprehensive unit tests for ShaderComposer and a validation test for production WGSL shader assets. This ensures the shader asset pipeline is robust and that all shaders contain required entry points and snippets. Also improved InitShaderComposer to be more robust during testing.
Diffstat (limited to 'src')
| -rw-r--r-- | src/gpu/effects/shaders.cc | 104 | ||||
| -rw-r--r-- | src/tests/test_assets.cc | 4 | ||||
| -rw-r--r-- | src/tests/test_shader_assets.cc | 66 | ||||
| -rw-r--r-- | src/tests/test_shader_composer.cc | 43 |
4 files changed, 197 insertions, 20 deletions
diff --git a/src/gpu/effects/shaders.cc b/src/gpu/effects/shaders.cc index 6b37869..cd516cd 100644 --- a/src/gpu/effects/shaders.cc +++ b/src/gpu/effects/shaders.cc @@ -2,39 +2,107 @@ // It defines WGSL shader code for various effects. #include "../demo_effects.h" + + + +#if defined(USE_TEST_ASSETS) + +#include "test_assets.h" + +#else + #include "generated/assets.h" + +#endif + + + #include "gpu/effects/shader_composer.h" + #include "util/asset_manager.h" + + void InitShaderComposer() { + auto& sc = ShaderComposer::Get(); - sc.RegisterSnippet("common_uniforms", - (const char*)GetAsset(AssetId::ASSET_SHADER_COMMON_UNIFORMS)); - sc.RegisterSnippet("sdf_primitives", - (const char*)GetAsset(AssetId::ASSET_SHADER_SDF_PRIMITIVES)); - sc.RegisterSnippet("lighting", - (const char*)GetAsset(AssetId::ASSET_SHADER_LIGHTING)); - sc.RegisterSnippet("ray_box", - (const char*)GetAsset(AssetId::ASSET_SHADER_RAY_BOX)); + + + auto register_if_exists = [&](const char* name, AssetId id) { + + size_t size; + + const char* data = (const char*)GetAsset(id, &size); + + if (data) { + + sc.RegisterSnippet(name, std::string(data, size)); + + } + + }; + + + + register_if_exists("common_uniforms", AssetId::ASSET_SHADER_COMMON_UNIFORMS); + + register_if_exists("sdf_primitives", AssetId::ASSET_SHADER_SDF_PRIMITIVES); + + register_if_exists("lighting", AssetId::ASSET_SHADER_LIGHTING); + + register_if_exists("ray_box", AssetId::ASSET_SHADER_RAY_BOX); + } -const char* main_shader_wgsl = (const char*)GetAsset(AssetId::ASSET_SHADER_MAIN); + + +// Helper to get asset string or empty string + +static const char* SafeGetAsset(AssetId id) { + + const uint8_t* data = GetAsset(id); + + return data ? (const char*)data : ""; + +} + + + +const char* main_shader_wgsl = SafeGetAsset(AssetId::ASSET_SHADER_MAIN); + const char* particle_compute_wgsl = - (const char*)GetAsset(AssetId::ASSET_SHADER_PARTICLE_COMPUTE); + + SafeGetAsset(AssetId::ASSET_SHADER_PARTICLE_COMPUTE); + const char* particle_render_wgsl = - (const char*)GetAsset(AssetId::ASSET_SHADER_PARTICLE_RENDER); + + SafeGetAsset(AssetId::ASSET_SHADER_PARTICLE_RENDER); + const char* passthrough_shader_wgsl = - (const char*)GetAsset(AssetId::ASSET_SHADER_PASSTHROUGH); + + SafeGetAsset(AssetId::ASSET_SHADER_PASSTHROUGH); + const char* ellipse_shader_wgsl = - (const char*)GetAsset(AssetId::ASSET_SHADER_ELLIPSE); + + SafeGetAsset(AssetId::ASSET_SHADER_ELLIPSE); + const char* particle_spray_compute_wgsl = - (const char*)GetAsset(AssetId::ASSET_SHADER_PARTICLE_SPRAY_COMPUTE); + + SafeGetAsset(AssetId::ASSET_SHADER_PARTICLE_SPRAY_COMPUTE); + const char* gaussian_blur_shader_wgsl = - (const char*)GetAsset(AssetId::ASSET_SHADER_GAUSSIAN_BLUR); + + SafeGetAsset(AssetId::ASSET_SHADER_GAUSSIAN_BLUR); + const char* solarize_shader_wgsl = - (const char*)GetAsset(AssetId::ASSET_SHADER_SOLARIZE); + + SafeGetAsset(AssetId::ASSET_SHADER_SOLARIZE); + const char* distort_shader_wgsl = - (const char*)GetAsset(AssetId::ASSET_SHADER_DISTORT); + + SafeGetAsset(AssetId::ASSET_SHADER_DISTORT); + const char* chroma_aberration_shader_wgsl = - (const char*)GetAsset(AssetId::ASSET_SHADER_CHROMA_ABERRATION);
\ No newline at end of file + + SafeGetAsset(AssetId::ASSET_SHADER_CHROMA_ABERRATION); diff --git a/src/tests/test_assets.cc b/src/tests/test_assets.cc index 6f57d8f..7f26e71 100644 --- a/src/tests/test_assets.cc +++ b/src/tests/test_assets.cc @@ -16,7 +16,7 @@ int main() { printf("Running AssetManager test...\n"); size_t size = 0; - const uint8_t* data1 = GetAsset(AssetId::ASSET_TEST_ASSET, &size); + const uint8_t* data1 = GetAsset(AssetId::ASSET_TEST_ASSET_1, &size); assert(data1 != nullptr); assert(size > 0); @@ -33,7 +33,7 @@ int main() { // Test caching: request the same asset again and verify pointer is identical size_t size2 = 0; - const uint8_t* data2 = GetAsset(AssetId::ASSET_TEST_ASSET, &size2); + const uint8_t* data2 = GetAsset(AssetId::ASSET_TEST_ASSET_1, &size2); assert(data2 != nullptr); assert(size2 == size); assert(data1 == data2); // Pointers should be the same for cached static asset diff --git a/src/tests/test_shader_assets.cc b/src/tests/test_shader_assets.cc new file mode 100644 index 0000000..42d1c4c --- /dev/null +++ b/src/tests/test_shader_assets.cc @@ -0,0 +1,66 @@ +// This file is part of the 64k demo project. +// It validates that WGSL shader assets are present and look like valid WGSL. + +#include "generated/assets.h" +#include <cassert> +#include <cstdio> +#include <cstring> +#include <string> +#include <vector> + +bool validate_shader(AssetId id, const char* name, const std::vector<const char*>& expected_keywords) { + printf("Validating shader: %s...\n", name); + size_t size = 0; + const char* data = (const char*)GetAsset(id, &size); + + if (data == nullptr || size == 0) { + printf("FAILED: Shader %s is missing or empty!\n", name); + return false; + } + + std::string code(data, size); + for (const char* keyword : expected_keywords) { + if (code.find(keyword) == std::string::npos) { + printf("FAILED: Shader %s missing expected keyword '%s'!\n", name, keyword); + // printf("Code snippet:\n%.100s...\n", data); + return false; + } + } + + printf("PASSED: %s (%zu bytes)\n", name, size); + return true; +} + +int main() { + printf("--- RUNNING SHADER ASSET VALIDATION ---\n"); + + bool all_passed = true; + + // Snippets + all_passed &= validate_shader(AssetId::ASSET_SHADER_COMMON_UNIFORMS, "COMMON_UNIFORMS", {"struct", "GlobalUniforms"}); + all_passed &= validate_shader(AssetId::ASSET_SHADER_SDF_PRIMITIVES, "SDF_PRIMITIVES", {"fn", "sd"}); + all_passed &= validate_shader(AssetId::ASSET_SHADER_LIGHTING, "LIGHTING", {"fn", "calc"}); + all_passed &= validate_shader(AssetId::ASSET_SHADER_RAY_BOX, "RAY_BOX", {"fn", "intersect"}); + + // Full Shaders (Entry points) + all_passed &= validate_shader(AssetId::ASSET_SHADER_RENDERER_3D, "RENDERER_3D", {"@vertex", "vs_main", "@fragment", "fs_main"}); + all_passed &= validate_shader(AssetId::ASSET_SHADER_MAIN, "MAIN", {"@vertex", "vs_main", "@fragment", "fs_main"}); + all_passed &= validate_shader(AssetId::ASSET_SHADER_PARTICLE_COMPUTE, "PARTICLE_COMPUTE", {"@compute", "main"}); + all_passed &= validate_shader(AssetId::ASSET_SHADER_PARTICLE_RENDER, "PARTICLE_RENDER", {"@vertex", "vs_main", "@fragment", "fs_main"}); + all_passed &= validate_shader(AssetId::ASSET_SHADER_PASSTHROUGH, "PASSTHROUGH", {"@vertex", "vs_main", "@fragment", "fs_main"}); + all_passed &= validate_shader(AssetId::ASSET_SHADER_ELLIPSE, "ELLIPSE", {"@vertex", "vs_main", "@fragment", "fs_main"}); + all_passed &= validate_shader(AssetId::ASSET_SHADER_PARTICLE_SPRAY_COMPUTE, "PARTICLE_SPRAY_COMPUTE", {"@compute", "main"}); + all_passed &= validate_shader(AssetId::ASSET_SHADER_GAUSSIAN_BLUR, "GAUSSIAN_BLUR", {"@vertex", "vs_main", "@fragment", "fs_main"}); + all_passed &= validate_shader(AssetId::ASSET_SHADER_SOLARIZE, "SOLARIZE", {"@vertex", "vs_main", "@fragment", "fs_main"}); + all_passed &= validate_shader(AssetId::ASSET_SHADER_DISTORT, "DISTORT", {"@vertex", "vs_main", "@fragment", "fs_main"}); + all_passed &= validate_shader(AssetId::ASSET_SHADER_CHROMA_ABERRATION, "CHROMA_ABERRATION", {"@vertex", "vs_main", "@fragment", "fs_main"}); + all_passed &= validate_shader(AssetId::ASSET_SHADER_VISUAL_DEBUG, "VISUAL_DEBUG", {"@vertex", "vs_main", "@fragment", "fs_main"}); + + if (!all_passed) { + printf("--- SHADER ASSET VALIDATION FAILED ---\n"); + return 1; + } + + printf("--- ALL SHADER ASSETS VALIDATED ---\n"); + return 0; +} diff --git a/src/tests/test_shader_composer.cc b/src/tests/test_shader_composer.cc index cdb5c88..7efcd83 100644 --- a/src/tests/test_shader_composer.cc +++ b/src/tests/test_shader_composer.cc @@ -6,6 +6,16 @@ #include <iostream> #include <string> +#if defined(USE_TEST_ASSETS) +#include "test_assets.h" +#else +#include "generated/assets.h" +#endif + +// Forward declaration for asset loading +const uint8_t* GetAsset(AssetId asset_id, size_t* out_size); + + void test_composition() { std::cout << "Testing Shader Composition..." << std::endl; auto& sc = ShaderComposer::Get(); @@ -31,8 +41,41 @@ void test_composition() { std::cout << "Composition logic verified." << std::endl; } +void test_asset_composition() { + std::cout << "Testing Asset-Based Shader Composition..." << std::endl; + + // Use test assets + auto& sc = ShaderComposer::Get(); + + size_t snippet_a_size; + const char* snippet_a_code = (const char*)GetAsset(AssetId::ASSET_SHADER_SNIPPET_A, &snippet_a_size); + assert(snippet_a_code != nullptr); + sc.RegisterSnippet("SNIPPET_A", std::string(snippet_a_code, snippet_a_size)); + + size_t snippet_b_size; + const char* snippet_b_code = (const char*)GetAsset(AssetId::ASSET_SHADER_SNIPPET_B, &snippet_b_size); + sc.RegisterSnippet("SNIPPET_B", std::string(snippet_b_code, snippet_b_size)); + + std::string main_code = "fn main() -> f32 { return snippet_a() + snippet_b(); }"; + std::string result = sc.Compose({"SNIPPET_A", "SNIPPET_B"}, main_code); + + assert(result.find("fn snippet_a()") != std::string::npos); + assert(result.find("fn snippet_b()") != std::string::npos); + assert(result.find("fn main()") != std::string::npos); + + size_t pos_a = result.find("snippet_a"); + size_t pos_b = result.find("snippet_b"); + size_t pos_main = result.find("main"); + + assert(pos_a < pos_b); + assert(pos_b < pos_main); + + std::cout << "Asset-based composition logic verified." << std::endl; +} + int main() { test_composition(); + test_asset_composition(); std::cout << "--- ALL SHADER COMPOSER TESTS PASSED ---" << std::endl; return 0; } |
