diff --git a/engine/assets/builtin/shaders/color-scale-post-process/color-scale-post-process.shader b/engine/assets/builtin/shaders/color-scale-post-process/color-scale-post-process.shader index cc06c4fd..fcfe6e18 100644 --- a/engine/assets/builtin/shaders/color-scale-post-process/color-scale-post-process.shader +++ b/engine/assets/builtin/shaders/color-scale-post-process/color-scale-post-process.shader @@ -5,13 +5,13 @@ Shader "Builtin Color Scale Post Process" _ColorScale ("Color Scale", Color) = (0.65,0.80,1.00,1.0) } HLSLINCLUDE - cbuffer PostProcessConstants : register(b0) + cbuffer PostProcessConstants { float4 gColorScale; }; - Texture2D gSourceColorTexture : register(t0); - SamplerState gLinearClampSampler : register(s0); + Texture2D gSourceColorTexture; + SamplerState gLinearClampSampler; struct VSOutput { diff --git a/engine/assets/builtin/shaders/depth-only/depth-only.shader b/engine/assets/builtin/shaders/depth-only/depth-only.shader index 3af8365e..bd840a6a 100644 --- a/engine/assets/builtin/shaders/depth-only/depth-only.shader +++ b/engine/assets/builtin/shaders/depth-only/depth-only.shader @@ -7,21 +7,21 @@ Shader "Builtin Depth Only" _MainTex ("Base Map", 2D) = "white" [Semantic(BaseColorTexture)] } HLSLINCLUDE - cbuffer PerObjectConstants : register(b0) + cbuffer PerObjectConstants { float4x4 gProjectionMatrix; float4x4 gViewMatrix; float4x4 gModelMatrix; }; - cbuffer MaterialConstants : register(b1) + cbuffer MaterialConstants { float4 gBaseColorFactor; float4 gAlphaCutoffParams; }; - Texture2D BaseColorTexture : register(t0); - SamplerState LinearClampSampler : register(s0); + Texture2D BaseColorTexture; + SamplerState LinearClampSampler; struct VSInput { diff --git a/engine/assets/builtin/shaders/final-color/final-color.shader b/engine/assets/builtin/shaders/final-color/final-color.shader index cae6b422..df83b65d 100644 --- a/engine/assets/builtin/shaders/final-color/final-color.shader +++ b/engine/assets/builtin/shaders/final-color/final-color.shader @@ -8,14 +8,14 @@ Shader "Builtin Final Color" _ToneMappingMode ("Tone Mapping Mode", Float) = 0.0 } HLSLINCLUDE - cbuffer FinalColorConstants : register(b0) + cbuffer FinalColorConstants { float4 gColorScale; float4 gFinalColorParams; }; - Texture2D gSourceColorTexture : register(t0); - SamplerState gLinearClampSampler : register(s0); + Texture2D gSourceColorTexture; + SamplerState gLinearClampSampler; struct VSOutput { diff --git a/engine/assets/builtin/shaders/forward-lit/forward-lit.shader b/engine/assets/builtin/shaders/forward-lit/forward-lit.shader index 46212d2e..01f1a5ee 100644 --- a/engine/assets/builtin/shaders/forward-lit/forward-lit.shader +++ b/engine/assets/builtin/shaders/forward-lit/forward-lit.shader @@ -7,7 +7,7 @@ Shader "Builtin Forward Lit" _MainTex ("Base Map", 2D) = "white" [Semantic(BaseColorTexture)] } HLSLINCLUDE - cbuffer PerObjectConstants : register(b0) + cbuffer PerObjectConstants { float4x4 gProjectionMatrix; float4x4 gViewMatrix; @@ -25,7 +25,7 @@ Shader "Builtin Forward Lit" float4 spotAnglesAndFlags; }; - cbuffer LightingConstants : register(b1) + cbuffer LightingConstants { float4 gMainLightDirectionAndIntensity; float4 gMainLightColorAndFlags; @@ -33,23 +33,23 @@ Shader "Builtin Forward Lit" AdditionalLightData gAdditionalLights[XC_MAX_ADDITIONAL_LIGHTS]; }; - cbuffer MaterialConstants : register(b2) + cbuffer MaterialConstants { float4 gBaseColorFactor; float4 gAlphaCutoffParams; }; - cbuffer ShadowReceiverConstants : register(b3) + cbuffer ShadowReceiverConstants { float4x4 gWorldToShadowMatrix; float4 gShadowBiasAndTexelSize; float4 gShadowOptions; }; - Texture2D BaseColorTexture : register(t0); - SamplerState LinearClampSampler : register(s0); - Texture2D ShadowMapTexture : register(t1); - SamplerState ShadowMapSampler : register(s1); + Texture2D BaseColorTexture; + SamplerState LinearClampSampler; + Texture2D ShadowMapTexture; + SamplerState ShadowMapSampler; struct VSInput { diff --git a/engine/assets/builtin/shaders/object-id/object-id.shader b/engine/assets/builtin/shaders/object-id/object-id.shader index 67241e9f..9e9302b1 100644 --- a/engine/assets/builtin/shaders/object-id/object-id.shader +++ b/engine/assets/builtin/shaders/object-id/object-id.shader @@ -13,7 +13,7 @@ Shader "Builtin Object Id" #pragma target 4.5 #pragma vertex MainVS #pragma fragment MainPS - cbuffer PerObjectConstants : register(b0) + cbuffer PerObjectConstants { float4x4 gProjectionMatrix; float4x4 gViewMatrix; diff --git a/engine/assets/builtin/shaders/shadow-caster/shadow-caster.shader b/engine/assets/builtin/shaders/shadow-caster/shadow-caster.shader index 645fa8fc..b02f0171 100644 --- a/engine/assets/builtin/shaders/shadow-caster/shadow-caster.shader +++ b/engine/assets/builtin/shaders/shadow-caster/shadow-caster.shader @@ -7,21 +7,21 @@ Shader "Builtin Shadow Caster" _MainTex ("Base Map", 2D) = "white" [Semantic(BaseColorTexture)] } HLSLINCLUDE - cbuffer PerObjectConstants : register(b0) + cbuffer PerObjectConstants { float4x4 gProjectionMatrix; float4x4 gViewMatrix; float4x4 gModelMatrix; }; - cbuffer MaterialConstants : register(b1) + cbuffer MaterialConstants { float4 gBaseColorFactor; float4 gAlphaCutoffParams; }; - Texture2D BaseColorTexture : register(t0); - SamplerState LinearClampSampler : register(s0); + Texture2D BaseColorTexture; + SamplerState LinearClampSampler; struct VSInput { diff --git a/engine/assets/builtin/shaders/skybox/skybox.shader b/engine/assets/builtin/shaders/skybox/skybox.shader index 597ab351..1a55f75d 100644 --- a/engine/assets/builtin/shaders/skybox/skybox.shader +++ b/engine/assets/builtin/shaders/skybox/skybox.shader @@ -9,7 +9,7 @@ Shader "Builtin Skybox" _Tex ("Cubemap (HDR)", Cube) = "white" [Semantic(SkyboxTexture)] } HLSLINCLUDE - cbuffer EnvironmentConstants : register(b0) + cbuffer EnvironmentConstants { float4 gSkyboxTopColor; float4 gSkyboxHorizonColor; @@ -19,15 +19,15 @@ Shader "Builtin Skybox" float4 gCameraForwardAndUnused; }; - cbuffer MaterialConstants : register(b1) + cbuffer MaterialConstants { float4 gSkyboxTintAndExposure; float4 gSkyboxRotationAndMode; }; - Texture2D SkyboxPanoramicTexture : register(t0); - TextureCube SkyboxTexture : register(t1); - SamplerState LinearClampSampler : register(s0); + Texture2D SkyboxPanoramicTexture; + TextureCube SkyboxTexture; + SamplerState LinearClampSampler; struct VSOutput { diff --git a/engine/assets/builtin/shaders/unlit/unlit.shader b/engine/assets/builtin/shaders/unlit/unlit.shader index 24973227..456a1636 100644 --- a/engine/assets/builtin/shaders/unlit/unlit.shader +++ b/engine/assets/builtin/shaders/unlit/unlit.shader @@ -18,7 +18,7 @@ Shader "Builtin Unlit" #pragma target 4.5 #pragma vertex MainVS #pragma fragment MainPS - cbuffer PerObjectConstants : register(b0) + cbuffer PerObjectConstants { float4x4 gProjectionMatrix; float4x4 gViewMatrix; @@ -26,13 +26,13 @@ Shader "Builtin Unlit" float4x4 gNormalMatrix; }; - cbuffer MaterialConstants : register(b1) + cbuffer MaterialConstants { float4 gBaseColorFactor; }; - Texture2D BaseColorTexture : register(t0); - SamplerState LinearClampSampler : register(s0); + Texture2D BaseColorTexture; + SamplerState LinearClampSampler; struct VSInput { diff --git a/engine/src/RHI/OpenGL/OpenGLDevice.cpp b/engine/src/RHI/OpenGL/OpenGLDevice.cpp index 2a6dd776..196b065c 100644 --- a/engine/src/RHI/OpenGL/OpenGLDevice.cpp +++ b/engine/src/RHI/OpenGL/OpenGLDevice.cpp @@ -190,10 +190,10 @@ void CollectHlslTextureRegisterBindings( std::unordered_map& outTextureUnits, std::unordered_set& outSamplerNames) { static const std::regex kTexturePattern( - R"(((?:Texture2D|TextureCube)\s+([A-Za-z_][A-Za-z0-9_]*)\s*:\s*register\s*\(\s*t([0-9]+)))", + R"(((?:Texture2D|TextureCube)\s+([A-Za-z_][A-Za-z0-9_]*)\s*:\s*register\s*\(\s*t([0-9]+)(?:\s*,\s*space[0-9]+)?\s*\)))", std::regex::ECMAScript); static const std::regex kSamplerPattern( - R"(((?:SamplerState|SamplerComparisonState)\s+([A-Za-z_][A-Za-z0-9_]*)\s*:\s*register\s*\(\s*s([0-9]+)))", + R"(((?:SamplerState|SamplerComparisonState)\s+([A-Za-z_][A-Za-z0-9_]*)\s*:\s*register\s*\(\s*s([0-9]+)(?:\s*,\s*space[0-9]+)?\s*\)))", std::regex::ECMAScript); for (std::sregex_iterator it(sourceText.begin(), sourceText.end(), kTexturePattern), end; it != end; ++it) { diff --git a/engine/src/Rendering/Detail/ShaderVariantUtils.h b/engine/src/Rendering/Detail/ShaderVariantUtils.h index 9e9c8ec4..13453fe4 100644 --- a/engine/src/Rendering/Detail/ShaderVariantUtils.h +++ b/engine/src/Rendering/Detail/ShaderVariantUtils.h @@ -3,8 +3,10 @@ #include #include #include +#include #include +#include #include namespace XCEngine { @@ -44,6 +46,316 @@ inline std::wstring ToWideAscii(const Containers::String& value) { return wide; } +inline std::string ToStdString(const Containers::String& value) { + return std::string(value.CStr(), value.Length()); +} + +inline std::string EscapeRegexLiteral(const Containers::String& value) { + std::string escaped; + escaped.reserve(value.Length() * 2u); + for (size_t index = 0; index < value.Length(); ++index) { + const char ch = value[index]; + switch (ch) { + case '\\': + case '^': + case '$': + case '.': + case '|': + case '?': + case '*': + case '+': + case '(': + case ')': + case '[': + case ']': + case '{': + case '}': + escaped.push_back('\\'); + break; + default: + break; + } + + escaped.push_back(ch); + } + + return escaped; +} + +inline bool TryCollectShaderPassResourceBindings( + const Resources::ShaderPass& pass, + Containers::Array& outBindings) { + outBindings.Clear(); + + if (!pass.resources.Empty()) { + outBindings.Reserve(pass.resources.Size()); + for (const Resources::ShaderResourceBindingDesc& binding : pass.resources) { + outBindings.PushBack(binding); + } + return true; + } + + return TryBuildImplicitBuiltinPassResourceBindings(pass, outBindings); +} + +inline Containers::String ResolveLegacyHlslBindingDeclarationAlias( + const Resources::ShaderResourceBindingDesc& binding) { + switch (ResolveBuiltinPassResourceSemantic(binding)) { + case BuiltinPassResourceSemantic::BaseColorTexture: + return "gBaseColorTexture"; + case BuiltinPassResourceSemantic::ShadowMapTexture: + return "gShadowMapTexture"; + case BuiltinPassResourceSemantic::LinearClampSampler: + return "gLinearSampler"; + case BuiltinPassResourceSemantic::ShadowMapSampler: + return "gShadowMapSampler"; + case BuiltinPassResourceSemantic::Unknown: + default: + return Containers::String(); + } +} + +inline bool TryRewriteHlslRegisterBindingWithName( + std::string& sourceText, + const Containers::String& declarationName, + const char* registerPrefix, + Core::uint32 bindingIndex, + Core::uint32 setIndex, + bool includeRegisterSpace, + Resources::ShaderResourceType resourceType) { + if (declarationName.Empty()) { + return false; + } + + const std::string registerClause = + includeRegisterSpace + ? std::string("register(") + registerPrefix + + std::to_string(bindingIndex) + + ", space" + + std::to_string(setIndex) + + ")" + : std::string("register(") + registerPrefix + + std::to_string(bindingIndex) + + ")"; + const std::string escapedName = EscapeRegexLiteral(declarationName); + + if (resourceType == Resources::ShaderResourceType::ConstantBuffer) { + const std::regex pattern( + "(cbuffer\\s+" + escapedName + "\\s*)(:\\s*register\\s*\\([^\\)]*\\))?(\\s*\\{)", + std::regex::ECMAScript); + const std::string rewritten = + std::regex_replace(sourceText, pattern, "$1: " + registerClause + "$3"); + if (rewritten != sourceText) { + sourceText = rewritten; + return true; + } + + return false; + } + + const std::regex pattern( + "((?:Texture2D|TextureCube|SamplerState|SamplerComparisonState)\\s+" + escapedName + + "\\s*)(:\\s*register\\s*\\([^\\)]*\\))?(\\s*;)", + std::regex::ECMAScript); + const std::string rewritten = + std::regex_replace(sourceText, pattern, "$1: " + registerClause + "$3"); + if (rewritten != sourceText) { + sourceText = rewritten; + return true; + } + + return false; +} + +inline const char* TryGetHlslRegisterPrefix(Resources::ShaderResourceType type) { + switch (type) { + case Resources::ShaderResourceType::ConstantBuffer: + return "b"; + case Resources::ShaderResourceType::Texture2D: + case Resources::ShaderResourceType::TextureCube: + return "t"; + case Resources::ShaderResourceType::Sampler: + return "s"; + default: + return nullptr; + } +} + +inline bool TryRewriteHlslRegisterBinding( + std::string& sourceText, + const Resources::ShaderResourceBindingDesc& binding, + bool includeRegisterSpace) { + const char* registerPrefix = TryGetHlslRegisterPrefix(binding.type); + if (registerPrefix == nullptr) { + return false; + } + + if (TryRewriteHlslRegisterBindingWithName( + sourceText, + binding.name, + registerPrefix, + binding.binding, + binding.set, + includeRegisterSpace, + binding.type)) { + return true; + } + + const Containers::String legacyAlias = ResolveLegacyHlslBindingDeclarationAlias(binding); + if (legacyAlias != binding.name && + TryRewriteHlslRegisterBindingWithName( + sourceText, + legacyAlias, + registerPrefix, + binding.binding, + binding.set, + includeRegisterSpace, + binding.type)) { + return true; + } + + if ((binding.type == Resources::ShaderResourceType::Texture2D || + binding.type == Resources::ShaderResourceType::TextureCube || + binding.type == Resources::ShaderResourceType::Sampler)) { + const Containers::String prefixedName = Containers::String("g") + binding.name; + if (prefixedName != binding.name && + prefixedName != legacyAlias && + TryRewriteHlslRegisterBindingWithName( + sourceText, + prefixedName, + registerPrefix, + binding.binding, + binding.set, + includeRegisterSpace, + binding.type)) { + return true; + } + } + + return false; +} + +inline bool TryBuildRuntimeShaderBindings( + const Resources::ShaderPass& pass, + Resources::ShaderBackend backend, + Containers::Array& outBindings, + bool& outIncludeRegisterSpace) { + outBindings.Clear(); + outIncludeRegisterSpace = false; + + if (backend == Resources::ShaderBackend::Vulkan) { + outIncludeRegisterSpace = true; + return TryCollectShaderPassResourceBindings(pass, outBindings); + } + + if (backend != Resources::ShaderBackend::D3D12 && + backend != Resources::ShaderBackend::OpenGL) { + return false; + } + + if (!pass.resources.Empty()) { + return false; + } + + if (!TryBuildImplicitBuiltinPassResourceBindings(pass, outBindings)) { + return false; + } + + Core::uint32 nextConstantBufferRegister = 0; + Core::uint32 nextTextureRegister = 0; + Core::uint32 nextSamplerRegister = 0; + Core::uint32 nextUnorderedAccessRegister = 0; + for (Resources::ShaderResourceBindingDesc& binding : outBindings) { + binding.set = 0; + switch (binding.type) { + case Resources::ShaderResourceType::ConstantBuffer: + binding.binding = nextConstantBufferRegister++; + break; + case Resources::ShaderResourceType::Texture2D: + case Resources::ShaderResourceType::TextureCube: + binding.binding = nextTextureRegister++; + break; + case Resources::ShaderResourceType::Sampler: + binding.binding = nextSamplerRegister++; + break; + default: + binding.binding = nextUnorderedAccessRegister++; + break; + } + } + + return true; +} + +inline std::string BuildRuntimeShaderSource( + const Resources::ShaderPass& pass, + Resources::ShaderBackend backend, + const Resources::ShaderStageVariant& variant) { + std::string sourceText = ToStdString(variant.sourceCode); + + if (variant.language != Resources::ShaderLanguage::HLSL || + backend == Resources::ShaderBackend::Generic) { + return sourceText; + } + + Containers::Array bindings; + bool includeRegisterSpace = false; + if (!TryBuildRuntimeShaderBindings(pass, backend, bindings, includeRegisterSpace)) { + return sourceText; + } + + for (const Resources::ShaderResourceBindingDesc& binding : bindings) { + TryRewriteHlslRegisterBinding(sourceText, binding, includeRegisterSpace); + } + + return sourceText; +} + +inline void AddShaderCompileMacro( + RHI::ShaderCompileDesc& compileDesc, + const wchar_t* name, + const wchar_t* definition = L"1") { + if (name == nullptr || *name == L'\0') { + return; + } + + for (const RHI::ShaderCompileMacro& existingMacro : compileDesc.macros) { + if (existingMacro.name == name) { + return; + } + } + + RHI::ShaderCompileMacro macro = {}; + macro.name = name; + macro.definition = definition != nullptr ? definition : L""; + compileDesc.macros.push_back(std::move(macro)); +} + +inline void InjectUnityStyleBackendMacros( + Resources::ShaderBackend backend, + RHI::ShaderCompileDesc& compileDesc) { + switch (backend) { + case Resources::ShaderBackend::OpenGL: + AddShaderCompileMacro(compileDesc, L"SHADER_API_GLCORE"); + AddShaderCompileMacro(compileDesc, L"UNITY_UV_STARTS_AT_TOP", L"0"); + AddShaderCompileMacro(compileDesc, L"UNITY_NEAR_CLIP_VALUE", L"-1"); + break; + case Resources::ShaderBackend::Vulkan: + AddShaderCompileMacro(compileDesc, L"SHADER_API_VULKAN"); + AddShaderCompileMacro(compileDesc, L"UNITY_UV_STARTS_AT_TOP", L"1"); + AddShaderCompileMacro(compileDesc, L"UNITY_NEAR_CLIP_VALUE", L"0"); + break; + case Resources::ShaderBackend::D3D12: + AddShaderCompileMacro(compileDesc, L"SHADER_API_D3D12"); + AddShaderCompileMacro(compileDesc, L"UNITY_UV_STARTS_AT_TOP", L"1"); + AddShaderCompileMacro(compileDesc, L"UNITY_NEAR_CLIP_VALUE", L"0"); + break; + case Resources::ShaderBackend::Generic: + default: + break; + } +} + inline void ApplyShaderStageVariant( const Resources::ShaderStageVariant& variant, RHI::ShaderCompileDesc& compileDesc) { @@ -55,6 +367,19 @@ inline void ApplyShaderStageVariant( compileDesc.profile = ToWideAscii(variant.profile); } +inline void ApplyShaderStageVariant( + const Resources::ShaderPass& pass, + Resources::ShaderBackend backend, + const Resources::ShaderStageVariant& variant, + RHI::ShaderCompileDesc& compileDesc) { + const std::string sourceText = BuildRuntimeShaderSource(pass, backend, variant); + compileDesc.source.assign(sourceText.begin(), sourceText.end()); + compileDesc.sourceLanguage = ToRHIShaderLanguage(variant.language); + compileDesc.entryPoint = ToWideAscii(variant.entryPoint); + compileDesc.profile = ToWideAscii(variant.profile); + InjectUnityStyleBackendMacros(backend, compileDesc); +} + inline Containers::String BuildShaderKeywordSignature( const Resources::ShaderKeywordSet& keywordSet) { Resources::ShaderKeywordSet normalizedKeywords = keywordSet; diff --git a/tests/Rendering/unit/test_builtin_forward_pipeline.cpp b/tests/Rendering/unit/test_builtin_forward_pipeline.cpp index 5ae70764..eca3f7e4 100644 --- a/tests/Rendering/unit/test_builtin_forward_pipeline.cpp +++ b/tests/Rendering/unit/test_builtin_forward_pipeline.cpp @@ -15,7 +15,7 @@ #include #include -#include +#include using namespace XCEngine::Rendering::Pipelines; using namespace XCEngine::Rendering::Passes; @@ -24,6 +24,58 @@ using namespace XCEngine::Containers; using namespace XCEngine::Resources; using namespace XCEngine::RHI; +namespace { + +std::string EscapeRegexLiteralForTest(const std::string& value) { + std::string escaped; + escaped.reserve(value.size() * 2u); + for (const char ch : value) { + switch (ch) { + case '\\': + case '^': + case '$': + case '.': + case '|': + case '?': + case '*': + case '+': + case '(': + case ')': + case '[': + case ']': + case '{': + case '}': + escaped.push_back('\\'); + break; + default: + break; + } + + escaped.push_back(ch); + } + + return escaped; +} + +::testing::AssertionResult SourceContainsRegisterBinding( + const std::string& source, + const std::string& declaration, + const std::string& registerClause) { + const std::regex pattern( + EscapeRegexLiteralForTest(declaration) + "\\s*:\\s*" + + EscapeRegexLiteralForTest(registerClause), + std::regex::ECMAScript); + if (std::regex_search(source, pattern)) { + return ::testing::AssertionSuccess(); + } + + return ::testing::AssertionFailure() + << "Missing binding '" << declaration << " : " << registerClause << "' in source:\n" + << source; +} + +} // namespace + TEST(BuiltinForwardPipeline_Test, UsesFloat3PositionInputLayoutForStaticMeshVertices) { const InputLayoutDesc inputLayout = BuiltinForwardPipeline::BuildInputLayout(); @@ -81,6 +133,15 @@ TEST(BuiltinForwardPipeline_Test, BuiltinForwardShaderUsesUnityStyleSingleSource EXPECT_TRUE(pass->fixedFunctionState.depthWriteEnable); EXPECT_EQ(pass->fixedFunctionState.depthFunc, MaterialComparisonFunc::LessEqual); + const ShaderStageVariant* fragmentVariant = shader->FindVariant( + "ForwardLit", + XCEngine::Resources::ShaderType::Fragment, + XCEngine::Resources::ShaderBackend::D3D12); + ASSERT_NE(fragmentVariant, nullptr); + EXPECT_EQ( + std::string(fragmentVariant->sourceCode.CStr()).find("register("), + std::string::npos); + delete shader; } @@ -101,6 +162,15 @@ TEST(BuiltinForwardPipeline_Test, BuiltinUnlitShaderUsesUnityStyleSingleSourceSu EXPECT_TRUE(pass->fixedFunctionState.depthWriteEnable); EXPECT_EQ(pass->fixedFunctionState.depthFunc, MaterialComparisonFunc::LessEqual); + const ShaderStageVariant* fragmentVariant = shader->FindVariant( + "Unlit", + XCEngine::Resources::ShaderType::Fragment, + XCEngine::Resources::ShaderBackend::D3D12); + ASSERT_NE(fragmentVariant, nullptr); + EXPECT_EQ( + std::string(fragmentVariant->sourceCode.CStr()).find("register("), + std::string::npos); + delete shader; } @@ -129,9 +199,10 @@ TEST(BuiltinForwardPipeline_Test, BuiltinUnlitShaderBuildsVulkanRuntimeSourceWit XCEngine::Resources::ShaderBackend::Vulkan, *fragmentVariant); - EXPECT_NE( - runtimeSource.find("cbuffer MaterialConstants : register(b0, space1)"), - std::string::npos); + EXPECT_TRUE(SourceContainsRegisterBinding( + runtimeSource, + "cbuffer MaterialConstants", + "register(b0, space1)")); EXPECT_NE(runtimeSource.find("BaseColorTexture"), std::string::npos); EXPECT_NE(runtimeSource.find("space2"), std::string::npos); EXPECT_NE(runtimeSource.find("LinearClampSampler"), std::string::npos); @@ -167,10 +238,18 @@ TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesUnityStyleUnli const std::string d3d12Source( reinterpret_cast(d3d12CompileDesc.source.data()), d3d12CompileDesc.source.size()); - EXPECT_NE(d3d12Source.find("BaseColorTexture"), std::string::npos); - EXPECT_NE(d3d12Source.find("LinearClampSampler"), std::string::npos); - EXPECT_EQ(d3d12Source.find("space2"), std::string::npos); - EXPECT_EQ(d3d12Source.find("space3"), std::string::npos); + EXPECT_TRUE(SourceContainsRegisterBinding( + d3d12Source, + "cbuffer MaterialConstants", + "register(b1)")); + EXPECT_TRUE(SourceContainsRegisterBinding( + d3d12Source, + "Texture2D BaseColorTexture", + "register(t0)")); + EXPECT_TRUE(SourceContainsRegisterBinding( + d3d12Source, + "SamplerState LinearClampSampler", + "register(s0)")); const ShaderStageVariant* vulkanFragment = shader->FindVariant( "Unlit", @@ -187,7 +266,10 @@ TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesUnityStyleUnli const std::string vulkanSource( reinterpret_cast(vulkanCompileDesc.source.data()), vulkanCompileDesc.source.size()); - EXPECT_NE(vulkanSource.find("cbuffer MaterialConstants : register(b0, space1)"), std::string::npos); + EXPECT_TRUE(SourceContainsRegisterBinding( + vulkanSource, + "cbuffer MaterialConstants", + "register(b0, space1)")); EXPECT_NE(vulkanSource.find("BaseColorTexture"), std::string::npos); EXPECT_NE(vulkanSource.find("space2"), std::string::npos); EXPECT_NE(vulkanSource.find("LinearClampSampler"), std::string::npos); @@ -227,10 +309,40 @@ TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesUnityStyleForw const std::string d3d12Source( reinterpret_cast(d3d12CompileDesc.source.data()), d3d12CompileDesc.source.size()); - EXPECT_NE(d3d12Source.find("cbuffer LightingConstants : register(b1)"), std::string::npos); - EXPECT_NE(d3d12Source.find("Texture2D ShadowMapTexture : register(t1)"), std::string::npos); + EXPECT_TRUE(SourceContainsRegisterBinding( + d3d12Source, + "cbuffer PerObjectConstants", + "register(b0)")); + EXPECT_TRUE(SourceContainsRegisterBinding( + d3d12Source, + "cbuffer LightingConstants", + "register(b1)")); + EXPECT_TRUE(SourceContainsRegisterBinding( + d3d12Source, + "cbuffer MaterialConstants", + "register(b2)")); + EXPECT_TRUE(SourceContainsRegisterBinding( + d3d12Source, + "cbuffer ShadowReceiverConstants", + "register(b3)")); + EXPECT_TRUE(SourceContainsRegisterBinding( + d3d12Source, + "Texture2D BaseColorTexture", + "register(t0)")); + EXPECT_TRUE(SourceContainsRegisterBinding( + d3d12Source, + "SamplerState LinearClampSampler", + "register(s0)")); + EXPECT_TRUE(SourceContainsRegisterBinding( + d3d12Source, + "Texture2D ShadowMapTexture", + "register(t1)")); + EXPECT_TRUE(SourceContainsRegisterBinding( + d3d12Source, + "SamplerState ShadowMapSampler", + "register(s1)")); + EXPECT_EQ(d3d12Source.find("space0"), std::string::npos); EXPECT_EQ(d3d12Source.find("space1"), std::string::npos); - EXPECT_EQ(d3d12Source.find("space6"), std::string::npos); const ShaderStageVariant* vulkanFragment = shader->FindVariant( "ForwardLit", @@ -248,14 +360,38 @@ TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesUnityStyleForw const std::string vulkanSource( reinterpret_cast(vulkanCompileDesc.source.data()), vulkanCompileDesc.source.size()); - EXPECT_NE(vulkanSource.find("cbuffer PerObjectConstants : register(b0, space0)"), std::string::npos); - EXPECT_NE(vulkanSource.find("cbuffer LightingConstants : register(b0, space1)"), std::string::npos); - EXPECT_NE(vulkanSource.find("cbuffer MaterialConstants : register(b0, space2)"), std::string::npos); - EXPECT_NE(vulkanSource.find("cbuffer ShadowReceiverConstants : register(b0, space3)"), std::string::npos); - EXPECT_NE(vulkanSource.find("Texture2D BaseColorTexture : register(t0, space4)"), std::string::npos); - EXPECT_NE(vulkanSource.find("SamplerState LinearClampSampler : register(s0, space5)"), std::string::npos); - EXPECT_NE(vulkanSource.find("Texture2D ShadowMapTexture : register(t0, space6)"), std::string::npos); - EXPECT_NE(vulkanSource.find("SamplerState ShadowMapSampler : register(s0, space7)"), std::string::npos); + EXPECT_TRUE(SourceContainsRegisterBinding( + vulkanSource, + "cbuffer PerObjectConstants", + "register(b0, space0)")); + EXPECT_TRUE(SourceContainsRegisterBinding( + vulkanSource, + "cbuffer LightingConstants", + "register(b0, space1)")); + EXPECT_TRUE(SourceContainsRegisterBinding( + vulkanSource, + "cbuffer MaterialConstants", + "register(b0, space2)")); + EXPECT_TRUE(SourceContainsRegisterBinding( + vulkanSource, + "cbuffer ShadowReceiverConstants", + "register(b0, space3)")); + EXPECT_TRUE(SourceContainsRegisterBinding( + vulkanSource, + "Texture2D BaseColorTexture", + "register(t0, space4)")); + EXPECT_TRUE(SourceContainsRegisterBinding( + vulkanSource, + "SamplerState LinearClampSampler", + "register(s0, space5)")); + EXPECT_TRUE(SourceContainsRegisterBinding( + vulkanSource, + "Texture2D ShadowMapTexture", + "register(t0, space6)")); + EXPECT_TRUE(SourceContainsRegisterBinding( + vulkanSource, + "SamplerState ShadowMapSampler", + "register(s0, space7)")); delete shader; } @@ -664,11 +800,20 @@ TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesUnityStyleFina const std::string d3d12Source( reinterpret_cast(d3d12CompileDesc.source.data()), d3d12CompileDesc.source.size()); - EXPECT_NE(d3d12Source.find("cbuffer FinalColorConstants : register(b0)"), std::string::npos); - EXPECT_NE(d3d12Source.find("Texture2D gSourceColorTexture : register(t0)"), std::string::npos); - EXPECT_NE(d3d12Source.find("SamplerState gLinearClampSampler : register(s0)"), std::string::npos); + EXPECT_TRUE(SourceContainsRegisterBinding( + d3d12Source, + "cbuffer FinalColorConstants", + "register(b0)")); + EXPECT_TRUE(SourceContainsRegisterBinding( + d3d12Source, + "Texture2D gSourceColorTexture", + "register(t0)")); + EXPECT_TRUE(SourceContainsRegisterBinding( + d3d12Source, + "SamplerState gLinearClampSampler", + "register(s0)")); + EXPECT_EQ(d3d12Source.find("space0"), std::string::npos); EXPECT_EQ(d3d12Source.find("space1"), std::string::npos); - EXPECT_EQ(d3d12Source.find("space2"), std::string::npos); const ShaderStageVariant* vulkanFragment = shader->FindVariant( "FinalColor", @@ -681,15 +826,18 @@ TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesUnityStyleFina *pass, XCEngine::Resources::ShaderBackend::Vulkan, *vulkanFragment); - EXPECT_NE( - runtimeSource.find("cbuffer FinalColorConstants : register(b0, space0)"), - std::string::npos); - EXPECT_NE( - runtimeSource.find("Texture2D gSourceColorTexture : register(t0, space1)"), - std::string::npos); - EXPECT_NE( - runtimeSource.find("SamplerState gLinearClampSampler : register(s0, space2)"), - std::string::npos); + EXPECT_TRUE(SourceContainsRegisterBinding( + runtimeSource, + "cbuffer FinalColorConstants", + "register(b0, space0)")); + EXPECT_TRUE(SourceContainsRegisterBinding( + runtimeSource, + "Texture2D gSourceColorTexture", + "register(t0, space1)")); + EXPECT_TRUE(SourceContainsRegisterBinding( + runtimeSource, + "SamplerState gLinearClampSampler", + "register(s0, space2)")); ShaderCompileDesc vulkanCompileDesc = {}; ::XCEngine::Rendering::Detail::ApplyShaderStageVariant( @@ -778,11 +926,20 @@ TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesUnityStyleColo const std::string d3d12Source( reinterpret_cast(d3d12CompileDesc.source.data()), d3d12CompileDesc.source.size()); - EXPECT_NE(d3d12Source.find("cbuffer PostProcessConstants : register(b0)"), std::string::npos); - EXPECT_NE(d3d12Source.find("Texture2D gSourceColorTexture : register(t0)"), std::string::npos); - EXPECT_NE(d3d12Source.find("SamplerState gLinearClampSampler : register(s0)"), std::string::npos); + EXPECT_TRUE(SourceContainsRegisterBinding( + d3d12Source, + "cbuffer PostProcessConstants", + "register(b0)")); + EXPECT_TRUE(SourceContainsRegisterBinding( + d3d12Source, + "Texture2D gSourceColorTexture", + "register(t0)")); + EXPECT_TRUE(SourceContainsRegisterBinding( + d3d12Source, + "SamplerState gLinearClampSampler", + "register(s0)")); + EXPECT_EQ(d3d12Source.find("space0"), std::string::npos); EXPECT_EQ(d3d12Source.find("space1"), std::string::npos); - EXPECT_EQ(d3d12Source.find("space2"), std::string::npos); const ShaderStageVariant* vulkanFragment = shader->FindVariant( "ColorScale", @@ -795,15 +952,18 @@ TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesUnityStyleColo *pass, XCEngine::Resources::ShaderBackend::Vulkan, *vulkanFragment); - EXPECT_NE( - runtimeSource.find("cbuffer PostProcessConstants : register(b0, space0)"), - std::string::npos); - EXPECT_NE( - runtimeSource.find("Texture2D gSourceColorTexture : register(t0, space1)"), - std::string::npos); - EXPECT_NE( - runtimeSource.find("SamplerState gLinearClampSampler : register(s0, space2)"), - std::string::npos); + EXPECT_TRUE(SourceContainsRegisterBinding( + runtimeSource, + "cbuffer PostProcessConstants", + "register(b0, space0)")); + EXPECT_TRUE(SourceContainsRegisterBinding( + runtimeSource, + "Texture2D gSourceColorTexture", + "register(t0, space1)")); + EXPECT_TRUE(SourceContainsRegisterBinding( + runtimeSource, + "SamplerState gLinearClampSampler", + "register(s0, space2)")); ShaderCompileDesc vulkanCompileDesc = {}; ::XCEngine::Rendering::Detail::ApplyShaderStageVariant(