Seperation
This commit is contained in:
@@ -307,6 +307,9 @@ set(RENDER_CADENCE_APP_SOURCES
|
|||||||
"${RENDER_CADENCE_APP_DIR}/app/AppConfigProvider.cpp"
|
"${RENDER_CADENCE_APP_DIR}/app/AppConfigProvider.cpp"
|
||||||
"${RENDER_CADENCE_APP_DIR}/app/AppConfigProvider.h"
|
"${RENDER_CADENCE_APP_DIR}/app/AppConfigProvider.h"
|
||||||
"${RENDER_CADENCE_APP_DIR}/app/RenderCadenceApp.h"
|
"${RENDER_CADENCE_APP_DIR}/app/RenderCadenceApp.h"
|
||||||
|
"${RENDER_CADENCE_APP_DIR}/app/RuntimeLayerController.cpp"
|
||||||
|
"${RENDER_CADENCE_APP_DIR}/app/RuntimeLayerController.h"
|
||||||
|
"${RENDER_CADENCE_APP_DIR}/control/ControlActionResult.h"
|
||||||
"${RENDER_CADENCE_APP_DIR}/control/HttpControlServer.cpp"
|
"${RENDER_CADENCE_APP_DIR}/control/HttpControlServer.cpp"
|
||||||
"${RENDER_CADENCE_APP_DIR}/control/HttpControlServer.h"
|
"${RENDER_CADENCE_APP_DIR}/control/HttpControlServer.h"
|
||||||
"${RENDER_CADENCE_APP_DIR}/control/RuntimeStateJson.h"
|
"${RENDER_CADENCE_APP_DIR}/control/RuntimeStateJson.h"
|
||||||
|
|||||||
@@ -2,21 +2,15 @@
|
|||||||
|
|
||||||
#include "AppConfig.h"
|
#include "AppConfig.h"
|
||||||
#include "AppConfigProvider.h"
|
#include "AppConfigProvider.h"
|
||||||
|
#include "RuntimeLayerController.h"
|
||||||
#include "../logging/Logger.h"
|
#include "../logging/Logger.h"
|
||||||
#include "../runtime/RuntimeLayerModel.h"
|
|
||||||
#include "../runtime/RuntimeShaderBridge.h"
|
|
||||||
#include "../runtime/SupportedShaderCatalog.h"
|
|
||||||
#include "../control/RuntimeStateJson.h"
|
#include "../control/RuntimeStateJson.h"
|
||||||
#include "../telemetry/TelemetryHealthMonitor.h"
|
#include "../telemetry/TelemetryHealthMonitor.h"
|
||||||
#include "../video/DeckLinkOutput.h"
|
#include "../video/DeckLinkOutput.h"
|
||||||
#include "../video/DeckLinkOutputThread.h"
|
#include "../video/DeckLinkOutputThread.h"
|
||||||
#include "RuntimeJson.h"
|
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <map>
|
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
@@ -60,7 +54,10 @@ public:
|
|||||||
mFrameExchange(frameExchange),
|
mFrameExchange(frameExchange),
|
||||||
mConfig(config),
|
mConfig(config),
|
||||||
mOutputThread(mOutput, mFrameExchange, mConfig.outputThread),
|
mOutputThread(mOutput, mFrameExchange, mConfig.outputThread),
|
||||||
mTelemetryHealth(mConfig.telemetry)
|
mTelemetryHealth(mConfig.telemetry),
|
||||||
|
mRuntimeLayers([this](const std::vector<RuntimeRenderLayerModel>& layers) {
|
||||||
|
mRenderThread.SubmitRuntimeRenderLayers(layers);
|
||||||
|
})
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,8 +71,10 @@ public:
|
|||||||
|
|
||||||
bool Start(std::string& error)
|
bool Start(std::string& error)
|
||||||
{
|
{
|
||||||
LoadSupportedShaderCatalog();
|
mRuntimeLayers.Initialize(
|
||||||
InitializeRuntimeLayerModel();
|
mConfig.shaderLibrary,
|
||||||
|
static_cast<unsigned>(mConfig.maxTemporalHistoryFrames),
|
||||||
|
mConfig.runtimeShaderId);
|
||||||
|
|
||||||
Log("app", "Starting render thread.");
|
Log("app", "Starting render thread.");
|
||||||
if (!detail::StartRenderThread(mRenderThread, error, 0))
|
if (!detail::StartRenderThread(mRenderThread, error, 0))
|
||||||
@@ -84,7 +83,7 @@ public:
|
|||||||
Stop();
|
Stop();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
StartRuntimeShaderBuild();
|
mRuntimeLayers.StartStartupBuild(mConfig.runtimeShaderId);
|
||||||
|
|
||||||
Log("app", "Waiting for rendered warmup frames.");
|
Log("app", "Waiting for rendered warmup frames.");
|
||||||
if (!mFrameExchange.WaitForCompletedDepth(mConfig.warmupCompletedFrames, mConfig.warmupTimeout))
|
if (!mFrameExchange.WaitForCompletedDepth(mConfig.warmupCompletedFrames, mConfig.warmupTimeout))
|
||||||
@@ -109,7 +108,7 @@ public:
|
|||||||
mTelemetryHealth.Stop();
|
mTelemetryHealth.Stop();
|
||||||
mOutputThread.Stop();
|
mOutputThread.Stop();
|
||||||
mOutput.Stop();
|
mOutput.Stop();
|
||||||
StopRuntimeShaderBuild();
|
mRuntimeLayers.Stop();
|
||||||
mRenderThread.Stop();
|
mRenderThread.Stop();
|
||||||
mOutput.ReleaseResources();
|
mOutput.ReleaseResources();
|
||||||
if (mStarted)
|
if (mStarted)
|
||||||
@@ -180,10 +179,10 @@ private:
|
|||||||
return BuildStateJson();
|
return BuildStateJson();
|
||||||
};
|
};
|
||||||
callbacks.addLayer = [this](const std::string& body) {
|
callbacks.addLayer = [this](const std::string& body) {
|
||||||
return HandleAddLayer(body);
|
return mRuntimeLayers.HandleAddLayer(body);
|
||||||
};
|
};
|
||||||
callbacks.removeLayer = [this](const std::string& body) {
|
callbacks.removeLayer = [this](const std::string& body) {
|
||||||
return HandleRemoveLayer(body);
|
return mRuntimeLayers.HandleRemoveLayer(body);
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string error;
|
std::string error;
|
||||||
@@ -202,14 +201,14 @@ private:
|
|||||||
std::string BuildStateJson()
|
std::string BuildStateJson()
|
||||||
{
|
{
|
||||||
CadenceTelemetrySnapshot telemetry = mHttpTelemetry.Sample(mFrameExchange, mOutput, mOutputThread, mRenderThread);
|
CadenceTelemetrySnapshot telemetry = mHttpTelemetry.Sample(mFrameExchange, mOutput, mOutputThread, mRenderThread);
|
||||||
RuntimeLayerModelSnapshot layerSnapshot = CopyRuntimeLayerSnapshot(telemetry);
|
RuntimeLayerModelSnapshot layerSnapshot = mRuntimeLayers.Snapshot(telemetry);
|
||||||
return RuntimeStateToJson(RuntimeStateJsonInput{
|
return RuntimeStateToJson(RuntimeStateJsonInput{
|
||||||
mConfig,
|
mConfig,
|
||||||
telemetry,
|
telemetry,
|
||||||
mHttpServer.Port(),
|
mHttpServer.Port(),
|
||||||
mVideoOutputEnabled,
|
mVideoOutputEnabled,
|
||||||
mVideoOutputStatus,
|
mVideoOutputStatus,
|
||||||
mShaderCatalog,
|
mRuntimeLayers.ShaderCatalog(),
|
||||||
layerSnapshot
|
layerSnapshot
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -226,230 +225,6 @@ private:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void StartRuntimeShaderBuild()
|
|
||||||
{
|
|
||||||
if (mConfig.runtimeShaderId.empty())
|
|
||||||
{
|
|
||||||
Log("runtime-shader", "Runtime shader build disabled.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Log("runtime-shader", "Starting background Slang build for shader '" + mConfig.runtimeShaderId + "'.");
|
|
||||||
const std::string layerId = FirstRuntimeLayerId();
|
|
||||||
if (!layerId.empty())
|
|
||||||
StartLayerShaderBuild(layerId, mConfig.runtimeShaderId);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LoadSupportedShaderCatalog()
|
|
||||||
{
|
|
||||||
const std::filesystem::path shaderRoot = FindRepoPath(mConfig.shaderLibrary);
|
|
||||||
std::string error;
|
|
||||||
if (!mShaderCatalog.Load(shaderRoot, static_cast<unsigned>(mConfig.maxTemporalHistoryFrames), error))
|
|
||||||
{
|
|
||||||
LogWarning("runtime-shader", "Supported shader catalog is empty: " + error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Log("runtime-shader", "Supported shader catalog loaded with " + std::to_string(mShaderCatalog.Shaders().size()) + " shader(s).");
|
|
||||||
}
|
|
||||||
|
|
||||||
void InitializeRuntimeLayerModel()
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
|
|
||||||
std::string error;
|
|
||||||
if (!mRuntimeLayerModel.InitializeSingleLayer(mShaderCatalog, mConfig.runtimeShaderId, error))
|
|
||||||
{
|
|
||||||
LogWarning("runtime-shader", error + " Runtime shader build disabled.");
|
|
||||||
mConfig.runtimeShaderId.clear();
|
|
||||||
mRuntimeLayerModel.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void StopRuntimeShaderBuild()
|
|
||||||
{
|
|
||||||
StopAllRuntimeShaderBuilds();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MarkRuntimeBuildStarted(const std::string& message)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
|
|
||||||
std::string error;
|
|
||||||
const std::string layerId = mRuntimeLayerModel.FirstLayerId();
|
|
||||||
if (!layerId.empty())
|
|
||||||
mRuntimeLayerModel.MarkBuildStarted(layerId, message, error);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MarkRuntimeBuildReady(const RuntimeShaderArtifact& artifact)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
|
|
||||||
std::string error;
|
|
||||||
if (!mRuntimeLayerModel.MarkBuildReady(artifact, error))
|
|
||||||
{
|
|
||||||
LogWarning("runtime-shader", error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MarkRuntimeBuildFailed(const std::string& message)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
|
|
||||||
if (!mRuntimeLayerModel.MarkBuildFailedForShader(mConfig.runtimeShaderId, message))
|
|
||||||
LogWarning("runtime-shader", "Runtime shader failed without a matching display layer: " + message);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MarkRuntimeBuildFailedForLayer(const std::string& layerId, const std::string& message)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
|
|
||||||
std::string error;
|
|
||||||
if (!mRuntimeLayerModel.MarkBuildFailed(layerId, message, error))
|
|
||||||
LogWarning("runtime-shader", error);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string FirstRuntimeLayerId() const
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
|
|
||||||
return mRuntimeLayerModel.FirstLayerId();
|
|
||||||
}
|
|
||||||
|
|
||||||
void StartLayerShaderBuild(const std::string& layerId, const std::string& shaderId)
|
|
||||||
{
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
|
|
||||||
std::string error;
|
|
||||||
mRuntimeLayerModel.MarkBuildStarted(layerId, "Runtime Slang build started for shader '" + shaderId + "'.", error);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto bridge = std::make_unique<RuntimeShaderBridge>();
|
|
||||||
RuntimeShaderBridge* bridgePtr = bridge.get();
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(mShaderBuildMutex);
|
|
||||||
auto existingIt = mShaderBuilds.find(layerId);
|
|
||||||
if (existingIt != mShaderBuilds.end())
|
|
||||||
existingIt->second->Stop();
|
|
||||||
mShaderBuilds[layerId] = std::move(bridge);
|
|
||||||
}
|
|
||||||
|
|
||||||
bridgePtr->Start(
|
|
||||||
layerId,
|
|
||||||
shaderId,
|
|
||||||
[this](const RuntimeShaderArtifact& artifact) {
|
|
||||||
if (MarkRuntimeBuildReady(artifact))
|
|
||||||
PublishRuntimeRenderLayers();
|
|
||||||
},
|
|
||||||
[this, layerId](const std::string& message) {
|
|
||||||
MarkRuntimeBuildFailedForLayer(layerId, message);
|
|
||||||
LogError("runtime-shader", "Runtime Slang build failed: " + message);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void StopLayerShaderBuild(const std::string& layerId)
|
|
||||||
{
|
|
||||||
std::unique_ptr<RuntimeShaderBridge> bridge;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(mShaderBuildMutex);
|
|
||||||
auto bridgeIt = mShaderBuilds.find(layerId);
|
|
||||||
if (bridgeIt == mShaderBuilds.end())
|
|
||||||
return;
|
|
||||||
bridge = std::move(bridgeIt->second);
|
|
||||||
mShaderBuilds.erase(bridgeIt);
|
|
||||||
}
|
|
||||||
bridge->Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void StopAllRuntimeShaderBuilds()
|
|
||||||
{
|
|
||||||
std::map<std::string, std::unique_ptr<RuntimeShaderBridge>> builds;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(mShaderBuildMutex);
|
|
||||||
builds.swap(mShaderBuilds);
|
|
||||||
}
|
|
||||||
for (auto& entry : builds)
|
|
||||||
entry.second->Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
ControlActionResult HandleAddLayer(const std::string& body)
|
|
||||||
{
|
|
||||||
std::string shaderId;
|
|
||||||
std::string error;
|
|
||||||
if (!ExtractStringField(body, "shaderId", shaderId, error))
|
|
||||||
return { false, error };
|
|
||||||
|
|
||||||
std::string layerId;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
|
|
||||||
if (!mRuntimeLayerModel.AddLayer(mShaderCatalog, shaderId, layerId, error))
|
|
||||||
return { false, error };
|
|
||||||
}
|
|
||||||
|
|
||||||
Log("runtime-shader", "Layer added: " + layerId + " shader=" + shaderId);
|
|
||||||
StartLayerShaderBuild(layerId, shaderId);
|
|
||||||
return { true, std::string() };
|
|
||||||
}
|
|
||||||
|
|
||||||
ControlActionResult HandleRemoveLayer(const std::string& body)
|
|
||||||
{
|
|
||||||
std::string layerId;
|
|
||||||
std::string error;
|
|
||||||
if (!ExtractStringField(body, "layerId", layerId, error))
|
|
||||||
return { false, error };
|
|
||||||
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
|
|
||||||
if (!mRuntimeLayerModel.RemoveLayer(layerId, error))
|
|
||||||
return { false, error };
|
|
||||||
}
|
|
||||||
|
|
||||||
Log("runtime-shader", "Layer removed: " + layerId);
|
|
||||||
StopLayerShaderBuild(layerId);
|
|
||||||
PublishRuntimeRenderLayers();
|
|
||||||
return { true, std::string() };
|
|
||||||
}
|
|
||||||
|
|
||||||
void PublishRuntimeRenderLayers()
|
|
||||||
{
|
|
||||||
std::vector<RuntimeRenderLayerModel> renderLayers;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
|
|
||||||
renderLayers = mRuntimeLayerModel.Snapshot().renderLayers;
|
|
||||||
}
|
|
||||||
mRenderThread.SubmitRuntimeRenderLayers(renderLayers);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool ExtractStringField(const std::string& body, const char* fieldName, std::string& value, std::string& error)
|
|
||||||
{
|
|
||||||
JsonValue root;
|
|
||||||
std::string parseError;
|
|
||||||
if (!ParseJson(body.empty() ? "{}" : body, root, parseError) || !root.isObject())
|
|
||||||
{
|
|
||||||
error = parseError.empty() ? "Request body must be a JSON object." : parseError;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const JsonValue* field = root.find(fieldName);
|
|
||||||
if (!field || !field->isString() || field->asString().empty())
|
|
||||||
{
|
|
||||||
error = std::string("Request field '") + fieldName + "' must be a non-empty string.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
value = field->asString();
|
|
||||||
error.clear();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
RuntimeLayerModelSnapshot CopyRuntimeLayerSnapshot(const CadenceTelemetrySnapshot& telemetry) const
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
|
|
||||||
RuntimeLayerModelSnapshot snapshot = mRuntimeLayerModel.Snapshot();
|
|
||||||
if (telemetry.shaderBuildFailures > 0)
|
|
||||||
{
|
|
||||||
snapshot.compileSucceeded = false;
|
|
||||||
snapshot.compileMessage = "Runtime shader GL commit failed; see logs for details.";
|
|
||||||
}
|
|
||||||
return snapshot;
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderThread& mRenderThread;
|
RenderThread& mRenderThread;
|
||||||
SystemFrameExchange& mFrameExchange;
|
SystemFrameExchange& mFrameExchange;
|
||||||
AppConfig mConfig;
|
AppConfig mConfig;
|
||||||
@@ -458,11 +233,7 @@ private:
|
|||||||
TelemetryHealthMonitor mTelemetryHealth;
|
TelemetryHealthMonitor mTelemetryHealth;
|
||||||
CadenceTelemetry mHttpTelemetry;
|
CadenceTelemetry mHttpTelemetry;
|
||||||
HttpControlServer mHttpServer;
|
HttpControlServer mHttpServer;
|
||||||
SupportedShaderCatalog mShaderCatalog;
|
RuntimeLayerController mRuntimeLayers;
|
||||||
mutable std::mutex mRuntimeLayerMutex;
|
|
||||||
RuntimeLayerModel mRuntimeLayerModel;
|
|
||||||
std::mutex mShaderBuildMutex;
|
|
||||||
std::map<std::string, std::unique_ptr<RuntimeShaderBridge>> mShaderBuilds;
|
|
||||||
bool mStarted = false;
|
bool mStarted = false;
|
||||||
bool mVideoOutputEnabled = false;
|
bool mVideoOutputEnabled = false;
|
||||||
std::string mVideoOutputStatus = "DeckLink output not started.";
|
std::string mVideoOutputStatus = "DeckLink output not started.";
|
||||||
|
|||||||
242
apps/RenderCadenceCompositor/app/RuntimeLayerController.cpp
Normal file
242
apps/RenderCadenceCompositor/app/RuntimeLayerController.cpp
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
#include "RuntimeLayerController.h"
|
||||||
|
|
||||||
|
#include "AppConfigProvider.h"
|
||||||
|
#include "RuntimeJson.h"
|
||||||
|
#include "../logging/Logger.h"
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
namespace RenderCadenceCompositor
|
||||||
|
{
|
||||||
|
RuntimeLayerController::RuntimeLayerController(RenderLayerPublisher publisher) :
|
||||||
|
mPublisher(std::move(publisher))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
RuntimeLayerController::~RuntimeLayerController()
|
||||||
|
{
|
||||||
|
Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RuntimeLayerController::SetPublisher(RenderLayerPublisher publisher)
|
||||||
|
{
|
||||||
|
mPublisher = std::move(publisher);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RuntimeLayerController::Initialize(const std::string& shaderLibrary, unsigned maxTemporalHistoryFrames, std::string& runtimeShaderId)
|
||||||
|
{
|
||||||
|
LoadSupportedShaderCatalog(shaderLibrary, maxTemporalHistoryFrames);
|
||||||
|
InitializeLayerModel(runtimeShaderId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RuntimeLayerController::StartStartupBuild(const std::string& runtimeShaderId)
|
||||||
|
{
|
||||||
|
if (runtimeShaderId.empty())
|
||||||
|
{
|
||||||
|
Log("runtime-shader", "Runtime shader build disabled.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log("runtime-shader", "Starting background Slang build for shader '" + runtimeShaderId + "'.");
|
||||||
|
const std::string layerId = FirstRuntimeLayerId();
|
||||||
|
if (!layerId.empty())
|
||||||
|
StartLayerShaderBuild(layerId, runtimeShaderId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RuntimeLayerController::Stop()
|
||||||
|
{
|
||||||
|
StopAllRuntimeShaderBuilds();
|
||||||
|
}
|
||||||
|
|
||||||
|
ControlActionResult RuntimeLayerController::HandleAddLayer(const std::string& body)
|
||||||
|
{
|
||||||
|
std::string shaderId;
|
||||||
|
std::string error;
|
||||||
|
if (!ExtractStringField(body, "shaderId", shaderId, error))
|
||||||
|
return { false, error };
|
||||||
|
|
||||||
|
std::string layerId;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
|
||||||
|
if (!mRuntimeLayerModel.AddLayer(mShaderCatalog, shaderId, layerId, error))
|
||||||
|
return { false, error };
|
||||||
|
}
|
||||||
|
|
||||||
|
Log("runtime-shader", "Layer added: " + layerId + " shader=" + shaderId);
|
||||||
|
StartLayerShaderBuild(layerId, shaderId);
|
||||||
|
return { true, std::string() };
|
||||||
|
}
|
||||||
|
|
||||||
|
ControlActionResult RuntimeLayerController::HandleRemoveLayer(const std::string& body)
|
||||||
|
{
|
||||||
|
std::string layerId;
|
||||||
|
std::string error;
|
||||||
|
if (!ExtractStringField(body, "layerId", layerId, error))
|
||||||
|
return { false, error };
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
|
||||||
|
if (!mRuntimeLayerModel.RemoveLayer(layerId, error))
|
||||||
|
return { false, error };
|
||||||
|
}
|
||||||
|
|
||||||
|
Log("runtime-shader", "Layer removed: " + layerId);
|
||||||
|
StopLayerShaderBuild(layerId);
|
||||||
|
PublishRuntimeRenderLayers();
|
||||||
|
return { true, std::string() };
|
||||||
|
}
|
||||||
|
|
||||||
|
RuntimeLayerModelSnapshot RuntimeLayerController::Snapshot(const CadenceTelemetrySnapshot& telemetry) const
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
|
||||||
|
RuntimeLayerModelSnapshot snapshot = mRuntimeLayerModel.Snapshot();
|
||||||
|
if (telemetry.shaderBuildFailures > 0)
|
||||||
|
{
|
||||||
|
snapshot.compileSucceeded = false;
|
||||||
|
snapshot.compileMessage = "Runtime shader GL commit failed; see logs for details.";
|
||||||
|
}
|
||||||
|
return snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RuntimeLayerController::LoadSupportedShaderCatalog(const std::string& shaderLibrary, unsigned maxTemporalHistoryFrames)
|
||||||
|
{
|
||||||
|
const std::filesystem::path shaderRoot = FindRepoPath(shaderLibrary);
|
||||||
|
std::string error;
|
||||||
|
if (!mShaderCatalog.Load(shaderRoot, maxTemporalHistoryFrames, error))
|
||||||
|
{
|
||||||
|
LogWarning("runtime-shader", "Supported shader catalog is empty: " + error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log("runtime-shader", "Supported shader catalog loaded with " + std::to_string(mShaderCatalog.Shaders().size()) + " shader(s).");
|
||||||
|
}
|
||||||
|
|
||||||
|
void RuntimeLayerController::InitializeLayerModel(std::string& runtimeShaderId)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
|
||||||
|
std::string error;
|
||||||
|
if (!mRuntimeLayerModel.InitializeSingleLayer(mShaderCatalog, runtimeShaderId, error))
|
||||||
|
{
|
||||||
|
LogWarning("runtime-shader", error + " Runtime shader build disabled.");
|
||||||
|
runtimeShaderId.clear();
|
||||||
|
mRuntimeLayerModel.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RuntimeLayerController::StartLayerShaderBuild(const std::string& layerId, const std::string& shaderId)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
|
||||||
|
std::string error;
|
||||||
|
mRuntimeLayerModel.MarkBuildStarted(layerId, "Runtime Slang build started for shader '" + shaderId + "'.", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto bridge = std::make_unique<RuntimeShaderBridge>();
|
||||||
|
RuntimeShaderBridge* bridgePtr = bridge.get();
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mShaderBuildMutex);
|
||||||
|
auto existingIt = mShaderBuilds.find(layerId);
|
||||||
|
if (existingIt != mShaderBuilds.end())
|
||||||
|
existingIt->second->Stop();
|
||||||
|
mShaderBuilds[layerId] = std::move(bridge);
|
||||||
|
}
|
||||||
|
|
||||||
|
bridgePtr->Start(
|
||||||
|
layerId,
|
||||||
|
shaderId,
|
||||||
|
[this](const RuntimeShaderArtifact& artifact) {
|
||||||
|
if (MarkRuntimeBuildReady(artifact))
|
||||||
|
PublishRuntimeRenderLayers();
|
||||||
|
},
|
||||||
|
[this, layerId](const std::string& message) {
|
||||||
|
MarkRuntimeBuildFailedForLayer(layerId, message);
|
||||||
|
LogError("runtime-shader", "Runtime Slang build failed: " + message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void RuntimeLayerController::StopLayerShaderBuild(const std::string& layerId)
|
||||||
|
{
|
||||||
|
std::unique_ptr<RuntimeShaderBridge> bridge;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mShaderBuildMutex);
|
||||||
|
auto bridgeIt = mShaderBuilds.find(layerId);
|
||||||
|
if (bridgeIt == mShaderBuilds.end())
|
||||||
|
return;
|
||||||
|
bridge = std::move(bridgeIt->second);
|
||||||
|
mShaderBuilds.erase(bridgeIt);
|
||||||
|
}
|
||||||
|
bridge->Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RuntimeLayerController::StopAllRuntimeShaderBuilds()
|
||||||
|
{
|
||||||
|
std::map<std::string, std::unique_ptr<RuntimeShaderBridge>> builds;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mShaderBuildMutex);
|
||||||
|
builds.swap(mShaderBuilds);
|
||||||
|
}
|
||||||
|
for (auto& entry : builds)
|
||||||
|
entry.second->Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RuntimeLayerController::PublishRuntimeRenderLayers()
|
||||||
|
{
|
||||||
|
if (!mPublisher)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::vector<RuntimeRenderLayerModel> renderLayers;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
|
||||||
|
renderLayers = mRuntimeLayerModel.Snapshot().renderLayers;
|
||||||
|
}
|
||||||
|
mPublisher(renderLayers);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RuntimeLayerController::MarkRuntimeBuildReady(const RuntimeShaderArtifact& artifact)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
|
||||||
|
std::string error;
|
||||||
|
if (!mRuntimeLayerModel.MarkBuildReady(artifact, error))
|
||||||
|
{
|
||||||
|
LogWarning("runtime-shader", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RuntimeLayerController::MarkRuntimeBuildFailedForLayer(const std::string& layerId, const std::string& message)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
|
||||||
|
std::string error;
|
||||||
|
if (!mRuntimeLayerModel.MarkBuildFailed(layerId, message, error))
|
||||||
|
LogWarning("runtime-shader", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string RuntimeLayerController::FirstRuntimeLayerId() const
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
|
||||||
|
return mRuntimeLayerModel.FirstLayerId();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RuntimeLayerController::ExtractStringField(const std::string& body, const char* fieldName, std::string& value, std::string& error)
|
||||||
|
{
|
||||||
|
JsonValue root;
|
||||||
|
std::string parseError;
|
||||||
|
if (!ParseJson(body.empty() ? "{}" : body, root, parseError) || !root.isObject())
|
||||||
|
{
|
||||||
|
error = parseError.empty() ? "Request body must be a JSON object." : parseError;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const JsonValue* field = root.find(fieldName);
|
||||||
|
if (!field || !field->isString() || field->asString().empty())
|
||||||
|
{
|
||||||
|
error = std::string("Request field '") + fieldName + "' must be a non-empty string.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = field->asString();
|
||||||
|
error.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
59
apps/RenderCadenceCompositor/app/RuntimeLayerController.h
Normal file
59
apps/RenderCadenceCompositor/app/RuntimeLayerController.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../control/ControlActionResult.h"
|
||||||
|
#include "../runtime/RuntimeLayerModel.h"
|
||||||
|
#include "../runtime/RuntimeShaderBridge.h"
|
||||||
|
#include "../runtime/SupportedShaderCatalog.h"
|
||||||
|
#include "../telemetry/CadenceTelemetry.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace RenderCadenceCompositor
|
||||||
|
{
|
||||||
|
class RuntimeLayerController
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using RenderLayerPublisher = std::function<void(const std::vector<RuntimeRenderLayerModel>&)>;
|
||||||
|
|
||||||
|
explicit RuntimeLayerController(RenderLayerPublisher publisher = RenderLayerPublisher());
|
||||||
|
RuntimeLayerController(const RuntimeLayerController&) = delete;
|
||||||
|
RuntimeLayerController& operator=(const RuntimeLayerController&) = delete;
|
||||||
|
~RuntimeLayerController();
|
||||||
|
|
||||||
|
void SetPublisher(RenderLayerPublisher publisher);
|
||||||
|
void Initialize(const std::string& shaderLibrary, unsigned maxTemporalHistoryFrames, std::string& runtimeShaderId);
|
||||||
|
void StartStartupBuild(const std::string& runtimeShaderId);
|
||||||
|
void Stop();
|
||||||
|
|
||||||
|
ControlActionResult HandleAddLayer(const std::string& body);
|
||||||
|
ControlActionResult HandleRemoveLayer(const std::string& body);
|
||||||
|
|
||||||
|
RuntimeLayerModelSnapshot Snapshot(const CadenceTelemetrySnapshot& telemetry) const;
|
||||||
|
const SupportedShaderCatalog& ShaderCatalog() const { return mShaderCatalog; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void LoadSupportedShaderCatalog(const std::string& shaderLibrary, unsigned maxTemporalHistoryFrames);
|
||||||
|
void InitializeLayerModel(std::string& runtimeShaderId);
|
||||||
|
void StartLayerShaderBuild(const std::string& layerId, const std::string& shaderId);
|
||||||
|
void StopLayerShaderBuild(const std::string& layerId);
|
||||||
|
void StopAllRuntimeShaderBuilds();
|
||||||
|
void PublishRuntimeRenderLayers();
|
||||||
|
bool MarkRuntimeBuildReady(const RuntimeShaderArtifact& artifact);
|
||||||
|
void MarkRuntimeBuildFailedForLayer(const std::string& layerId, const std::string& message);
|
||||||
|
|
||||||
|
std::string FirstRuntimeLayerId() const;
|
||||||
|
static bool ExtractStringField(const std::string& body, const char* fieldName, std::string& value, std::string& error);
|
||||||
|
|
||||||
|
RenderLayerPublisher mPublisher;
|
||||||
|
SupportedShaderCatalog mShaderCatalog;
|
||||||
|
mutable std::mutex mRuntimeLayerMutex;
|
||||||
|
RuntimeLayerModel mRuntimeLayerModel;
|
||||||
|
std::mutex mShaderBuildMutex;
|
||||||
|
std::map<std::string, std::unique_ptr<RuntimeShaderBridge>> mShaderBuilds;
|
||||||
|
};
|
||||||
|
}
|
||||||
12
apps/RenderCadenceCompositor/control/ControlActionResult.h
Normal file
12
apps/RenderCadenceCompositor/control/ControlActionResult.h
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace RenderCadenceCompositor
|
||||||
|
{
|
||||||
|
struct ControlActionResult
|
||||||
|
{
|
||||||
|
bool ok = false;
|
||||||
|
std::string error;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "ControlActionResult.h"
|
||||||
|
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
@@ -19,12 +21,6 @@ struct HttpControlServerConfig
|
|||||||
std::chrono::milliseconds idleSleep = std::chrono::milliseconds(10);
|
std::chrono::milliseconds idleSleep = std::chrono::milliseconds(10);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ControlActionResult
|
|
||||||
{
|
|
||||||
bool ok = false;
|
|
||||||
std::string error;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct HttpControlServerCallbacks
|
struct HttpControlServerCallbacks
|
||||||
{
|
{
|
||||||
std::function<std::string()> getStateJson;
|
std::function<std::string()> getStateJson;
|
||||||
|
|||||||
Reference in New Issue
Block a user