Remove legacy shader pass and artifact fallbacks
This commit is contained in:
@@ -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<Core::int32>(MaterialRenderQueue::Geometry);
|
||||
MaterialRenderState renderState = {};
|
||||
Core::uint32 tagCount = 0;
|
||||
Core::uint32 propertyCount = 0;
|
||||
Core::uint32 textureBindingCount = 0;
|
||||
};
|
||||
|
||||
struct MaterialArtifactHeaderV3 {
|
||||
Core::int32 renderQueue = static_cast<Core::int32>(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<Core::int32>(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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include <XCEngine/Core/Asset/ArtifactFormats.h>
|
||||
#include <XCEngine/Debug/Logger.h>
|
||||
#include <XCEngine/Rendering/Builtin/BuiltinPassMetadataUtils.h>
|
||||
#include <XCEngine/Resources/Material/MaterialLoader.h>
|
||||
#include <XCEngine/Resources/Mesh/MeshLoader.h>
|
||||
#include <XCEngine/Resources/Shader/ShaderLoader.h>
|
||||
@@ -11,6 +10,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
@@ -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<char*>(&header), sizeof(header));
|
||||
if (!input || input.gcount() != static_cast<std::streamsize>(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 ||
|
||||
|
||||
@@ -291,7 +291,6 @@ BuiltinDepthStylePassBase::ResolvedShaderPass BuiltinDepthStylePassBase::Resolve
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool shaderHasExplicitBuiltinMetadata = ShaderHasExplicitBuiltinMetadata(*shader);
|
||||
const Resources::ShaderKeywordSet keywordSet = ResolvePassKeywordSet(sceneData, ownerMaterial);
|
||||
|
||||
auto tryAcceptPass =
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<ShaderType>(variantHeader.stage);
|
||||
variant.language = static_cast<ShaderLanguage>(variantHeader.language);
|
||||
variant.backend = static_cast<ShaderBackend>(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<ShaderType>(variantHeader.stage);
|
||||
variant.language = static_cast<ShaderLanguage>(variantHeader.language);
|
||||
variant.backend = static_cast<ShaderBackend>(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<ShaderType>(variantHeader.stage);
|
||||
variant.language = static_cast<ShaderLanguage>(variantHeader.language);
|
||||
variant.backend = static_cast<ShaderBackend>(variantHeader.backend);
|
||||
keywordCount = variantHeader.keywordCount;
|
||||
compiledBinarySize = variantHeader.compiledBinarySize;
|
||||
|
||||
if (!ReadShaderArtifactString(data, offset, variant.entryPoint) ||
|
||||
!ReadShaderArtifactString(data, offset, variant.profile) ||
|
||||
!ReadShaderArtifactString(data, offset, variant.sourceCode)) {
|
||||
|
||||
@@ -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>(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>(shader));
|
||||
|
||||
EXPECT_TRUE(MatchesBuiltinPass(&material, BuiltinMaterialPass::ForwardLit));
|
||||
EXPECT_FALSE(MatchesBuiltinPass(&material, BuiltinMaterialPass::ForwardLit));
|
||||
EXPECT_FALSE(MatchesBuiltinPass(&material, BuiltinMaterialPass::Unlit));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <XCEngine/Core/Asset/AssetDatabase.h>
|
||||
#include <XCEngine/Core/Asset/ArtifactFormats.h>
|
||||
#include <XCEngine/Resources/BuiltinResources.h>
|
||||
#include <XCEngine/Core/Asset/ResourceManager.h>
|
||||
#include <XCEngine/Resources/Shader/ShaderLoader.h>
|
||||
@@ -7,6 +8,7 @@
|
||||
#include <XCEngine/Core/Containers/Array.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
@@ -24,6 +26,15 @@ void WriteTextFile(const std::filesystem::path& path, const std::string& content
|
||||
ASSERT_TRUE(static_cast<bool>(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<char*>(&header), sizeof(header));
|
||||
EXPECT_TRUE(static_cast<bool>(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<const char*>(&header), sizeof(header));
|
||||
ASSERT_TRUE(static_cast<bool>(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<const char*>(&legacyHeader), sizeof(legacyHeader));
|
||||
ASSERT_TRUE(static_cast<bool>(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<Shader*>(result.resource);
|
||||
|
||||
database.Shutdown();
|
||||
fs::remove_all(projectRoot);
|
||||
}
|
||||
|
||||
TEST(ShaderLoader, AssetDatabaseCreatesShaderArtifactFromAuthoringPreservesKeywords) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user