More changes
This commit is contained in:
@@ -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)
|
||||
|
||||
206
src/runtime/RuntimeStatePersistence.cpp
Normal file
206
src/runtime/RuntimeStatePersistence.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
43
src/runtime/RuntimeStatePersistence.h
Normal file
43
src/runtime/RuntimeStatePersistence.h
Normal 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;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user