#include "RuntimeLayerController.h" #include "AppConfigProvider.h" #include "RuntimeJson.h" #include "../logging/Logger.h" #include 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 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 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 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 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 lock(mRuntimeLayerMutex); std::string error; mRuntimeLayerModel.MarkBuildStarted(layerId, "Runtime Slang build started for shader '" + shaderId + "'.", error); } auto bridge = std::make_unique(); RuntimeShaderBridge* bridgePtr = bridge.get(); { std::lock_guard 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 bridge; { std::lock_guard 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> readyToStop; { std::lock_guard 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& bridge : readyToStop) bridge->Stop(); } void RuntimeLayerController::StopAllRuntimeShaderBuilds() { std::map> builds; std::vector> retiredBuilds; { std::lock_guard 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 renderLayers; { std::lock_guard lock(mRuntimeLayerMutex); renderLayers = mRuntimeLayerModel.Snapshot().renderLayers; } mPublisher(renderLayers); } bool RuntimeLayerController::MarkRuntimeBuildReady(const RuntimeShaderArtifact& artifact) { std::lock_guard 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 lock(mRuntimeLayerMutex); std::string error; if (!mRuntimeLayerModel.MarkBuildFailed(layerId, message, error)) LogWarning("runtime-shader", error); } std::string RuntimeLayerController::FirstRuntimeLayerId() const { std::lock_guard 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; } }