diff --git a/src/runtime/RuntimeLayerModel.cpp b/src/runtime/RuntimeLayerModel.cpp index fb58e06..3711dff 100644 --- a/src/runtime/RuntimeLayerModel.cpp +++ b/src/runtime/RuntimeLayerModel.cpp @@ -5,73 +5,10 @@ #include #include -#include -#include #include namespace RenderCadenceCompositor { -namespace -{ -JsonValue ParameterValueToJson(const ShaderParameterDefinition& definition, const ShaderParameterValue& value) -{ - switch (definition.type) - { - case ShaderParameterType::Float: - return JsonValue(value.numberValues.empty() ? 0.0 : value.numberValues.front()); - case ShaderParameterType::Vec2: - case ShaderParameterType::Color: - { - JsonValue array = JsonValue::MakeArray(); - for (double number : value.numberValues) - array.pushBack(JsonValue(number)); - return array; - } - case ShaderParameterType::Boolean: - return JsonValue(value.booleanValue); - case ShaderParameterType::Enum: - return JsonValue(value.enumValue); - case ShaderParameterType::Text: - return JsonValue(value.textValue); - case ShaderParameterType::Trigger: - return JsonValue(value.numberValues.empty() ? 0.0 : value.numberValues.front()); - } - return JsonValue(); -} - -bool ParseRuntimeLayerNumber(const std::string& layerId, uint64_t& number) -{ - const std::string prefix = "runtime-layer-"; - if (layerId.compare(0, prefix.size(), prefix) != 0) - return false; - - const std::string suffix = layerId.substr(prefix.size()); - if (suffix.empty()) - return false; - - uint64_t parsed = 0; - for (char character : suffix) - { - if (!std::isdigit(static_cast(character))) - return false; - parsed = parsed * 10 + static_cast(character - '0'); - } - - number = parsed; - return true; -} - -std::string AllocateRestoredLayerId(std::set& usedLayerIds, uint64_t& nextLayerNumber) -{ - for (;;) - { - std::string candidate = "runtime-layer-" + std::to_string(nextLayerNumber++); - if (usedLayerIds.insert(candidate).second) - return candidate; - } -} -} - bool RuntimeLayerModel::InitializeSingleLayer(const SupportedShaderCatalog& shaderCatalog, const std::string& shaderId, std::string& error) { Clear(); @@ -270,145 +207,6 @@ bool RuntimeLayerModel::ResetParameters(const std::string& layerId, std::string& return true; } -bool RuntimeLayerModel::InitializeFromRuntimeState(const SupportedShaderCatalog& shaderCatalog, const JsonValue& runtimeState, std::string& error) -{ - if (!runtimeState.isObject()) - { - error = "Runtime state root must be a JSON object."; - return false; - } - - const JsonValue* layersValue = runtimeState.find("layers"); - if (!layersValue || !layersValue->isArray()) - { - error = "Runtime state must contain a layers array."; - return false; - } - - std::vector restoredLayers; - std::set usedLayerIds; - uint64_t nextLayerNumber = 1; - - for (const JsonValue& layerValue : layersValue->asArray()) - { - if (!layerValue.isObject()) - continue; - - const JsonValue* shaderIdValue = layerValue.find("shaderId"); - if (!shaderIdValue || !shaderIdValue->isString() || shaderIdValue->asString().empty()) - continue; - - const ShaderPackage* shaderPackage = shaderCatalog.FindPackage(shaderIdValue->asString()); - if (!shaderPackage) - continue; - - Layer layer; - const JsonValue* layerIdValue = layerValue.find("id"); - if (layerIdValue && layerIdValue->isString() && !layerIdValue->asString().empty() && usedLayerIds.insert(layerIdValue->asString()).second) - layer.id = layerIdValue->asString(); - else - layer.id = AllocateRestoredLayerId(usedLayerIds, nextLayerNumber); - - uint64_t restoredLayerNumber = 0; - if (ParseRuntimeLayerNumber(layer.id, restoredLayerNumber) && restoredLayerNumber >= nextLayerNumber) - nextLayerNumber = restoredLayerNumber + 1; - - layer.shaderId = shaderPackage->id; - layer.packageFingerprint = ShaderPackageFingerprint(*shaderPackage); - layer.shaderName = shaderPackage->displayName.empty() ? shaderPackage->id : shaderPackage->displayName; - const JsonValue* bypassValue = layerValue.find("bypass"); - layer.bypass = bypassValue && bypassValue->isBoolean() ? bypassValue->asBoolean() : false; - layer.buildState = RuntimeLayerBuildState::Pending; - layer.message = "Runtime Slang build is waiting to start."; - InitializeDefaultParameterValues(layer, *shaderPackage); - - const JsonValue* parameterValues = layerValue.find("parameterValues"); - if (parameterValues && parameterValues->isObject()) - { - for (const ShaderParameterDefinition& definition : layer.parameterDefinitions) - { - const JsonValue* value = parameterValues->find(definition.id); - if (!value) - continue; - - ShaderParameterValue normalizedValue; - std::string normalizeError; - if (NormalizeAndValidateParameterValue(definition, *value, normalizedValue, normalizeError)) - layer.parameterValues[definition.id] = normalizedValue; - } - } - - restoredLayers.push_back(std::move(layer)); - } - - if (restoredLayers.empty()) - { - error = "Runtime state did not contain any supported layers."; - return false; - } - - mLayers = std::move(restoredLayers); - mNextLayerNumber = nextLayerNumber; - error.clear(); - return true; -} - -bool RuntimeLayerModel::ReloadFromCatalog(const SupportedShaderCatalog& shaderCatalog, std::vector>& buildsToStart, std::string& error) -{ - buildsToStart.clear(); - for (Layer& layer : mLayers) - { - const ShaderPackage* shaderPackage = shaderCatalog.FindPackage(layer.shaderId); - if (!shaderPackage) - { - layer.buildState = RuntimeLayerBuildState::Failed; - layer.message = "Shader '" + layer.shaderId + "' is no longer available after reload."; - continue; - } - - const std::string nextFingerprint = ShaderPackageFingerprint(*shaderPackage); - if (layer.packageFingerprint == nextFingerprint) - { - buildsToStart.push_back({ layer.id, layer.shaderId }); - continue; - } - - std::map previousDefinitions; - for (const ShaderParameterDefinition& definition : layer.parameterDefinitions) - previousDefinitions[definition.id] = definition; - - std::map nextValues; - for (const ShaderParameterDefinition& nextDefinition : shaderPackage->parameters) - { - const auto previousDefinitionIt = previousDefinitions.find(nextDefinition.id); - const auto previousValueIt = layer.parameterValues.find(nextDefinition.id); - if (previousDefinitionIt != previousDefinitions.end() - && previousValueIt != layer.parameterValues.end() - && previousDefinitionIt->second.type == nextDefinition.type) - { - ShaderParameterValue preservedValue; - JsonValue valueJson = ParameterValueToJson(previousDefinitionIt->second, previousValueIt->second); - std::string normalizeError; - if (NormalizeAndValidateParameterValue(nextDefinition, valueJson, preservedValue, normalizeError)) - { - nextValues[nextDefinition.id] = preservedValue; - continue; - } - } - nextValues[nextDefinition.id] = DefaultValueForDefinition(nextDefinition); - } - - layer.shaderName = shaderPackage->displayName.empty() ? shaderPackage->id : shaderPackage->displayName; - layer.packageFingerprint = nextFingerprint; - layer.parameterDefinitions = shaderPackage->parameters; - layer.parameterValues = std::move(nextValues); - buildsToStart.push_back({ layer.id, layer.shaderId }); - } - - error.clear(); - return true; -} - void RuntimeLayerModel::Clear() { mLayers.clear(); @@ -508,37 +306,6 @@ bool RuntimeLayerModel::MarkRenderCommitFailed(const std::string& layerId, const return MarkBuildFailed(layerId, message, error); } -RuntimeLayerModelSnapshot RuntimeLayerModel::Snapshot() const -{ - RuntimeLayerModelSnapshot snapshot; - snapshot.compileSucceeded = true; - - for (const Layer& layer : mLayers) - { - snapshot.displayLayers.push_back(ToReadModel(layer)); - if (!layer.message.empty() && snapshot.compileMessage.empty()) - snapshot.compileMessage = layer.message; - if (layer.buildState == RuntimeLayerBuildState::Failed) - snapshot.compileSucceeded = false; - if (layer.renderReady) - { - RuntimeRenderLayerModel renderLayer; - renderLayer.id = layer.id; - renderLayer.bypass = layer.bypass; - renderLayer.artifact = layer.artifact; - renderLayer.shaderId = renderLayer.artifact.shaderId.empty() ? layer.shaderId : renderLayer.artifact.shaderId; - if (layer.buildState == RuntimeLayerBuildState::Ready) - renderLayer.artifact.parameterValues = layer.parameterValues; - renderLayer.artifact.fontAtlases.clear(); - snapshot.renderLayers.push_back(std::move(renderLayer)); - } - } - - if (snapshot.compileMessage.empty()) - snapshot.compileMessage = mLayers.empty() ? "Runtime shader build disabled." : "Runtime shader build has not completed yet."; - return snapshot; -} - std::string RuntimeLayerModel::FirstLayerId() const { return mLayers.empty() ? std::string() : mLayers.front().id; @@ -608,21 +375,6 @@ std::string RuntimeLayerModel::AllocateLayerId() return "runtime-layer-" + std::to_string(mNextLayerNumber++); } -RuntimeLayerReadModel RuntimeLayerModel::ToReadModel(const Layer& layer) -{ - RuntimeLayerReadModel readModel; - readModel.id = layer.id; - readModel.shaderId = layer.shaderId; - readModel.shaderName = layer.shaderName; - readModel.bypass = layer.bypass; - readModel.buildState = layer.buildState; - readModel.message = layer.message; - readModel.renderReady = layer.renderReady; - readModel.parameterDefinitions = layer.parameterDefinitions; - readModel.parameterValues = layer.parameterValues; - return readModel; -} - double RuntimeLayerModel::RuntimeElapsedSeconds() const { return std::chrono::duration_cast>(std::chrono::steady_clock::now() - mStartTime).count(); diff --git a/src/runtime/RuntimeLayerReload.cpp b/src/runtime/RuntimeLayerReload.cpp new file mode 100644 index 0000000..e2076b1 --- /dev/null +++ b/src/runtime/RuntimeLayerReload.cpp @@ -0,0 +1,94 @@ +#include "RuntimeLayerModel.h" + +#include "RuntimeParameterUtils.h" + +#include +#include + +namespace RenderCadenceCompositor +{ +namespace +{ +JsonValue ParameterValueToJson(const ShaderParameterDefinition& definition, const ShaderParameterValue& value) +{ + switch (definition.type) + { + case ShaderParameterType::Float: + return JsonValue(value.numberValues.empty() ? 0.0 : value.numberValues.front()); + case ShaderParameterType::Vec2: + case ShaderParameterType::Color: + { + JsonValue array = JsonValue::MakeArray(); + for (double number : value.numberValues) + array.pushBack(JsonValue(number)); + return array; + } + case ShaderParameterType::Boolean: + return JsonValue(value.booleanValue); + case ShaderParameterType::Enum: + return JsonValue(value.enumValue); + case ShaderParameterType::Text: + return JsonValue(value.textValue); + case ShaderParameterType::Trigger: + return JsonValue(value.numberValues.empty() ? 0.0 : value.numberValues.front()); + } + return JsonValue(); +} +} + +bool RuntimeLayerModel::ReloadFromCatalog(const SupportedShaderCatalog& shaderCatalog, std::vector>& buildsToStart, std::string& error) +{ + buildsToStart.clear(); + for (Layer& layer : mLayers) + { + const ShaderPackage* shaderPackage = shaderCatalog.FindPackage(layer.shaderId); + if (!shaderPackage) + { + layer.buildState = RuntimeLayerBuildState::Failed; + layer.message = "Shader '" + layer.shaderId + "' is no longer available after reload."; + continue; + } + + const std::string nextFingerprint = ShaderPackageFingerprint(*shaderPackage); + if (layer.packageFingerprint == nextFingerprint) + { + buildsToStart.push_back({ layer.id, layer.shaderId }); + continue; + } + + std::map previousDefinitions; + for (const ShaderParameterDefinition& definition : layer.parameterDefinitions) + previousDefinitions[definition.id] = definition; + + std::map nextValues; + for (const ShaderParameterDefinition& nextDefinition : shaderPackage->parameters) + { + const auto previousDefinitionIt = previousDefinitions.find(nextDefinition.id); + const auto previousValueIt = layer.parameterValues.find(nextDefinition.id); + if (previousDefinitionIt != previousDefinitions.end() + && previousValueIt != layer.parameterValues.end() + && previousDefinitionIt->second.type == nextDefinition.type) + { + ShaderParameterValue preservedValue; + JsonValue valueJson = ParameterValueToJson(previousDefinitionIt->second, previousValueIt->second); + std::string normalizeError; + if (NormalizeAndValidateParameterValue(nextDefinition, valueJson, preservedValue, normalizeError)) + { + nextValues[nextDefinition.id] = preservedValue; + continue; + } + } + nextValues[nextDefinition.id] = DefaultValueForDefinition(nextDefinition); + } + + layer.shaderName = shaderPackage->displayName.empty() ? shaderPackage->id : shaderPackage->displayName; + layer.packageFingerprint = nextFingerprint; + layer.parameterDefinitions = shaderPackage->parameters; + layer.parameterValues = std::move(nextValues); + buildsToStart.push_back({ layer.id, layer.shaderId }); + } + + error.clear(); + return true; +} +} diff --git a/src/runtime/RuntimeLayerSnapshot.cpp b/src/runtime/RuntimeLayerSnapshot.cpp new file mode 100644 index 0000000..536321d --- /dev/null +++ b/src/runtime/RuntimeLayerSnapshot.cpp @@ -0,0 +1,52 @@ +#include "RuntimeLayerModel.h" + +#include + +namespace RenderCadenceCompositor +{ +RuntimeLayerModelSnapshot RuntimeLayerModel::Snapshot() const +{ + RuntimeLayerModelSnapshot snapshot; + snapshot.compileSucceeded = true; + + for (const Layer& layer : mLayers) + { + snapshot.displayLayers.push_back(ToReadModel(layer)); + if (!layer.message.empty() && snapshot.compileMessage.empty()) + snapshot.compileMessage = layer.message; + if (layer.buildState == RuntimeLayerBuildState::Failed) + snapshot.compileSucceeded = false; + if (layer.renderReady) + { + RuntimeRenderLayerModel renderLayer; + renderLayer.id = layer.id; + renderLayer.bypass = layer.bypass; + renderLayer.artifact = layer.artifact; + renderLayer.shaderId = renderLayer.artifact.shaderId.empty() ? layer.shaderId : renderLayer.artifact.shaderId; + if (layer.buildState == RuntimeLayerBuildState::Ready) + renderLayer.artifact.parameterValues = layer.parameterValues; + renderLayer.artifact.fontAtlases.clear(); + snapshot.renderLayers.push_back(std::move(renderLayer)); + } + } + + if (snapshot.compileMessage.empty()) + snapshot.compileMessage = mLayers.empty() ? "Runtime shader build disabled." : "Runtime shader build has not completed yet."; + return snapshot; +} + +RuntimeLayerReadModel RuntimeLayerModel::ToReadModel(const Layer& layer) +{ + RuntimeLayerReadModel readModel; + readModel.id = layer.id; + readModel.shaderId = layer.shaderId; + readModel.shaderName = layer.shaderName; + readModel.bypass = layer.bypass; + readModel.buildState = layer.buildState; + readModel.message = layer.message; + readModel.renderReady = layer.renderReady; + readModel.parameterDefinitions = layer.parameterDefinitions; + readModel.parameterValues = layer.parameterValues; + return readModel; +} +} diff --git a/src/runtime/RuntimeLayerStateRestore.cpp b/src/runtime/RuntimeLayerStateRestore.cpp new file mode 100644 index 0000000..f5fb941 --- /dev/null +++ b/src/runtime/RuntimeLayerStateRestore.cpp @@ -0,0 +1,129 @@ +#include "RuntimeLayerModel.h" + +#include "RuntimeParameterUtils.h" + +#include +#include +#include +#include + +namespace RenderCadenceCompositor +{ +namespace +{ +bool ParseRuntimeLayerNumber(const std::string& layerId, uint64_t& number) +{ + const std::string prefix = "runtime-layer-"; + if (layerId.compare(0, prefix.size(), prefix) != 0) + return false; + + const std::string suffix = layerId.substr(prefix.size()); + if (suffix.empty()) + return false; + + uint64_t parsed = 0; + for (char character : suffix) + { + if (!std::isdigit(static_cast(character))) + return false; + parsed = parsed * 10 + static_cast(character - '0'); + } + + number = parsed; + return true; +} + +std::string AllocateRestoredLayerId(std::set& usedLayerIds, uint64_t& nextLayerNumber) +{ + for (;;) + { + std::string candidate = "runtime-layer-" + std::to_string(nextLayerNumber++); + if (usedLayerIds.insert(candidate).second) + return candidate; + } +} +} + +bool RuntimeLayerModel::InitializeFromRuntimeState(const SupportedShaderCatalog& shaderCatalog, const JsonValue& runtimeState, std::string& error) +{ + if (!runtimeState.isObject()) + { + error = "Runtime state root must be a JSON object."; + return false; + } + + const JsonValue* layersValue = runtimeState.find("layers"); + if (!layersValue || !layersValue->isArray()) + { + error = "Runtime state must contain a layers array."; + return false; + } + + std::vector restoredLayers; + std::set usedLayerIds; + uint64_t nextLayerNumber = 1; + + for (const JsonValue& layerValue : layersValue->asArray()) + { + if (!layerValue.isObject()) + continue; + + const JsonValue* shaderIdValue = layerValue.find("shaderId"); + if (!shaderIdValue || !shaderIdValue->isString() || shaderIdValue->asString().empty()) + continue; + + const ShaderPackage* shaderPackage = shaderCatalog.FindPackage(shaderIdValue->asString()); + if (!shaderPackage) + continue; + + Layer layer; + const JsonValue* layerIdValue = layerValue.find("id"); + if (layerIdValue && layerIdValue->isString() && !layerIdValue->asString().empty() && usedLayerIds.insert(layerIdValue->asString()).second) + layer.id = layerIdValue->asString(); + else + layer.id = AllocateRestoredLayerId(usedLayerIds, nextLayerNumber); + + uint64_t restoredLayerNumber = 0; + if (ParseRuntimeLayerNumber(layer.id, restoredLayerNumber) && restoredLayerNumber >= nextLayerNumber) + nextLayerNumber = restoredLayerNumber + 1; + + layer.shaderId = shaderPackage->id; + layer.packageFingerprint = ShaderPackageFingerprint(*shaderPackage); + layer.shaderName = shaderPackage->displayName.empty() ? shaderPackage->id : shaderPackage->displayName; + const JsonValue* bypassValue = layerValue.find("bypass"); + layer.bypass = bypassValue && bypassValue->isBoolean() ? bypassValue->asBoolean() : false; + layer.buildState = RuntimeLayerBuildState::Pending; + layer.message = "Runtime Slang build is waiting to start."; + InitializeDefaultParameterValues(layer, *shaderPackage); + + const JsonValue* parameterValues = layerValue.find("parameterValues"); + if (parameterValues && parameterValues->isObject()) + { + for (const ShaderParameterDefinition& definition : layer.parameterDefinitions) + { + const JsonValue* value = parameterValues->find(definition.id); + if (!value) + continue; + + ShaderParameterValue normalizedValue; + std::string normalizeError; + if (NormalizeAndValidateParameterValue(definition, *value, normalizedValue, normalizeError)) + layer.parameterValues[definition.id] = normalizedValue; + } + } + + restoredLayers.push_back(std::move(layer)); + } + + if (restoredLayers.empty()) + { + error = "Runtime state did not contain any supported layers."; + return false; + } + + mLayers = std::move(restoredLayers); + mNextLayerNumber = nextLayerNumber; + error.clear(); + return true; +} +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f7cb756..11543cd 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -42,6 +42,9 @@ add_video_shader_test(RenderCadenceCompositorRuntimeShaderParamsTests add_video_shader_test(RenderCadenceCompositorRuntimeLayerModelTests "${SRC_DIR}/runtime/FontAtlasBuilder.cpp" "${SRC_DIR}/runtime/RuntimeLayerModel.cpp" + "${SRC_DIR}/runtime/RuntimeLayerReload.cpp" + "${SRC_DIR}/runtime/RuntimeLayerSnapshot.cpp" + "${SRC_DIR}/runtime/RuntimeLayerStateRestore.cpp" "${SRC_DIR}/runtime/RuntimeJson.cpp" "${SRC_DIR}/runtime/RuntimeParameterUtils.cpp" "${SRC_DIR}/runtime/RuntimeTextTextureComposer.cpp" @@ -79,6 +82,9 @@ add_video_shader_test(RenderCadenceCompositorRuntimeStateJsonTests "${SRC_DIR}/runtime/FontAtlasBuilder.cpp" "${SRC_DIR}/runtime/RuntimeJson.cpp" "${SRC_DIR}/runtime/RuntimeLayerModel.cpp" + "${SRC_DIR}/runtime/RuntimeLayerReload.cpp" + "${SRC_DIR}/runtime/RuntimeLayerSnapshot.cpp" + "${SRC_DIR}/runtime/RuntimeLayerStateRestore.cpp" "${SRC_DIR}/runtime/RuntimeParameterUtils.cpp" "${SRC_DIR}/runtime/RuntimeTextTextureComposer.cpp" "${SRC_DIR}/runtime/SupportedShaderCatalog.cpp"