rendering: strip redundant builtin material pass hints

This commit is contained in:
2026-04-07 09:30:36 +08:00
parent 945420f3bd
commit 5913462178
8 changed files with 327 additions and 17 deletions

View File

@@ -1,6 +1,7 @@
#include <gtest/gtest.h>
#include <XCEngine/Core/Asset/ArtifactFormats.h>
#include <XCEngine/Core/Asset/AssetDatabase.h>
#include <XCEngine/Resources/BuiltinResources.h>
#include <XCEngine/Resources/Material/MaterialLoader.h>
#include <XCEngine/Core/Asset/ResourceManager.h>
#include <XCEngine/Core/Asset/ResourceTypes.h>
@@ -251,12 +252,36 @@ TEST(MaterialLoader, LoadValidMaterialParsesRenderMetadata) {
EXPECT_EQ(material->GetRenderState().dstBlend, MaterialBlendFactor::InvSrcAlpha);
EXPECT_FALSE(material->GetRenderState().depthWriteEnable);
EXPECT_EQ(material->GetRenderState().depthFunc, MaterialComparisonFunc::LessEqual);
EXPECT_TRUE(material->HasRenderStateOverride());
delete material;
std::remove(materialPath.string().c_str());
std::remove(shaderPath.string().c_str());
}
TEST(MaterialLoader, LoadMaterialWithoutRenderStateLeavesOverrideDisabled) {
const std::filesystem::path materialPath =
std::filesystem::current_path() / "material_loader_no_render_state.material";
{
std::ofstream materialFile(materialPath);
ASSERT_TRUE(materialFile.is_open());
materialFile << "{ \"renderQueue\": \"Geometry\" }";
}
MaterialLoader loader;
LoadResult result = loader.Load(materialPath.string().c_str());
ASSERT_TRUE(result);
ASSERT_NE(result.resource, nullptr);
auto* material = static_cast<Material*>(result.resource);
ASSERT_NE(material, nullptr);
EXPECT_FALSE(material->HasRenderStateOverride());
delete material;
std::remove(materialPath.string().c_str());
}
TEST(MaterialLoader, LoadMaterialWithShaderManifestResolvesShaderPass) {
namespace fs = std::filesystem;
@@ -319,7 +344,7 @@ TEST(MaterialLoader, LoadMaterialWithShaderManifestResolvesShaderPass) {
Material* material = static_cast<Material*>(result.resource);
ASSERT_NE(material, nullptr);
ASSERT_NE(material->GetShader(), nullptr);
EXPECT_EQ(material->GetShaderPass(), "ForwardLit");
EXPECT_TRUE(material->GetShaderPass().Empty());
ASSERT_NE(material->GetShader()->FindPass("ForwardLit"), nullptr);
const ShaderStageVariant* vertexVariant =
material->GetShader()->FindVariant("ForwardLit", ShaderType::Vertex, ShaderBackend::OpenGL);
@@ -361,7 +386,7 @@ TEST(MaterialLoader, LoadMaterialWithPropertiesObjectAppliesTypedOverrides) {
auto* material = static_cast<Material*>(result.resource);
ASSERT_NE(material, nullptr);
ASSERT_NE(material->GetShader(), nullptr);
EXPECT_EQ(material->GetShaderPass(), "ForwardLit");
EXPECT_TRUE(material->GetShaderPass().Empty());
EXPECT_EQ(material->GetFloat4("_BaseColor"), XCEngine::Math::Vector4(0.2f, 0.4f, 0.6f, 0.8f));
EXPECT_FLOAT_EQ(material->GetFloat("_Metallic"), 0.15f);
EXPECT_EQ(material->GetInt("_Mode"), 5);
@@ -372,6 +397,40 @@ TEST(MaterialLoader, LoadMaterialWithPropertiesObjectAppliesTypedOverrides) {
fs::remove_all(rootPath);
}
TEST(MaterialLoader, LoadBuiltinShaderMaterialDropsRedundantBuiltinShaderPassHint) {
namespace fs = std::filesystem;
ResourceManager& manager = ResourceManager::Get();
manager.Initialize();
const fs::path rootPath = fs::temp_directory_path() / "xc_material_loader_builtin_hint_strip_test";
fs::remove_all(rootPath);
fs::create_directories(rootPath);
const fs::path materialPath = rootPath / "builtin_unlit.material";
WriteTextFile(
materialPath,
"{\n"
" \"shader\": \"" + std::string(GetBuiltinUnlitShaderPath().CStr()) + "\",\n"
" \"shaderPass\": \"Unlit\"\n"
"}\n");
MaterialLoader loader;
LoadResult result = loader.Load(materialPath.generic_string().c_str());
ASSERT_TRUE(result);
ASSERT_NE(result.resource, nullptr);
auto* material = static_cast<Material*>(result.resource);
ASSERT_NE(material, nullptr);
ASSERT_NE(material->GetShader(), nullptr);
EXPECT_TRUE(material->GetShaderPass().Empty());
EXPECT_NE(material->GetShader()->FindPass("Unlit"), nullptr);
delete material;
manager.Shutdown();
fs::remove_all(rootPath);
}
TEST(MaterialLoader, RejectsUnknownPropertyAgainstShaderSchema) {
namespace fs = std::filesystem;
@@ -676,6 +735,43 @@ TEST(MaterialLoader, AssetDatabaseCreatesMaterialArtifact) {
fs::remove_all(projectRoot);
}
TEST(MaterialLoader, AssetDatabaseMaterialArtifactPreservesImplicitRenderStateFlag) {
namespace fs = std::filesystem;
const fs::path projectRoot = fs::temp_directory_path() / "xc_material_implicit_render_state_artifact";
const fs::path assetsDir = projectRoot / "Assets";
const fs::path materialPath = assetsDir / "implicit.material";
fs::remove_all(projectRoot);
fs::create_directories(assetsDir);
WriteTextFile(
materialPath,
"{\n"
" \"renderQueue\": \"Geometry\"\n"
"}\n");
AssetDatabase database;
database.Initialize(projectRoot.string().c_str());
AssetDatabase::ResolvedAsset resolvedAsset;
ASSERT_TRUE(database.EnsureArtifact("Assets/implicit.material", ResourceType::Material, resolvedAsset));
ASSERT_TRUE(resolvedAsset.artifactReady);
MaterialLoader loader;
LoadResult result = loader.Load(resolvedAsset.artifactMainPath.CStr());
ASSERT_TRUE(result);
ASSERT_NE(result.resource, nullptr);
auto* material = static_cast<Material*>(result.resource);
ASSERT_NE(material, nullptr);
EXPECT_FALSE(material->HasRenderStateOverride());
delete material;
database.Shutdown();
fs::remove_all(projectRoot);
}
TEST(MaterialLoader, AssetDatabaseMaterialArtifactRoundTripsKeywords) {
namespace fs = std::filesystem;
@@ -728,6 +824,57 @@ TEST(MaterialLoader, AssetDatabaseMaterialArtifactRoundTripsKeywords) {
fs::remove_all(projectRoot);
}
TEST(MaterialLoader, AssetDatabaseMaterialArtifactStripsRedundantBuiltinShaderPassHint) {
namespace fs = std::filesystem;
ResourceManager& manager = ResourceManager::Get();
manager.Initialize();
const fs::path projectRoot = fs::temp_directory_path() / "xc_material_artifact_builtin_hint_strip_test";
const fs::path assetsDir = projectRoot / "Assets";
const fs::path materialPath = assetsDir / "builtin_unlit.material";
fs::remove_all(projectRoot);
fs::create_directories(assetsDir);
WriteTextFile(
materialPath,
"{\n"
" \"shader\": \"" + std::string(GetBuiltinUnlitShaderPath().CStr()) + "\",\n"
" \"shaderPass\": \"Unlit\",\n"
" \"properties\": {\n"
" \"_BaseColor\": [1.0, 1.0, 1.0, 1.0]\n"
" }\n"
"}\n");
manager.SetResourceRoot(projectRoot.string().c_str());
manager.RefreshProjectAssets();
AssetDatabase database;
database.Initialize(projectRoot.string().c_str());
AssetDatabase::ResolvedAsset resolvedAsset;
ASSERT_TRUE(database.EnsureArtifact("Assets/builtin_unlit.material", ResourceType::Material, resolvedAsset));
ASSERT_TRUE(resolvedAsset.artifactReady);
MaterialLoader loader;
LoadResult result = loader.Load(resolvedAsset.artifactMainPath.CStr());
ASSERT_TRUE(result);
ASSERT_NE(result.resource, nullptr);
auto* material = static_cast<Material*>(result.resource);
ASSERT_NE(material, nullptr);
ASSERT_NE(material->GetShader(), nullptr);
EXPECT_TRUE(material->GetShaderPass().Empty());
EXPECT_EQ(material->GetFloat4("_BaseColor"), XCEngine::Math::Vector4(1.0f, 1.0f, 1.0f, 1.0f));
delete material;
database.Shutdown();
manager.SetResourceRoot("");
manager.Shutdown();
fs::remove_all(projectRoot);
}
TEST(MaterialLoader, ResourceManagerLoadsProjectMaterialTextureAsLazyDependency) {
namespace fs = std::filesystem;