feat: add unity-aligned shader contract metadata
This commit is contained in:
@@ -1,11 +1,57 @@
|
||||
{
|
||||
"name": "Builtin Forward Lit",
|
||||
"properties": [
|
||||
{
|
||||
"name": "_BaseColor",
|
||||
"displayName": "Base Color",
|
||||
"type": "Color",
|
||||
"defaultValue": "(1,1,1,1)",
|
||||
"semantic": "BaseColor"
|
||||
},
|
||||
{
|
||||
"name": "_MainTex",
|
||||
"displayName": "Base Map",
|
||||
"type": "2D",
|
||||
"defaultValue": "white",
|
||||
"semantic": "BaseColorTexture"
|
||||
}
|
||||
],
|
||||
"passes": [
|
||||
{
|
||||
"name": "ForwardLit",
|
||||
"tags": {
|
||||
"LightMode": "ForwardBase"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"name": "PerObjectConstants",
|
||||
"type": "ConstantBuffer",
|
||||
"set": 1,
|
||||
"binding": 0,
|
||||
"semantic": "PerObject"
|
||||
},
|
||||
{
|
||||
"name": "MaterialConstants",
|
||||
"type": "ConstantBuffer",
|
||||
"set": 2,
|
||||
"binding": 0,
|
||||
"semantic": "Material"
|
||||
},
|
||||
{
|
||||
"name": "BaseColorTexture",
|
||||
"type": "Texture2D",
|
||||
"set": 3,
|
||||
"binding": 0,
|
||||
"semantic": "BaseColorTexture"
|
||||
},
|
||||
{
|
||||
"name": "LinearClampSampler",
|
||||
"type": "Sampler",
|
||||
"set": 4,
|
||||
"binding": 0,
|
||||
"semantic": "LinearClampSampler"
|
||||
}
|
||||
],
|
||||
"variants": [
|
||||
{
|
||||
"stage": "Vertex",
|
||||
|
||||
@@ -122,11 +122,36 @@ struct BuiltinForwardMaterialData {
|
||||
Math::Vector4 baseColorFactor = Math::Vector4::One();
|
||||
};
|
||||
|
||||
inline const Resources::ShaderPropertyDesc* FindShaderPropertyBySemantic(
|
||||
const Resources::Material* material,
|
||||
const Containers::String& semantic) {
|
||||
if (material == nullptr || material->GetShader() == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const Containers::String normalizedSemantic = NormalizeBuiltinPassMetadataValue(semantic);
|
||||
for (const Resources::ShaderPropertyDesc& property : material->GetShader()->GetProperties()) {
|
||||
if (NormalizeBuiltinPassMetadataValue(property.semantic) == normalizedSemantic) {
|
||||
return &property;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline Math::Vector4 ResolveBuiltinBaseColorFactor(const Resources::Material* material) {
|
||||
if (material == nullptr) {
|
||||
return Math::Vector4::One();
|
||||
}
|
||||
|
||||
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "BaseColor")) {
|
||||
if (material->HasProperty(property->name) &&
|
||||
(property->type == Resources::ShaderPropertyType::Color ||
|
||||
property->type == Resources::ShaderPropertyType::Vector)) {
|
||||
return material->GetFloat4(property->name);
|
||||
}
|
||||
}
|
||||
|
||||
static const char* kBaseColorPropertyNames[] = {
|
||||
"baseColor",
|
||||
"_BaseColor",
|
||||
@@ -162,6 +187,13 @@ inline const Resources::Texture* ResolveBuiltinBaseColorTexture(const Resources:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "BaseColorTexture")) {
|
||||
const Resources::ResourceHandle<Resources::Texture> textureHandle = material->GetTexture(property->name);
|
||||
if (textureHandle.Get() != nullptr && textureHandle->IsValid()) {
|
||||
return textureHandle.Get();
|
||||
}
|
||||
}
|
||||
|
||||
static const char* kTextureNames[] = {
|
||||
"baseColorTexture",
|
||||
"_BaseColorTexture",
|
||||
|
||||
@@ -29,6 +29,25 @@ enum class ShaderBackend : Core::uint8 {
|
||||
Vulkan
|
||||
};
|
||||
|
||||
// Keep shader property kinds close to Unity's public shader syntax so the
|
||||
// runtime contract can be reused when ShaderLab-compatible parsing is added.
|
||||
enum class ShaderPropertyType : Core::uint8 {
|
||||
Float = 0,
|
||||
Range,
|
||||
Int,
|
||||
Vector,
|
||||
Color,
|
||||
Texture2D,
|
||||
TextureCube
|
||||
};
|
||||
|
||||
enum class ShaderResourceType : Core::uint8 {
|
||||
ConstantBuffer = 0,
|
||||
Texture2D,
|
||||
TextureCube,
|
||||
Sampler
|
||||
};
|
||||
|
||||
struct ShaderUniform {
|
||||
Containers::String name;
|
||||
Core::uint32 location;
|
||||
@@ -48,6 +67,22 @@ struct ShaderPassTagEntry {
|
||||
Containers::String value;
|
||||
};
|
||||
|
||||
struct ShaderPropertyDesc {
|
||||
Containers::String name;
|
||||
Containers::String displayName;
|
||||
ShaderPropertyType type = ShaderPropertyType::Float;
|
||||
Containers::String defaultValue;
|
||||
Containers::String semantic;
|
||||
};
|
||||
|
||||
struct ShaderResourceBindingDesc {
|
||||
Containers::String name;
|
||||
ShaderResourceType type = ShaderResourceType::ConstantBuffer;
|
||||
Core::uint32 set = 0;
|
||||
Core::uint32 binding = 0;
|
||||
Containers::String semantic;
|
||||
};
|
||||
|
||||
struct ShaderStageVariant {
|
||||
ShaderType stage = ShaderType::Fragment;
|
||||
ShaderLanguage language = ShaderLanguage::GLSL;
|
||||
@@ -61,6 +96,7 @@ struct ShaderStageVariant {
|
||||
struct ShaderPass {
|
||||
Containers::String name;
|
||||
Containers::Array<ShaderPassTagEntry> tags;
|
||||
Containers::Array<ShaderResourceBindingDesc> resources;
|
||||
Containers::Array<ShaderStageVariant> variants;
|
||||
};
|
||||
|
||||
@@ -95,6 +131,11 @@ public:
|
||||
void AddAttribute(const ShaderAttribute& attribute);
|
||||
const Containers::Array<ShaderAttribute>& GetAttributes() const { return m_attributes; }
|
||||
|
||||
void AddProperty(const ShaderPropertyDesc& property);
|
||||
void ClearProperties();
|
||||
const Containers::Array<ShaderPropertyDesc>& GetProperties() const { return m_properties; }
|
||||
const ShaderPropertyDesc* FindProperty(const Containers::String& propertyName) const;
|
||||
|
||||
void AddPass(const ShaderPass& pass);
|
||||
void ClearPasses();
|
||||
Core::uint32 GetPassCount() const { return static_cast<Core::uint32>(m_passes.Size()); }
|
||||
@@ -105,9 +146,15 @@ public:
|
||||
const Containers::String& passName,
|
||||
const Containers::String& tagName,
|
||||
const Containers::String& tagValue);
|
||||
void AddPassResourceBinding(
|
||||
const Containers::String& passName,
|
||||
const ShaderResourceBindingDesc& binding);
|
||||
bool HasPass(const Containers::String& passName) const;
|
||||
const ShaderPass* FindPass(const Containers::String& passName) const;
|
||||
ShaderPass* FindPass(const Containers::String& passName);
|
||||
const ShaderResourceBindingDesc* FindPassResourceBinding(
|
||||
const Containers::String& passName,
|
||||
const Containers::String& resourceName) const;
|
||||
const ShaderStageVariant* FindVariant(
|
||||
const Containers::String& passName,
|
||||
ShaderType stage,
|
||||
@@ -129,6 +176,7 @@ private:
|
||||
|
||||
Containers::Array<ShaderUniform> m_uniforms;
|
||||
Containers::Array<ShaderAttribute> m_attributes;
|
||||
Containers::Array<ShaderPropertyDesc> m_properties;
|
||||
Containers::Array<ShaderPass> m_passes;
|
||||
|
||||
class IRHIShader* m_rhiResource = nullptr;
|
||||
|
||||
@@ -617,12 +617,22 @@ Mesh* BuildMeshResource(
|
||||
|
||||
size_t CalculateBuiltinShaderMemorySize(const Shader& shader) {
|
||||
size_t memorySize = sizeof(Shader) + shader.GetName().Length() + shader.GetPath().Length();
|
||||
for (const ShaderPropertyDesc& property : shader.GetProperties()) {
|
||||
memorySize += property.name.Length();
|
||||
memorySize += property.displayName.Length();
|
||||
memorySize += property.defaultValue.Length();
|
||||
memorySize += property.semantic.Length();
|
||||
}
|
||||
for (const ShaderPass& pass : shader.GetPasses()) {
|
||||
memorySize += pass.name.Length();
|
||||
for (const ShaderPassTagEntry& tag : pass.tags) {
|
||||
memorySize += tag.name.Length();
|
||||
memorySize += tag.value.Length();
|
||||
}
|
||||
for (const ShaderResourceBindingDesc& binding : pass.resources) {
|
||||
memorySize += binding.name.Length();
|
||||
memorySize += binding.semantic.Length();
|
||||
}
|
||||
for (const ShaderStageVariant& variant : pass.variants) {
|
||||
memorySize += variant.entryPoint.Length();
|
||||
memorySize += variant.profile.Length();
|
||||
|
||||
@@ -20,6 +20,7 @@ void Shader::Release() {
|
||||
m_compiledBinary.Clear();
|
||||
m_uniforms.Clear();
|
||||
m_attributes.Clear();
|
||||
m_properties.Clear();
|
||||
m_passes.Clear();
|
||||
m_rhiResource = nullptr;
|
||||
m_isValid = false;
|
||||
@@ -53,6 +54,31 @@ void Shader::AddAttribute(const ShaderAttribute& attribute) {
|
||||
m_attributes.PushBack(attribute);
|
||||
}
|
||||
|
||||
void Shader::AddProperty(const ShaderPropertyDesc& property) {
|
||||
for (ShaderPropertyDesc& existingProperty : m_properties) {
|
||||
if (existingProperty.name == property.name) {
|
||||
existingProperty = property;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_properties.PushBack(property);
|
||||
}
|
||||
|
||||
void Shader::ClearProperties() {
|
||||
m_properties.Clear();
|
||||
}
|
||||
|
||||
const ShaderPropertyDesc* Shader::FindProperty(const Containers::String& propertyName) const {
|
||||
for (const ShaderPropertyDesc& property : m_properties) {
|
||||
if (property.name == propertyName) {
|
||||
return &property;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Shader::AddPass(const ShaderPass& pass) {
|
||||
m_passes.PushBack(pass);
|
||||
}
|
||||
@@ -85,6 +111,20 @@ void Shader::SetPassTag(
|
||||
tag.value = tagValue;
|
||||
}
|
||||
|
||||
void Shader::AddPassResourceBinding(
|
||||
const Containers::String& passName,
|
||||
const ShaderResourceBindingDesc& binding) {
|
||||
ShaderPass& pass = GetOrCreatePass(passName);
|
||||
for (ShaderResourceBindingDesc& existingBinding : pass.resources) {
|
||||
if (existingBinding.name == binding.name) {
|
||||
existingBinding = binding;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
pass.resources.PushBack(binding);
|
||||
}
|
||||
|
||||
bool Shader::HasPass(const Containers::String& passName) const {
|
||||
return FindPass(passName) != nullptr;
|
||||
}
|
||||
@@ -109,6 +149,23 @@ ShaderPass* Shader::FindPass(const Containers::String& passName) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const ShaderResourceBindingDesc* Shader::FindPassResourceBinding(
|
||||
const Containers::String& passName,
|
||||
const Containers::String& resourceName) const {
|
||||
const ShaderPass* pass = FindPass(passName);
|
||||
if (pass == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (const ShaderResourceBindingDesc& binding : pass->resources) {
|
||||
if (binding.name == resourceName) {
|
||||
return &binding;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const ShaderStageVariant* Shader::FindVariant(
|
||||
const Containers::String& passName,
|
||||
ShaderType stage,
|
||||
|
||||
@@ -431,6 +431,62 @@ bool TryParseShaderBackend(const Containers::String& value, ShaderBackend& outBa
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TryParseShaderPropertyType(const Containers::String& value, ShaderPropertyType& outType) {
|
||||
const Containers::String normalized = value.Trim().ToLower();
|
||||
if (normalized == "float") {
|
||||
outType = ShaderPropertyType::Float;
|
||||
return true;
|
||||
}
|
||||
if (normalized == "range") {
|
||||
outType = ShaderPropertyType::Range;
|
||||
return true;
|
||||
}
|
||||
if (normalized == "int" || normalized == "integer") {
|
||||
outType = ShaderPropertyType::Int;
|
||||
return true;
|
||||
}
|
||||
if (normalized == "vector" || normalized == "float4") {
|
||||
outType = ShaderPropertyType::Vector;
|
||||
return true;
|
||||
}
|
||||
if (normalized == "color") {
|
||||
outType = ShaderPropertyType::Color;
|
||||
return true;
|
||||
}
|
||||
if (normalized == "2d" || normalized == "texture2d" || normalized == "texture") {
|
||||
outType = ShaderPropertyType::Texture2D;
|
||||
return true;
|
||||
}
|
||||
if (normalized == "cube" || normalized == "cubemap" || normalized == "texturecube") {
|
||||
outType = ShaderPropertyType::TextureCube;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TryParseShaderResourceType(const Containers::String& value, ShaderResourceType& outType) {
|
||||
const Containers::String normalized = value.Trim().ToLower();
|
||||
if (normalized == "constantbuffer" || normalized == "cbuffer" || normalized == "cbv") {
|
||||
outType = ShaderResourceType::ConstantBuffer;
|
||||
return true;
|
||||
}
|
||||
if (normalized == "texture2d" || normalized == "texture" || normalized == "srvtexture2d") {
|
||||
outType = ShaderResourceType::Texture2D;
|
||||
return true;
|
||||
}
|
||||
if (normalized == "texturecube" || normalized == "cubemap") {
|
||||
outType = ShaderResourceType::TextureCube;
|
||||
return true;
|
||||
}
|
||||
if (normalized == "sampler" || normalized == "samplerstate") {
|
||||
outType = ShaderResourceType::Sampler;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Containers::String GetDefaultEntryPoint(ShaderLanguage language, ShaderType stage) {
|
||||
if (language != ShaderLanguage::HLSL) {
|
||||
return Containers::String("main");
|
||||
@@ -520,14 +576,47 @@ bool ReadTextFile(const Containers::String& path, Containers::String& outText) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TryParseUnsignedValue(const std::string& json, const char* key, Core::uint32& outValue) {
|
||||
size_t valuePos = 0;
|
||||
if (!FindValueStart(json, key, valuePos)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t endPos = valuePos;
|
||||
while (endPos < json.size() && std::isdigit(static_cast<unsigned char>(json[endPos])) != 0) {
|
||||
++endPos;
|
||||
}
|
||||
|
||||
if (endPos == valuePos) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
outValue = static_cast<Core::uint32>(std::stoul(json.substr(valuePos, endPos - valuePos)));
|
||||
return true;
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
size_t CalculateShaderMemorySize(const Shader& shader) {
|
||||
size_t memorySize = sizeof(Shader) + shader.GetName().Length() + shader.GetPath().Length();
|
||||
for (const ShaderPropertyDesc& property : shader.GetProperties()) {
|
||||
memorySize += property.name.Length();
|
||||
memorySize += property.displayName.Length();
|
||||
memorySize += property.defaultValue.Length();
|
||||
memorySize += property.semantic.Length();
|
||||
}
|
||||
for (const ShaderPass& pass : shader.GetPasses()) {
|
||||
memorySize += pass.name.Length();
|
||||
for (const ShaderPassTagEntry& tag : pass.tags) {
|
||||
memorySize += tag.name.Length();
|
||||
memorySize += tag.value.Length();
|
||||
}
|
||||
for (const ShaderResourceBindingDesc& binding : pass.resources) {
|
||||
memorySize += binding.name.Length();
|
||||
memorySize += binding.semantic.Length();
|
||||
}
|
||||
for (const ShaderStageVariant& variant : pass.variants) {
|
||||
memorySize += variant.entryPoint.Length();
|
||||
memorySize += variant.profile.Length();
|
||||
@@ -583,6 +672,38 @@ LoadResult LoadShaderManifest(const Containers::String& path, const std::string&
|
||||
|
||||
shader->Initialize(params);
|
||||
|
||||
std::string propertiesArray;
|
||||
if (TryExtractArray(jsonText, "properties", propertiesArray)) {
|
||||
std::vector<std::string> propertyObjects;
|
||||
if (!SplitTopLevelArrayElements(propertiesArray, propertyObjects)) {
|
||||
return LoadResult("Shader manifest properties array could not be parsed: " + path);
|
||||
}
|
||||
|
||||
for (const std::string& propertyObject : propertyObjects) {
|
||||
ShaderPropertyDesc property = {};
|
||||
if (!TryParseStringValue(propertyObject, "name", property.name) || property.name.Empty()) {
|
||||
return LoadResult("Shader manifest property is missing a valid name: " + path);
|
||||
}
|
||||
|
||||
Containers::String propertyTypeName;
|
||||
if (!TryParseStringValue(propertyObject, "type", propertyTypeName) ||
|
||||
!TryParseShaderPropertyType(propertyTypeName, property.type)) {
|
||||
return LoadResult("Shader manifest property has an invalid type: " + path);
|
||||
}
|
||||
|
||||
if (!TryParseStringValue(propertyObject, "displayName", property.displayName)) {
|
||||
property.displayName = property.name;
|
||||
}
|
||||
|
||||
if (!TryParseStringValue(propertyObject, "defaultValue", property.defaultValue)) {
|
||||
TryParseStringValue(propertyObject, "default", property.defaultValue);
|
||||
}
|
||||
|
||||
TryParseStringValue(propertyObject, "semantic", property.semantic);
|
||||
shader->AddProperty(property);
|
||||
}
|
||||
}
|
||||
|
||||
for (const std::string& passObject : passObjects) {
|
||||
Containers::String passName;
|
||||
if (!TryParseStringValue(passObject, "name", passName) || passName.Empty()) {
|
||||
@@ -600,6 +721,38 @@ LoadResult LoadShaderManifest(const Containers::String& path, const std::string&
|
||||
}
|
||||
}
|
||||
|
||||
std::string resourcesArray;
|
||||
if (TryExtractArray(passObject, "resources", resourcesArray)) {
|
||||
std::vector<std::string> resourceObjects;
|
||||
if (!SplitTopLevelArrayElements(resourcesArray, resourceObjects)) {
|
||||
return LoadResult("Shader manifest pass resources could not be parsed: " + path);
|
||||
}
|
||||
|
||||
for (const std::string& resourceObject : resourceObjects) {
|
||||
ShaderResourceBindingDesc resourceBinding = {};
|
||||
if (!TryParseStringValue(resourceObject, "name", resourceBinding.name) ||
|
||||
resourceBinding.name.Empty()) {
|
||||
return LoadResult("Shader manifest pass resource is missing a valid name: " + path);
|
||||
}
|
||||
|
||||
Containers::String resourceTypeName;
|
||||
if (!TryParseStringValue(resourceObject, "type", resourceTypeName) ||
|
||||
!TryParseShaderResourceType(resourceTypeName, resourceBinding.type)) {
|
||||
return LoadResult("Shader manifest pass resource has an invalid type: " + path);
|
||||
}
|
||||
|
||||
if (!TryParseUnsignedValue(resourceObject, "set", resourceBinding.set)) {
|
||||
return LoadResult("Shader manifest pass resource is missing a valid set: " + path);
|
||||
}
|
||||
if (!TryParseUnsignedValue(resourceObject, "binding", resourceBinding.binding)) {
|
||||
return LoadResult("Shader manifest pass resource is missing a valid binding: " + path);
|
||||
}
|
||||
|
||||
TryParseStringValue(resourceObject, "semantic", resourceBinding.semantic);
|
||||
shader->AddPassResourceBinding(passName, resourceBinding);
|
||||
}
|
||||
}
|
||||
|
||||
std::string variantsArray;
|
||||
if (!TryExtractArray(passObject, "variants", variantsArray)) {
|
||||
return LoadResult("Shader manifest pass is missing variants: " + path);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user