2 Commits

Author SHA1 Message Date
Aiden
c5fd8e72b4 non-blocking http
All checks were successful
CI / React UI Build (push) Successful in 11s
CI / Native Windows Build And Tests (push) Successful in 2m58s
CI / Windows Release Package (push) Has been skipped
2026-05-12 15:05:54 +10:00
Aiden
95b4a54326 Seperation 2026-05-12 14:57:18 +10:00
8 changed files with 384 additions and 252 deletions

View File

@@ -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"

View File

@@ -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.";

View File

@@ -0,0 +1,272 @@
#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)
{
CleanupRetiredShaderBuilds();
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)
{
CleanupRetiredShaderBuilds();
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);
RetireLayerShaderBuild(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)
{
CleanupRetiredShaderBuilds();
RetireLayerShaderBuild(layerId);
{
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);
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::RetireLayerShaderBuild(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->RequestStop();
mRetiredShaderBuilds.push_back(std::move(bridge));
}
}
void RuntimeLayerController::CleanupRetiredShaderBuilds()
{
std::vector<std::unique_ptr<RuntimeShaderBridge>> readyToStop;
{
std::lock_guard<std::mutex> lock(mShaderBuildMutex);
for (auto it = mRetiredShaderBuilds.begin(); it != mRetiredShaderBuilds.end();)
{
if ((*it)->CanStopWithoutWaiting())
{
readyToStop.push_back(std::move(*it));
it = mRetiredShaderBuilds.erase(it);
continue;
}
++it;
}
}
for (std::unique_ptr<RuntimeShaderBridge>& bridge : readyToStop)
bridge->Stop();
}
void RuntimeLayerController::StopAllRuntimeShaderBuilds()
{
std::map<std::string, std::unique_ptr<RuntimeShaderBridge>> builds;
std::vector<std::unique_ptr<RuntimeShaderBridge>> retiredBuilds;
{
std::lock_guard<std::mutex> lock(mShaderBuildMutex);
builds.swap(mShaderBuilds);
retiredBuilds.swap(mRetiredShaderBuilds);
}
for (auto& entry : builds)
entry.second->Stop();
for (auto& bridge : retiredBuilds)
bridge->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;
}
}

View File

@@ -0,0 +1,61 @@
#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 RetireLayerShaderBuild(const std::string& layerId);
void CleanupRetiredShaderBuilds();
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;
std::vector<std::unique_ptr<RuntimeShaderBridge>> mRetiredShaderBuilds;
};
}

View File

@@ -0,0 +1,12 @@
#pragma once
#include <string>
namespace RenderCadenceCompositor
{
struct ControlActionResult
{
bool ok = false;
std::string error;
};
}

View File

@@ -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;

View File

@@ -22,19 +22,31 @@ void RuntimeShaderBridge::Start(const std::string& layerId, const std::string& s
mOnArtifactReady = std::move(onArtifactReady); mOnArtifactReady = std::move(onArtifactReady);
mOnError = std::move(onError); mOnError = std::move(onError);
mStopping.store(false, std::memory_order_release); mStopping.store(false, std::memory_order_release);
mFinished.store(false, std::memory_order_release);
mCompiler.StartShaderBuild(shaderId); mCompiler.StartShaderBuild(shaderId);
mThread = std::thread([this]() { ThreadMain(); }); mThread = std::thread([this]() { ThreadMain(); });
} }
void RuntimeShaderBridge::Stop() void RuntimeShaderBridge::RequestStop()
{ {
mStopping.store(true, std::memory_order_release); mStopping.store(true, std::memory_order_release);
}
void RuntimeShaderBridge::Stop()
{
RequestStop();
if (mThread.joinable()) if (mThread.joinable())
mThread.join(); mThread.join();
mCompiler.Stop(); mCompiler.Stop();
mLayerId.clear(); mLayerId.clear();
mOnArtifactReady = ArtifactCallback(); mOnArtifactReady = ArtifactCallback();
mOnError = ErrorCallback(); mOnError = ErrorCallback();
mFinished.store(true, std::memory_order_release);
}
bool RuntimeShaderBridge::CanStopWithoutWaiting() const
{
return mFinished.load(std::memory_order_acquire) && !mCompiler.Running();
} }
void RuntimeShaderBridge::ThreadMain() void RuntimeShaderBridge::ThreadMain()
@@ -54,8 +66,10 @@ void RuntimeShaderBridge::ThreadMain()
{ {
mOnError(build.message); mOnError(build.message);
} }
mFinished.store(true, std::memory_order_release);
return; return;
} }
std::this_thread::sleep_for(std::chrono::milliseconds(5)); std::this_thread::sleep_for(std::chrono::milliseconds(5));
} }
mFinished.store(true, std::memory_order_release);
} }

View File

@@ -21,7 +21,9 @@ public:
void Start(const std::string& shaderId, ArtifactCallback onArtifactReady, ErrorCallback onError); void Start(const std::string& shaderId, ArtifactCallback onArtifactReady, ErrorCallback onError);
void Start(const std::string& layerId, const std::string& shaderId, ArtifactCallback onArtifactReady, ErrorCallback onError); void Start(const std::string& layerId, const std::string& shaderId, ArtifactCallback onArtifactReady, ErrorCallback onError);
void RequestStop();
void Stop(); void Stop();
bool CanStopWithoutWaiting() const;
private: private:
void ThreadMain(); void ThreadMain();
@@ -29,6 +31,7 @@ private:
RuntimeSlangShaderCompiler mCompiler; RuntimeSlangShaderCompiler mCompiler;
std::thread mThread; std::thread mThread;
std::atomic<bool> mStopping{ false }; std::atomic<bool> mStopping{ false };
std::atomic<bool> mFinished{ true };
std::string mLayerId; std::string mLayerId;
ArtifactCallback mOnArtifactReady; ArtifactCallback mOnArtifactReady;
ErrorCallback mOnError; ErrorCallback mOnError;