From d7d4411251f961723d49d554baa0f54bbc100f65 Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Mon, 5 Jan 2026 23:44:00 +0000 Subject: [PATCH] feat: Refactor shader handling by implementing inline shader sources and enhancing error handling --- docs/test_error_handling.md | 7 +- scripts/gui_demo.lua | 33 +++- scripts/shader_toolkit.lua | 284 +++++++++++++++++++++++++++++ shaders/gui_2d.frag | 8 - shaders/gui_2d.vert | 33 ---- shaders/shadow.frag | 6 - shaders/shadow.vert | 30 --- shaders/ssgi.frag | 79 -------- shaders/volumetric.frag | 62 ------- src/services/impl/gui_renderer.cpp | 126 ++++++++++++- src/services/impl/gui_renderer.hpp | 3 + tests/test_gxm_backend.cpp | 41 ++++- 12 files changed, 473 insertions(+), 239 deletions(-) delete mode 100644 shaders/gui_2d.frag delete mode 100644 shaders/gui_2d.vert delete mode 100644 shaders/shadow.frag delete mode 100644 shaders/shadow.vert delete mode 100644 shaders/ssgi.frag delete mode 100644 shaders/volumetric.frag diff --git a/docs/test_error_handling.md b/docs/test_error_handling.md index 146f245..6e5143c 100644 --- a/docs/test_error_handling.md +++ b/docs/test_error_handling.md @@ -83,7 +83,7 @@ The file exists but cannot be opened. Check file permissions. **Example error message**: ``` -Vertex shader not found: shaders/gui_2d.vert +Vertex shader not found: shaders/missing.vert Shader key: default @@ -160,19 +160,20 @@ Plus a message box with the same error. ### Test Case 2: Missing Shader File ```bash cd build/Release -mv shaders/gui_2d.vert shaders/gui_2d.vert.backup +sed -i 's/return {default = variant}/return {default = {vertex = "shaders\/missing.vert", fragment = "shaders\/missing.frag"}}/' ../scripts/gui_demo.lua ./sdl3_app --json-file-in ./config/gui_runtime.json ``` **Expected Result**: Message box showing: ``` -Vertex shader not found: shaders/gui_2d.vert +Vertex shader not found: shaders/missing.vert Shader key: default Please ensure shader files are compiled and present in the shaders directory. ``` +Restore `scripts/gui_demo.lua` after the test run. ### Test Case 3: Normal Operation ```bash diff --git a/scripts/gui_demo.lua b/scripts/gui_demo.lua index 6c19226..6598299 100644 --- a/scripts/gui_demo.lua +++ b/scripts/gui_demo.lua @@ -66,12 +66,26 @@ local function updateFpsModeToggle() end end -local shader_variants = { - default = { - vertex = "shaders/gui_2d.vert", - fragment = "shaders/gui_2d.frag", - }, -} +local function build_shader_variants() + local ok, toolkit = pcall(require, "shader_toolkit") + if not ok then + error("Shader toolkit unavailable: " .. tostring(toolkit)) + end + + local ok_generate, variant = pcall(toolkit.generate_variant, { + key = "default", + template = "gui_2d", + output_mode = "source", + compile = false, + }) + if not ok_generate then + error("Shader generation failed: " .. tostring(variant)) + end + + return {default = variant} +end + +local shader_variants = build_shader_variants() local function drawTestButtons() -- Background panel @@ -177,9 +191,12 @@ function get_scene_objects() end function get_shader_paths() + local default_variant = shader_variants.default or {} + local vertex_label = default_variant.vertex or "inline" + local fragment_label = default_variant.fragment or "inline" log_trace("GUI demo shader variants: default vertex=%s fragment=%s", - shader_variants.default.vertex, - shader_variants.default.fragment) + vertex_label, + fragment_label) return shader_variants end diff --git a/scripts/shader_toolkit.lua b/scripts/shader_toolkit.lua index e6d591a..2c67bb9 100644 --- a/scripts/shader_toolkit.lua +++ b/scripts/shader_toolkit.lua @@ -338,6 +338,258 @@ void main() { } ]] +local gui_2d_vertex_source = [[ +#version 450 + +layout(location = 0) in vec3 inPos; +layout(location = 1) in vec4 inColor; + +layout(location = 0) out vec4 fragColor; + +layout(push_constant) uniform PushConstants { + mat4 model; + mat4 viewProj; + // Extended fields for PBR/atmospherics (ignored by basic shaders) + mat4 view; + mat4 proj; + mat4 lightViewProj; + vec3 cameraPos; + float time; + // Atmospherics parameters + float ambientStrength; + float fogDensity; + float fogStart; + float fogEnd; + vec3 fogColor; + float gamma; + float exposure; + int enableShadows; + int enableFog; +} pushConstants; + +void main() { + fragColor = inColor; + vec4 worldPos = pushConstants.model * vec4(inPos, 1.0); + gl_Position = pushConstants.viewProj * worldPos; +} +]] + +local gui_2d_fragment_source = [[ +#version 450 + +layout(location = 0) in vec4 fragColor; +layout(location = 0) out vec4 outColor; + +void main() { + outColor = fragColor; +} +]] + +local shadow_vertex_source = [[ +#version 450 + +layout(location = 0) in vec3 inPosition; +layout(location = 1) in vec3 inColor; +// layout(location = 2) in vec2 inTexCoord; // Not used + +layout(push_constant) uniform PushConstants { + mat4 model; + mat4 viewProj; + // Extended fields for PBR/atmospherics + mat4 view; + mat4 proj; + mat4 lightViewProj; + vec3 cameraPos; + float time; + // Atmospherics parameters + float ambientStrength; + float fogDensity; + float fogStart; + float fogEnd; + vec3 fogColor; + float gamma; + float exposure; + int enableShadows; + int enableFog; +} pc; + +void main() { + gl_Position = pc.lightViewProj * pc.model * vec4(inPosition, 1.0); +} +]] + +local shadow_fragment_source = [[ +#version 450 + +void main() { + // Empty fragment shader for shadow mapping + // Depth is automatically written +} +]] + +local fullscreen_vertex_source = [[ +#version 450 + +layout(location = 0) in vec2 inPosition; +layout(location = 1) in vec2 inTexCoord; + +layout(location = 0) out vec2 fragPosition; +layout(location = 1) out vec2 fragTexCoord; + +void main() { + fragPosition = inPosition; + fragTexCoord = inTexCoord; + gl_Position = vec4(inPosition, 0.0, 1.0); +} +]] + +local ssgi_fragment_source = [[ +#version 450 + +layout(location = 0) in vec2 inPosition; +layout(location = 1) in vec2 inTexCoord; + +layout(location = 0) out vec4 outColor; + +layout(set = 0, binding = 0) uniform sampler2D sceneColor; +layout(set = 0, binding = 1) uniform sampler2D normalBuffer; +layout(set = 0, binding = 2) uniform sampler2D depthBuffer; + +layout(push_constant) uniform PushConstants { + mat4 model; + mat4 viewProj; + // Extended fields for PBR/atmospherics + mat4 view; + mat4 proj; + mat4 lightViewProj; + vec3 cameraPos; + float time; + // Atmospherics parameters + float ambientStrength; + float fogDensity; + float fogStart; + float fogEnd; + vec3 fogColor; + float gamma; + float exposure; + int enableShadows; + int enableFog; +} pc; + +const int NUM_SAMPLES = 16; +const float SAMPLE_RADIUS = 0.5; + +// Reconstruct world position from depth +vec3 worldPosFromDepth(float depth, vec2 texCoord) { + vec4 clipSpace = vec4(texCoord * 2.0 - 1.0, depth, 1.0); + vec4 viewSpace = pc.proj * clipSpace; // Use proj as invProj for now (dummy) + viewSpace /= viewSpace.w; + vec4 worldSpace = pc.view * viewSpace; // Use view as invView for now (dummy) + return worldSpace.xyz; +} + +vec3 ssao(vec2 texCoord) { + float depth = texture(depthBuffer, texCoord).r; + if (depth >= 1.0) return vec3(1.0); // Skybox + + vec3 worldPos = worldPosFromDepth(depth, texCoord); + vec3 normal = normalize(texture(normalBuffer, texCoord).rgb * 2.0 - 1.0); + + float occlusion = 0.0; + + for (int i = 0; i < NUM_SAMPLES; i++) { + // Generate sample position in hemisphere around normal + float angle = (float(i) / float(NUM_SAMPLES)) * 6.283185; + vec2 offset = vec2(cos(angle), sin(angle)) * SAMPLE_RADIUS; + + vec2 sampleTexCoord = texCoord + offset / textureSize(depthBuffer, 0); + float sampleDepth = texture(depthBuffer, sampleTexCoord).r; + + if (sampleDepth < depth - 0.01) { // Occluded + occlusion += 1.0; + } + } + + occlusion = 1.0 - (occlusion / float(NUM_SAMPLES)); + return vec3(occlusion); +} + +void main() { + vec3 sceneColor = texture(sceneColor, inTexCoord).rgb; + vec3 ao = ssao(inTexCoord); + + // Apply ambient occlusion + vec3 finalColor = sceneColor * (0.3 + 0.7 * ao); // Mix AO with direct lighting + + outColor = vec4(finalColor, 1.0); +} +]] + +local volumetric_fragment_source = [[ +#version 450 + +layout(location = 0) in vec2 inPosition; +layout(location = 1) in vec2 inTexCoord; + +layout(location = 0) out vec4 outColor; + +layout(set = 0, binding = 0) uniform sampler2D sceneColor; +layout(set = 0, binding = 1) uniform sampler2D depthBuffer; + +layout(push_constant) uniform PushConstants { + mat4 model; + mat4 viewProj; + // Extended fields for PBR/atmospherics + mat4 view; + mat4 proj; + mat4 lightViewProj; + vec3 cameraPos; + float time; + // Atmospherics parameters + float ambientStrength; + float fogDensity; + float fogStart; + float fogEnd; + vec3 fogColor; + float gamma; + float exposure; + int enableShadows; + int enableFog; +} pc; + +const int NUM_SAMPLES = 100; +const float DECAY_BASE = 0.96815; +const float WEIGHT_BASE = 0.58767; +const float EXPOSURE = 0.2; + +void main() { + vec2 texCoord = inTexCoord; + vec2 lightScreenPos = vec2(0.5, 0.5); // Dummy light position + vec2 deltaTexCoord = (texCoord - lightScreenPos); + deltaTexCoord *= 1.0 / float(NUM_SAMPLES) * 0.5; // Scale for effect + + vec3 color = texture(sceneColor, texCoord).rgb; + + // Only apply god rays if we're looking towards the light + float centerDistance = length(lightScreenPos - vec2(0.5, 0.5)); + if (centerDistance < 0.8) { // Light is visible on screen + vec3 godRayColor = vec3(0.0); + float weight = WEIGHT_BASE; + + for (int i = 0; i < NUM_SAMPLES; i++) { + texCoord -= deltaTexCoord; + vec3 sampleColor = texture(sceneColor, texCoord).rgb; + godRayColor += sampleColor * weight; + weight *= DECAY_BASE; + } + + color += godRayColor * EXPOSURE * 1.0; // Dummy intensity + } + + outColor = vec4(color, 1.0); +} +]] + local vertex_world_color_source = [[ #version 450 @@ -996,6 +1248,13 @@ local function build_vertex_color_sources() } end +local function build_gui_2d_sources() + return { + vertex = gui_2d_vertex_source, + fragment = gui_2d_fragment_source, + } +end + local function build_solid_color_sources(options) local color = normalize_color(options.color) local fragment = string.format([[ @@ -1013,6 +1272,13 @@ void main() { } end +local function build_shadow_sources() + return { + vertex = shadow_vertex_source, + fragment = shadow_fragment_source, + } +end + local function build_cube_rainbow_sources(options) return { vertex = vertex_world_color_source, @@ -1055,6 +1321,20 @@ local function build_pbr_sources(options) } end +local function build_ssgi_sources() + return { + vertex = fullscreen_vertex_source, + fragment = ssgi_fragment_source, + } +end + +local function build_volumetric_sources() + return { + vertex = fullscreen_vertex_source, + fragment = volumetric_fragment_source, + } +end + local templates = {} local function register_template_object(template) @@ -1068,9 +1348,11 @@ local function register_template_object(template) end register_template_object(ShaderTemplate:new("vertex_color", build_vertex_color_sources)) +register_template_object(ShaderTemplate:new("gui_2d", build_gui_2d_sources)) register_template_object(ShaderTemplate:new("solid_color", build_solid_color_sources, { color = {1.0, 1.0, 1.0, 1.0}, })) +register_template_object(ShaderTemplate:new("shadow", build_shadow_sources)) register_template_object(ShaderTemplate:new("cube_rainbow", build_cube_rainbow_sources, { band_scale = 0.35, diagonal_scale = 0.25, @@ -1114,6 +1396,8 @@ register_template_object(ShaderTemplate:new("pbr", build_pbr_sources, { light_color = {1.0, 0.9, 0.6}, light_intensity = 1.2, })) +register_template_object(ShaderTemplate:new("ssgi", build_ssgi_sources)) +register_template_object(ShaderTemplate:new("volumetric", build_volumetric_sources)) shader_toolkit.templates = templates diff --git a/shaders/gui_2d.frag b/shaders/gui_2d.frag deleted file mode 100644 index c000e75..0000000 --- a/shaders/gui_2d.frag +++ /dev/null @@ -1,8 +0,0 @@ -#version 450 - -layout(location = 0) in vec4 fragColor; -layout(location = 0) out vec4 outColor; - -void main() { - outColor = fragColor; -} diff --git a/shaders/gui_2d.vert b/shaders/gui_2d.vert deleted file mode 100644 index 41fadcb..0000000 --- a/shaders/gui_2d.vert +++ /dev/null @@ -1,33 +0,0 @@ -#version 450 - -layout(location = 0) in vec3 inPos; -layout(location = 1) in vec4 inColor; - -layout(location = 0) out vec4 fragColor; - -layout(push_constant) uniform PushConstants { - mat4 model; - mat4 viewProj; - // Extended fields for PBR/atmospherics (ignored by basic shaders) - mat4 view; - mat4 proj; - mat4 lightViewProj; - vec3 cameraPos; - float time; - // Atmospherics parameters - float ambientStrength; - float fogDensity; - float fogStart; - float fogEnd; - vec3 fogColor; - float gamma; - float exposure; - int enableShadows; - int enableFog; -} pushConstants; - -void main() { - fragColor = inColor; - vec4 worldPos = pushConstants.model * vec4(inPos, 1.0); - gl_Position = pushConstants.viewProj * worldPos; -} diff --git a/shaders/shadow.frag b/shaders/shadow.frag deleted file mode 100644 index b7217da..0000000 --- a/shaders/shadow.frag +++ /dev/null @@ -1,6 +0,0 @@ -#version 450 - -void main() { - // Empty fragment shader for shadow mapping - // Depth is automatically written -} \ No newline at end of file diff --git a/shaders/shadow.vert b/shaders/shadow.vert deleted file mode 100644 index 522a6ff..0000000 --- a/shaders/shadow.vert +++ /dev/null @@ -1,30 +0,0 @@ -#version 450 - -layout(location = 0) in vec3 inPosition; -layout(location = 1) in vec3 inColor; -// layout(location = 2) in vec2 inTexCoord; // Not used - -layout(push_constant) uniform PushConstants { - mat4 model; - mat4 viewProj; - // Extended fields for PBR/atmospherics - mat4 view; - mat4 proj; - mat4 lightViewProj; - vec3 cameraPos; - float time; - // Atmospherics parameters - float ambientStrength; - float fogDensity; - float fogStart; - float fogEnd; - vec3 fogColor; - float gamma; - float exposure; - int enableShadows; - int enableFog; -} pc; - -void main() { - gl_Position = pc.lightViewProj * pc.model * vec4(inPosition, 1.0); -} \ No newline at end of file diff --git a/shaders/ssgi.frag b/shaders/ssgi.frag deleted file mode 100644 index 9458c48..0000000 --- a/shaders/ssgi.frag +++ /dev/null @@ -1,79 +0,0 @@ -#version 450 - -layout(location = 0) in vec2 inPosition; -layout(location = 1) in vec2 inTexCoord; - -layout(location = 0) out vec4 outColor; - -layout(set = 0, binding = 0) uniform sampler2D sceneColor; -layout(set = 0, binding = 1) uniform sampler2D normalBuffer; -layout(set = 0, binding = 2) uniform sampler2D depthBuffer; - -layout(push_constant) uniform PushConstants { - mat4 model; - mat4 viewProj; - // Extended fields for PBR/atmospherics - mat4 view; - mat4 proj; - mat4 lightViewProj; - vec3 cameraPos; - float time; - // Atmospherics parameters - float ambientStrength; - float fogDensity; - float fogStart; - float fogEnd; - vec3 fogColor; - float gamma; - float exposure; - int enableShadows; - int enableFog; -} pc; - -const int NUM_SAMPLES = 16; -const float SAMPLE_RADIUS = 0.5; - -// Reconstruct world position from depth -vec3 worldPosFromDepth(float depth, vec2 texCoord) { - vec4 clipSpace = vec4(texCoord * 2.0 - 1.0, depth, 1.0); - vec4 viewSpace = pc.proj * clipSpace; // Use proj as invProj for now (dummy) - viewSpace /= viewSpace.w; - vec4 worldSpace = pc.view * viewSpace; // Use view as invView for now (dummy) - return worldSpace.xyz; -} - -vec3 ssao(vec2 texCoord) { - float depth = texture(depthBuffer, texCoord).r; - if (depth >= 1.0) return vec3(1.0); // Skybox - - vec3 worldPos = worldPosFromDepth(depth, texCoord); - vec3 normal = normalize(texture(normalBuffer, texCoord).rgb * 2.0 - 1.0); - - float occlusion = 0.0; - - for (int i = 0; i < NUM_SAMPLES; i++) { - // Generate sample position in hemisphere around normal - float angle = (float(i) / float(NUM_SAMPLES)) * 6.283185; - vec2 offset = vec2(cos(angle), sin(angle)) * SAMPLE_RADIUS; - - vec2 sampleTexCoord = texCoord + offset / textureSize(depthBuffer, 0); - float sampleDepth = texture(depthBuffer, sampleTexCoord).r; - - if (sampleDepth < depth - 0.01) { // Occluded - occlusion += 1.0; - } - } - - occlusion = 1.0 - (occlusion / float(NUM_SAMPLES)); - return vec3(occlusion); -} - -void main() { - vec3 sceneColor = texture(sceneColor, inTexCoord).rgb; - vec3 ao = ssao(inTexCoord); - - // Apply ambient occlusion - vec3 finalColor = sceneColor * (0.3 + 0.7 * ao); // Mix AO with direct lighting - - outColor = vec4(finalColor, 1.0); -} \ No newline at end of file diff --git a/shaders/volumetric.frag b/shaders/volumetric.frag deleted file mode 100644 index 24cf0e0..0000000 --- a/shaders/volumetric.frag +++ /dev/null @@ -1,62 +0,0 @@ -#version 450 - -layout(location = 0) in vec2 inPosition; -layout(location = 1) in vec2 inTexCoord; - -layout(location = 0) out vec4 outColor; - -layout(set = 0, binding = 0) uniform sampler2D sceneColor; -layout(set = 0, binding = 1) uniform sampler2D depthBuffer; - -layout(push_constant) uniform PushConstants { - mat4 model; - mat4 viewProj; - // Extended fields for PBR/atmospherics - mat4 view; - mat4 proj; - mat4 lightViewProj; - vec3 cameraPos; - float time; - // Atmospherics parameters - float ambientStrength; - float fogDensity; - float fogStart; - float fogEnd; - vec3 fogColor; - float gamma; - float exposure; - int enableShadows; - int enableFog; -} pc; - -const int NUM_SAMPLES = 100; -const float DECAY_BASE = 0.96815; -const float WEIGHT_BASE = 0.58767; -const float EXPOSURE = 0.2; - -void main() { - vec2 texCoord = inTexCoord; - vec2 lightScreenPos = vec2(0.5, 0.5); // Dummy light position - vec2 deltaTexCoord = (texCoord - lightScreenPos); - deltaTexCoord *= 1.0 / float(NUM_SAMPLES) * 0.5; // Scale for effect - - vec3 color = texture(sceneColor, texCoord).rgb; - - // Only apply god rays if we're looking towards the light - float centerDistance = length(lightScreenPos - vec2(0.5, 0.5)); - if (centerDistance < 0.8) { // Light is visible on screen - vec3 godRayColor = vec3(0.0); - float weight = WEIGHT_BASE; - - for (int i = 0; i < NUM_SAMPLES; i++) { - texCoord -= deltaTexCoord; - vec3 sampleColor = texture(sceneColor, texCoord).rgb; - godRayColor += sampleColor * weight; - weight *= DECAY_BASE; - } - - color += godRayColor * EXPOSURE * 1.0; // Dummy intensity - } - - outColor = vec4(color, 1.0); -} \ No newline at end of file diff --git a/src/services/impl/gui_renderer.cpp b/src/services/impl/gui_renderer.cpp index 0ff44f0..cb9ef6d 100644 --- a/src/services/impl/gui_renderer.cpp +++ b/src/services/impl/gui_renderer.cpp @@ -316,6 +316,93 @@ std::vector ReadShaderFile(const std::filesystem::path& path, return buffer; } +std::vector ReadShaderSource(const std::string& source, + VkShaderStageFlagBits stage, + const std::string& label, + ILogger* logger) { + if (logger) { + logger->Trace("GuiRenderer", "ReadShaderSource", + "label=" + label + ", stage=" + + std::to_string(static_cast(stage))); + } + + if (source.empty()) { + throw std::runtime_error("Shader source is empty"); + } + + shaderc::Compiler compiler; + shaderc::CompileOptions options; + options.SetTargetEnvironment(shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_2); + + shaderc_shader_kind kind = ShadercKindFromStage(stage); + auto result = compiler.CompileGlslToSpv(source, kind, label.c_str(), options); + if (result.GetCompilationStatus() != shaderc_compilation_status_success) { + std::string error = result.GetErrorMessage(); + if (logger) { + logger->Error("GuiRenderer shader compilation failed: " + label + "\n" + error); + } + throw std::runtime_error("Shader compilation failed: " + label + "\n" + error); + } + + std::vector spirv(result.cbegin(), result.cend()); + std::vector buffer(spirv.size() * sizeof(uint32_t)); + if (!buffer.empty()) { + std::memcpy(buffer.data(), spirv.data(), buffer.size()); + } + if (logger) { + logger->Trace("GuiRenderer", "ReadShaderSource", + "compiledBytes=" + std::to_string(buffer.size())); + } + return buffer; +} + +const char* kGuiVertexSource = R"( +#version 450 + +layout(location = 0) in vec3 inPos; +layout(location = 1) in vec4 inColor; + +layout(location = 0) out vec4 fragColor; + +layout(push_constant) uniform PushConstants { + mat4 model; + mat4 viewProj; + // Extended fields for PBR/atmospherics (ignored by basic shaders) + mat4 view; + mat4 proj; + mat4 lightViewProj; + vec3 cameraPos; + float time; + // Atmospherics parameters + float ambientStrength; + float fogDensity; + float fogStart; + float fogEnd; + vec3 fogColor; + float gamma; + float exposure; + int enableShadows; + int enableFog; +} pushConstants; + +void main() { + fragColor = inColor; + vec4 worldPos = pushConstants.model * vec4(inPos, 1.0); + gl_Position = pushConstants.viewProj * worldPos; +} +)"; + +const char* kGuiFragmentSource = R"( +#version 450 + +layout(location = 0) in vec4 fragColor; +layout(location = 0) out vec4 outColor; + +void main() { + outColor = fragColor; +} +)"; + } // namespace GuiRenderer::GuiRenderer(VkDevice device, VkPhysicalDevice physicalDevice, VkFormat swapchainFormat, @@ -663,22 +750,45 @@ const std::vector& GuiRenderer::LoadShaderBytes(const std::filesystem:: return inserted.first->second; } +const std::vector& GuiRenderer::LoadShaderBytes(const std::string& cacheKey, + const std::string& source, + VkShaderStageFlagBits stage) { + auto cached = shaderSpirvCache_.find(cacheKey); + if (cached != shaderSpirvCache_.end()) { + if (logger_) { + logger_->Trace("GuiRenderer", "LoadShaderBytes", + "cacheHit=true, key=" + cacheKey + + ", bytes=" + std::to_string(cached->second.size())); + } + return cached->second; + } + + std::vector shaderBytes = ReadShaderSource(source, stage, cacheKey, logger_.get()); + auto inserted = shaderSpirvCache_.emplace(cacheKey, std::move(shaderBytes)); + if (logger_) { + logger_->Trace("GuiRenderer", "LoadShaderBytes", + "cacheHit=false, key=" + cacheKey + + ", bytes=" + std::to_string(inserted.first->second.size())); + } + return inserted.first->second; +} + void GuiRenderer::CreatePipeline(VkRenderPass renderPass, VkExtent2D extent) { // Load shader modules - const std::filesystem::path vertexShaderPath = - scriptDirectory_.parent_path() / "shaders" / "gui_2d.vert"; - const std::filesystem::path fragmentShaderPath = - scriptDirectory_.parent_path() / "shaders" / "gui_2d.frag"; + const std::string vertexLabel = "inline:gui_2d.vert"; + const std::string fragmentLabel = "inline:gui_2d.frag"; if (logger_) { logger_->Trace("GuiRenderer", "CreatePipeline", "renderPassIsNull=" + std::string(renderPass == VK_NULL_HANDLE ? "true" : "false") + ", extent=" + std::to_string(extent.width) + "x" + std::to_string(extent.height) + - ", vertexShader=" + vertexShaderPath.string() + - ", fragmentShader=" + fragmentShaderPath.string()); + ", vertexShader=" + vertexLabel + + ", fragmentShader=" + fragmentLabel); } - const auto& vertShaderCode = LoadShaderBytes(vertexShaderPath, VK_SHADER_STAGE_VERTEX_BIT); - const auto& fragShaderCode = LoadShaderBytes(fragmentShaderPath, VK_SHADER_STAGE_FRAGMENT_BIT); + const auto& vertShaderCode = LoadShaderBytes(vertexLabel, kGuiVertexSource, + VK_SHADER_STAGE_VERTEX_BIT); + const auto& fragShaderCode = LoadShaderBytes(fragmentLabel, kGuiFragmentSource, + VK_SHADER_STAGE_FRAGMENT_BIT); VkShaderModuleCreateInfo vertModuleInfo{}; vertModuleInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; diff --git a/src/services/impl/gui_renderer.hpp b/src/services/impl/gui_renderer.hpp index 07c6d58..3323ff6 100644 --- a/src/services/impl/gui_renderer.hpp +++ b/src/services/impl/gui_renderer.hpp @@ -59,6 +59,9 @@ private: void GenerateGuiGeometry(const std::vector& commands, uint32_t width, uint32_t height); const std::vector& LoadShaderBytes(const std::filesystem::path& path, VkShaderStageFlagBits stage); + const std::vector& LoadShaderBytes(const std::string& cacheKey, + const std::string& source, + VkShaderStageFlagBits stage); VkDevice device_; VkPhysicalDevice physicalDevice_; diff --git a/tests/test_gxm_backend.cpp b/tests/test_gxm_backend.cpp index 9fa16d9..316679e 100644 --- a/tests/test_gxm_backend.cpp +++ b/tests/test_gxm_backend.cpp @@ -46,8 +46,45 @@ int main() { // Test pipeline creation sdl3cpp::services::ShaderPaths shaderPaths; - shaderPaths.vertex = "shaders/gui_2d.vert"; - shaderPaths.fragment = "shaders/gui_2d.frag"; + shaderPaths.vertex = "inline:gui_2d.vert"; + shaderPaths.fragment = "inline:gui_2d.frag"; + shaderPaths.vertexSource = R"( +#version 450 +layout(location = 0) in vec3 inPos; +layout(location = 1) in vec4 inColor; +layout(location = 0) out vec4 fragColor; +layout(push_constant) uniform PushConstants { + mat4 model; + mat4 viewProj; + mat4 view; + mat4 proj; + mat4 lightViewProj; + vec3 cameraPos; + float time; + float ambientStrength; + float fogDensity; + float fogStart; + float fogEnd; + vec3 fogColor; + float gamma; + float exposure; + int enableShadows; + int enableFog; +} pushConstants; +void main() { + fragColor = inColor; + vec4 worldPos = pushConstants.model * vec4(inPos, 1.0); + gl_Position = pushConstants.viewProj * worldPos; +} +)"; + shaderPaths.fragmentSource = R"( +#version 450 +layout(location = 0) in vec4 fragColor; +layout(location = 0) out vec4 outColor; +void main() { + outColor = fragColor; +} +)"; auto pipeline = backend->CreatePipeline(device, "test_shader", shaderPaths); Assert(pipeline != nullptr, "pipeline creation failed", failures); std::cout << "GXM pipeline created successfully\n";