rendering: formalize shader keyword metadata contract
This commit is contained in:
@@ -126,6 +126,42 @@ std::filesystem::path WriteSchemaMaterialShaderManifest(const std::filesystem::p
|
||||
return manifestPath;
|
||||
}
|
||||
|
||||
std::filesystem::path WriteKeywordMaterialShaderAuthoring(const std::filesystem::path& rootPath) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
const fs::path shaderDir = rootPath / "Assets" / "Shaders";
|
||||
fs::create_directories(shaderDir);
|
||||
|
||||
const fs::path shaderPath = shaderDir / "keyword.shader";
|
||||
WriteTextFile(
|
||||
shaderPath,
|
||||
R"(Shader "KeywordMaterialShader"
|
||||
{
|
||||
Properties
|
||||
{
|
||||
_BaseColor ("Base Color", Color) = (1,1,1,1) [Semantic(BaseColor)]
|
||||
}
|
||||
SubShader
|
||||
{
|
||||
Pass
|
||||
{
|
||||
Name "ForwardLit"
|
||||
HLSLPROGRAM
|
||||
#pragma vertex Vert
|
||||
#pragma fragment Frag
|
||||
#pragma multi_compile _ XC_MAIN_LIGHT_SHADOWS
|
||||
#pragma shader_feature_local _ XC_ALPHA_TEST
|
||||
float4 Vert() : SV_POSITION { return 0; }
|
||||
float4 Frag() : SV_TARGET { return 1; }
|
||||
ENDHLSL
|
||||
}
|
||||
}
|
||||
}
|
||||
)");
|
||||
|
||||
return shaderPath;
|
||||
}
|
||||
|
||||
TEST(MaterialLoader, GetResourceType) {
|
||||
MaterialLoader loader;
|
||||
EXPECT_EQ(loader.GetResourceType(), ResourceType::Material);
|
||||
@@ -467,6 +503,41 @@ TEST(MaterialLoader, LoadMaterialMapsLegacySemanticKeysIntoShaderSchemaPropertie
|
||||
fs::remove_all(rootPath);
|
||||
}
|
||||
|
||||
TEST(MaterialLoader, LoadMaterialParsesKeywordArrayAgainstShaderSchema) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
const fs::path rootPath = fs::temp_directory_path() / "xc_material_loader_keywords_test";
|
||||
fs::remove_all(rootPath);
|
||||
|
||||
const fs::path shaderPath = WriteKeywordMaterialShaderAuthoring(rootPath);
|
||||
const fs::path materialPath = rootPath / "keyword.material";
|
||||
|
||||
WriteTextFile(
|
||||
materialPath,
|
||||
"{\n"
|
||||
" \"shader\": \"" + shaderPath.generic_string() + "\",\n"
|
||||
" \"shaderPass\": \"ForwardLit\",\n"
|
||||
" \"keywords\": [\"XC_MAIN_LIGHT_SHADOWS\", \"XC_ALPHA_TEST\", \"XC_ALPHA_TEST\", \"_\"]\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);
|
||||
ASSERT_EQ(material->GetKeywordCount(), 2u);
|
||||
EXPECT_EQ(material->GetKeyword(0), "XC_ALPHA_TEST");
|
||||
EXPECT_EQ(material->GetKeyword(1), "XC_MAIN_LIGHT_SHADOWS");
|
||||
EXPECT_TRUE(material->IsKeywordEnabled("XC_ALPHA_TEST"));
|
||||
EXPECT_TRUE(material->IsKeywordEnabled("XC_MAIN_LIGHT_SHADOWS"));
|
||||
|
||||
delete material;
|
||||
fs::remove_all(rootPath);
|
||||
}
|
||||
|
||||
TEST(MaterialLoader, RejectsUnknownTextureBindingAgainstShaderSchema) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
@@ -605,6 +676,58 @@ TEST(MaterialLoader, AssetDatabaseCreatesMaterialArtifact) {
|
||||
fs::remove_all(projectRoot);
|
||||
}
|
||||
|
||||
TEST(MaterialLoader, AssetDatabaseMaterialArtifactRoundTripsKeywords) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
ResourceManager& manager = ResourceManager::Get();
|
||||
manager.Initialize();
|
||||
|
||||
const fs::path projectRoot = fs::temp_directory_path() / "xc_material_keyword_artifact_test";
|
||||
const fs::path assetsDir = projectRoot / "Assets";
|
||||
const fs::path materialPath = assetsDir / "keyword.material";
|
||||
|
||||
fs::remove_all(projectRoot);
|
||||
fs::create_directories(assetsDir);
|
||||
WriteKeywordMaterialShaderAuthoring(projectRoot);
|
||||
|
||||
WriteTextFile(
|
||||
materialPath,
|
||||
"{\n"
|
||||
" \"shader\": \"Assets/Shaders/keyword.shader\",\n"
|
||||
" \"shaderPass\": \"ForwardLit\",\n"
|
||||
" \"keywords\": [\"XC_MAIN_LIGHT_SHADOWS\", \"XC_ALPHA_TEST\"]\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/keyword.material", ResourceType::Material, resolvedAsset));
|
||||
ASSERT_TRUE(resolvedAsset.artifactReady);
|
||||
EXPECT_TRUE(fs::exists(resolvedAsset.artifactMainPath.CStr()));
|
||||
|
||||
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);
|
||||
ASSERT_EQ(material->GetKeywordCount(), 2u);
|
||||
EXPECT_EQ(material->GetKeyword(0), "XC_ALPHA_TEST");
|
||||
EXPECT_EQ(material->GetKeyword(1), "XC_MAIN_LIGHT_SHADOWS");
|
||||
|
||||
delete material;
|
||||
database.Shutdown();
|
||||
manager.SetResourceRoot("");
|
||||
manager.Shutdown();
|
||||
fs::remove_all(projectRoot);
|
||||
}
|
||||
|
||||
TEST(MaterialLoader, ResourceManagerLoadsProjectMaterialTextureAsLazyDependency) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user