feat: add unity-aligned shader contract metadata

This commit is contained in:
2026-04-03 00:01:31 +08:00
parent b43d4048b8
commit 6636834b35
9 changed files with 484 additions and 0 deletions

View File

@@ -446,6 +446,39 @@ TEST(RenderMaterialUtility_Test, ResolvesBuiltinForwardMaterialContractFromCanon
EXPECT_EQ(ResolveBuiltinBaseColorTexture(&aliasMaterial), baseColorTexture);
}
TEST(RenderMaterialUtility_Test, ResolvesBuiltinForwardMaterialContractFromShaderSemanticMetadata) {
auto* shader = new Shader();
ShaderPropertyDesc colorProperty = {};
colorProperty.name = "TintColor";
colorProperty.displayName = "Tint";
colorProperty.type = ShaderPropertyType::Color;
colorProperty.semantic = "BaseColor";
shader->AddProperty(colorProperty);
ShaderPropertyDesc textureProperty = {};
textureProperty.name = "AlbedoMap";
textureProperty.displayName = "Albedo";
textureProperty.type = ShaderPropertyType::Texture2D;
textureProperty.semantic = "BaseColorTexture";
shader->AddProperty(textureProperty);
Material material;
material.SetShader(ResourceHandle<Shader>(shader));
material.SetFloat4("TintColor", Vector4(0.3f, 0.5f, 0.7f, 0.9f));
Texture* texture = new Texture();
IResource::ConstructParams textureParams = {};
textureParams.name = "SemanticTexture";
textureParams.path = "Textures/semantic_base_color.texture";
textureParams.guid = ResourceGUID::Generate(textureParams.path);
texture->Initialize(textureParams);
material.SetTexture("AlbedoMap", ResourceHandle<Texture>(texture));
EXPECT_EQ(ResolveBuiltinBaseColorFactor(&material), Vector4(0.3f, 0.5f, 0.7f, 0.9f));
EXPECT_EQ(ResolveBuiltinBaseColorTexture(&material), texture);
}
TEST(RenderMaterialUtility_Test, UsesOpacityOnlyWhenBaseColorFactorIsMissing) {
Material material;
material.SetFloat("opacity", 0.35f);

View File

@@ -158,9 +158,48 @@ TEST(Shader, StoresPerPassTags) {
EXPECT_EQ(pass->tags[1].value, "Geometry");
}
TEST(Shader, StoresShaderPropertiesAndPassResources) {
Shader shader;
ShaderPropertyDesc baseColor = {};
baseColor.name = "_BaseColor";
baseColor.displayName = "Base Color";
baseColor.type = ShaderPropertyType::Color;
baseColor.defaultValue = "(1,1,1,1)";
baseColor.semantic = "BaseColor";
shader.AddProperty(baseColor);
ShaderResourceBindingDesc perObject = {};
perObject.name = "PerObjectConstants";
perObject.type = ShaderResourceType::ConstantBuffer;
perObject.set = 1;
perObject.binding = 0;
perObject.semantic = "PerObject";
shader.AddPassResourceBinding("ForwardLit", perObject);
ASSERT_EQ(shader.GetProperties().Size(), 1u);
const ShaderPropertyDesc* storedProperty = shader.FindProperty("_BaseColor");
ASSERT_NE(storedProperty, nullptr);
EXPECT_EQ(storedProperty->displayName, "Base Color");
EXPECT_EQ(storedProperty->type, ShaderPropertyType::Color);
EXPECT_EQ(storedProperty->semantic, "BaseColor");
const ShaderResourceBindingDesc* storedBinding =
shader.FindPassResourceBinding("ForwardLit", "PerObjectConstants");
ASSERT_NE(storedBinding, nullptr);
EXPECT_EQ(storedBinding->type, ShaderResourceType::ConstantBuffer);
EXPECT_EQ(storedBinding->set, 1u);
EXPECT_EQ(storedBinding->binding, 0u);
EXPECT_EQ(storedBinding->semantic, "PerObject");
}
TEST(Shader, ReleaseClearsPassRuntimeData) {
Shader shader;
shader.SetSourceCode("void main() {}");
ShaderPropertyDesc property = {};
property.name = "_BaseColor";
property.type = ShaderPropertyType::Color;
shader.AddProperty(property);
ShaderStageVariant variant = {};
variant.stage = ShaderType::Fragment;
variant.sourceCode = "fragment";
@@ -168,6 +207,7 @@ TEST(Shader, ReleaseClearsPassRuntimeData) {
shader.Release();
EXPECT_EQ(shader.GetProperties().Size(), 0u);
EXPECT_EQ(shader.GetPassCount(), 0u);
EXPECT_EQ(shader.GetSourceCode(), "");
EXPECT_EQ(shader.GetCompiledBinary().Size(), 0u);

View File

@@ -107,6 +107,22 @@ TEST(ShaderLoader, LoadShaderManifestBuildsMultiPassBackendVariants) {
ASSERT_TRUE(manifest.is_open());
manifest << "{\n";
manifest << " \"name\": \"TestLitShader\",\n";
manifest << " \"properties\": [\n";
manifest << " {\n";
manifest << " \"name\": \"_BaseColor\",\n";
manifest << " \"displayName\": \"Base Color\",\n";
manifest << " \"type\": \"Color\",\n";
manifest << " \"defaultValue\": \"(1,1,1,1)\",\n";
manifest << " \"semantic\": \"BaseColor\"\n";
manifest << " },\n";
manifest << " {\n";
manifest << " \"name\": \"_MainTex\",\n";
manifest << " \"displayName\": \"Base Map\",\n";
manifest << " \"type\": \"2D\",\n";
manifest << " \"defaultValue\": \"white\",\n";
manifest << " \"semantic\": \"BaseColorTexture\"\n";
manifest << " }\n";
manifest << " ],\n";
manifest << " \"passes\": [\n";
manifest << " {\n";
manifest << " \"name\": \"ForwardLit\",\n";
@@ -114,6 +130,12 @@ TEST(ShaderLoader, LoadShaderManifestBuildsMultiPassBackendVariants) {
manifest << " \"LightMode\": \"ForwardBase\",\n";
manifest << " \"Queue\": \"Geometry\"\n";
manifest << " },\n";
manifest << " \"resources\": [\n";
manifest << " { \"name\": \"PerObjectConstants\", \"type\": \"ConstantBuffer\", \"set\": 1, \"binding\": 0, \"semantic\": \"PerObject\" },\n";
manifest << " { \"name\": \"MaterialConstants\", \"type\": \"ConstantBuffer\", \"set\": 2, \"binding\": 0, \"semantic\": \"Material\" },\n";
manifest << " { \"name\": \"BaseColorTexture\", \"type\": \"Texture2D\", \"set\": 3, \"binding\": 0, \"semantic\": \"BaseColorTexture\" },\n";
manifest << " { \"name\": \"LinearClampSampler\", \"type\": \"Sampler\", \"set\": 4, \"binding\": 0, \"semantic\": \"LinearClampSampler\" }\n";
manifest << " ],\n";
manifest << " \"variants\": [\n";
manifest << " { \"stage\": \"Vertex\", \"backend\": \"D3D12\", \"language\": \"HLSL\", \"source\": \"stages/forward_lit.vs.hlsl\", \"entryPoint\": \"MainVS\", \"profile\": \"vs_5_0\" },\n";
manifest << " { \"stage\": \"Fragment\", \"backend\": \"D3D12\", \"language\": \"HLSL\", \"source\": \"stages/forward_lit.ps.hlsl\", \"entryPoint\": \"MainPS\", \"profile\": \"ps_5_0\" },\n";
@@ -146,16 +168,39 @@ TEST(ShaderLoader, LoadShaderManifestBuildsMultiPassBackendVariants) {
ASSERT_NE(shader, nullptr);
ASSERT_TRUE(shader->IsValid());
EXPECT_EQ(shader->GetName(), "TestLitShader");
ASSERT_EQ(shader->GetProperties().Size(), 2u);
ASSERT_EQ(shader->GetPassCount(), 2u);
const ShaderPropertyDesc* baseColorProperty = shader->FindProperty("_BaseColor");
ASSERT_NE(baseColorProperty, nullptr);
EXPECT_EQ(baseColorProperty->displayName, "Base Color");
EXPECT_EQ(baseColorProperty->type, ShaderPropertyType::Color);
EXPECT_EQ(baseColorProperty->defaultValue, "(1,1,1,1)");
EXPECT_EQ(baseColorProperty->semantic, "BaseColor");
const ShaderPropertyDesc* baseMapProperty = shader->FindProperty("_MainTex");
ASSERT_NE(baseMapProperty, nullptr);
EXPECT_EQ(baseMapProperty->type, ShaderPropertyType::Texture2D);
EXPECT_EQ(baseMapProperty->defaultValue, "white");
EXPECT_EQ(baseMapProperty->semantic, "BaseColorTexture");
const ShaderPass* forwardLitPass = shader->FindPass("ForwardLit");
ASSERT_NE(forwardLitPass, nullptr);
ASSERT_EQ(forwardLitPass->tags.Size(), 2u);
ASSERT_EQ(forwardLitPass->resources.Size(), 4u);
EXPECT_EQ(forwardLitPass->tags[0].name, "LightMode");
EXPECT_EQ(forwardLitPass->tags[0].value, "ForwardBase");
EXPECT_EQ(forwardLitPass->tags[1].name, "Queue");
EXPECT_EQ(forwardLitPass->tags[1].value, "Geometry");
const ShaderResourceBindingDesc* baseTextureBinding =
shader->FindPassResourceBinding("ForwardLit", "BaseColorTexture");
ASSERT_NE(baseTextureBinding, nullptr);
EXPECT_EQ(baseTextureBinding->type, ShaderResourceType::Texture2D);
EXPECT_EQ(baseTextureBinding->set, 3u);
EXPECT_EQ(baseTextureBinding->binding, 0u);
EXPECT_EQ(baseTextureBinding->semantic, "BaseColorTexture");
const ShaderStageVariant* d3d12Vertex = shader->FindVariant("ForwardLit", ShaderType::Vertex, ShaderBackend::D3D12);
ASSERT_NE(d3d12Vertex, nullptr);
EXPECT_EQ(d3d12Vertex->entryPoint, "MainVS");
@@ -252,11 +297,31 @@ TEST(ShaderLoader, LoadBuiltinForwardLitShaderBuildsBackendVariants) {
const ShaderPass* pass = shader->FindPass("ForwardLit");
ASSERT_NE(pass, nullptr);
ASSERT_EQ(shader->GetProperties().Size(), 2u);
ASSERT_EQ(pass->variants.Size(), 6u);
ASSERT_EQ(pass->tags.Size(), 1u);
ASSERT_EQ(pass->resources.Size(), 4u);
EXPECT_EQ(pass->tags[0].name, "LightMode");
EXPECT_EQ(pass->tags[0].value, "ForwardBase");
const ShaderPropertyDesc* baseColorProperty = shader->FindProperty("_BaseColor");
ASSERT_NE(baseColorProperty, nullptr);
EXPECT_EQ(baseColorProperty->type, ShaderPropertyType::Color);
EXPECT_EQ(baseColorProperty->semantic, "BaseColor");
const ShaderPropertyDesc* baseMapProperty = shader->FindProperty("_MainTex");
ASSERT_NE(baseMapProperty, nullptr);
EXPECT_EQ(baseMapProperty->type, ShaderPropertyType::Texture2D);
EXPECT_EQ(baseMapProperty->semantic, "BaseColorTexture");
const ShaderResourceBindingDesc* perObjectBinding =
shader->FindPassResourceBinding("ForwardLit", "PerObjectConstants");
ASSERT_NE(perObjectBinding, nullptr);
EXPECT_EQ(perObjectBinding->type, ShaderResourceType::ConstantBuffer);
EXPECT_EQ(perObjectBinding->set, 1u);
EXPECT_EQ(perObjectBinding->binding, 0u);
EXPECT_EQ(perObjectBinding->semantic, "PerObject");
EXPECT_NE(shader->FindVariant("ForwardLit", ShaderType::Vertex, ShaderBackend::D3D12), nullptr);
EXPECT_NE(shader->FindVariant("ForwardLit", ShaderType::Fragment, ShaderBackend::D3D12), nullptr);
EXPECT_NE(shader->FindVariant("ForwardLit", ShaderType::Vertex, ShaderBackend::OpenGL), nullptr);