runtime read of json layer state
This commit is contained in:
@@ -29,16 +29,30 @@ void RuntimeLayerController::Initialize(const std::string& shaderLibrary, unsign
|
|||||||
|
|
||||||
void RuntimeLayerController::StartStartupBuild(const std::string& runtimeShaderId)
|
void RuntimeLayerController::StartStartupBuild(const std::string& runtimeShaderId)
|
||||||
{
|
{
|
||||||
if (runtimeShaderId.empty())
|
std::vector<std::pair<std::string, std::string>> buildsToStart;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
|
||||||
|
buildsToStart = mRuntimeLayerModel.PendingLayerBuilds();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buildsToStart.empty() && runtimeShaderId.empty())
|
||||||
{
|
{
|
||||||
Log("runtime-shader", "Runtime shader build disabled.");
|
Log("runtime-shader", "Runtime shader build disabled.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log("runtime-shader", "Starting background Slang build for shader '" + runtimeShaderId + "'.");
|
if (buildsToStart.empty())
|
||||||
|
{
|
||||||
const std::string layerId = FirstRuntimeLayerId();
|
const std::string layerId = FirstRuntimeLayerId();
|
||||||
if (!layerId.empty())
|
if (!layerId.empty())
|
||||||
StartLayerShaderBuild(layerId, runtimeShaderId);
|
buildsToStart.push_back({ layerId, runtimeShaderId });
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& build : buildsToStart)
|
||||||
|
{
|
||||||
|
Log("runtime-shader", "Starting background Slang build for layer '" + build.first + "' shader '" + build.second + "'.");
|
||||||
|
StartLayerShaderBuild(build.first, build.second);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RuntimeLayerController::Stop()
|
void RuntimeLayerController::Stop()
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
bool LoadSupportedShaderCatalog(const std::string& shaderLibrary, unsigned maxTemporalHistoryFrames);
|
bool LoadSupportedShaderCatalog(const std::string& shaderLibrary, unsigned maxTemporalHistoryFrames);
|
||||||
void InitializeLayerModel(std::string& runtimeShaderId);
|
void InitializeLayerModel(std::string& runtimeShaderId);
|
||||||
|
bool InitializeLayerModelFromRuntimeState();
|
||||||
void StartLayerShaderBuild(const std::string& layerId, const std::string& shaderId);
|
void StartLayerShaderBuild(const std::string& layerId, const std::string& shaderId);
|
||||||
void RetireLayerShaderBuild(const std::string& layerId);
|
void RetireLayerShaderBuild(const std::string& layerId);
|
||||||
void CleanupRetiredShaderBuilds();
|
void CleanupRetiredShaderBuilds();
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
#include "RuntimeLayerController.h"
|
#include "RuntimeLayerController.h"
|
||||||
|
|
||||||
#include "AppConfigProvider.h"
|
#include "AppConfigProvider.h"
|
||||||
|
#include "RuntimeJson.h"
|
||||||
#include "../logging/Logger.h"
|
#include "../logging/Logger.h"
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
namespace RenderCadenceCompositor
|
namespace RenderCadenceCompositor
|
||||||
{
|
{
|
||||||
@@ -30,6 +33,9 @@ bool RuntimeLayerController::LoadSupportedShaderCatalog(const std::string& shade
|
|||||||
|
|
||||||
void RuntimeLayerController::InitializeLayerModel(std::string& runtimeShaderId)
|
void RuntimeLayerController::InitializeLayerModel(std::string& runtimeShaderId)
|
||||||
{
|
{
|
||||||
|
if (InitializeLayerModelFromRuntimeState())
|
||||||
|
return;
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
|
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
|
||||||
std::string error;
|
std::string error;
|
||||||
if (!mRuntimeLayerModel.InitializeSingleLayer(mShaderCatalog, runtimeShaderId, error))
|
if (!mRuntimeLayerModel.InitializeSingleLayer(mShaderCatalog, runtimeShaderId, error))
|
||||||
@@ -40,6 +46,43 @@ void RuntimeLayerController::InitializeLayerModel(std::string& runtimeShaderId)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RuntimeLayerController::InitializeLayerModelFromRuntimeState()
|
||||||
|
{
|
||||||
|
const std::filesystem::path runtimeStatePath = FindRepoPath("runtime/runtime_state.json");
|
||||||
|
if (runtimeStatePath.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::ifstream input(runtimeStatePath, std::ios::binary);
|
||||||
|
if (!input)
|
||||||
|
{
|
||||||
|
LogWarning("runtime-state", "Could not open runtime state file: " + runtimeStatePath.string());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostringstream buffer;
|
||||||
|
buffer << input.rdbuf();
|
||||||
|
|
||||||
|
JsonValue runtimeState;
|
||||||
|
std::string error;
|
||||||
|
if (!ParseJson(buffer.str(), runtimeState, error))
|
||||||
|
{
|
||||||
|
LogWarning("runtime-state", "Could not parse runtime state file: " + error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
|
||||||
|
if (!mRuntimeLayerModel.InitializeFromRuntimeState(mShaderCatalog, runtimeState, error))
|
||||||
|
{
|
||||||
|
LogWarning("runtime-state", "Could not restore runtime state: " + error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log("runtime-state", "Restored runtime layer stack from " + runtimeStatePath.string() + ".");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void RuntimeLayerController::StartLayerShaderBuild(const std::string& layerId, const std::string& shaderId)
|
void RuntimeLayerController::StartLayerShaderBuild(const std::string& layerId, const std::string& shaderId)
|
||||||
{
|
{
|
||||||
CleanupRetiredShaderBuilds();
|
CleanupRetiredShaderBuilds();
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <cctype>
|
||||||
|
#include <set>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace RenderCadenceCompositor
|
namespace RenderCadenceCompositor
|
||||||
@@ -35,6 +37,38 @@ JsonValue ParameterValueToJson(const ShaderParameterDefinition& definition, cons
|
|||||||
}
|
}
|
||||||
return JsonValue();
|
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<unsigned char>(character)))
|
||||||
|
return false;
|
||||||
|
parsed = parsed * 10 + static_cast<uint64_t>(character - '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
number = parsed;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AllocateRestoredLayerId(std::set<std::string>& 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)
|
bool RuntimeLayerModel::InitializeSingleLayer(const SupportedShaderCatalog& shaderCatalog, const std::string& shaderId, std::string& error)
|
||||||
@@ -227,6 +261,89 @@ bool RuntimeLayerModel::ResetParameters(const std::string& layerId, std::string&
|
|||||||
return true;
|
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<Layer> restoredLayers;
|
||||||
|
std::set<std::string> 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<std::pair<std::string, std::string>>& buildsToStart, std::string& error)
|
bool RuntimeLayerModel::ReloadFromCatalog(const SupportedShaderCatalog& shaderCatalog, std::vector<std::pair<std::string, std::string>>& buildsToStart, std::string& error)
|
||||||
{
|
{
|
||||||
buildsToStart.clear();
|
buildsToStart.clear();
|
||||||
@@ -392,6 +509,17 @@ std::string RuntimeLayerModel::FirstLayerId() const
|
|||||||
return mLayers.empty() ? std::string() : mLayers.front().id;
|
return mLayers.empty() ? std::string() : mLayers.front().id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<std::string, std::string>> RuntimeLayerModel::PendingLayerBuilds() const
|
||||||
|
{
|
||||||
|
std::vector<std::pair<std::string, std::string>> builds;
|
||||||
|
for (const Layer& layer : mLayers)
|
||||||
|
{
|
||||||
|
if (layer.buildState == RuntimeLayerBuildState::Pending)
|
||||||
|
builds.push_back({ layer.id, layer.shaderId });
|
||||||
|
}
|
||||||
|
return builds;
|
||||||
|
}
|
||||||
|
|
||||||
RuntimeLayerModel::Layer* RuntimeLayerModel::FindLayer(const std::string& layerId)
|
RuntimeLayerModel::Layer* RuntimeLayerModel::FindLayer(const std::string& layerId)
|
||||||
{
|
{
|
||||||
for (Layer& layer : mLayers)
|
for (Layer& layer : mLayers)
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ class RuntimeLayerModel
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
bool InitializeSingleLayer(const SupportedShaderCatalog& shaderCatalog, const std::string& shaderId, std::string& error);
|
bool InitializeSingleLayer(const SupportedShaderCatalog& shaderCatalog, const std::string& shaderId, std::string& error);
|
||||||
|
bool InitializeFromRuntimeState(const SupportedShaderCatalog& shaderCatalog, const JsonValue& runtimeState, std::string& error);
|
||||||
void Clear();
|
void Clear();
|
||||||
|
|
||||||
bool AddLayer(const SupportedShaderCatalog& shaderCatalog, const std::string& shaderId, std::string& layerId, std::string& error);
|
bool AddLayer(const SupportedShaderCatalog& shaderCatalog, const std::string& shaderId, std::string& layerId, std::string& error);
|
||||||
@@ -71,6 +72,7 @@ public:
|
|||||||
|
|
||||||
RuntimeLayerModelSnapshot Snapshot() const;
|
RuntimeLayerModelSnapshot Snapshot() const;
|
||||||
std::string FirstLayerId() const;
|
std::string FirstLayerId() const;
|
||||||
|
std::vector<std::pair<std::string, std::string>> PendingLayerBuilds() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Layer
|
struct Layer
|
||||||
|
|||||||
@@ -159,6 +159,57 @@ void TestAddAndRemoveLayers()
|
|||||||
std::filesystem::remove_all(root);
|
std::filesystem::remove_all(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestInitializeFromRuntimeStateRestoresLayerStack()
|
||||||
|
{
|
||||||
|
std::filesystem::path root;
|
||||||
|
RenderCadenceCompositor::SupportedShaderCatalog catalog = MakeCatalog(root);
|
||||||
|
|
||||||
|
JsonValue runtimeState;
|
||||||
|
std::string error;
|
||||||
|
Expect(ParseJson(R"({
|
||||||
|
"layers": [
|
||||||
|
{
|
||||||
|
"id": "layer-31",
|
||||||
|
"shaderId": "solid",
|
||||||
|
"bypass": true,
|
||||||
|
"parameterValues": {
|
||||||
|
"gain": 0.75,
|
||||||
|
"drop": 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "layer-32",
|
||||||
|
"shaderId": "missing",
|
||||||
|
"parameterValues": {
|
||||||
|
"gain": 0.9
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "layer-33",
|
||||||
|
"shaderId": "solid",
|
||||||
|
"parameterValues": {
|
||||||
|
"gain": "bad"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})", runtimeState, error), "runtime state fixture parses");
|
||||||
|
|
||||||
|
RenderCadenceCompositor::RuntimeLayerModel model;
|
||||||
|
Expect(model.InitializeFromRuntimeState(catalog, runtimeState, error), "runtime state can initialize the layer model");
|
||||||
|
RenderCadenceCompositor::RuntimeLayerModelSnapshot snapshot = model.Snapshot();
|
||||||
|
Expect(snapshot.displayLayers.size() == 2, "restore keeps supported layers and skips missing shaders");
|
||||||
|
Expect(snapshot.displayLayers[0].id == "layer-31", "restore preserves saved layer id");
|
||||||
|
Expect(snapshot.displayLayers[0].bypass, "restore preserves bypass state");
|
||||||
|
Expect(snapshot.displayLayers[0].parameterValues.at("gain").numberValues.front() == 0.75, "restore preserves valid parameter values");
|
||||||
|
Expect(snapshot.displayLayers[1].id == "layer-33", "restore preserves later supported layer order");
|
||||||
|
Expect(snapshot.displayLayers[1].parameterValues.at("gain").numberValues.front() == 0.5, "restore falls back to defaults for invalid parameter values");
|
||||||
|
|
||||||
|
const std::vector<std::pair<std::string, std::string>> builds = model.PendingLayerBuilds();
|
||||||
|
Expect(builds.size() == 2 && builds[0].first == "layer-31" && builds[1].first == "layer-33", "restore queues startup builds for every restored layer");
|
||||||
|
|
||||||
|
std::filesystem::remove_all(root);
|
||||||
|
}
|
||||||
|
|
||||||
void TestLayerControlsUpdateDisplayAndRenderModels()
|
void TestLayerControlsUpdateDisplayAndRenderModels()
|
||||||
{
|
{
|
||||||
std::filesystem::path root;
|
std::filesystem::path root;
|
||||||
@@ -243,6 +294,7 @@ int main()
|
|||||||
TestRejectsUnsupportedStartupShader();
|
TestRejectsUnsupportedStartupShader();
|
||||||
TestBuildFailureStaysDisplaySide();
|
TestBuildFailureStaysDisplaySide();
|
||||||
TestAddAndRemoveLayers();
|
TestAddAndRemoveLayers();
|
||||||
|
TestInitializeFromRuntimeStateRestoresLayerStack();
|
||||||
TestLayerControlsUpdateDisplayAndRenderModels();
|
TestLayerControlsUpdateDisplayAndRenderModels();
|
||||||
TestReloadRefreshesChangedShaderMetadataAndPreservesValues();
|
TestReloadRefreshesChangedShaderMetadataAndPreservesValues();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user