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

@@ -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);