diff --git a/engine/assets/builtin/shaders/forward-lit/forward-lit.frag.glsl b/engine/assets/builtin/shaders/forward-lit/forward-lit.frag.glsl index 64cd9f76..82f66bb4 100644 --- a/engine/assets/builtin/shaders/forward-lit/forward-lit.frag.glsl +++ b/engine/assets/builtin/shaders/forward-lit/forward-lit.frag.glsl @@ -43,6 +43,9 @@ in vec3 vPositionWS; layout(location = 0) out vec4 fragColor; float ComputeShadowAttenuation(vec3 positionWS) { +#ifndef XC_MAIN_LIGHT_SHADOWS + return 1.0; +#else if (gShadowOptions.x < 0.5) { return 1.0; } @@ -66,6 +69,7 @@ float ComputeShadowAttenuation(vec3 positionWS) { float receiverDepth = shadowNdc.z * 0.5 + 0.5 - gShadowBiasAndTexelSize.x; float shadowStrength = clamp(gShadowBiasAndTexelSize.w, 0.0, 1.0); return receiverDepth <= shadowDepth ? 1.0 : (1.0 - shadowStrength); +#endif } float ComputeRangeAttenuation(float distanceSq, float range) { diff --git a/engine/assets/builtin/shaders/forward-lit/forward-lit.frag.vk.glsl b/engine/assets/builtin/shaders/forward-lit/forward-lit.frag.vk.glsl index 19f47dce..d7824046 100644 --- a/engine/assets/builtin/shaders/forward-lit/forward-lit.frag.vk.glsl +++ b/engine/assets/builtin/shaders/forward-lit/forward-lit.frag.vk.glsl @@ -45,6 +45,9 @@ layout(location = 2) in vec3 vPositionWS; layout(location = 0) out vec4 fragColor; float ComputeShadowAttenuation(vec3 positionWS) { +#ifndef XC_MAIN_LIGHT_SHADOWS + return 1.0; +#else if (gShadowOptions.x < 0.5) { return 1.0; } @@ -68,6 +71,7 @@ float ComputeShadowAttenuation(vec3 positionWS) { float receiverDepth = shadowNdc.z - gShadowBiasAndTexelSize.x; float shadowStrength = clamp(gShadowBiasAndTexelSize.w, 0.0, 1.0); return receiverDepth <= shadowDepth ? 1.0 : (1.0 - shadowStrength); +#endif } float ComputeRangeAttenuation(float distanceSq, float range) { diff --git a/engine/assets/builtin/shaders/forward-lit/forward-lit.ps.hlsl b/engine/assets/builtin/shaders/forward-lit/forward-lit.ps.hlsl index 97888dac..4aef0f97 100644 --- a/engine/assets/builtin/shaders/forward-lit/forward-lit.ps.hlsl +++ b/engine/assets/builtin/shaders/forward-lit/forward-lit.ps.hlsl @@ -45,6 +45,9 @@ struct PSInput { }; float ComputeShadowAttenuation(float3 positionWS) { +#ifndef XC_MAIN_LIGHT_SHADOWS + return 1.0f; +#else if (gShadowOptions.x < 0.5f) { return 1.0f; } @@ -68,6 +71,7 @@ float ComputeShadowAttenuation(float3 positionWS) { const float receiverDepth = shadowNdc.z - gShadowBiasAndTexelSize.x; const float shadowStrength = saturate(gShadowBiasAndTexelSize.w); return receiverDepth <= shadowDepth ? 1.0f : (1.0f - shadowStrength); +#endif } float ComputeRangeAttenuation(float distanceSq, float range) { diff --git a/engine/assets/builtin/shaders/forward-lit/forward-lit.shader b/engine/assets/builtin/shaders/forward-lit/forward-lit.shader index 36fe7c96..84f0dc9d 100644 --- a/engine/assets/builtin/shaders/forward-lit/forward-lit.shader +++ b/engine/assets/builtin/shaders/forward-lit/forward-lit.shader @@ -25,6 +25,7 @@ Shader "Builtin Forward Lit" HLSLPROGRAM #pragma vertex MainVS #pragma fragment MainPS + #pragma multi_compile _ XC_MAIN_LIGHT_SHADOWS #pragma backend D3D12 HLSL "forward-lit.vs.hlsl" "forward-lit.ps.hlsl" vs_5_0 ps_5_0 #pragma backend OpenGL GLSL "forward-lit.vert.glsl" "forward-lit.frag.glsl" #pragma backend Vulkan GLSL "forward-lit.vert.vk.glsl" "forward-lit.frag.vk.glsl" diff --git a/engine/include/XCEngine/Rendering/FrameData/RenderSceneData.h b/engine/include/XCEngine/Rendering/FrameData/RenderSceneData.h index 1535c2d5..777a2a7c 100644 --- a/engine/include/XCEngine/Rendering/FrameData/RenderSceneData.h +++ b/engine/include/XCEngine/Rendering/FrameData/RenderSceneData.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -84,6 +85,7 @@ struct RenderSceneData { RenderCameraData cameraData; RenderEnvironmentData environment; RenderLightingData lighting; + Resources::ShaderKeywordSet globalShaderKeywords; std::vector visibleItems; bool HasCamera() const { diff --git a/engine/include/XCEngine/Rendering/Passes/BuiltinDepthStylePassBase.h b/engine/include/XCEngine/Rendering/Passes/BuiltinDepthStylePassBase.h index b3f09564..a1599d43 100644 --- a/engine/include/XCEngine/Rendering/Passes/BuiltinDepthStylePassBase.h +++ b/engine/include/XCEngine/Rendering/Passes/BuiltinDepthStylePassBase.h @@ -139,7 +139,9 @@ private: bool CreateResources(const RenderContext& context); void DestroyResources(); - ResolvedShaderPass ResolveSurfaceShaderPass(const Resources::Material* material) const; + ResolvedShaderPass ResolveSurfaceShaderPass( + const RenderSceneData& sceneData, + const Resources::Material* material) const; bool TryBuildSupportedBindingPlan( const Resources::ShaderPass& shaderPass, BuiltinPassResourceBindingPlan& outPlan, @@ -150,6 +152,7 @@ private: RHI::RHIPipelineState* GetOrCreatePipelineState( const RenderContext& context, const RenderSurface& surface, + const RenderSceneData& sceneData, const Resources::Material* material); bool CreateOwnedDescriptorSet( const BuiltinPassSetLayoutMetadata& setLayout, diff --git a/engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h b/engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h index c52c1350..5c142235 100644 --- a/engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h +++ b/engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h @@ -222,12 +222,15 @@ private: static bool TryResolveSurfacePassType( const Resources::Material* material, BuiltinMaterialPass& outPass); - ResolvedShaderPass ResolveSurfaceShaderPass(const Resources::Material* material) const; + ResolvedShaderPass ResolveSurfaceShaderPass( + const RenderSceneData& sceneData, + const Resources::Material* material) const; PassResourceLayout* GetOrCreatePassResourceLayout( const RenderContext& context, const ResolvedShaderPass& resolvedShaderPass); RHI::RHIPipelineState* GetOrCreatePipelineState( const RenderContext& context, + const RenderSceneData& sceneData, const Resources::Material* material); bool CreateOwnedDescriptorSet( const BuiltinPassSetLayoutMetadata& setLayout, diff --git a/engine/include/XCEngine/Resources/Shader/ShaderKeywordTypes.h b/engine/include/XCEngine/Resources/Shader/ShaderKeywordTypes.h index 565071b9..a3ea8790 100644 --- a/engine/include/XCEngine/Resources/Shader/ShaderKeywordTypes.h +++ b/engine/include/XCEngine/Resources/Shader/ShaderKeywordTypes.h @@ -112,5 +112,17 @@ inline bool IsShaderKeywordSubset( return true; } +inline ShaderKeywordSet CombineShaderKeywordSets( + const ShaderKeywordSet& first, + const ShaderKeywordSet& second) { + ShaderKeywordSet combined = first; + for (const Containers::String& keyword : second.enabledKeywords) { + combined.enabledKeywords.PushBack(keyword); + } + + NormalizeShaderKeywordSetInPlace(combined); + return combined; +} + } // namespace Resources } // namespace XCEngine diff --git a/engine/src/Rendering/Execution/CameraRenderer.cpp b/engine/src/Rendering/Execution/CameraRenderer.cpp index f09099ea..348399ce 100644 --- a/engine/src/Rendering/Execution/CameraRenderer.cpp +++ b/engine/src/Rendering/Execution/CameraRenderer.cpp @@ -45,6 +45,17 @@ std::unique_ptr CreatePipelineFromAsset( return std::make_unique(); } +Resources::ShaderKeywordSet BuildSceneGlobalShaderKeywords( + const RenderSceneData& sceneData) { + Resources::ShaderKeywordSet keywords = {}; + if (sceneData.lighting.HasMainDirectionalShadow()) { + keywords.enabledKeywords.PushBack("XC_MAIN_LIGHT_SHADOWS"); + } + + Resources::NormalizeShaderKeywordSetInPlace(keywords); + return keywords; +} + bool InitializeStandalonePass( RenderPass* pass, const RenderContext& context) { @@ -563,6 +574,7 @@ bool CameraRenderer::BuildSceneDataForRequest( outSceneData.lighting.mainDirectionalShadow = BuildDirectionalShadowData(request.directionalShadow, shadowMapView); } + outSceneData.globalShaderKeywords = BuildSceneGlobalShaderKeywords(outSceneData); outSceneData.cameraData.clearFlags = request.clearFlags; outSceneData.environment = BuildEnvironmentData(request); diff --git a/engine/src/Rendering/Passes/BuiltinDepthStylePassBaseResources.cpp b/engine/src/Rendering/Passes/BuiltinDepthStylePassBaseResources.cpp index d05d9485..8206e086 100644 --- a/engine/src/Rendering/Passes/BuiltinDepthStylePassBaseResources.cpp +++ b/engine/src/Rendering/Passes/BuiltinDepthStylePassBaseResources.cpp @@ -20,6 +20,14 @@ namespace Passes { namespace { +Resources::ShaderKeywordSet ResolvePassKeywordSet( + const RenderSceneData& sceneData, + const Resources::Material* material) { + return Resources::CombineShaderKeywordSets( + sceneData.globalShaderKeywords, + material != nullptr ? material->GetKeywordSet() : Resources::ShaderKeywordSet()); +} + bool IsSupportedPerObjectOnlyBindingPlan(const BuiltinPassResourceBindingPlan& bindingPlan) { return bindingPlan.perObject.IsValid() && bindingPlan.bindings.Size() == 1u && @@ -57,6 +65,7 @@ RHI::GraphicsPipelineDesc CreatePipelineDesc( RHI::RHIPipelineLayout* pipelineLayout, const Resources::Shader& shader, const Containers::String& passName, + const Resources::ShaderKeywordSet& keywordSet, const Resources::Material* material, const RenderSurface& surface, const RHI::InputLayoutDesc& inputLayout) { @@ -81,8 +90,6 @@ RHI::GraphicsPipelineDesc CreatePipelineDesc( pipelineDesc.depthStencilState.depthFunc = static_cast(RHI::ComparisonFunc::LessEqual); const Resources::ShaderBackend backend = ::XCEngine::Rendering::Detail::ToShaderBackend(backendType); - const Resources::ShaderKeywordSet keywordSet = - material != nullptr ? material->GetKeywordSet() : Resources::ShaderKeywordSet(); if (const Resources::ShaderStageVariant* vertexVariant = shader.FindVariant(passName, Resources::ShaderType::Vertex, backend, keywordSet)) { ::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*vertexVariant, pipelineDesc.vertexShader); @@ -154,12 +161,13 @@ void BuiltinDepthStylePassBase::DestroyResources() { } BuiltinDepthStylePassBase::ResolvedShaderPass BuiltinDepthStylePassBase::ResolveSurfaceShaderPass( + const RenderSceneData& sceneData, const Resources::Material* material) const { ResolvedShaderPass resolved = {}; const Resources::ShaderBackend backend = ::XCEngine::Rendering::Detail::ToShaderBackend(m_backendType); auto tryResolveFromShader = - [this, backend, &resolved]( + [this, backend, &resolved, &sceneData]( const Resources::Shader* shader, const Resources::Material* ownerMaterial) -> bool { if (shader == nullptr) { @@ -167,8 +175,7 @@ BuiltinDepthStylePassBase::ResolvedShaderPass BuiltinDepthStylePassBase::Resolve } const bool shaderHasExplicitBuiltinMetadata = ShaderHasExplicitBuiltinMetadata(*shader); - const Resources::ShaderKeywordSet keywordSet = - ownerMaterial != nullptr ? ownerMaterial->GetKeywordSet() : Resources::ShaderKeywordSet(); + const Resources::ShaderKeywordSet keywordSet = ResolvePassKeywordSet(sceneData, ownerMaterial); auto tryAcceptPass = [this, shader, &resolved](const Resources::ShaderPass& shaderPass) -> bool { @@ -333,8 +340,10 @@ BuiltinDepthStylePassBase::PassResourceLayout* BuiltinDepthStylePassBase::GetOrC RHI::RHIPipelineState* BuiltinDepthStylePassBase::GetOrCreatePipelineState( const RenderContext& context, const RenderSurface& surface, + const RenderSceneData& sceneData, const Resources::Material* material) { - const ResolvedShaderPass resolvedShaderPass = ResolveSurfaceShaderPass(material); + const Resources::ShaderKeywordSet keywordSet = ResolvePassKeywordSet(sceneData, material); + const ResolvedShaderPass resolvedShaderPass = ResolveSurfaceShaderPass(sceneData, material); if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) { return nullptr; } @@ -349,9 +358,7 @@ RHI::RHIPipelineState* BuiltinDepthStylePassBase::GetOrCreatePipelineState( material != nullptr ? material->GetRenderState() : Resources::MaterialRenderState(); pipelineKey.shader = resolvedShaderPass.shader; pipelineKey.passName = resolvedShaderPass.passName; - pipelineKey.keywordSignature = - ::XCEngine::Rendering::Detail::BuildShaderKeywordSignature( - material != nullptr ? material->GetKeywordSet() : Resources::ShaderKeywordSet()); + pipelineKey.keywordSignature = ::XCEngine::Rendering::Detail::BuildShaderKeywordSignature(keywordSet); pipelineKey.renderTargetCount = ResolveSurfaceColorAttachmentCount(surface); pipelineKey.renderTargetFormat = static_cast(ResolveSurfaceColorFormat(surface)); pipelineKey.depthStencilFormat = static_cast(ResolveSurfaceDepthFormat(surface)); @@ -366,6 +373,7 @@ RHI::RHIPipelineState* BuiltinDepthStylePassBase::GetOrCreatePipelineState( passLayout->pipelineLayout, *resolvedShaderPass.shader, resolvedShaderPass.passName, + keywordSet, material, surface, BuildCommonInputLayout()); @@ -480,7 +488,7 @@ bool BuiltinDepthStylePassBase::DrawVisibleItem( } const Resources::Material* material = ResolveMaterial(visibleItem); - const ResolvedShaderPass resolvedShaderPass = ResolveSurfaceShaderPass(material); + const ResolvedShaderPass resolvedShaderPass = ResolveSurfaceShaderPass(sceneData, material); if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, @@ -502,7 +510,7 @@ bool BuiltinDepthStylePassBase::DrawVisibleItem( return false; } - RHI::RHIPipelineState* pipelineState = GetOrCreatePipelineState(context, surface, material); + RHI::RHIPipelineState* pipelineState = GetOrCreatePipelineState(context, surface, sceneData, material); if (pipelineState == nullptr) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, diff --git a/engine/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp b/engine/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp index 9a59ac1e..981f7382 100644 --- a/engine/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp +++ b/engine/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp @@ -506,7 +506,7 @@ bool BuiltinForwardPipeline::DrawVisibleItems( continue; } - RHI::RHIPipelineState* pipelineState = GetOrCreatePipelineState(context, material); + RHI::RHIPipelineState* pipelineState = GetOrCreatePipelineState(context, sceneData, material); if (pipelineState == nullptr) { continue; } diff --git a/engine/src/Rendering/Pipelines/BuiltinForwardPipelineResources.cpp b/engine/src/Rendering/Pipelines/BuiltinForwardPipelineResources.cpp index 4fe9f676..48934cb9 100644 --- a/engine/src/Rendering/Pipelines/BuiltinForwardPipelineResources.cpp +++ b/engine/src/Rendering/Pipelines/BuiltinForwardPipelineResources.cpp @@ -23,14 +23,22 @@ namespace { constexpr float kForwardAmbientIntensity = 0.28f; constexpr float kSpotInnerAngleRatio = 0.8f; +Resources::ShaderKeywordSet ResolvePassKeywordSet( + const RenderSceneData& sceneData, + const Resources::Material* material) { + return Resources::CombineShaderKeywordSets( + sceneData.globalShaderKeywords, + material != nullptr ? material->GetKeywordSet() : Resources::ShaderKeywordSet()); +} + const Resources::ShaderPass* FindCompatibleSurfacePass( const Resources::Shader& shader, + const RenderSceneData& sceneData, const Resources::Material* material, BuiltinMaterialPass pass, Resources::ShaderBackend backend) { const bool shaderHasExplicitBuiltinMetadata = ShaderHasExplicitBuiltinMetadata(shader); - const Resources::ShaderKeywordSet keywordSet = - material != nullptr ? material->GetKeywordSet() : Resources::ShaderKeywordSet(); + const Resources::ShaderKeywordSet keywordSet = ResolvePassKeywordSet(sceneData, material); for (const Resources::ShaderPass& shaderPass : shader.GetPasses()) { if (ShaderPassMatchesBuiltinPass(shaderPass, pass) && @@ -102,6 +110,7 @@ RHI::GraphicsPipelineDesc CreatePipelineDesc( RHI::RHIPipelineLayout* pipelineLayout, const Resources::Shader& shader, const Containers::String& passName, + const Resources::ShaderKeywordSet& keywordSet, const Resources::Material* material) { RHI::GraphicsPipelineDesc pipelineDesc = {}; pipelineDesc.pipelineLayout = pipelineLayout; @@ -115,8 +124,6 @@ RHI::GraphicsPipelineDesc CreatePipelineDesc( pipelineDesc.inputLayout = BuiltinForwardPipeline::BuildInputLayout(); const Resources::ShaderBackend backend = ::XCEngine::Rendering::Detail::ToShaderBackend(backendType); - const Resources::ShaderKeywordSet keywordSet = - material != nullptr ? material->GetKeywordSet() : Resources::ShaderKeywordSet(); const Resources::ShaderStageVariant* vertexVariant = shader.FindVariant(passName, Resources::ShaderType::Vertex, backend, keywordSet); const Resources::ShaderStageVariant* fragmentVariant = @@ -150,6 +157,7 @@ bool BuiltinForwardPipeline::TryResolveSurfacePassType( } BuiltinForwardPipeline::ResolvedShaderPass BuiltinForwardPipeline::ResolveSurfaceShaderPass( + const RenderSceneData& sceneData, const Resources::Material* material) const { ResolvedShaderPass resolved = {}; BuiltinMaterialPass pass = BuiltinMaterialPass::ForwardLit; @@ -162,7 +170,7 @@ BuiltinForwardPipeline::ResolvedShaderPass BuiltinForwardPipeline::ResolveSurfac if (material != nullptr && material->GetShader() != nullptr) { const Resources::Shader* materialShader = material->GetShader(); if (const Resources::ShaderPass* shaderPass = - FindCompatibleSurfacePass(*materialShader, material, pass, backend)) { + FindCompatibleSurfacePass(*materialShader, sceneData, material, pass, backend)) { resolved.shader = materialShader; resolved.pass = shaderPass; resolved.passName = shaderPass->name; @@ -175,7 +183,7 @@ BuiltinForwardPipeline::ResolvedShaderPass BuiltinForwardPipeline::ResolveSurfac if (builtinShaderHandle->IsValid()) { const Resources::Shader* builtinShader = builtinShaderHandle->Get(); if (const Resources::ShaderPass* shaderPass = - FindCompatibleSurfacePass(*builtinShader, nullptr, pass, backend)) { + FindCompatibleSurfacePass(*builtinShader, sceneData, nullptr, pass, backend)) { resolved.shader = builtinShader; resolved.pass = shaderPass; resolved.passName = shaderPass->name; @@ -274,8 +282,10 @@ BuiltinForwardPipeline::PassResourceLayout* BuiltinForwardPipeline::GetOrCreateP RHI::RHIPipelineState* BuiltinForwardPipeline::GetOrCreatePipelineState( const RenderContext& context, + const RenderSceneData& sceneData, const Resources::Material* material) { - const ResolvedShaderPass resolvedShaderPass = ResolveSurfaceShaderPass(material); + const Resources::ShaderKeywordSet keywordSet = ResolvePassKeywordSet(sceneData, material); + const ResolvedShaderPass resolvedShaderPass = ResolveSurfaceShaderPass(sceneData, material); if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, @@ -293,9 +303,7 @@ RHI::RHIPipelineState* BuiltinForwardPipeline::GetOrCreatePipelineState( material != nullptr ? material->GetRenderState() : Resources::MaterialRenderState(); pipelineKey.shader = resolvedShaderPass.shader; pipelineKey.passName = resolvedShaderPass.passName; - pipelineKey.keywordSignature = - ::XCEngine::Rendering::Detail::BuildShaderKeywordSignature( - material != nullptr ? material->GetKeywordSet() : Resources::ShaderKeywordSet()); + pipelineKey.keywordSignature = ::XCEngine::Rendering::Detail::BuildShaderKeywordSignature(keywordSet); const auto existing = m_pipelineStates.find(pipelineKey); if (existing != m_pipelineStates.end()) { @@ -308,6 +316,7 @@ RHI::RHIPipelineState* BuiltinForwardPipeline::GetOrCreatePipelineState( passLayout->pipelineLayout, *resolvedShaderPass.shader, resolvedShaderPass.passName, + keywordSet, material); RHI::RHIPipelineState* pipelineState = context.device->CreatePipelineState(pipelineDesc); if (pipelineState == nullptr || !pipelineState->IsValid()) { @@ -665,7 +674,7 @@ bool BuiltinForwardPipeline::DrawVisibleItem( }; const Resources::Material* material = ResolveMaterial(visibleItem); - const ResolvedShaderPass resolvedShaderPass = ResolveSurfaceShaderPass(material); + const ResolvedShaderPass resolvedShaderPass = ResolveSurfaceShaderPass(sceneData, material); if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) { return false; } diff --git a/tests/Rendering/unit/test_camera_scene_renderer.cpp b/tests/Rendering/unit/test_camera_scene_renderer.cpp index 97aea378..bdc5f171 100644 --- a/tests/Rendering/unit/test_camera_scene_renderer.cpp +++ b/tests/Rendering/unit/test_camera_scene_renderer.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -44,6 +45,7 @@ struct MockPipelineState { XCEngine::RHI::RHIResourceView* lastShadowMap = nullptr; XCEngine::Math::Matrix4x4 lastShadowViewProjection = XCEngine::Math::Matrix4x4::Identity(); XCEngine::Math::Vector4 lastShadowParams = XCEngine::Math::Vector4::Zero(); + bool lastHasMainDirectionalShadowKeyword = false; RenderEnvironmentMode lastEnvironmentMode = RenderEnvironmentMode::None; bool lastHasSkybox = false; const XCEngine::Resources::Material* lastSkyboxMaterial = nullptr; @@ -320,6 +322,10 @@ public: m_state->lastShadowMap = sceneData.lighting.mainDirectionalShadow.shadowMap; m_state->lastShadowViewProjection = sceneData.lighting.mainDirectionalShadow.viewProjection; m_state->lastShadowParams = sceneData.lighting.mainDirectionalShadow.shadowParams; + m_state->lastHasMainDirectionalShadowKeyword = + XCEngine::Resources::ShaderKeywordSetContains( + sceneData.globalShaderKeywords, + "XC_MAIN_LIGHT_SHADOWS"); m_state->lastEnvironmentMode = sceneData.environment.mode; m_state->lastHasSkybox = sceneData.environment.HasSkybox(); m_state->lastSkyboxMaterial = sceneData.environment.materialSkybox.material; @@ -1281,6 +1287,7 @@ TEST(CameraRenderer_Test, AutoAllocatesDirectionalShadowSurfaceFromShadowPlan) { EXPECT_FLOAT_EQ(pipelineState->lastShadowParams.y, 1.0f / 256.0f); EXPECT_FLOAT_EQ(pipelineState->lastShadowParams.z, 1.0f / 128.0f); EXPECT_FLOAT_EQ(pipelineState->lastShadowParams.w, 0.85f); + EXPECT_TRUE(pipelineState->lastHasMainDirectionalShadowKeyword); } EXPECT_EQ(allocationState->shutdownDepthViewCalls, 1); diff --git a/tests/Resources/Shader/test_shader_loader.cpp b/tests/Resources/Shader/test_shader_loader.cpp index 8b54f868..a37997c3 100644 --- a/tests/Resources/Shader/test_shader_loader.cpp +++ b/tests/Resources/Shader/test_shader_loader.cpp @@ -1269,7 +1269,7 @@ TEST(ShaderLoader, LoadBuiltinForwardLitShaderBuildsBackendVariants) { const ShaderPass* pass = shader->FindPass("ForwardLit"); ASSERT_NE(pass, nullptr); ASSERT_EQ(shader->GetProperties().Size(), 2u); - ASSERT_EQ(pass->variants.Size(), 6u); + ASSERT_EQ(pass->variants.Size(), 12u); ASSERT_EQ(pass->tags.Size(), 1u); ASSERT_EQ(pass->resources.Size(), 8u); EXPECT_EQ(pass->tags[0].name, "LightMode"); @@ -1324,6 +1324,13 @@ TEST(ShaderLoader, LoadBuiltinForwardLitShaderBuildsBackendVariants) { EXPECT_EQ(shadowTextureBinding->set, 6u); EXPECT_EQ(shadowTextureBinding->binding, 0u); EXPECT_EQ(shadowTextureBinding->semantic, "ShadowMapTexture"); + ASSERT_EQ(pass->keywordDeclarations.Size(), 1u); + EXPECT_EQ(pass->keywordDeclarations[0].type, ShaderKeywordDeclarationType::MultiCompile); + ASSERT_EQ(pass->keywordDeclarations[0].options.Size(), 2u); + EXPECT_EQ(pass->keywordDeclarations[0].options[0], "_"); + EXPECT_EQ(pass->keywordDeclarations[0].options[1], "XC_MAIN_LIGHT_SHADOWS"); + EXPECT_TRUE(shader->PassDeclaresKeyword("ForwardLit", "XC_MAIN_LIGHT_SHADOWS")); + ASSERT_EQ(pass->variants.Size(), 12u); EXPECT_NE(shader->FindVariant("ForwardLit", ShaderType::Vertex, ShaderBackend::D3D12), nullptr); EXPECT_NE(shader->FindVariant("ForwardLit", ShaderType::Fragment, ShaderBackend::D3D12), nullptr); @@ -1346,6 +1353,23 @@ TEST(ShaderLoader, LoadBuiltinForwardLitShaderBuildsBackendVariants) { ASSERT_NE(d3d12Fragment, nullptr); EXPECT_NE(std::string(d3d12Fragment->sourceCode.CStr()).find("gAdditionalLights"), std::string::npos); EXPECT_NE(std::string(d3d12Fragment->sourceCode.CStr()).find("gLightingParams"), std::string::npos); + EXPECT_EQ(std::string(d3d12Fragment->sourceCode.CStr()).find("#define XC_MAIN_LIGHT_SHADOWS 1"), std::string::npos); + + ShaderKeywordSet shadowKeywords = {}; + shadowKeywords.enabledKeywords.PushBack("XC_MAIN_LIGHT_SHADOWS"); + + const ShaderStageVariant* shadowD3D12Fragment = shader->FindVariant( + "ForwardLit", + ShaderType::Fragment, + ShaderBackend::D3D12, + shadowKeywords); + ASSERT_NE(shadowD3D12Fragment, nullptr); + EXPECT_NE( + std::string(shadowD3D12Fragment->sourceCode.CStr()).find("#define XC_MAIN_LIGHT_SHADOWS 1"), + std::string::npos); + EXPECT_NE( + std::string(shadowD3D12Fragment->sourceCode.CStr()).find("gShadowMapTexture.Sample"), + std::string::npos); const ShaderStageVariant* openglFragment = shader->FindVariant( "ForwardLit",