Seperation
This commit is contained in:
@@ -2,21 +2,15 @@
|
||||
|
||||
#include "AppConfig.h"
|
||||
#include "AppConfigProvider.h"
|
||||
#include "RuntimeLayerController.h"
|
||||
#include "../logging/Logger.h"
|
||||
#include "../runtime/RuntimeLayerModel.h"
|
||||
#include "../runtime/RuntimeShaderBridge.h"
|
||||
#include "../runtime/SupportedShaderCatalog.h"
|
||||
#include "../control/RuntimeStateJson.h"
|
||||
#include "../telemetry/TelemetryHealthMonitor.h"
|
||||
#include "../video/DeckLinkOutput.h"
|
||||
#include "../video/DeckLinkOutputThread.h"
|
||||
#include "RuntimeJson.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
@@ -60,7 +54,10 @@ public:
|
||||
mFrameExchange(frameExchange),
|
||||
mConfig(config),
|
||||
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)
|
||||
{
|
||||
LoadSupportedShaderCatalog();
|
||||
InitializeRuntimeLayerModel();
|
||||
mRuntimeLayers.Initialize(
|
||||
mConfig.shaderLibrary,
|
||||
static_cast<unsigned>(mConfig.maxTemporalHistoryFrames),
|
||||
mConfig.runtimeShaderId);
|
||||
|
||||
Log("app", "Starting render thread.");
|
||||
if (!detail::StartRenderThread(mRenderThread, error, 0))
|
||||
@@ -84,7 +83,7 @@ public:
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
StartRuntimeShaderBuild();
|
||||
mRuntimeLayers.StartStartupBuild(mConfig.runtimeShaderId);
|
||||
|
||||
Log("app", "Waiting for rendered warmup frames.");
|
||||
if (!mFrameExchange.WaitForCompletedDepth(mConfig.warmupCompletedFrames, mConfig.warmupTimeout))
|
||||
@@ -109,7 +108,7 @@ public:
|
||||
mTelemetryHealth.Stop();
|
||||
mOutputThread.Stop();
|
||||
mOutput.Stop();
|
||||
StopRuntimeShaderBuild();
|
||||
mRuntimeLayers.Stop();
|
||||
mRenderThread.Stop();
|
||||
mOutput.ReleaseResources();
|
||||
if (mStarted)
|
||||
@@ -180,10 +179,10 @@ private:
|
||||
return BuildStateJson();
|
||||
};
|
||||
callbacks.addLayer = [this](const std::string& body) {
|
||||
return HandleAddLayer(body);
|
||||
return mRuntimeLayers.HandleAddLayer(body);
|
||||
};
|
||||
callbacks.removeLayer = [this](const std::string& body) {
|
||||
return HandleRemoveLayer(body);
|
||||
return mRuntimeLayers.HandleRemoveLayer(body);
|
||||
};
|
||||
|
||||
std::string error;
|
||||
@@ -202,14 +201,14 @@ private:
|
||||
std::string BuildStateJson()
|
||||
{
|
||||
CadenceTelemetrySnapshot telemetry = mHttpTelemetry.Sample(mFrameExchange, mOutput, mOutputThread, mRenderThread);
|
||||
RuntimeLayerModelSnapshot layerSnapshot = CopyRuntimeLayerSnapshot(telemetry);
|
||||
RuntimeLayerModelSnapshot layerSnapshot = mRuntimeLayers.Snapshot(telemetry);
|
||||
return RuntimeStateToJson(RuntimeStateJsonInput{
|
||||
mConfig,
|
||||
telemetry,
|
||||
mHttpServer.Port(),
|
||||
mVideoOutputEnabled,
|
||||
mVideoOutputStatus,
|
||||
mShaderCatalog,
|
||||
mRuntimeLayers.ShaderCatalog(),
|
||||
layerSnapshot
|
||||
});
|
||||
}
|
||||
@@ -226,230 +225,6 @@ private:
|
||||
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;
|
||||
SystemFrameExchange& mFrameExchange;
|
||||
AppConfig mConfig;
|
||||
@@ -458,11 +233,7 @@ private:
|
||||
TelemetryHealthMonitor mTelemetryHealth;
|
||||
CadenceTelemetry mHttpTelemetry;
|
||||
HttpControlServer mHttpServer;
|
||||
SupportedShaderCatalog mShaderCatalog;
|
||||
mutable std::mutex mRuntimeLayerMutex;
|
||||
RuntimeLayerModel mRuntimeLayerModel;
|
||||
std::mutex mShaderBuildMutex;
|
||||
std::map<std::string, std::unique_ptr<RuntimeShaderBridge>> mShaderBuilds;
|
||||
RuntimeLayerController mRuntimeLayers;
|
||||
bool mStarted = false;
|
||||
bool mVideoOutputEnabled = false;
|
||||
std::string mVideoOutputStatus = "DeckLink output not started.";
|
||||
|
||||
Reference in New Issue
Block a user