More changes
Some checks failed
CI / React UI Build (push) Successful in 11s
CI / Native Windows Build And Tests (push) Failing after 2m12s
CI / Windows Release Package (push) Has been skipped

This commit is contained in:
2026-05-21 16:51:00 +10:00
parent d68cf9b1a0
commit 5c46eaf18a
10 changed files with 505 additions and 2 deletions

View File

@@ -359,7 +359,10 @@ bool RuntimeLayerModel::ReloadFromCatalog(const SupportedShaderCatalog& shaderCa
const std::string nextFingerprint = ShaderPackageFingerprint(*shaderPackage);
if (layer.packageFingerprint == nextFingerprint)
{
buildsToStart.push_back({ layer.id, layer.shaderId });
continue;
}
std::map<std::string, ShaderParameterDefinition> previousDefinitions;
for (const ShaderParameterDefinition& definition : layer.parameterDefinitions)

View File

@@ -0,0 +1,206 @@
#include "RuntimeStatePersistence.h"
#include "../logging/Logger.h"
#include <fstream>
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();
}
}
JsonValue RuntimeStateSnapshotToJson(const RuntimeLayerModelSnapshot& snapshot)
{
JsonValue root = JsonValue::MakeObject();
JsonValue layers = JsonValue::MakeArray();
for (const RuntimeLayerReadModel& layer : snapshot.displayLayers)
{
JsonValue layerJson = JsonValue::MakeObject();
layerJson.set("id", JsonValue(layer.id));
layerJson.set("shaderId", JsonValue(layer.shaderId));
layerJson.set("bypass", JsonValue(layer.bypass));
JsonValue parameterValues = JsonValue::MakeObject();
for (const ShaderParameterDefinition& definition : layer.parameterDefinitions)
{
const auto valueIt = layer.parameterValues.find(definition.id);
if (valueIt != layer.parameterValues.end())
parameterValues.set(definition.id, ParameterValueToJson(definition, valueIt->second));
}
layerJson.set("parameterValues", parameterValues);
layers.pushBack(layerJson);
}
root.set("layers", layers);
return root;
}
std::string SerializeRuntimeStateSnapshot(const RuntimeLayerModelSnapshot& snapshot)
{
return SerializeJson(RuntimeStateSnapshotToJson(snapshot), true);
}
RuntimeStatePersistenceWriter::~RuntimeStatePersistenceWriter()
{
Stop(true);
}
void RuntimeStatePersistenceWriter::Start(const std::filesystem::path& path, std::chrono::milliseconds debounce)
{
Stop(true);
mPath = path;
mDebounce = debounce;
{
std::lock_guard<std::mutex> lock(mMutex);
mStopping = false;
mHasPendingState = false;
mPendingState.clear();
}
mThread = std::thread([this]() { ThreadMain(); });
}
void RuntimeStatePersistenceWriter::RequestSave(const RuntimeLayerModelSnapshot& snapshot)
{
const std::string serializedState = SerializeRuntimeStateSnapshot(snapshot);
{
std::lock_guard<std::mutex> lock(mMutex);
mPendingState = serializedState;
mHasPendingState = true;
}
mCondition.notify_one();
}
void RuntimeStatePersistenceWriter::Stop(bool flushPending)
{
std::string finalState;
{
std::lock_guard<std::mutex> lock(mMutex);
if (flushPending && mHasPendingState)
finalState = mPendingState;
mStopping = true;
}
mCondition.notify_one();
if (mThread.joinable())
mThread.join();
if (!finalState.empty())
{
std::string error;
if (!WriteSnapshot(finalState, error))
LogWarning("runtime-state", error);
}
std::lock_guard<std::mutex> lock(mMutex);
mStopping = false;
mHasPendingState = false;
mPendingState.clear();
}
void RuntimeStatePersistenceWriter::ThreadMain()
{
for (;;)
{
std::unique_lock<std::mutex> lock(mMutex);
mCondition.wait(lock, [this]() {
return mStopping || mHasPendingState;
});
if (mStopping)
return;
const std::string pendingAtWake = mPendingState;
mCondition.wait_for(lock, mDebounce, [this, &pendingAtWake]() {
return mStopping || mPendingState != pendingAtWake;
});
if (mStopping)
return;
if (mPendingState != pendingAtWake)
continue;
const std::string stateToWrite = mPendingState;
mHasPendingState = false;
lock.unlock();
std::string error;
if (!WriteSnapshot(stateToWrite, error))
LogWarning("runtime-state", error);
}
}
bool RuntimeStatePersistenceWriter::WriteSnapshot(const std::string& serializedState, std::string& error) const
{
if (mPath.empty())
{
error = "Runtime state path is empty.";
return false;
}
std::error_code ec;
std::filesystem::create_directories(mPath.parent_path(), ec);
if (ec)
{
error = "Could not create runtime state directory: " + ec.message();
return false;
}
const std::filesystem::path tempPath = mPath.string() + ".tmp";
{
std::ofstream output(tempPath, std::ios::binary | std::ios::trunc);
if (!output)
{
error = "Could not open runtime state temp file: " + tempPath.string();
return false;
}
output << serializedState << "\n";
if (!output)
{
error = "Could not write runtime state temp file: " + tempPath.string();
return false;
}
}
std::filesystem::rename(tempPath, mPath, ec);
if (ec)
{
std::filesystem::remove(mPath, ec);
ec.clear();
std::filesystem::rename(tempPath, mPath, ec);
if (ec)
{
error = "Could not replace runtime state file: " + ec.message();
return false;
}
}
error.clear();
return true;
}
}

View File

@@ -0,0 +1,43 @@
#pragma once
#include "RuntimeLayerModel.h"
#include "RuntimeJson.h"
#include <chrono>
#include <condition_variable>
#include <filesystem>
#include <mutex>
#include <string>
#include <thread>
namespace RenderCadenceCompositor
{
JsonValue RuntimeStateSnapshotToJson(const RuntimeLayerModelSnapshot& snapshot);
std::string SerializeRuntimeStateSnapshot(const RuntimeLayerModelSnapshot& snapshot);
class RuntimeStatePersistenceWriter
{
public:
RuntimeStatePersistenceWriter() = default;
RuntimeStatePersistenceWriter(const RuntimeStatePersistenceWriter&) = delete;
RuntimeStatePersistenceWriter& operator=(const RuntimeStatePersistenceWriter&) = delete;
~RuntimeStatePersistenceWriter();
void Start(const std::filesystem::path& path, std::chrono::milliseconds debounce);
void RequestSave(const RuntimeLayerModelSnapshot& snapshot);
void Stop(bool flushPending = true);
private:
void ThreadMain();
bool WriteSnapshot(const std::string& serializedState, std::string& error) const;
std::filesystem::path mPath;
std::chrono::milliseconds mDebounce{ 250 };
std::mutex mMutex;
std::condition_variable mCondition;
std::thread mThread;
std::string mPendingState;
bool mHasPendingState = false;
bool mStopping = false;
};
}