From 4ddb5b64281fe6ee412b7f4ccca566135b4c2896 Mon Sep 17 00:00:00 2001 From: Aiden Date: Thu, 21 May 2026 17:48:27 +1000 Subject: [PATCH] split shaders --- src/shader/ShaderManifestAssets.cpp | 91 ++++ src/shader/ShaderManifestParameters.cpp | 228 ++++++++ src/shader/ShaderManifestParser.cpp | 370 +++++++++++++ src/shader/ShaderManifestParser.h | 31 ++ src/shader/ShaderPackageRegistry.cpp | 672 +----------------------- tests/CMakeLists.txt | 18 + 6 files changed, 740 insertions(+), 670 deletions(-) create mode 100644 src/shader/ShaderManifestAssets.cpp create mode 100644 src/shader/ShaderManifestParameters.cpp create mode 100644 src/shader/ShaderManifestParser.cpp create mode 100644 src/shader/ShaderManifestParser.h diff --git a/src/shader/ShaderManifestAssets.cpp b/src/shader/ShaderManifestAssets.cpp new file mode 100644 index 0000000..1e7cd3c --- /dev/null +++ b/src/shader/ShaderManifestAssets.cpp @@ -0,0 +1,91 @@ +#include "stdafx.h" +#include "ShaderManifestParser.h" + +namespace ShaderManifestParsing +{ +bool ParseTextureAssets(const JsonValue& manifestJson, ShaderPackage& shaderPackage, const std::filesystem::path& manifestPath, std::string& error) +{ + const JsonValue* texturesValue = nullptr; + if (!OptionalArrayField(manifestJson, "textures", texturesValue, manifestPath, error)) + return false; + if (!texturesValue) + return true; + + for (const JsonValue& textureJson : texturesValue->asArray()) + { + if (!textureJson.isObject()) + { + error = "Shader texture entry must be an object in: " + ManifestPathMessage(manifestPath); + return false; + } + + std::string textureId; + std::string texturePath; + if (!RequireNonEmptyStringField(textureJson, "id", textureId, manifestPath, error) || + !RequireNonEmptyStringField(textureJson, "path", texturePath, manifestPath, error)) + { + error = "Shader texture is missing required 'id' or 'path' in: " + ManifestPathMessage(manifestPath); + return false; + } + if (!ValidateShaderIdentifier(textureId, "textures[].id", manifestPath, error)) + return false; + + ShaderTextureAsset textureAsset; + textureAsset.id = textureId; + textureAsset.path = shaderPackage.directoryPath / texturePath; + if (!std::filesystem::exists(textureAsset.path)) + { + error = "Shader texture asset not found for package " + shaderPackage.id + ": " + textureAsset.path.string(); + return false; + } + + textureAsset.writeTime = std::filesystem::last_write_time(textureAsset.path); + shaderPackage.textureAssets.push_back(textureAsset); + } + + return true; +} + +bool ParseFontAssets(const JsonValue& manifestJson, ShaderPackage& shaderPackage, const std::filesystem::path& manifestPath, std::string& error) +{ + const JsonValue* fontsValue = nullptr; + if (!OptionalArrayField(manifestJson, "fonts", fontsValue, manifestPath, error)) + return false; + if (!fontsValue) + return true; + + for (const JsonValue& fontJson : fontsValue->asArray()) + { + if (!fontJson.isObject()) + { + error = "Shader font entry must be an object in: " + ManifestPathMessage(manifestPath); + return false; + } + + std::string fontId; + std::string fontPath; + if (!RequireNonEmptyStringField(fontJson, "id", fontId, manifestPath, error) || + !RequireNonEmptyStringField(fontJson, "path", fontPath, manifestPath, error)) + { + error = "Shader font is missing required 'id' or 'path' in: " + ManifestPathMessage(manifestPath); + return false; + } + if (!ValidateShaderIdentifier(fontId, "fonts[].id", manifestPath, error)) + return false; + + ShaderFontAsset fontAsset; + fontAsset.id = fontId; + fontAsset.path = shaderPackage.directoryPath / fontPath; + if (!std::filesystem::exists(fontAsset.path)) + { + error = "Shader font asset not found for package " + shaderPackage.id + ": " + fontAsset.path.string(); + return false; + } + + fontAsset.writeTime = std::filesystem::last_write_time(fontAsset.path); + shaderPackage.fontAssets.push_back(fontAsset); + } + + return true; +} +} diff --git a/src/shader/ShaderManifestParameters.cpp b/src/shader/ShaderManifestParameters.cpp new file mode 100644 index 0000000..4c7b1c7 --- /dev/null +++ b/src/shader/ShaderManifestParameters.cpp @@ -0,0 +1,228 @@ +#include "stdafx.h" +#include "ShaderManifestParser.h" + +namespace ShaderManifestParsing +{ +namespace +{ +bool ParseShaderParameterType(const std::string& typeName, ShaderParameterType& type) +{ + if (typeName == "float") + { + type = ShaderParameterType::Float; + return true; + } + if (typeName == "vec2") + { + type = ShaderParameterType::Vec2; + return true; + } + if (typeName == "color") + { + type = ShaderParameterType::Color; + return true; + } + if (typeName == "bool") + { + type = ShaderParameterType::Boolean; + return true; + } + if (typeName == "enum") + { + type = ShaderParameterType::Enum; + return true; + } + if (typeName == "text") + { + type = ShaderParameterType::Text; + return true; + } + if (typeName == "trigger") + { + type = ShaderParameterType::Trigger; + return true; + } + return false; +} + +bool ParseParameterNumberField(const JsonValue& parameterJson, const char* fieldName, std::vector& values, const std::filesystem::path& manifestPath, std::string& error) +{ + if (const JsonValue* fieldValue = parameterJson.find(fieldName)) + return NumberListFromJsonValue(*fieldValue, values, fieldName, manifestPath, error); + return true; +} + +bool ParseParameterDefault(const JsonValue& parameterJson, ShaderParameterDefinition& definition, const std::filesystem::path& manifestPath, std::string& error) +{ + const JsonValue* defaultValue = parameterJson.find("default"); + if (!defaultValue) + return true; + + if (definition.type == ShaderParameterType::Boolean) + { + if (!defaultValue->isBoolean()) + { + error = "Boolean parameter default must be a boolean for: " + definition.id; + return false; + } + definition.defaultBoolean = defaultValue->asBoolean(false); + return true; + } + + if (definition.type == ShaderParameterType::Enum) + { + if (!defaultValue->isString()) + { + error = "Enum parameter default must be a string for: " + definition.id; + return false; + } + definition.defaultEnumValue = defaultValue->asString(); + return true; + } + + if (definition.type == ShaderParameterType::Text) + { + if (!defaultValue->isString()) + { + error = "Text parameter default must be a string for: " + definition.id; + return false; + } + definition.defaultTextValue = defaultValue->asString(); + return true; + } + + return NumberListFromJsonValue(*defaultValue, definition.defaultNumbers, "default", manifestPath, error); +} + +bool ParseParameterOptions(const JsonValue& parameterJson, ShaderParameterDefinition& definition, const std::filesystem::path& manifestPath, std::string& error) +{ + const JsonValue* optionsValue = nullptr; + if (!OptionalArrayField(parameterJson, "options", optionsValue, manifestPath, error) || !optionsValue) + { + error = "Enum parameter is missing 'options' in: " + ManifestPathMessage(manifestPath); + return false; + } + + for (const JsonValue& optionJson : optionsValue->asArray()) + { + if (!optionJson.isObject()) + { + error = "Enum parameter option must be an object in: " + ManifestPathMessage(manifestPath); + return false; + } + + ShaderParameterOption option; + if (!RequireStringField(optionJson, "value", option.value, manifestPath, error) || + !RequireStringField(optionJson, "label", option.label, manifestPath, error)) + { + error = "Enum parameter option is missing 'value' or 'label' in: " + ManifestPathMessage(manifestPath); + return false; + } + definition.enumOptions.push_back(option); + } + + bool defaultFound = definition.defaultEnumValue.empty(); + for (const ShaderParameterOption& option : definition.enumOptions) + { + if (option.value == definition.defaultEnumValue) + { + defaultFound = true; + break; + } + } + + if (!defaultFound) + { + error = "Enum parameter default is not present in its option list for: " + definition.id; + return false; + } + + return true; +} + +bool ParseParameterDefinition(const JsonValue& parameterJson, ShaderParameterDefinition& definition, const std::filesystem::path& manifestPath, std::string& error) +{ + if (!parameterJson.isObject()) + { + error = "Shader parameter entry must be an object in: " + ManifestPathMessage(manifestPath); + return false; + } + + std::string typeName; + if (!RequireStringField(parameterJson, "id", definition.id, manifestPath, error) || + !RequireStringField(parameterJson, "label", definition.label, manifestPath, error) || + !RequireStringField(parameterJson, "type", typeName, manifestPath, error)) + { + error = "Shader parameter is missing required fields in: " + ManifestPathMessage(manifestPath); + return false; + } + + if (!ParseShaderParameterType(typeName, definition.type)) + { + error = "Unsupported parameter type '" + typeName + "' in: " + ManifestPathMessage(manifestPath); + return false; + } + if (!ValidateShaderIdentifier(definition.id, "parameters[].id", manifestPath, error)) + return false; + + if (!OptionalStringField(parameterJson, "description", definition.description, "", manifestPath, error)) + return false; + + if (!ParseParameterDefault(parameterJson, definition, manifestPath, error) || + !ParseParameterNumberField(parameterJson, "min", definition.minNumbers, manifestPath, error) || + !ParseParameterNumberField(parameterJson, "max", definition.maxNumbers, manifestPath, error) || + !ParseParameterNumberField(parameterJson, "step", definition.stepNumbers, manifestPath, error)) + { + return false; + } + + if (definition.type == ShaderParameterType::Text) + { + if (const JsonValue* fontValue = parameterJson.find("font")) + { + if (!fontValue->isString()) + { + error = "Text parameter 'font' must be a string for: " + definition.id; + return false; + } + definition.fontId = fontValue->asString(); + if (!definition.fontId.empty() && !ValidateShaderIdentifier(definition.fontId, "parameters[].font", manifestPath, error)) + return false; + } + if (const JsonValue* maxLengthValue = parameterJson.find("maxLength")) + { + if (!maxLengthValue->isNumber() || maxLengthValue->asNumber() < 1.0 || maxLengthValue->asNumber() > 256.0) + { + error = "Text parameter 'maxLength' must be a number from 1 to 256 for: " + definition.id; + return false; + } + definition.maxLength = static_cast(maxLengthValue->asNumber()); + } + } + + if (definition.type == ShaderParameterType::Enum) + return ParseParameterOptions(parameterJson, definition, manifestPath, error); + + return true; +} +} + +bool ParseParameterDefinitions(const JsonValue& manifestJson, ShaderPackage& shaderPackage, const std::filesystem::path& manifestPath, std::string& error) +{ + const JsonValue* parametersValue = nullptr; + if (!OptionalArrayField(manifestJson, "parameters", parametersValue, manifestPath, error)) + return false; + if (!parametersValue) + return true; + + for (const JsonValue& parameterJson : parametersValue->asArray()) + { + ShaderParameterDefinition definition; + if (!ParseParameterDefinition(parameterJson, definition, manifestPath, error)) + return false; + shaderPackage.parameters.push_back(definition); + } + + return true; +} +} diff --git a/src/shader/ShaderManifestParser.cpp b/src/shader/ShaderManifestParser.cpp new file mode 100644 index 0000000..9cda48c --- /dev/null +++ b/src/shader/ShaderManifestParser.cpp @@ -0,0 +1,370 @@ +#include "stdafx.h" +#include "ShaderManifestParser.h" + +#include +#include +#include + +namespace ShaderManifestParsing +{ +std::string Trim(const std::string& text) +{ + std::size_t start = 0; + while (start < text.size() && std::isspace(static_cast(text[start]))) + ++start; + + std::size_t end = text.size(); + while (end > start && std::isspace(static_cast(text[end - 1]))) + --end; + + return text.substr(start, end - start); +} + +bool IsFiniteNumber(double value) +{ + return std::isfinite(value) != 0; +} + +namespace +{ +bool ParseTemporalHistorySource(const std::string& sourceName, TemporalHistorySource& source) +{ + if (sourceName == "source") + { + source = TemporalHistorySource::Source; + return true; + } + if (sourceName == "preLayerInput") + { + source = TemporalHistorySource::PreLayerInput; + return true; + } + if (sourceName == "none") + { + source = TemporalHistorySource::None; + return true; + } + return false; +} +} + +std::string ManifestPathMessage(const std::filesystem::path& manifestPath) +{ + return manifestPath.string(); +} + +bool RequireStringField(const JsonValue& object, const char* fieldName, std::string& value, const std::filesystem::path& manifestPath, std::string& error) +{ + const JsonValue* fieldValue = object.find(fieldName); + if (!fieldValue || !fieldValue->isString()) + { + error = "Shader manifest is missing required string '" + std::string(fieldName) + "' field: " + ManifestPathMessage(manifestPath); + return false; + } + + value = fieldValue->asString(); + return true; +} + +bool RequireNonEmptyStringField(const JsonValue& object, const char* fieldName, std::string& value, const std::filesystem::path& manifestPath, std::string& error) +{ + if (!RequireStringField(object, fieldName, value, manifestPath, error)) + return false; + if (Trim(value).empty()) + { + error = "Shader manifest string '" + std::string(fieldName) + "' must not be empty: " + ManifestPathMessage(manifestPath); + return false; + } + return true; +} + +bool OptionalStringField(const JsonValue& object, const char* fieldName, std::string& value, const std::string& fallback, const std::filesystem::path& manifestPath, std::string& error) +{ + const JsonValue* fieldValue = object.find(fieldName); + if (!fieldValue) + { + value = fallback; + return true; + } + if (!fieldValue->isString()) + { + error = "Shader manifest field '" + std::string(fieldName) + "' must be a string in: " + ManifestPathMessage(manifestPath); + return false; + } + value = fieldValue->asString(); + return true; +} + +bool OptionalArrayField(const JsonValue& object, const char* fieldName, const JsonValue*& value, const std::filesystem::path& manifestPath, std::string& error) +{ + value = object.find(fieldName); + if (!value) + return true; + if (!value->isArray()) + { + error = "Shader manifest '" + std::string(fieldName) + "' field must be an array in: " + ManifestPathMessage(manifestPath); + return false; + } + return true; +} + +bool OptionalObjectField(const JsonValue& object, const char* fieldName, const JsonValue*& value, const std::filesystem::path& manifestPath, std::string& error) +{ + value = object.find(fieldName); + if (!value) + return true; + if (!value->isObject()) + { + error = "Shader manifest '" + std::string(fieldName) + "' field must be an object in: " + ManifestPathMessage(manifestPath); + return false; + } + return true; +} + +bool NumberListFromJsonValue(const JsonValue& value, std::vector& numbers, const std::string& fieldName, const std::filesystem::path& manifestPath, std::string& error) +{ + if (value.isNumber()) + { + numbers.push_back(value.asNumber()); + return true; + } + if (value.isArray()) + { + numbers = JsonArrayToNumbers(value); + if (numbers.size() != value.asArray().size()) + { + error = "Shader parameter field '" + fieldName + "' must contain only numbers in: " + ManifestPathMessage(manifestPath); + return false; + } + return true; + } + + error = "Shader parameter field '" + fieldName + "' must be a number or array of numbers in: " + ManifestPathMessage(manifestPath); + return false; +} + +bool ValidateShaderIdentifier(const std::string& identifier, const std::string& fieldName, const std::filesystem::path& manifestPath, std::string& error) +{ + if (identifier.empty() || !(std::isalpha(static_cast(identifier.front())) || identifier.front() == '_')) + { + error = "Shader manifest field '" + fieldName + "' must be a valid shader identifier in: " + ManifestPathMessage(manifestPath); + return false; + } + + for (char ch : identifier) + { + const unsigned char unsignedCh = static_cast(ch); + if (!(std::isalnum(unsignedCh) || ch == '_')) + { + error = "Shader manifest field '" + fieldName + "' must be a valid shader identifier in: " + ManifestPathMessage(manifestPath); + return false; + } + } + + return true; +} + +bool ParseShaderMetadata(const JsonValue& manifestJson, ShaderPackage& shaderPackage, const std::filesystem::path& manifestPath, std::string& error) +{ + if (!RequireStringField(manifestJson, "id", shaderPackage.id, manifestPath, error) || + !RequireStringField(manifestJson, "name", shaderPackage.displayName, manifestPath, error) || + !OptionalStringField(manifestJson, "description", shaderPackage.description, "", manifestPath, error) || + !OptionalStringField(manifestJson, "category", shaderPackage.category, "", manifestPath, error) || + !OptionalStringField(manifestJson, "entryPoint", shaderPackage.entryPoint, "shadeVideo", manifestPath, error)) + { + return false; + } + + if (!ValidateShaderIdentifier(shaderPackage.entryPoint, "entryPoint", manifestPath, error)) + return false; + + shaderPackage.directoryPath = manifestPath.parent_path(); + shaderPackage.shaderPath = shaderPackage.directoryPath / "shader.slang"; + shaderPackage.manifestPath = manifestPath; + return true; +} + +bool ParsePassDefinitions(const JsonValue& manifestJson, ShaderPackage& shaderPackage, const std::filesystem::path& manifestPath, std::string& error) +{ + const JsonValue* passesValue = nullptr; + if (!OptionalArrayField(manifestJson, "passes", passesValue, manifestPath, error)) + return false; + + if (!passesValue) + { + ShaderPassDefinition pass; + pass.id = "main"; + pass.entryPoint = shaderPackage.entryPoint; + pass.sourcePath = shaderPackage.shaderPath; + pass.outputName = "layerOutput"; + if (!std::filesystem::exists(pass.sourcePath)) + { + error = "Shader source not found for package " + shaderPackage.id + ": " + pass.sourcePath.string(); + return false; + } + pass.sourceWriteTime = std::filesystem::last_write_time(pass.sourcePath); + shaderPackage.passes.push_back(pass); + return true; + } + + if (passesValue->asArray().empty()) + { + error = "Shader manifest 'passes' field must not be empty in: " + ManifestPathMessage(manifestPath); + return false; + } + + for (const JsonValue& passJson : passesValue->asArray()) + { + if (!passJson.isObject()) + { + error = "Shader pass entry must be an object in: " + ManifestPathMessage(manifestPath); + return false; + } + + std::string passId; + std::string sourcePath; + if (!RequireNonEmptyStringField(passJson, "id", passId, manifestPath, error) || + !RequireNonEmptyStringField(passJson, "source", sourcePath, manifestPath, error)) + { + error = "Shader pass is missing required 'id' or 'source' in: " + ManifestPathMessage(manifestPath); + return false; + } + if (!ValidateShaderIdentifier(passId, "passes[].id", manifestPath, error)) + return false; + + for (const ShaderPassDefinition& existingPass : shaderPackage.passes) + { + if (existingPass.id == passId) + { + error = "Duplicate shader pass id '" + passId + "' in: " + ManifestPathMessage(manifestPath); + return false; + } + } + + ShaderPassDefinition pass; + pass.id = passId; + pass.sourcePath = shaderPackage.directoryPath / sourcePath; + if (!OptionalStringField(passJson, "entryPoint", pass.entryPoint, shaderPackage.entryPoint, manifestPath, error) || + !OptionalStringField(passJson, "output", pass.outputName, passId, manifestPath, error)) + { + return false; + } + if (!ValidateShaderIdentifier(pass.entryPoint, "passes[].entryPoint", manifestPath, error)) + return false; + + const JsonValue* inputsValue = nullptr; + if (!OptionalArrayField(passJson, "inputs", inputsValue, manifestPath, error)) + return false; + if (inputsValue) + { + for (const JsonValue& inputValue : inputsValue->asArray()) + { + if (!inputValue.isString()) + { + error = "Shader pass inputs must be strings in: " + ManifestPathMessage(manifestPath); + return false; + } + pass.inputNames.push_back(inputValue.asString()); + } + } + + if (!std::filesystem::exists(pass.sourcePath)) + { + error = "Shader pass source not found for package " + shaderPackage.id + ": " + pass.sourcePath.string(); + return false; + } + pass.sourceWriteTime = std::filesystem::last_write_time(pass.sourcePath); + shaderPackage.passes.push_back(pass); + } + + shaderPackage.shaderPath = shaderPackage.passes.front().sourcePath; + return true; +} + +bool ParseTemporalSettings(const JsonValue& manifestJson, ShaderPackage& shaderPackage, unsigned maxTemporalHistoryFrames, const std::filesystem::path& manifestPath, std::string& error) +{ + const JsonValue* temporalValue = nullptr; + if (!OptionalObjectField(manifestJson, "temporal", temporalValue, manifestPath, error)) + return false; + if (!temporalValue) + return true; + + const JsonValue* enabledValue = temporalValue->find("enabled"); + if (!enabledValue || !enabledValue->asBoolean(false)) + return true; + + std::string historySourceName; + if (!RequireNonEmptyStringField(*temporalValue, "historySource", historySourceName, manifestPath, error)) + { + error = "Temporal shader is missing required 'historySource' in: " + ManifestPathMessage(manifestPath); + return false; + } + + const JsonValue* historyLengthValue = temporalValue->find("historyLength"); + if (!historyLengthValue || !historyLengthValue->isNumber()) + { + error = "Temporal shader is missing required numeric 'historyLength' in: " + ManifestPathMessage(manifestPath); + return false; + } + + TemporalHistorySource historySource = TemporalHistorySource::None; + if (!ParseTemporalHistorySource(historySourceName, historySource)) + { + error = "Unsupported temporal historySource '" + historySourceName + "' in: " + ManifestPathMessage(manifestPath); + return false; + } + + const double requestedHistoryLength = historyLengthValue->asNumber(); + if (!IsFiniteNumber(requestedHistoryLength) || requestedHistoryLength <= 0.0 || std::floor(requestedHistoryLength) != requestedHistoryLength) + { + error = "Temporal shader 'historyLength' must be a positive integer in: " + ManifestPathMessage(manifestPath); + return false; + } + + shaderPackage.temporal.enabled = true; + shaderPackage.temporal.historySource = historySource; + shaderPackage.temporal.requestedHistoryLength = static_cast(requestedHistoryLength); + shaderPackage.temporal.effectiveHistoryLength = std::min(shaderPackage.temporal.requestedHistoryLength, maxTemporalHistoryFrames); + return true; +} + +bool ParseFeedbackSettings(const JsonValue& manifestJson, ShaderPackage& shaderPackage, const std::filesystem::path& manifestPath, std::string& error) +{ + const JsonValue* feedbackValue = nullptr; + if (!OptionalObjectField(manifestJson, "feedback", feedbackValue, manifestPath, error)) + return false; + if (!feedbackValue) + return true; + + const JsonValue* enabledValue = feedbackValue->find("enabled"); + if (!enabledValue || !enabledValue->asBoolean(false)) + return true; + + shaderPackage.feedback.enabled = true; + if (!OptionalStringField(*feedbackValue, "writePass", shaderPackage.feedback.writePassId, "", manifestPath, error)) + return false; + + if (shaderPackage.feedback.writePassId.empty()) + { + if (shaderPackage.passes.empty()) + { + error = "Feedback-enabled shader has no passes to target in: " + ManifestPathMessage(manifestPath); + return false; + } + shaderPackage.feedback.writePassId = shaderPackage.passes.back().id; + } + + if (!ValidateShaderIdentifier(shaderPackage.feedback.writePassId, "feedback.writePass", manifestPath, error)) + return false; + + const auto passIt = std::find_if(shaderPackage.passes.begin(), shaderPackage.passes.end(), + [&shaderPackage](const ShaderPassDefinition& pass) { return pass.id == shaderPackage.feedback.writePassId; }); + if (passIt == shaderPackage.passes.end()) + { + error = "Feedback writePass '" + shaderPackage.feedback.writePassId + "' does not match any declared pass in: " + ManifestPathMessage(manifestPath); + return false; + } + + return true; +} +} diff --git a/src/shader/ShaderManifestParser.h b/src/shader/ShaderManifestParser.h new file mode 100644 index 0000000..3633263 --- /dev/null +++ b/src/shader/ShaderManifestParser.h @@ -0,0 +1,31 @@ +#pragma once + +#include "RuntimeJson.h" +#include "ShaderTypes.h" + +#include +#include +#include + +namespace ShaderManifestParsing +{ +std::string Trim(const std::string& text); +bool IsFiniteNumber(double value); +std::string ManifestPathMessage(const std::filesystem::path& manifestPath); + +bool RequireStringField(const JsonValue& object, const char* fieldName, std::string& value, const std::filesystem::path& manifestPath, std::string& error); +bool RequireNonEmptyStringField(const JsonValue& object, const char* fieldName, std::string& value, const std::filesystem::path& manifestPath, std::string& error); +bool OptionalStringField(const JsonValue& object, const char* fieldName, std::string& value, const std::string& fallback, const std::filesystem::path& manifestPath, std::string& error); +bool OptionalArrayField(const JsonValue& object, const char* fieldName, const JsonValue*& value, const std::filesystem::path& manifestPath, std::string& error); +bool OptionalObjectField(const JsonValue& object, const char* fieldName, const JsonValue*& value, const std::filesystem::path& manifestPath, std::string& error); +bool NumberListFromJsonValue(const JsonValue& value, std::vector& numbers, const std::string& fieldName, const std::filesystem::path& manifestPath, std::string& error); +bool ValidateShaderIdentifier(const std::string& identifier, const std::string& fieldName, const std::filesystem::path& manifestPath, std::string& error); + +bool ParseShaderMetadata(const JsonValue& manifestJson, ShaderPackage& shaderPackage, const std::filesystem::path& manifestPath, std::string& error); +bool ParsePassDefinitions(const JsonValue& manifestJson, ShaderPackage& shaderPackage, const std::filesystem::path& manifestPath, std::string& error); +bool ParseTemporalSettings(const JsonValue& manifestJson, ShaderPackage& shaderPackage, unsigned maxTemporalHistoryFrames, const std::filesystem::path& manifestPath, std::string& error); +bool ParseFeedbackSettings(const JsonValue& manifestJson, ShaderPackage& shaderPackage, const std::filesystem::path& manifestPath, std::string& error); +bool ParseTextureAssets(const JsonValue& manifestJson, ShaderPackage& shaderPackage, const std::filesystem::path& manifestPath, std::string& error); +bool ParseFontAssets(const JsonValue& manifestJson, ShaderPackage& shaderPackage, const std::filesystem::path& manifestPath, std::string& error); +bool ParseParameterDefinitions(const JsonValue& manifestJson, ShaderPackage& shaderPackage, const std::filesystem::path& manifestPath, std::string& error); +} diff --git a/src/shader/ShaderPackageRegistry.cpp b/src/shader/ShaderPackageRegistry.cpp index 59fecbe..6f8b982 100644 --- a/src/shader/ShaderPackageRegistry.cpp +++ b/src/shader/ShaderPackageRegistry.cpp @@ -2,93 +2,14 @@ #include "ShaderPackageRegistry.h" #include "RuntimeJson.h" +#include "ShaderManifestParser.h" #include -#include -#include #include #include namespace { -std::string Trim(const std::string& text) -{ - std::size_t start = 0; - while (start < text.size() && std::isspace(static_cast(text[start]))) - ++start; - - std::size_t end = text.size(); - while (end > start && std::isspace(static_cast(text[end - 1]))) - --end; - - return text.substr(start, end - start); -} - -bool IsFiniteNumber(double value) -{ - return std::isfinite(value) != 0; -} - -bool ParseShaderParameterType(const std::string& typeName, ShaderParameterType& type) -{ - if (typeName == "float") - { - type = ShaderParameterType::Float; - return true; - } - if (typeName == "vec2") - { - type = ShaderParameterType::Vec2; - return true; - } - if (typeName == "color") - { - type = ShaderParameterType::Color; - return true; - } - if (typeName == "bool") - { - type = ShaderParameterType::Boolean; - return true; - } - if (typeName == "enum") - { - type = ShaderParameterType::Enum; - return true; - } - if (typeName == "text") - { - type = ShaderParameterType::Text; - return true; - } - if (typeName == "trigger") - { - type = ShaderParameterType::Trigger; - return true; - } - return false; -} - -bool ParseTemporalHistorySource(const std::string& sourceName, TemporalHistorySource& source) -{ - if (sourceName == "source") - { - source = TemporalHistorySource::Source; - return true; - } - if (sourceName == "preLayerInput") - { - source = TemporalHistorySource::PreLayerInput; - return true; - } - if (sourceName == "none") - { - source = TemporalHistorySource::None; - return true; - } - return false; -} - std::string ReadTextFile(const std::filesystem::path& path, std::string& error) { std::ifstream input(path, std::ios::binary); @@ -103,596 +24,6 @@ std::string ReadTextFile(const std::filesystem::path& path, std::string& error) return buffer.str(); } -std::string ManifestPathMessage(const std::filesystem::path& manifestPath) -{ - return manifestPath.string(); -} - -bool RequireStringField(const JsonValue& object, const char* fieldName, std::string& value, const std::filesystem::path& manifestPath, std::string& error) -{ - const JsonValue* fieldValue = object.find(fieldName); - if (!fieldValue || !fieldValue->isString()) - { - error = "Shader manifest is missing required string '" + std::string(fieldName) + "' field: " + ManifestPathMessage(manifestPath); - return false; - } - - value = fieldValue->asString(); - return true; -} - -bool RequireNonEmptyStringField(const JsonValue& object, const char* fieldName, std::string& value, const std::filesystem::path& manifestPath, std::string& error) -{ - if (!RequireStringField(object, fieldName, value, manifestPath, error)) - return false; - if (Trim(value).empty()) - { - error = "Shader manifest string '" + std::string(fieldName) + "' must not be empty: " + ManifestPathMessage(manifestPath); - return false; - } - return true; -} - -bool OptionalStringField(const JsonValue& object, const char* fieldName, std::string& value, const std::string& fallback, const std::filesystem::path& manifestPath, std::string& error) -{ - const JsonValue* fieldValue = object.find(fieldName); - if (!fieldValue) - { - value = fallback; - return true; - } - if (!fieldValue->isString()) - { - error = "Shader manifest field '" + std::string(fieldName) + "' must be a string in: " + ManifestPathMessage(manifestPath); - return false; - } - value = fieldValue->asString(); - return true; -} - -bool OptionalArrayField(const JsonValue& object, const char* fieldName, const JsonValue*& value, const std::filesystem::path& manifestPath, std::string& error) -{ - value = object.find(fieldName); - if (!value) - return true; - if (!value->isArray()) - { - error = "Shader manifest '" + std::string(fieldName) + "' field must be an array in: " + ManifestPathMessage(manifestPath); - return false; - } - return true; -} - -bool OptionalObjectField(const JsonValue& object, const char* fieldName, const JsonValue*& value, const std::filesystem::path& manifestPath, std::string& error) -{ - value = object.find(fieldName); - if (!value) - return true; - if (!value->isObject()) - { - error = "Shader manifest '" + std::string(fieldName) + "' field must be an object in: " + ManifestPathMessage(manifestPath); - return false; - } - return true; -} - -bool NumberListFromJsonValue(const JsonValue& value, std::vector& numbers, const std::string& fieldName, const std::filesystem::path& manifestPath, std::string& error) -{ - if (value.isNumber()) - { - numbers.push_back(value.asNumber()); - return true; - } - if (value.isArray()) - { - numbers = JsonArrayToNumbers(value); - if (numbers.size() != value.asArray().size()) - { - error = "Shader parameter field '" + fieldName + "' must contain only numbers in: " + ManifestPathMessage(manifestPath); - return false; - } - return true; - } - - error = "Shader parameter field '" + fieldName + "' must be a number or array of numbers in: " + ManifestPathMessage(manifestPath); - return false; -} - -bool ValidateShaderIdentifier(const std::string& identifier, const std::string& fieldName, const std::filesystem::path& manifestPath, std::string& error) -{ - if (identifier.empty() || !(std::isalpha(static_cast(identifier.front())) || identifier.front() == '_')) - { - error = "Shader manifest field '" + fieldName + "' must be a valid shader identifier in: " + ManifestPathMessage(manifestPath); - return false; - } - - for (char ch : identifier) - { - const unsigned char unsignedCh = static_cast(ch); - if (!(std::isalnum(unsignedCh) || ch == '_')) - { - error = "Shader manifest field '" + fieldName + "' must be a valid shader identifier in: " + ManifestPathMessage(manifestPath); - return false; - } - } - - return true; -} - -bool ParseShaderMetadata(const JsonValue& manifestJson, ShaderPackage& shaderPackage, const std::filesystem::path& manifestPath, std::string& error) -{ - if (!RequireStringField(manifestJson, "id", shaderPackage.id, manifestPath, error) || - !RequireStringField(manifestJson, "name", shaderPackage.displayName, manifestPath, error) || - !OptionalStringField(manifestJson, "description", shaderPackage.description, "", manifestPath, error) || - !OptionalStringField(manifestJson, "category", shaderPackage.category, "", manifestPath, error) || - !OptionalStringField(manifestJson, "entryPoint", shaderPackage.entryPoint, "shadeVideo", manifestPath, error)) - { - return false; - } - - if (!ValidateShaderIdentifier(shaderPackage.entryPoint, "entryPoint", manifestPath, error)) - return false; - - shaderPackage.directoryPath = manifestPath.parent_path(); - shaderPackage.shaderPath = shaderPackage.directoryPath / "shader.slang"; - shaderPackage.manifestPath = manifestPath; - return true; -} - -bool ParsePassDefinitions(const JsonValue& manifestJson, ShaderPackage& shaderPackage, const std::filesystem::path& manifestPath, std::string& error) -{ - const JsonValue* passesValue = nullptr; - if (!OptionalArrayField(manifestJson, "passes", passesValue, manifestPath, error)) - return false; - - if (!passesValue) - { - // Existing shader packages are treated as a single implicit pass, so - // multipass support does not require manifest churn. - ShaderPassDefinition pass; - pass.id = "main"; - pass.entryPoint = shaderPackage.entryPoint; - pass.sourcePath = shaderPackage.shaderPath; - pass.outputName = "layerOutput"; - if (!std::filesystem::exists(pass.sourcePath)) - { - error = "Shader source not found for package " + shaderPackage.id + ": " + pass.sourcePath.string(); - return false; - } - pass.sourceWriteTime = std::filesystem::last_write_time(pass.sourcePath); - shaderPackage.passes.push_back(pass); - return true; - } - - if (passesValue->asArray().empty()) - { - error = "Shader manifest 'passes' field must not be empty in: " + ManifestPathMessage(manifestPath); - return false; - } - - for (const JsonValue& passJson : passesValue->asArray()) - { - if (!passJson.isObject()) - { - error = "Shader pass entry must be an object in: " + ManifestPathMessage(manifestPath); - return false; - } - - std::string passId; - std::string sourcePath; - if (!RequireNonEmptyStringField(passJson, "id", passId, manifestPath, error) || - !RequireNonEmptyStringField(passJson, "source", sourcePath, manifestPath, error)) - { - error = "Shader pass is missing required 'id' or 'source' in: " + ManifestPathMessage(manifestPath); - return false; - } - if (!ValidateShaderIdentifier(passId, "passes[].id", manifestPath, error)) - return false; - - for (const ShaderPassDefinition& existingPass : shaderPackage.passes) - { - if (existingPass.id == passId) - { - error = "Duplicate shader pass id '" + passId + "' in: " + ManifestPathMessage(manifestPath); - return false; - } - } - - ShaderPassDefinition pass; - pass.id = passId; - pass.sourcePath = shaderPackage.directoryPath / sourcePath; - if (!OptionalStringField(passJson, "entryPoint", pass.entryPoint, shaderPackage.entryPoint, manifestPath, error) || - !OptionalStringField(passJson, "output", pass.outputName, passId, manifestPath, error)) - { - return false; - } - if (!ValidateShaderIdentifier(pass.entryPoint, "passes[].entryPoint", manifestPath, error)) - return false; - - const JsonValue* inputsValue = nullptr; - if (!OptionalArrayField(passJson, "inputs", inputsValue, manifestPath, error)) - return false; - if (inputsValue) - { - for (const JsonValue& inputValue : inputsValue->asArray()) - { - if (!inputValue.isString()) - { - error = "Shader pass inputs must be strings in: " + ManifestPathMessage(manifestPath); - return false; - } - pass.inputNames.push_back(inputValue.asString()); - } - } - - // Keep source validation in the registry. Bad pass declarations then - // appear as unavailable shaders instead of failing at render time. - if (!std::filesystem::exists(pass.sourcePath)) - { - error = "Shader pass source not found for package " + shaderPackage.id + ": " + pass.sourcePath.string(); - return false; - } - pass.sourceWriteTime = std::filesystem::last_write_time(pass.sourcePath); - shaderPackage.passes.push_back(pass); - } - - shaderPackage.shaderPath = shaderPackage.passes.front().sourcePath; - return true; -} - -bool ParseTextureAssets(const JsonValue& manifestJson, ShaderPackage& shaderPackage, const std::filesystem::path& manifestPath, std::string& error) -{ - const JsonValue* texturesValue = nullptr; - if (!OptionalArrayField(manifestJson, "textures", texturesValue, manifestPath, error)) - return false; - if (!texturesValue) - return true; - - for (const JsonValue& textureJson : texturesValue->asArray()) - { - if (!textureJson.isObject()) - { - error = "Shader texture entry must be an object in: " + ManifestPathMessage(manifestPath); - return false; - } - - std::string textureId; - std::string texturePath; - if (!RequireNonEmptyStringField(textureJson, "id", textureId, manifestPath, error) || - !RequireNonEmptyStringField(textureJson, "path", texturePath, manifestPath, error)) - { - error = "Shader texture is missing required 'id' or 'path' in: " + ManifestPathMessage(manifestPath); - return false; - } - if (!ValidateShaderIdentifier(textureId, "textures[].id", manifestPath, error)) - return false; - - ShaderTextureAsset textureAsset; - textureAsset.id = textureId; - textureAsset.path = shaderPackage.directoryPath / texturePath; - if (!std::filesystem::exists(textureAsset.path)) - { - error = "Shader texture asset not found for package " + shaderPackage.id + ": " + textureAsset.path.string(); - return false; - } - - textureAsset.writeTime = std::filesystem::last_write_time(textureAsset.path); - shaderPackage.textureAssets.push_back(textureAsset); - } - - return true; -} - -bool ParseFontAssets(const JsonValue& manifestJson, ShaderPackage& shaderPackage, const std::filesystem::path& manifestPath, std::string& error) -{ - const JsonValue* fontsValue = nullptr; - if (!OptionalArrayField(manifestJson, "fonts", fontsValue, manifestPath, error)) - return false; - if (!fontsValue) - return true; - - for (const JsonValue& fontJson : fontsValue->asArray()) - { - if (!fontJson.isObject()) - { - error = "Shader font entry must be an object in: " + ManifestPathMessage(manifestPath); - return false; - } - - std::string fontId; - std::string fontPath; - if (!RequireNonEmptyStringField(fontJson, "id", fontId, manifestPath, error) || - !RequireNonEmptyStringField(fontJson, "path", fontPath, manifestPath, error)) - { - error = "Shader font is missing required 'id' or 'path' in: " + ManifestPathMessage(manifestPath); - return false; - } - if (!ValidateShaderIdentifier(fontId, "fonts[].id", manifestPath, error)) - return false; - - ShaderFontAsset fontAsset; - fontAsset.id = fontId; - fontAsset.path = shaderPackage.directoryPath / fontPath; - if (!std::filesystem::exists(fontAsset.path)) - { - error = "Shader font asset not found for package " + shaderPackage.id + ": " + fontAsset.path.string(); - return false; - } - - fontAsset.writeTime = std::filesystem::last_write_time(fontAsset.path); - shaderPackage.fontAssets.push_back(fontAsset); - } - - return true; -} - -bool ParseTemporalSettings(const JsonValue& manifestJson, ShaderPackage& shaderPackage, unsigned maxTemporalHistoryFrames, const std::filesystem::path& manifestPath, std::string& error) -{ - const JsonValue* temporalValue = nullptr; - if (!OptionalObjectField(manifestJson, "temporal", temporalValue, manifestPath, error)) - return false; - if (!temporalValue) - return true; - - const JsonValue* enabledValue = temporalValue->find("enabled"); - if (!enabledValue || !enabledValue->asBoolean(false)) - return true; - - std::string historySourceName; - if (!RequireNonEmptyStringField(*temporalValue, "historySource", historySourceName, manifestPath, error)) - { - error = "Temporal shader is missing required 'historySource' in: " + ManifestPathMessage(manifestPath); - return false; - } - - const JsonValue* historyLengthValue = temporalValue->find("historyLength"); - if (!historyLengthValue || !historyLengthValue->isNumber()) - { - error = "Temporal shader is missing required numeric 'historyLength' in: " + ManifestPathMessage(manifestPath); - return false; - } - - TemporalHistorySource historySource = TemporalHistorySource::None; - if (!ParseTemporalHistorySource(historySourceName, historySource)) - { - error = "Unsupported temporal historySource '" + historySourceName + "' in: " + ManifestPathMessage(manifestPath); - return false; - } - - const double requestedHistoryLength = historyLengthValue->asNumber(); - if (!IsFiniteNumber(requestedHistoryLength) || requestedHistoryLength <= 0.0 || std::floor(requestedHistoryLength) != requestedHistoryLength) - { - error = "Temporal shader 'historyLength' must be a positive integer in: " + ManifestPathMessage(manifestPath); - return false; - } - - shaderPackage.temporal.enabled = true; - shaderPackage.temporal.historySource = historySource; - shaderPackage.temporal.requestedHistoryLength = static_cast(requestedHistoryLength); - shaderPackage.temporal.effectiveHistoryLength = std::min(shaderPackage.temporal.requestedHistoryLength, maxTemporalHistoryFrames); - return true; -} - -bool ParseFeedbackSettings(const JsonValue& manifestJson, ShaderPackage& shaderPackage, const std::filesystem::path& manifestPath, std::string& error) -{ - const JsonValue* feedbackValue = nullptr; - if (!OptionalObjectField(manifestJson, "feedback", feedbackValue, manifestPath, error)) - return false; - if (!feedbackValue) - return true; - - const JsonValue* enabledValue = feedbackValue->find("enabled"); - if (!enabledValue || !enabledValue->asBoolean(false)) - return true; - - shaderPackage.feedback.enabled = true; - if (!OptionalStringField(*feedbackValue, "writePass", shaderPackage.feedback.writePassId, "", manifestPath, error)) - return false; - - if (shaderPackage.feedback.writePassId.empty()) - { - if (shaderPackage.passes.empty()) - { - error = "Feedback-enabled shader has no passes to target in: " + ManifestPathMessage(manifestPath); - return false; - } - shaderPackage.feedback.writePassId = shaderPackage.passes.back().id; - } - - if (!ValidateShaderIdentifier(shaderPackage.feedback.writePassId, "feedback.writePass", manifestPath, error)) - return false; - - const auto passIt = std::find_if(shaderPackage.passes.begin(), shaderPackage.passes.end(), - [&shaderPackage](const ShaderPassDefinition& pass) { return pass.id == shaderPackage.feedback.writePassId; }); - if (passIt == shaderPackage.passes.end()) - { - error = "Feedback writePass '" + shaderPackage.feedback.writePassId + "' does not match any declared pass in: " + ManifestPathMessage(manifestPath); - return false; - } - - return true; -} - -bool ParseParameterNumberField(const JsonValue& parameterJson, const char* fieldName, std::vector& values, const std::filesystem::path& manifestPath, std::string& error) -{ - if (const JsonValue* fieldValue = parameterJson.find(fieldName)) - return NumberListFromJsonValue(*fieldValue, values, fieldName, manifestPath, error); - return true; -} - -bool ParseParameterDefault(const JsonValue& parameterJson, ShaderParameterDefinition& definition, const std::filesystem::path& manifestPath, std::string& error) -{ - const JsonValue* defaultValue = parameterJson.find("default"); - if (!defaultValue) - return true; - - if (definition.type == ShaderParameterType::Boolean) - { - if (!defaultValue->isBoolean()) - { - error = "Boolean parameter default must be a boolean for: " + definition.id; - return false; - } - definition.defaultBoolean = defaultValue->asBoolean(false); - return true; - } - - if (definition.type == ShaderParameterType::Enum) - { - if (!defaultValue->isString()) - { - error = "Enum parameter default must be a string for: " + definition.id; - return false; - } - definition.defaultEnumValue = defaultValue->asString(); - return true; - } - - if (definition.type == ShaderParameterType::Text) - { - if (!defaultValue->isString()) - { - error = "Text parameter default must be a string for: " + definition.id; - return false; - } - definition.defaultTextValue = defaultValue->asString(); - return true; - } - - return NumberListFromJsonValue(*defaultValue, definition.defaultNumbers, "default", manifestPath, error); -} - -bool ParseParameterOptions(const JsonValue& parameterJson, ShaderParameterDefinition& definition, const std::filesystem::path& manifestPath, std::string& error) -{ - const JsonValue* optionsValue = nullptr; - if (!OptionalArrayField(parameterJson, "options", optionsValue, manifestPath, error) || !optionsValue) - { - error = "Enum parameter is missing 'options' in: " + ManifestPathMessage(manifestPath); - return false; - } - - for (const JsonValue& optionJson : optionsValue->asArray()) - { - if (!optionJson.isObject()) - { - error = "Enum parameter option must be an object in: " + ManifestPathMessage(manifestPath); - return false; - } - - ShaderParameterOption option; - if (!RequireStringField(optionJson, "value", option.value, manifestPath, error) || - !RequireStringField(optionJson, "label", option.label, manifestPath, error)) - { - error = "Enum parameter option is missing 'value' or 'label' in: " + ManifestPathMessage(manifestPath); - return false; - } - definition.enumOptions.push_back(option); - } - - bool defaultFound = definition.defaultEnumValue.empty(); - for (const ShaderParameterOption& option : definition.enumOptions) - { - if (option.value == definition.defaultEnumValue) - { - defaultFound = true; - break; - } - } - - if (!defaultFound) - { - error = "Enum parameter default is not present in its option list for: " + definition.id; - return false; - } - - return true; -} - -bool ParseParameterDefinition(const JsonValue& parameterJson, ShaderParameterDefinition& definition, const std::filesystem::path& manifestPath, std::string& error) -{ - if (!parameterJson.isObject()) - { - error = "Shader parameter entry must be an object in: " + ManifestPathMessage(manifestPath); - return false; - } - - std::string typeName; - if (!RequireStringField(parameterJson, "id", definition.id, manifestPath, error) || - !RequireStringField(parameterJson, "label", definition.label, manifestPath, error) || - !RequireStringField(parameterJson, "type", typeName, manifestPath, error)) - { - error = "Shader parameter is missing required fields in: " + ManifestPathMessage(manifestPath); - return false; - } - - if (!ParseShaderParameterType(typeName, definition.type)) - { - error = "Unsupported parameter type '" + typeName + "' in: " + ManifestPathMessage(manifestPath); - return false; - } - if (!ValidateShaderIdentifier(definition.id, "parameters[].id", manifestPath, error)) - return false; - - if (!OptionalStringField(parameterJson, "description", definition.description, "", manifestPath, error)) - return false; - - if (!ParseParameterDefault(parameterJson, definition, manifestPath, error) || - !ParseParameterNumberField(parameterJson, "min", definition.minNumbers, manifestPath, error) || - !ParseParameterNumberField(parameterJson, "max", definition.maxNumbers, manifestPath, error) || - !ParseParameterNumberField(parameterJson, "step", definition.stepNumbers, manifestPath, error)) - { - return false; - } - - if (definition.type == ShaderParameterType::Text) - { - if (const JsonValue* fontValue = parameterJson.find("font")) - { - if (!fontValue->isString()) - { - error = "Text parameter 'font' must be a string for: " + definition.id; - return false; - } - definition.fontId = fontValue->asString(); - if (!definition.fontId.empty() && !ValidateShaderIdentifier(definition.fontId, "parameters[].font", manifestPath, error)) - return false; - } - if (const JsonValue* maxLengthValue = parameterJson.find("maxLength")) - { - if (!maxLengthValue->isNumber() || maxLengthValue->asNumber() < 1.0 || maxLengthValue->asNumber() > 256.0) - { - error = "Text parameter 'maxLength' must be a number from 1 to 256 for: " + definition.id; - return false; - } - definition.maxLength = static_cast(maxLengthValue->asNumber()); - } - } - - if (definition.type == ShaderParameterType::Enum) - return ParseParameterOptions(parameterJson, definition, manifestPath, error); - - return true; -} - -bool ParseParameterDefinitions(const JsonValue& manifestJson, ShaderPackage& shaderPackage, const std::filesystem::path& manifestPath, std::string& error) -{ - const JsonValue* parametersValue = nullptr; - if (!OptionalArrayField(manifestJson, "parameters", parametersValue, manifestPath, error)) - return false; - if (!parametersValue) - return true; - - for (const JsonValue& parameterJson : parametersValue->asArray()) - { - ShaderParameterDefinition definition; - if (!ParseParameterDefinition(parameterJson, definition, manifestPath, error)) - return false; - shaderPackage.parameters.push_back(definition); - } - - return true; -} - std::string UniqueUnavailableShaderId(const std::filesystem::path& manifestPath, const std::string& parsedId) { const std::string fallbackId = manifestPath.parent_path().filename().string(); @@ -796,6 +127,7 @@ bool ShaderPackageRegistry::ParseManifest(const std::filesystem::path& manifestP return false; } + using namespace ShaderManifestParsing; if (!ParseShaderMetadata(manifestJson, shaderPackage, manifestPath, error)) return false; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 11543cd..ac99ece 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -49,6 +49,9 @@ add_video_shader_test(RenderCadenceCompositorRuntimeLayerModelTests "${SRC_DIR}/runtime/RuntimeParameterUtils.cpp" "${SRC_DIR}/runtime/RuntimeTextTextureComposer.cpp" "${SRC_DIR}/runtime/SupportedShaderCatalog.cpp" + "${SRC_DIR}/shader/ShaderManifestAssets.cpp" + "${SRC_DIR}/shader/ShaderManifestParameters.cpp" + "${SRC_DIR}/shader/ShaderManifestParser.cpp" "${SRC_DIR}/shader/ShaderPackageRegistry.cpp" "${TEST_DIR}/RenderCadenceCompositorRuntimeLayerModelTests.cpp" ) @@ -63,6 +66,9 @@ add_video_shader_test(RuntimeStatePersistenceTests add_video_shader_test(FontAtlasBuilderTests "${SRC_DIR}/runtime/FontAtlasBuilder.cpp" "${SRC_DIR}/runtime/RuntimeJson.cpp" + "${SRC_DIR}/shader/ShaderManifestAssets.cpp" + "${SRC_DIR}/shader/ShaderManifestParameters.cpp" + "${SRC_DIR}/shader/ShaderManifestParser.cpp" "${SRC_DIR}/shader/ShaderPackageRegistry.cpp" "${TEST_DIR}/FontAtlasBuilderTests.cpp" ) @@ -71,6 +77,9 @@ add_video_shader_test(RenderCadenceCompositorSupportedShaderCatalogTests "${SRC_DIR}/runtime/FontAtlasBuilder.cpp" "${SRC_DIR}/runtime/RuntimeJson.cpp" "${SRC_DIR}/runtime/SupportedShaderCatalog.cpp" + "${SRC_DIR}/shader/ShaderManifestAssets.cpp" + "${SRC_DIR}/shader/ShaderManifestParameters.cpp" + "${SRC_DIR}/shader/ShaderManifestParser.cpp" "${SRC_DIR}/shader/ShaderPackageRegistry.cpp" "${TEST_DIR}/RenderCadenceCompositorSupportedShaderCatalogTests.cpp" ) @@ -88,6 +97,9 @@ add_video_shader_test(RenderCadenceCompositorRuntimeStateJsonTests "${SRC_DIR}/runtime/RuntimeParameterUtils.cpp" "${SRC_DIR}/runtime/RuntimeTextTextureComposer.cpp" "${SRC_DIR}/runtime/SupportedShaderCatalog.cpp" + "${SRC_DIR}/shader/ShaderManifestAssets.cpp" + "${SRC_DIR}/shader/ShaderManifestParameters.cpp" + "${SRC_DIR}/shader/ShaderManifestParser.cpp" "${SRC_DIR}/shader/ShaderPackageRegistry.cpp" "${TEST_DIR}/RenderCadenceCompositorRuntimeStateJsonTests.cpp" ) @@ -124,6 +136,9 @@ add_video_shader_test(RuntimeParameterUtilsTests add_video_shader_test(ShaderPackageRegistryTests "${SRC_DIR}/runtime/RuntimeJson.cpp" + "${SRC_DIR}/shader/ShaderManifestAssets.cpp" + "${SRC_DIR}/shader/ShaderManifestParameters.cpp" + "${SRC_DIR}/shader/ShaderManifestParser.cpp" "${SRC_DIR}/shader/ShaderPackageRegistry.cpp" "${TEST_DIR}/ShaderPackageRegistryTests.cpp" ) @@ -131,6 +146,9 @@ add_video_shader_test(ShaderPackageRegistryTests add_video_shader_test(ShaderSlangValidationTests "${SRC_DIR}/runtime/RuntimeJson.cpp" "${SRC_DIR}/shader/ShaderCompiler.cpp" + "${SRC_DIR}/shader/ShaderManifestAssets.cpp" + "${SRC_DIR}/shader/ShaderManifestParameters.cpp" + "${SRC_DIR}/shader/ShaderManifestParser.cpp" "${SRC_DIR}/shader/ShaderPackageRegistry.cpp" "${TEST_DIR}/ShaderSlangValidationTests.cpp" )