From efdd6bd68f6adb09884d80e0e8e66f798e7c0e34 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Wed, 8 Apr 2026 04:59:49 +0800 Subject: [PATCH] Remove legacy shader pass and artifact fallbacks --- .../XCEngine/Core/Asset/ArtifactFormats.h | 47 +------- .../Builtin/BuiltinPassMetadataUtils.h | 68 +---------- .../Materials/RenderMaterialResolve.h | 26 ++-- engine/src/Core/Asset/AssetDatabase.cpp | 37 +++--- .../BuiltinDepthStylePassBaseResources.cpp | 1 - .../BuiltinForwardPipelineResources.cpp | 38 ------ .../Shader/Internal/ShaderArtifactLoader.cpp | 111 ++++------------- .../unit/test_render_scene_extractor.cpp | 18 ++- tests/Resources/Shader/test_shader_loader.cpp | 113 ++++++++++++++++++ 9 files changed, 186 insertions(+), 273 deletions(-) diff --git a/engine/include/XCEngine/Core/Asset/ArtifactFormats.h b/engine/include/XCEngine/Core/Asset/ArtifactFormats.h index 48583656..bfc378a4 100644 --- a/engine/include/XCEngine/Core/Asset/ArtifactFormats.h +++ b/engine/include/XCEngine/Core/Asset/ArtifactFormats.h @@ -11,9 +11,9 @@ namespace XCEngine { namespace Resources { constexpr Core::uint32 kTextureArtifactSchemaVersion = 1; -constexpr Core::uint32 kMaterialArtifactSchemaVersion = 4; +constexpr Core::uint32 kMaterialArtifactSchemaVersion = 6; constexpr Core::uint32 kMeshArtifactSchemaVersion = 2; -constexpr Core::uint32 kShaderArtifactSchemaVersion = 4; +constexpr Core::uint32 kShaderArtifactSchemaVersion = 5; constexpr Core::uint32 kUIDocumentArtifactSchemaVersion = 2; struct TextureArtifactHeader { @@ -46,27 +46,10 @@ struct MeshArtifactHeader { }; struct MaterialArtifactFileHeader { - char magic[8] = { 'X', 'C', 'M', 'A', 'T', '0', '4', '\0' }; + char magic[8] = { 'X', 'C', 'M', 'A', 'T', '0', '6', '\0' }; Core::uint32 schemaVersion = kMaterialArtifactSchemaVersion; }; -struct MaterialArtifactHeaderV2 { - Core::int32 renderQueue = static_cast(MaterialRenderQueue::Geometry); - MaterialRenderState renderState = {}; - Core::uint32 tagCount = 0; - Core::uint32 propertyCount = 0; - Core::uint32 textureBindingCount = 0; -}; - -struct MaterialArtifactHeaderV3 { - Core::int32 renderQueue = static_cast(MaterialRenderQueue::Geometry); - MaterialRenderState renderState = {}; - Core::uint32 tagCount = 0; - Core::uint32 keywordCount = 0; - Core::uint32 propertyCount = 0; - Core::uint32 textureBindingCount = 0; -}; - struct MaterialArtifactHeader { Core::int32 renderQueue = static_cast(MaterialRenderQueue::Geometry); MaterialRenderState renderState = {}; @@ -83,7 +66,7 @@ struct MaterialPropertyArtifact { }; struct ShaderArtifactFileHeader { - char magic[8] = { 'X', 'C', 'S', 'H', 'D', '0', '4', '\0' }; + char magic[8] = { 'X', 'C', 'S', 'H', 'D', '0', '5', '\0' }; Core::uint32 schemaVersion = kShaderArtifactSchemaVersion; }; @@ -92,20 +75,7 @@ struct ShaderArtifactHeader { Core::uint32 passCount = 0; }; -struct ShaderPassArtifactHeaderV1 { - Core::uint32 tagCount = 0; - Core::uint32 resourceCount = 0; - Core::uint32 variantCount = 0; -}; - -struct ShaderPassArtifactHeader { - Core::uint32 tagCount = 0; - Core::uint32 resourceCount = 0; - Core::uint32 keywordDeclarationCount = 0; - Core::uint32 variantCount = 0; -}; - -struct ShaderPassArtifactHeaderV4 { +struct ShaderPassArtifactHeaderV5 { Core::uint32 tagCount = 0; Core::uint32 resourceCount = 0; Core::uint32 keywordDeclarationCount = 0; @@ -124,13 +94,6 @@ struct ShaderResourceArtifact { Core::uint32 binding = 0; }; -struct ShaderVariantArtifactHeaderV2 { - Core::uint32 stage = 0; - Core::uint32 language = 0; - Core::uint32 backend = 0; - Core::uint64 compiledBinarySize = 0; -}; - struct ShaderVariantArtifactHeader { Core::uint32 stage = 0; Core::uint32 language = 0; diff --git a/engine/include/XCEngine/Rendering/Builtin/BuiltinPassMetadataUtils.h b/engine/include/XCEngine/Rendering/Builtin/BuiltinPassMetadataUtils.h index 843c2327..1972c00a 100644 --- a/engine/include/XCEngine/Rendering/Builtin/BuiltinPassMetadataUtils.h +++ b/engine/include/XCEngine/Rendering/Builtin/BuiltinPassMetadataUtils.h @@ -13,8 +13,7 @@ inline Containers::String NormalizeBuiltinPassMetadataValue(const Containers::St inline bool IsForwardPassName(const Containers::String& value) { const Containers::String normalized = NormalizeBuiltinPassMetadataValue(value); - return normalized.Empty() || - normalized == Containers::String("forward") || + return normalized == Containers::String("forward") || normalized == Containers::String("forwardbase") || normalized == Containers::String("forwardlit") || normalized == Containers::String("forwardonly"); @@ -87,50 +86,6 @@ inline bool MatchesBuiltinPassName( } } -inline bool TryResolveBuiltinMaterialPassHint( - const Containers::String& value, - BuiltinMaterialPass& outPass) { - const Containers::String normalized = NormalizeBuiltinPassMetadataValue(value); - if (normalized.Empty()) { - return false; - } - - if (IsForwardPassName(normalized)) { - outPass = BuiltinMaterialPass::ForwardLit; - return true; - } - if (IsUnlitPassName(normalized)) { - outPass = BuiltinMaterialPass::Unlit; - return true; - } - if (IsDepthOnlyPassName(normalized)) { - outPass = BuiltinMaterialPass::DepthOnly; - return true; - } - if (IsShadowCasterPassName(normalized)) { - outPass = BuiltinMaterialPass::ShadowCaster; - return true; - } - if (IsObjectIdPassName(normalized)) { - outPass = BuiltinMaterialPass::ObjectId; - return true; - } - if (IsSkyboxPassName(normalized)) { - outPass = BuiltinMaterialPass::Skybox; - return true; - } - if (IsPostProcessPassName(normalized)) { - outPass = BuiltinMaterialPass::PostProcess; - return true; - } - if (IsFinalColorPassName(normalized)) { - outPass = BuiltinMaterialPass::FinalColor; - return true; - } - - return false; -} - inline bool ShaderPassHasExplicitBuiltinMetadata(const Resources::ShaderPass& shaderPass) { if (!shaderPass.name.Empty() && shaderPass.name != Containers::String("Default")) { @@ -183,27 +138,6 @@ inline bool ShaderPassMatchesBuiltinPass( return hasMetadata; } -inline bool IsRedundantLegacyMaterialShaderPassHint( - const Resources::Shader* shader, - const Containers::String& shaderPass) { - if (shader == nullptr) { - return false; - } - - BuiltinMaterialPass builtinPass = BuiltinMaterialPass::ForwardLit; - if (!TryResolveBuiltinMaterialPassHint(shaderPass, builtinPass)) { - return false; - } - - for (const Resources::ShaderPass& pass : shader->GetPasses()) { - if (ShaderPassMatchesBuiltinPass(pass, builtinPass)) { - return true; - } - } - - return false; -} - inline BuiltinPassResourceSemantic ResolveBuiltinPassResourceSemantic( const Resources::ShaderResourceBindingDesc& binding) { Containers::String semantic = NormalizeBuiltinPassMetadataValue(binding.semantic); diff --git a/engine/include/XCEngine/Rendering/Materials/RenderMaterialResolve.h b/engine/include/XCEngine/Rendering/Materials/RenderMaterialResolve.h index 3a3d59d9..dc27158f 100644 --- a/engine/include/XCEngine/Rendering/Materials/RenderMaterialResolve.h +++ b/engine/include/XCEngine/Rendering/Materials/RenderMaterialResolve.h @@ -441,23 +441,21 @@ inline bool IsTransparentRenderQueue(Core::int32 renderQueue) { inline bool MatchesBuiltinPass(const Resources::Material* material, BuiltinMaterialPass pass) { if (material == nullptr) { - return pass == BuiltinMaterialPass::ForwardLit; - } - - const Resources::Shader* shader = material->GetShader(); - if (shader != nullptr) { - for (const Resources::ShaderPass& shaderPassEntry : shader->GetPasses()) { - if (ShaderPassMatchesBuiltinPass(shaderPassEntry, pass)) { - return true; - } - } - } - - if (shader != nullptr && ShaderHasExplicitBuiltinMetadata(*shader)) { return false; } - return pass == BuiltinMaterialPass::ForwardLit; + const Resources::Shader* shader = material->GetShader(); + if (shader == nullptr) { + return false; + } + + for (const Resources::ShaderPass& shaderPassEntry : shader->GetPasses()) { + if (ShaderPassMatchesBuiltinPass(shaderPassEntry, pass)) { + return true; + } + } + + return false; } } // namespace Rendering diff --git a/engine/src/Core/Asset/AssetDatabase.cpp b/engine/src/Core/Asset/AssetDatabase.cpp index 608eccf2..bcc5ba88 100644 --- a/engine/src/Core/Asset/AssetDatabase.cpp +++ b/engine/src/Core/Asset/AssetDatabase.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include #include @@ -11,6 +10,7 @@ #include #include +#include #include #include #include @@ -108,6 +108,22 @@ void AddUniqueDependencyPath(const fs::path& path, } } +bool IsCurrentShaderArtifactFile(const fs::path& artifactPath) { + std::ifstream input(artifactPath, std::ios::binary); + if (!input.is_open()) { + return false; + } + + ShaderArtifactFileHeader header = {}; + input.read(reinterpret_cast(&header), sizeof(header)); + if (!input || input.gcount() != static_cast(sizeof(header))) { + return false; + } + + return std::memcmp(header.magic, "XCSHD05", 7) == 0 && + header.schemaVersion == kShaderArtifactSchemaVersion; +} + std::string TrimCopy(const std::string& text) { const auto begin = std::find_if_not(text.begin(), text.end(), [](unsigned char ch) { return std::isspace(ch) != 0; @@ -425,19 +441,6 @@ Containers::String ResolveTextureBindingPath( return NormalizeArtifactPathString(material.GetTextureBindingPath(bindingIndex)); } -Containers::String ResolveSerializedLegacyMaterialShaderPassHint(const Material& material) { - const Containers::String& shaderPass = material.GetLegacyShaderPassHint(); - if (shaderPass.Empty()) { - return Containers::String(); - } - - if (Rendering::IsRedundantLegacyMaterialShaderPassHint(material.GetShader(), shaderPass)) { - return Containers::String(); - } - - return shaderPass; -} - bool WriteMaterialArtifactFile( const fs::path& artifactPath, const Material& material, @@ -459,7 +462,6 @@ bool WriteMaterialArtifactFile( material.GetShader() != nullptr ? material.GetShader()->GetPath() : Containers::String()); - WriteString(output, ResolveSerializedLegacyMaterialShaderPassHint(material)); MaterialArtifactHeader header; header.renderQueue = material.GetRenderQueue(); @@ -1454,6 +1456,11 @@ bool AssetDatabase::ShouldReimport(const SourceAssetRecord& sourceRecord, return true; } + if (artifactRecord->resourceType == ResourceType::Shader && + !IsCurrentShaderArtifactFile(artifactMainPath)) { + return true; + } + return artifactRecord->importerVersion != sourceRecord.importerVersion || artifactRecord->sourceHash != sourceRecord.sourceHash || artifactRecord->metaHash != sourceRecord.metaHash || diff --git a/engine/src/Rendering/Passes/BuiltinDepthStylePassBaseResources.cpp b/engine/src/Rendering/Passes/BuiltinDepthStylePassBaseResources.cpp index 9b771873..f0f3e09b 100644 --- a/engine/src/Rendering/Passes/BuiltinDepthStylePassBaseResources.cpp +++ b/engine/src/Rendering/Passes/BuiltinDepthStylePassBaseResources.cpp @@ -291,7 +291,6 @@ BuiltinDepthStylePassBase::ResolvedShaderPass BuiltinDepthStylePassBase::Resolve return false; } - const bool shaderHasExplicitBuiltinMetadata = ShaderHasExplicitBuiltinMetadata(*shader); const Resources::ShaderKeywordSet keywordSet = ResolvePassKeywordSet(sceneData, ownerMaterial); auto tryAcceptPass = diff --git a/engine/src/Rendering/Pipelines/BuiltinForwardPipelineResources.cpp b/engine/src/Rendering/Pipelines/BuiltinForwardPipelineResources.cpp index 6af6214e..b256a5c9 100644 --- a/engine/src/Rendering/Pipelines/BuiltinForwardPipelineResources.cpp +++ b/engine/src/Rendering/Pipelines/BuiltinForwardPipelineResources.cpp @@ -37,7 +37,6 @@ const Resources::ShaderPass* FindCompatibleSurfacePass( const Resources::Material* material, BuiltinMaterialPass pass, Resources::ShaderBackend backend) { - const bool shaderHasExplicitBuiltinMetadata = ShaderHasExplicitBuiltinMetadata(shader); const Resources::ShaderKeywordSet keywordSet = ResolvePassKeywordSet(sceneData, material); for (const Resources::ShaderPass& shaderPass : shader.GetPasses()) { @@ -51,43 +50,6 @@ const Resources::ShaderPass* FindCompatibleSurfacePass( } } - if (shaderHasExplicitBuiltinMetadata) { - return nullptr; - } - - if (pass != BuiltinMaterialPass::ForwardLit) { - return nullptr; - } - - const Resources::ShaderPass* defaultPass = shader.FindPass("ForwardLit"); - if (defaultPass != nullptr && - ::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants( - shader, - defaultPass->name, - backend, - keywordSet)) { - return defaultPass; - } - - defaultPass = shader.FindPass("Default"); - if (defaultPass != nullptr && - ::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants( - shader, - defaultPass->name, - backend, - keywordSet)) { - return defaultPass; - } - - if (shader.GetPassCount() > 0 && - ::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants( - shader, - shader.GetPasses()[0].name, - backend, - keywordSet)) { - return &shader.GetPasses()[0]; - } - return nullptr; } diff --git a/engine/src/Resources/Shader/Internal/ShaderArtifactLoader.cpp b/engine/src/Resources/Shader/Internal/ShaderArtifactLoader.cpp index d5f07282..9d35ca00 100644 --- a/engine/src/Resources/Shader/Internal/ShaderArtifactLoader.cpp +++ b/engine/src/Resources/Shader/Internal/ShaderArtifactLoader.cpp @@ -48,23 +48,6 @@ bool ReadShaderArtifactString( return true; } -MaterialRenderState ExpandSerializedMaterialRenderStateV4(const SerializedMaterialRenderStateV4& legacyState) { - MaterialRenderState renderState = {}; - renderState.blendEnable = legacyState.blendEnable; - renderState.srcBlend = legacyState.srcBlend; - renderState.dstBlend = legacyState.dstBlend; - renderState.srcBlendAlpha = legacyState.srcBlendAlpha; - renderState.dstBlendAlpha = legacyState.dstBlendAlpha; - renderState.blendOp = legacyState.blendOp; - renderState.blendOpAlpha = legacyState.blendOpAlpha; - renderState.colorWriteMask = legacyState.colorWriteMask; - renderState.depthTestEnable = legacyState.depthTestEnable; - renderState.depthWriteEnable = legacyState.depthWriteEnable; - renderState.depthFunc = legacyState.depthFunc; - renderState.cullMode = legacyState.cullMode; - return renderState; -} - } // namespace LoadResult LoadShaderArtifact(const Containers::String& path) { @@ -80,13 +63,9 @@ LoadResult LoadShaderArtifact(const Containers::String& path) { } const std::string magic(fileHeader.magic, fileHeader.magic + 7); - const bool isLegacySchema = magic == "XCSHD01" && fileHeader.schemaVersion == 1u; - const bool isSchemaV2 = magic == "XCSHD02" && fileHeader.schemaVersion == 2u; - const bool isSchemaV3 = magic == "XCSHD03" && fileHeader.schemaVersion == 3u; - const bool isSchemaV4 = magic == "XCSHD04" && fileHeader.schemaVersion == 4u; const bool isCurrentSchema = magic == "XCSHD05" && fileHeader.schemaVersion == kShaderArtifactSchemaVersion; - if (!isLegacySchema && !isSchemaV2 && !isSchemaV3 && !isSchemaV4 && !isCurrentSchema) { + if (!isCurrentSchema) { return LoadResult("Invalid shader artifact header: " + path); } @@ -96,10 +75,7 @@ LoadResult LoadShaderArtifact(const Containers::String& path) { Containers::String shaderSourcePath; Containers::String shaderFallback; if (!ReadShaderArtifactString(data, offset, shaderName) || - !ReadShaderArtifactString(data, offset, shaderSourcePath)) { - return LoadResult("Failed to parse shader artifact strings: " + path); - } - if ((isSchemaV4 || isCurrentSchema) && + !ReadShaderArtifactString(data, offset, shaderSourcePath) || !ReadShaderArtifactString(data, offset, shaderFallback)) { return LoadResult("Failed to parse shader artifact strings: " + path); } @@ -141,51 +117,18 @@ LoadResult LoadShaderArtifact(const Containers::String& path) { return LoadResult("Failed to read shader artifact passes: " + path); } - if (isLegacySchema) { - ShaderPassArtifactHeaderV1 passHeader = {}; - if (!ReadShaderArtifactValue(data, offset, passHeader)) { - return LoadResult("Failed to read shader artifact passes: " + path); - } - - tagCount = passHeader.tagCount; - resourceCount = passHeader.resourceCount; - variantCount = passHeader.variantCount; - } else if (isSchemaV2 || isSchemaV3) { - ShaderPassArtifactHeader passHeader = {}; - if (!ReadShaderArtifactValue(data, offset, passHeader)) { - return LoadResult("Failed to read shader artifact passes: " + path); - } - - tagCount = passHeader.tagCount; - resourceCount = passHeader.resourceCount; - keywordDeclarationCount = passHeader.keywordDeclarationCount; - variantCount = passHeader.variantCount; - } else if (isSchemaV4) { - ShaderPassArtifactHeaderV4 passHeader = {}; - if (!ReadShaderArtifactValue(data, offset, passHeader)) { - return LoadResult("Failed to read shader artifact passes: " + path); - } - - tagCount = passHeader.tagCount; - resourceCount = passHeader.resourceCount; - keywordDeclarationCount = passHeader.keywordDeclarationCount; - variantCount = passHeader.variantCount; - hasFixedFunctionState = passHeader.hasFixedFunctionState; - fixedFunctionState = ExpandSerializedMaterialRenderStateV4(passHeader.fixedFunctionState); - } else { - ShaderPassArtifactHeaderV5 passHeader = {}; - if (!ReadShaderArtifactValue(data, offset, passHeader)) { - return LoadResult("Failed to read shader artifact passes: " + path); - } - - tagCount = passHeader.tagCount; - resourceCount = passHeader.resourceCount; - keywordDeclarationCount = passHeader.keywordDeclarationCount; - variantCount = passHeader.variantCount; - hasFixedFunctionState = passHeader.hasFixedFunctionState; - fixedFunctionState = passHeader.fixedFunctionState; + ShaderPassArtifactHeaderV5 passHeader = {}; + if (!ReadShaderArtifactValue(data, offset, passHeader)) { + return LoadResult("Failed to read shader artifact passes: " + path); } + tagCount = passHeader.tagCount; + resourceCount = passHeader.resourceCount; + keywordDeclarationCount = passHeader.keywordDeclarationCount; + variantCount = passHeader.variantCount; + hasFixedFunctionState = passHeader.hasFixedFunctionState; + fixedFunctionState = passHeader.fixedFunctionState; + ShaderPass pass = {}; pass.name = passName; pass.hasFixedFunctionState = hasFixedFunctionState != 0u; @@ -244,29 +187,17 @@ LoadResult LoadShaderArtifact(const Containers::String& path) { ShaderStageVariant variant = {}; Core::uint64 compiledBinarySize = 0; Core::uint32 keywordCount = 0; - if (isSchemaV4 || isCurrentSchema) { - ShaderVariantArtifactHeader variantHeader = {}; - if (!ReadShaderArtifactValue(data, offset, variantHeader)) { - return LoadResult("Failed to read shader artifact variants: " + path); - } - - variant.stage = static_cast(variantHeader.stage); - variant.language = static_cast(variantHeader.language); - variant.backend = static_cast(variantHeader.backend); - keywordCount = variantHeader.keywordCount; - compiledBinarySize = variantHeader.compiledBinarySize; - } else { - ShaderVariantArtifactHeaderV2 variantHeader = {}; - if (!ReadShaderArtifactValue(data, offset, variantHeader)) { - return LoadResult("Failed to read shader artifact variants: " + path); - } - - variant.stage = static_cast(variantHeader.stage); - variant.language = static_cast(variantHeader.language); - variant.backend = static_cast(variantHeader.backend); - compiledBinarySize = variantHeader.compiledBinarySize; + ShaderVariantArtifactHeader variantHeader = {}; + if (!ReadShaderArtifactValue(data, offset, variantHeader)) { + return LoadResult("Failed to read shader artifact variants: " + path); } + variant.stage = static_cast(variantHeader.stage); + variant.language = static_cast(variantHeader.language); + variant.backend = static_cast(variantHeader.backend); + keywordCount = variantHeader.keywordCount; + compiledBinarySize = variantHeader.compiledBinarySize; + if (!ReadShaderArtifactString(data, offset, variant.entryPoint) || !ReadShaderArtifactString(data, offset, variant.profile) || !ReadShaderArtifactString(data, offset, variant.sourceCode)) { diff --git a/tests/Rendering/unit/test_render_scene_extractor.cpp b/tests/Rendering/unit/test_render_scene_extractor.cpp index e2c8db3b..73835cfc 100644 --- a/tests/Rendering/unit/test_render_scene_extractor.cpp +++ b/tests/Rendering/unit/test_render_scene_extractor.cpp @@ -447,9 +447,15 @@ TEST(RenderSceneExtractor_Test, FallsBackToEmbeddedMeshMaterialsWhenRendererHasN delete mesh; } -TEST(RenderMaterialUtility_Test, MaterialsWithoutShaderMetadataUseImplicitForwardFallback) { +TEST(RenderMaterialUtility_Test, NullMaterialsDoNotMatchBuiltinSurfacePasses) { + EXPECT_FALSE(MatchesBuiltinPass(nullptr, BuiltinMaterialPass::ForwardLit)); + EXPECT_FALSE(MatchesBuiltinPass(nullptr, BuiltinMaterialPass::Unlit)); +} + +TEST(RenderMaterialUtility_Test, MaterialsWithoutShaderMetadataDoNotMatchBuiltinSurfacePasses) { Material noMetadataMaterial; - EXPECT_TRUE(MatchesBuiltinPass(&noMetadataMaterial, BuiltinMaterialPass::ForwardLit)); + EXPECT_FALSE(MatchesBuiltinPass(&noMetadataMaterial, BuiltinMaterialPass::ForwardLit)); + EXPECT_FALSE(MatchesBuiltinPass(&noMetadataMaterial, BuiltinMaterialPass::Unlit)); } TEST(RenderMaterialUtility_Test, ShaderPassMetadataSupportsUnlitDepthAndObjectId) { @@ -527,7 +533,7 @@ TEST(RenderMaterialUtility_Test, ShaderMetadataOverridesConflictingMaterialTags) EXPECT_FALSE(MatchesBuiltinPass(&material, BuiltinMaterialPass::DepthOnly)); } -TEST(RenderMaterialUtility_Test, MaterialTagsDoNotOverrideImplicitForwardFallbackWithoutShaderMetadata) { +TEST(RenderMaterialUtility_Test, MaterialTagsDoNotOverrideMissingShaderMetadata) { Material material; auto* shader = new Shader(); @@ -538,11 +544,11 @@ TEST(RenderMaterialUtility_Test, MaterialTagsDoNotOverrideImplicitForwardFallbac material.SetShader(ResourceHandle(shader)); material.SetTag("LightMode", "ShadowCaster"); - EXPECT_TRUE(MatchesBuiltinPass(&material, BuiltinMaterialPass::ForwardLit)); + EXPECT_FALSE(MatchesBuiltinPass(&material, BuiltinMaterialPass::ForwardLit)); EXPECT_FALSE(MatchesBuiltinPass(&material, BuiltinMaterialPass::ShadowCaster)); } -TEST(RenderMaterialUtility_Test, ShadersWithoutBuiltinMetadataKeepImplicitForwardFallback) { +TEST(RenderMaterialUtility_Test, ShadersWithoutBuiltinMetadataDoNotMatchBuiltinSurfacePasses) { Material material; auto* shader = new Shader(); @@ -552,7 +558,7 @@ TEST(RenderMaterialUtility_Test, ShadersWithoutBuiltinMetadataKeepImplicitForwar material.SetShader(ResourceHandle(shader)); - EXPECT_TRUE(MatchesBuiltinPass(&material, BuiltinMaterialPass::ForwardLit)); + EXPECT_FALSE(MatchesBuiltinPass(&material, BuiltinMaterialPass::ForwardLit)); EXPECT_FALSE(MatchesBuiltinPass(&material, BuiltinMaterialPass::Unlit)); } diff --git a/tests/Resources/Shader/test_shader_loader.cpp b/tests/Resources/Shader/test_shader_loader.cpp index 92580952..57c79713 100644 --- a/tests/Resources/Shader/test_shader_loader.cpp +++ b/tests/Resources/Shader/test_shader_loader.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -7,6 +8,7 @@ #include #include +#include #include #include #include @@ -24,6 +26,15 @@ void WriteTextFile(const std::filesystem::path& path, const std::string& content ASSERT_TRUE(static_cast(output)); } +ShaderArtifactFileHeader ReadShaderArtifactFileHeader(const std::filesystem::path& path) { + ShaderArtifactFileHeader header = {}; + std::ifstream input(path, std::ios::binary); + EXPECT_TRUE(input.is_open()); + input.read(reinterpret_cast(&header), sizeof(header)); + EXPECT_TRUE(static_cast(input)); + return header; +} + const ShaderPassTagEntry* FindPassTag(const ShaderPass* pass, const char* name) { if (pass == nullptr || name == nullptr) { return nullptr; @@ -71,6 +82,34 @@ TEST(ShaderLoader, LoadInvalidPath) { EXPECT_FALSE(result); } +TEST(ShaderLoader, RejectsLegacyShaderArtifactSchemaHeaders) { + namespace fs = std::filesystem; + + const fs::path artifactPath = fs::temp_directory_path() / "xc_shader_loader_legacy_schema.xcshader"; + fs::remove(artifactPath); + + ShaderArtifactFileHeader header = {}; + std::memcpy(header.magic, "XCSHD04", 7); + header.magic[7] = '\0'; + header.schemaVersion = 4u; + + { + std::ofstream output(artifactPath, std::ios::binary | std::ios::trunc); + ASSERT_TRUE(output.is_open()); + output.write(reinterpret_cast(&header), sizeof(header)); + ASSERT_TRUE(static_cast(output)); + } + + ShaderLoader loader; + LoadResult result = loader.Load(artifactPath.string().c_str()); + EXPECT_FALSE(result); + EXPECT_NE( + std::string(result.errorMessage.CStr()).find("Invalid shader artifact header"), + std::string::npos); + + fs::remove(artifactPath); +} + TEST(ShaderLoader, RejectsLegacySingleStageShaderSourceFiles) { namespace fs = std::filesystem; @@ -1663,6 +1702,80 @@ TEST(ShaderLoader, AssetDatabaseCreatesShaderArtifactFromAuthoringAndLoaderReads fs::remove_all(projectRoot); } +TEST(ShaderLoader, AssetDatabaseReimportsLegacyShaderArtifactHeaderBeforeLoad) { + namespace fs = std::filesystem; + + const fs::path projectRoot = fs::temp_directory_path() / "xc_shader_legacy_artifact_reimport"; + const fs::path shaderDir = projectRoot / "Assets" / "Shaders"; + const fs::path shaderPath = shaderDir / "lit.shader"; + + fs::remove_all(projectRoot); + fs::create_directories(shaderDir); + WriteTextFile( + shaderPath, + R"(Shader "ArtifactSchemaReimportShader" +{ + SubShader + { + Pass + { + Name "ForwardLit" + Tags { "LightMode" = "ForwardBase" } + HLSLPROGRAM + #pragma vertex MainVS + #pragma fragment MainPS + float4 MainVS() : SV_POSITION + { + return 0; + } + float4 MainPS() : SV_TARGET + { + return 1; + } + ENDHLSL + } + } +} +)"); + + AssetDatabase database; + database.Initialize(projectRoot.string().c_str()); + + AssetDatabase::ResolvedAsset firstResolve; + ASSERT_TRUE(database.EnsureArtifact("Assets/Shaders/lit.shader", ResourceType::Shader, firstResolve)); + ASSERT_TRUE(firstResolve.artifactReady); + ASSERT_TRUE(fs::exists(firstResolve.artifactMainPath.CStr())); + + ShaderArtifactFileHeader legacyHeader = ReadShaderArtifactFileHeader(firstResolve.artifactMainPath.CStr()); + std::memcpy(legacyHeader.magic, "XCSHD04", 7); + legacyHeader.magic[7] = '\0'; + legacyHeader.schemaVersion = 4u; + { + std::fstream output(firstResolve.artifactMainPath.CStr(), std::ios::binary | std::ios::in | std::ios::out); + ASSERT_TRUE(output.is_open()); + output.write(reinterpret_cast(&legacyHeader), sizeof(legacyHeader)); + ASSERT_TRUE(static_cast(output)); + } + + AssetDatabase::ResolvedAsset secondResolve; + ASSERT_TRUE(database.EnsureArtifact("Assets/Shaders/lit.shader", ResourceType::Shader, secondResolve)); + EXPECT_TRUE(secondResolve.imported); + ASSERT_TRUE(secondResolve.artifactReady); + + const ShaderArtifactFileHeader currentHeader = ReadShaderArtifactFileHeader(secondResolve.artifactMainPath.CStr()); + EXPECT_EQ(std::string(currentHeader.magic, currentHeader.magic + 7), std::string("XCSHD05")); + EXPECT_EQ(currentHeader.schemaVersion, kShaderArtifactSchemaVersion); + + ShaderLoader loader; + LoadResult result = loader.Load(secondResolve.artifactMainPath.CStr()); + ASSERT_TRUE(result); + ASSERT_NE(result.resource, nullptr); + delete static_cast(result.resource); + + database.Shutdown(); + fs::remove_all(projectRoot); +} + TEST(ShaderLoader, AssetDatabaseCreatesShaderArtifactFromAuthoringPreservesKeywords) { namespace fs = std::filesystem;