shader control
This commit is contained in:
@@ -290,6 +290,8 @@ set(RENDER_CADENCE_APP_SOURCES
|
|||||||
"${APP_DIR}/gl/shader/Std140Buffer.h"
|
"${APP_DIR}/gl/shader/Std140Buffer.h"
|
||||||
"${APP_DIR}/runtime/support/RuntimeJson.cpp"
|
"${APP_DIR}/runtime/support/RuntimeJson.cpp"
|
||||||
"${APP_DIR}/runtime/support/RuntimeJson.h"
|
"${APP_DIR}/runtime/support/RuntimeJson.h"
|
||||||
|
"${APP_DIR}/runtime/support/RuntimeParameterUtils.cpp"
|
||||||
|
"${APP_DIR}/runtime/support/RuntimeParameterUtils.h"
|
||||||
"${APP_DIR}/shader/ShaderCompiler.cpp"
|
"${APP_DIR}/shader/ShaderCompiler.cpp"
|
||||||
"${APP_DIR}/shader/ShaderCompiler.h"
|
"${APP_DIR}/shader/ShaderCompiler.h"
|
||||||
"${APP_DIR}/shader/ShaderPackageRegistry.cpp"
|
"${APP_DIR}/shader/ShaderPackageRegistry.cpp"
|
||||||
@@ -310,6 +312,8 @@ set(RENDER_CADENCE_APP_SOURCES
|
|||||||
"${RENDER_CADENCE_APP_DIR}/app/RuntimeLayerController.cpp"
|
"${RENDER_CADENCE_APP_DIR}/app/RuntimeLayerController.cpp"
|
||||||
"${RENDER_CADENCE_APP_DIR}/app/RuntimeLayerController.h"
|
"${RENDER_CADENCE_APP_DIR}/app/RuntimeLayerController.h"
|
||||||
"${RENDER_CADENCE_APP_DIR}/control/ControlActionResult.h"
|
"${RENDER_CADENCE_APP_DIR}/control/ControlActionResult.h"
|
||||||
|
"${RENDER_CADENCE_APP_DIR}/control/RuntimeControlCommand.cpp"
|
||||||
|
"${RENDER_CADENCE_APP_DIR}/control/RuntimeControlCommand.h"
|
||||||
"${RENDER_CADENCE_APP_DIR}/control/http/HttpControlServer.cpp"
|
"${RENDER_CADENCE_APP_DIR}/control/http/HttpControlServer.cpp"
|
||||||
"${RENDER_CADENCE_APP_DIR}/control/http/HttpControlServer.h"
|
"${RENDER_CADENCE_APP_DIR}/control/http/HttpControlServer.h"
|
||||||
"${RENDER_CADENCE_APP_DIR}/control/http/HttpControlServerWebSocket.cpp"
|
"${RENDER_CADENCE_APP_DIR}/control/http/HttpControlServerWebSocket.cpp"
|
||||||
@@ -841,6 +845,7 @@ add_executable(RenderCadenceCompositorRuntimeLayerModelTests
|
|||||||
"${RENDER_CADENCE_APP_DIR}/runtime/SupportedShaderCatalog.cpp"
|
"${RENDER_CADENCE_APP_DIR}/runtime/SupportedShaderCatalog.cpp"
|
||||||
"${APP_DIR}/shader/ShaderPackageRegistry.cpp"
|
"${APP_DIR}/shader/ShaderPackageRegistry.cpp"
|
||||||
"${APP_DIR}/runtime/support/RuntimeJson.cpp"
|
"${APP_DIR}/runtime/support/RuntimeJson.cpp"
|
||||||
|
"${APP_DIR}/runtime/support/RuntimeParameterUtils.cpp"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/tests/RenderCadenceCompositorRuntimeLayerModelTests.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/tests/RenderCadenceCompositorRuntimeLayerModelTests.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -909,6 +914,7 @@ add_test(NAME RenderCadenceCompositorJsonWriterTests COMMAND RenderCadenceCompos
|
|||||||
|
|
||||||
add_executable(RenderCadenceCompositorRuntimeStateJsonTests
|
add_executable(RenderCadenceCompositorRuntimeStateJsonTests
|
||||||
"${APP_DIR}/runtime/support/RuntimeJson.cpp"
|
"${APP_DIR}/runtime/support/RuntimeJson.cpp"
|
||||||
|
"${APP_DIR}/runtime/support/RuntimeParameterUtils.cpp"
|
||||||
"${APP_DIR}/shader/ShaderPackageRegistry.cpp"
|
"${APP_DIR}/shader/ShaderPackageRegistry.cpp"
|
||||||
"${RENDER_CADENCE_APP_DIR}/app/AppConfig.cpp"
|
"${RENDER_CADENCE_APP_DIR}/app/AppConfig.cpp"
|
||||||
"${RENDER_CADENCE_APP_DIR}/app/AppConfigProvider.cpp"
|
"${RENDER_CADENCE_APP_DIR}/app/AppConfigProvider.cpp"
|
||||||
@@ -940,6 +946,8 @@ endif()
|
|||||||
add_test(NAME RenderCadenceCompositorRuntimeStateJsonTests COMMAND RenderCadenceCompositorRuntimeStateJsonTests)
|
add_test(NAME RenderCadenceCompositorRuntimeStateJsonTests COMMAND RenderCadenceCompositorRuntimeStateJsonTests)
|
||||||
|
|
||||||
add_executable(RenderCadenceCompositorHttpControlServerTests
|
add_executable(RenderCadenceCompositorHttpControlServerTests
|
||||||
|
"${APP_DIR}/runtime/support/RuntimeJson.cpp"
|
||||||
|
"${RENDER_CADENCE_APP_DIR}/control/RuntimeControlCommand.cpp"
|
||||||
"${RENDER_CADENCE_APP_DIR}/control/http/HttpControlServer.cpp"
|
"${RENDER_CADENCE_APP_DIR}/control/http/HttpControlServer.cpp"
|
||||||
"${RENDER_CADENCE_APP_DIR}/control/http/HttpControlServerWebSocket.cpp"
|
"${RENDER_CADENCE_APP_DIR}/control/http/HttpControlServerWebSocket.cpp"
|
||||||
"${RENDER_CADENCE_APP_DIR}/json/JsonWriter.cpp"
|
"${RENDER_CADENCE_APP_DIR}/json/JsonWriter.cpp"
|
||||||
@@ -948,6 +956,8 @@ add_executable(RenderCadenceCompositorHttpControlServerTests
|
|||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(RenderCadenceCompositorHttpControlServerTests PRIVATE
|
target_include_directories(RenderCadenceCompositorHttpControlServerTests PRIVATE
|
||||||
|
"${APP_DIR}"
|
||||||
|
"${APP_DIR}/runtime/support"
|
||||||
"${RENDER_CADENCE_APP_DIR}/control"
|
"${RENDER_CADENCE_APP_DIR}/control"
|
||||||
"${RENDER_CADENCE_APP_DIR}/control/http"
|
"${RENDER_CADENCE_APP_DIR}/control/http"
|
||||||
"${RENDER_CADENCE_APP_DIR}/json"
|
"${RENDER_CADENCE_APP_DIR}/json"
|
||||||
|
|||||||
@@ -198,10 +198,10 @@ Current endpoints:
|
|||||||
- `GET /ws`: upgrades to a WebSocket and streams state snapshots when they change
|
- `GET /ws`: upgrades to a WebSocket and streams state snapshots when they change
|
||||||
- `GET /docs/openapi.yaml` and `GET /openapi.yaml`: serves the OpenAPI document
|
- `GET /docs/openapi.yaml` and `GET /openapi.yaml`: serves the OpenAPI document
|
||||||
- `GET /docs`: serves Swagger UI
|
- `GET /docs`: serves Swagger UI
|
||||||
- `POST /api/layers/add` and `POST /api/layers/remove` mutate the app-owned display layer model only
|
- `POST /api/layers/add`, `/remove`, `/reorder`, `/set-bypass`, `/set-shader`, `/update-parameter`, and `/reset-parameters` use the shared runtime control-command path
|
||||||
- other OpenAPI POST routes are present but return `{ "ok": false, "error": "Endpoint is not implemented in RenderCadenceCompositor yet." }`
|
- other OpenAPI POST routes are present but return `{ "ok": false, "error": "Endpoint is not implemented in RenderCadenceCompositor yet." }`
|
||||||
|
|
||||||
The HTTP server runs on its own thread. It serves static UI/docs files, samples/copies telemetry through callbacks, and does not call render work or DeckLink scheduling.
|
The HTTP server runs on its own thread. It serves static UI/docs files, samples/copies telemetry through callbacks, and translates POST bodies into runtime control commands. Command execution is app-owned, so future OSC ingress can create the same commands without depending on HTTP route code. Control commands may update the display layer model, start background shader builds, or publish an already-built render-layer snapshot, but they do not call render work or DeckLink scheduling directly.
|
||||||
|
|
||||||
## Optional DeckLink Output
|
## Optional DeckLink Output
|
||||||
|
|
||||||
|
|||||||
@@ -184,6 +184,13 @@ private:
|
|||||||
callbacks.removeLayer = [this](const std::string& body) {
|
callbacks.removeLayer = [this](const std::string& body) {
|
||||||
return mRuntimeLayers.HandleRemoveLayer(body);
|
return mRuntimeLayers.HandleRemoveLayer(body);
|
||||||
};
|
};
|
||||||
|
callbacks.executePost = [this](const std::string& path, const std::string& body) {
|
||||||
|
RuntimeControlCommand command;
|
||||||
|
std::string error;
|
||||||
|
if (!ParseRuntimeControlCommand(path, body, command, error))
|
||||||
|
return ControlActionResult{ false, error };
|
||||||
|
return mRuntimeLayers.HandleControlCommand(command);
|
||||||
|
};
|
||||||
|
|
||||||
std::string error;
|
std::string error;
|
||||||
if (!mHttpServer.Start(
|
if (!mHttpServer.Start(
|
||||||
|
|||||||
@@ -90,6 +90,95 @@ ControlActionResult RuntimeLayerController::HandleRemoveLayer(const std::string&
|
|||||||
return { true, std::string() };
|
return { true, std::string() };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ControlActionResult RuntimeLayerController::HandleControlCommand(const RuntimeControlCommand& command)
|
||||||
|
{
|
||||||
|
CleanupRetiredShaderBuilds();
|
||||||
|
|
||||||
|
std::string error;
|
||||||
|
switch (command.type)
|
||||||
|
{
|
||||||
|
case RuntimeControlCommandType::AddLayer:
|
||||||
|
{
|
||||||
|
std::string layerId;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
|
||||||
|
if (!mRuntimeLayerModel.AddLayer(mShaderCatalog, command.shaderId, layerId, error))
|
||||||
|
return { false, error };
|
||||||
|
}
|
||||||
|
Log("runtime-shader", "Layer added: " + layerId + " shader=" + command.shaderId);
|
||||||
|
StartLayerShaderBuild(layerId, command.shaderId);
|
||||||
|
return { true, std::string() };
|
||||||
|
}
|
||||||
|
case RuntimeControlCommandType::RemoveLayer:
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
|
||||||
|
if (!mRuntimeLayerModel.RemoveLayer(command.layerId, error))
|
||||||
|
return { false, error };
|
||||||
|
}
|
||||||
|
Log("runtime-shader", "Layer removed: " + command.layerId);
|
||||||
|
RetireLayerShaderBuild(command.layerId);
|
||||||
|
PublishRuntimeRenderLayers();
|
||||||
|
return { true, std::string() };
|
||||||
|
}
|
||||||
|
case RuntimeControlCommandType::ReorderLayer:
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
|
||||||
|
if (!mRuntimeLayerModel.ReorderLayer(command.layerId, command.targetIndex, error))
|
||||||
|
return { false, error };
|
||||||
|
}
|
||||||
|
PublishRuntimeRenderLayers();
|
||||||
|
return { true, std::string() };
|
||||||
|
}
|
||||||
|
case RuntimeControlCommandType::SetLayerBypass:
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
|
||||||
|
if (!mRuntimeLayerModel.SetLayerBypass(command.layerId, command.bypass, error))
|
||||||
|
return { false, error };
|
||||||
|
}
|
||||||
|
PublishRuntimeRenderLayers();
|
||||||
|
return { true, std::string() };
|
||||||
|
}
|
||||||
|
case RuntimeControlCommandType::SetLayerShader:
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
|
||||||
|
if (!mRuntimeLayerModel.SetLayerShader(mShaderCatalog, command.layerId, command.shaderId, error))
|
||||||
|
return { false, error };
|
||||||
|
}
|
||||||
|
Log("runtime-shader", "Layer shader changed: " + command.layerId + " shader=" + command.shaderId);
|
||||||
|
StartLayerShaderBuild(command.layerId, command.shaderId);
|
||||||
|
return { true, std::string() };
|
||||||
|
}
|
||||||
|
case RuntimeControlCommandType::UpdateLayerParameter:
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
|
||||||
|
if (!mRuntimeLayerModel.UpdateParameter(command.layerId, command.parameterId, command.value, error))
|
||||||
|
return { false, error };
|
||||||
|
}
|
||||||
|
PublishRuntimeRenderLayers();
|
||||||
|
return { true, std::string() };
|
||||||
|
}
|
||||||
|
case RuntimeControlCommandType::ResetLayerParameters:
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
|
||||||
|
if (!mRuntimeLayerModel.ResetParameters(command.layerId, error))
|
||||||
|
return { false, error };
|
||||||
|
}
|
||||||
|
PublishRuntimeRenderLayers();
|
||||||
|
return { true, std::string() };
|
||||||
|
}
|
||||||
|
case RuntimeControlCommandType::Unsupported:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { false, "Unsupported runtime control command." };
|
||||||
|
}
|
||||||
|
|
||||||
RuntimeLayerModelSnapshot RuntimeLayerController::Snapshot(const CadenceTelemetrySnapshot& telemetry) const
|
RuntimeLayerModelSnapshot RuntimeLayerController::Snapshot(const CadenceTelemetrySnapshot& telemetry) const
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
|
std::lock_guard<std::mutex> lock(mRuntimeLayerMutex);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../control/ControlActionResult.h"
|
#include "../control/ControlActionResult.h"
|
||||||
|
#include "../control/RuntimeControlCommand.h"
|
||||||
#include "../runtime/RuntimeLayerModel.h"
|
#include "../runtime/RuntimeLayerModel.h"
|
||||||
#include "../runtime/RuntimeShaderBridge.h"
|
#include "../runtime/RuntimeShaderBridge.h"
|
||||||
#include "../runtime/SupportedShaderCatalog.h"
|
#include "../runtime/SupportedShaderCatalog.h"
|
||||||
@@ -32,6 +33,7 @@ public:
|
|||||||
|
|
||||||
ControlActionResult HandleAddLayer(const std::string& body);
|
ControlActionResult HandleAddLayer(const std::string& body);
|
||||||
ControlActionResult HandleRemoveLayer(const std::string& body);
|
ControlActionResult HandleRemoveLayer(const std::string& body);
|
||||||
|
ControlActionResult HandleControlCommand(const RuntimeControlCommand& command);
|
||||||
|
|
||||||
RuntimeLayerModelSnapshot Snapshot(const CadenceTelemetrySnapshot& telemetry) const;
|
RuntimeLayerModelSnapshot Snapshot(const CadenceTelemetrySnapshot& telemetry) const;
|
||||||
const SupportedShaderCatalog& ShaderCatalog() const { return mShaderCatalog; }
|
const SupportedShaderCatalog& ShaderCatalog() const { return mShaderCatalog; }
|
||||||
|
|||||||
127
apps/RenderCadenceCompositor/control/RuntimeControlCommand.cpp
Normal file
127
apps/RenderCadenceCompositor/control/RuntimeControlCommand.cpp
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
#include "RuntimeControlCommand.h"
|
||||||
|
|
||||||
|
namespace RenderCadenceCompositor
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
const JsonValue* RequireObjectField(const JsonValue& root, const char* fieldName, std::string& error)
|
||||||
|
{
|
||||||
|
const JsonValue* field = root.find(fieldName);
|
||||||
|
if (!field)
|
||||||
|
error = std::string("Request field '") + fieldName + "' is required.";
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RequireStringField(const JsonValue& root, const char* fieldName, std::string& value, std::string& error)
|
||||||
|
{
|
||||||
|
const JsonValue* field = RequireObjectField(root, fieldName, error);
|
||||||
|
if (!field)
|
||||||
|
return false;
|
||||||
|
if (!field->isString() || field->asString().empty())
|
||||||
|
{
|
||||||
|
error = std::string("Request field '") + fieldName + "' must be a non-empty string.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
value = field->asString();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RequireBoolField(const JsonValue& root, const char* fieldName, bool& value, std::string& error)
|
||||||
|
{
|
||||||
|
const JsonValue* field = RequireObjectField(root, fieldName, error);
|
||||||
|
if (!field)
|
||||||
|
return false;
|
||||||
|
if (!field->isBoolean())
|
||||||
|
{
|
||||||
|
error = std::string("Request field '") + fieldName + "' must be a boolean.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
value = field->asBoolean();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RequireIntegerField(const JsonValue& root, const char* fieldName, int& value, std::string& error)
|
||||||
|
{
|
||||||
|
const JsonValue* field = RequireObjectField(root, fieldName, error);
|
||||||
|
if (!field)
|
||||||
|
return false;
|
||||||
|
if (!field->isNumber())
|
||||||
|
{
|
||||||
|
error = std::string("Request field '") + fieldName + "' must be a number.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
value = static_cast<int>(field->asNumber());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ParseRuntimeControlCommand(
|
||||||
|
const std::string& path,
|
||||||
|
const std::string& body,
|
||||||
|
RuntimeControlCommand& command,
|
||||||
|
std::string& error)
|
||||||
|
{
|
||||||
|
command = RuntimeControlCommand();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path == "/api/layers/add")
|
||||||
|
{
|
||||||
|
command.type = RuntimeControlCommandType::AddLayer;
|
||||||
|
return RequireStringField(root, "shaderId", command.shaderId, error);
|
||||||
|
}
|
||||||
|
if (path == "/api/layers/remove")
|
||||||
|
{
|
||||||
|
command.type = RuntimeControlCommandType::RemoveLayer;
|
||||||
|
return RequireStringField(root, "layerId", command.layerId, error);
|
||||||
|
}
|
||||||
|
if (path == "/api/layers/reorder")
|
||||||
|
{
|
||||||
|
command.type = RuntimeControlCommandType::ReorderLayer;
|
||||||
|
return RequireStringField(root, "layerId", command.layerId, error)
|
||||||
|
&& RequireIntegerField(root, "targetIndex", command.targetIndex, error);
|
||||||
|
}
|
||||||
|
if (path == "/api/layers/set-bypass")
|
||||||
|
{
|
||||||
|
command.type = RuntimeControlCommandType::SetLayerBypass;
|
||||||
|
return RequireStringField(root, "layerId", command.layerId, error)
|
||||||
|
&& RequireBoolField(root, "bypass", command.bypass, error);
|
||||||
|
}
|
||||||
|
if (path == "/api/layers/set-shader")
|
||||||
|
{
|
||||||
|
command.type = RuntimeControlCommandType::SetLayerShader;
|
||||||
|
return RequireStringField(root, "layerId", command.layerId, error)
|
||||||
|
&& RequireStringField(root, "shaderId", command.shaderId, error);
|
||||||
|
}
|
||||||
|
if (path == "/api/layers/update-parameter")
|
||||||
|
{
|
||||||
|
command.type = RuntimeControlCommandType::UpdateLayerParameter;
|
||||||
|
const JsonValue* value = nullptr;
|
||||||
|
if (!RequireStringField(root, "layerId", command.layerId, error)
|
||||||
|
|| !RequireStringField(root, "parameterId", command.parameterId, error))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
value = RequireObjectField(root, "value", error);
|
||||||
|
if (!value)
|
||||||
|
return false;
|
||||||
|
command.value = *value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (path == "/api/layers/reset-parameters")
|
||||||
|
{
|
||||||
|
command.type = RuntimeControlCommandType::ResetLayerParameters;
|
||||||
|
return RequireStringField(root, "layerId", command.layerId, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
command.type = RuntimeControlCommandType::Unsupported;
|
||||||
|
error = "Endpoint is not implemented in RenderCadenceCompositor yet.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
37
apps/RenderCadenceCompositor/control/RuntimeControlCommand.h
Normal file
37
apps/RenderCadenceCompositor/control/RuntimeControlCommand.h
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "RuntimeJson.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace RenderCadenceCompositor
|
||||||
|
{
|
||||||
|
enum class RuntimeControlCommandType
|
||||||
|
{
|
||||||
|
AddLayer,
|
||||||
|
RemoveLayer,
|
||||||
|
ReorderLayer,
|
||||||
|
SetLayerBypass,
|
||||||
|
SetLayerShader,
|
||||||
|
UpdateLayerParameter,
|
||||||
|
ResetLayerParameters,
|
||||||
|
Unsupported
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RuntimeControlCommand
|
||||||
|
{
|
||||||
|
RuntimeControlCommandType type = RuntimeControlCommandType::Unsupported;
|
||||||
|
std::string layerId;
|
||||||
|
std::string shaderId;
|
||||||
|
std::string parameterId;
|
||||||
|
int targetIndex = 0;
|
||||||
|
bool bypass = false;
|
||||||
|
JsonValue value;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool ParseRuntimeControlCommand(
|
||||||
|
const std::string& path,
|
||||||
|
const std::string& body,
|
||||||
|
RuntimeControlCommand& command,
|
||||||
|
std::string& error);
|
||||||
|
}
|
||||||
@@ -93,6 +93,31 @@ inline void WriteDefaultParameterValue(JsonWriter& writer, const ShaderParameter
|
|||||||
writer.Null();
|
writer.Null();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void WriteParameterValue(JsonWriter& writer, const ShaderParameterDefinition& parameter, const ShaderParameterValue& value)
|
||||||
|
{
|
||||||
|
switch (parameter.type)
|
||||||
|
{
|
||||||
|
case ShaderParameterType::Boolean:
|
||||||
|
writer.Bool(value.booleanValue);
|
||||||
|
return;
|
||||||
|
case ShaderParameterType::Enum:
|
||||||
|
writer.String(value.enumValue);
|
||||||
|
return;
|
||||||
|
case ShaderParameterType::Text:
|
||||||
|
writer.String(value.textValue);
|
||||||
|
return;
|
||||||
|
case ShaderParameterType::Trigger:
|
||||||
|
case ShaderParameterType::Float:
|
||||||
|
writer.Double(value.numberValues.empty() ? 0.0 : value.numberValues.front());
|
||||||
|
return;
|
||||||
|
case ShaderParameterType::Vec2:
|
||||||
|
case ShaderParameterType::Color:
|
||||||
|
WriteNumberArray(writer, value.numberValues);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
writer.Null();
|
||||||
|
}
|
||||||
|
|
||||||
inline void WriteTemporalJson(JsonWriter& writer, const TemporalSettings& temporal)
|
inline void WriteTemporalJson(JsonWriter& writer, const TemporalSettings& temporal)
|
||||||
{
|
{
|
||||||
writer.BeginObject();
|
writer.BeginObject();
|
||||||
@@ -122,7 +147,7 @@ inline const char* RuntimeLayerBuildStateName(RuntimeLayerBuildState state)
|
|||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void WriteParameterDefinitionJson(JsonWriter& writer, const ShaderParameterDefinition& parameter)
|
inline void WriteParameterDefinitionJson(JsonWriter& writer, const ShaderParameterDefinition& parameter, const ShaderParameterValue* value)
|
||||||
{
|
{
|
||||||
writer.BeginObject();
|
writer.BeginObject();
|
||||||
writer.KeyString("id", parameter.id);
|
writer.KeyString("id", parameter.id);
|
||||||
@@ -132,7 +157,10 @@ inline void WriteParameterDefinitionJson(JsonWriter& writer, const ShaderParamet
|
|||||||
writer.Key("defaultValue");
|
writer.Key("defaultValue");
|
||||||
WriteDefaultParameterValue(writer, parameter);
|
WriteDefaultParameterValue(writer, parameter);
|
||||||
writer.Key("value");
|
writer.Key("value");
|
||||||
WriteDefaultParameterValue(writer, parameter);
|
if (value)
|
||||||
|
WriteParameterValue(writer, parameter, *value);
|
||||||
|
else
|
||||||
|
WriteDefaultParameterValue(writer, parameter);
|
||||||
|
|
||||||
if (!parameter.minNumbers.empty())
|
if (!parameter.minNumbers.empty())
|
||||||
{
|
{
|
||||||
@@ -197,10 +225,10 @@ inline void WriteLayersJson(JsonWriter& writer, const RuntimeStateJsonInput& inp
|
|||||||
WriteFeedbackJson(writer, FeedbackSettings());
|
WriteFeedbackJson(writer, FeedbackSettings());
|
||||||
writer.Key("parameters");
|
writer.Key("parameters");
|
||||||
writer.BeginArray();
|
writer.BeginArray();
|
||||||
if (shaderPackage)
|
for (const ShaderParameterDefinition& parameter : layer.parameterDefinitions)
|
||||||
{
|
{
|
||||||
for (const ShaderParameterDefinition& parameter : shaderPackage->parameters)
|
const auto valueIt = layer.parameterValues.find(parameter.id);
|
||||||
WriteParameterDefinitionJson(writer, parameter);
|
WriteParameterDefinitionJson(writer, parameter, valueIt == layer.parameterValues.end() ? nullptr : &valueIt->second);
|
||||||
}
|
}
|
||||||
writer.EndArray();
|
writer.EndArray();
|
||||||
writer.EndObject();
|
writer.EndObject();
|
||||||
|
|||||||
@@ -286,6 +286,12 @@ HttpControlServer::HttpResponse HttpControlServer::ServePost(const HttpRequest&
|
|||||||
if (!IsKnownPostEndpoint(request.path))
|
if (!IsKnownPostEndpoint(request.path))
|
||||||
return TextResponse("404 Not Found", "Not Found");
|
return TextResponse("404 Not Found", "Not Found");
|
||||||
|
|
||||||
|
if (mCallbacks.executePost)
|
||||||
|
{
|
||||||
|
const ControlActionResult result = mCallbacks.executePost(request.path, request.body);
|
||||||
|
return JsonResponse(result.ok ? "200 OK" : "400 Bad Request", ActionResponse(result.ok, result.error));
|
||||||
|
}
|
||||||
|
|
||||||
if (request.path == "/api/layers/add" && mCallbacks.addLayer)
|
if (request.path == "/api/layers/add" && mCallbacks.addLayer)
|
||||||
{
|
{
|
||||||
const ControlActionResult result = mCallbacks.addLayer(request.body);
|
const ControlActionResult result = mCallbacks.addLayer(request.body);
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ struct HttpControlServerCallbacks
|
|||||||
std::function<std::string()> getStateJson;
|
std::function<std::string()> getStateJson;
|
||||||
std::function<ControlActionResult(const std::string&)> addLayer;
|
std::function<ControlActionResult(const std::string&)> addLayer;
|
||||||
std::function<ControlActionResult(const std::string&)> removeLayer;
|
std::function<ControlActionResult(const std::string&)> removeLayer;
|
||||||
|
std::function<ControlActionResult(const std::string&, const std::string&)> executePost;
|
||||||
};
|
};
|
||||||
|
|
||||||
class UniqueSocket
|
class UniqueSocket
|
||||||
|
|||||||
@@ -28,7 +28,10 @@ bool RuntimeRenderScene::CommitRenderLayers(const std::vector<RenderCadenceCompo
|
|||||||
std::vector<RenderCadenceCompositor::RuntimeRenderLayerModel> layersToPrepare;
|
std::vector<RenderCadenceCompositor::RuntimeRenderLayerModel> layersToPrepare;
|
||||||
nextOrder.reserve(layers.size());
|
nextOrder.reserve(layers.size());
|
||||||
for (const RenderCadenceCompositor::RuntimeRenderLayerModel& layer : layers)
|
for (const RenderCadenceCompositor::RuntimeRenderLayerModel& layer : layers)
|
||||||
nextOrder.push_back(layer.id);
|
{
|
||||||
|
if (!layer.bypass)
|
||||||
|
nextOrder.push_back(layer.id);
|
||||||
|
}
|
||||||
|
|
||||||
for (auto layerIt = mLayers.begin(); layerIt != mLayers.end();)
|
for (auto layerIt = mLayers.begin(); layerIt != mLayers.end();)
|
||||||
{
|
{
|
||||||
@@ -50,6 +53,8 @@ bool RuntimeRenderScene::CommitRenderLayers(const std::vector<RenderCadenceCompo
|
|||||||
|
|
||||||
for (const RenderCadenceCompositor::RuntimeRenderLayerModel& layer : layers)
|
for (const RenderCadenceCompositor::RuntimeRenderLayerModel& layer : layers)
|
||||||
{
|
{
|
||||||
|
if (layer.bypass)
|
||||||
|
continue;
|
||||||
if (layer.artifact.passes.empty() && layer.artifact.fragmentShaderSource.empty())
|
if (layer.artifact.passes.empty() && layer.artifact.fragmentShaderSource.empty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -73,7 +78,14 @@ bool RuntimeRenderScene::CommitRenderLayers(const std::vector<RenderCadenceCompo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (program->shaderId == layer.shaderId && program->sourceFingerprint == fingerprint && hasReadyPass)
|
if (program->shaderId == layer.shaderId && program->sourceFingerprint == fingerprint && hasReadyPass)
|
||||||
|
{
|
||||||
|
for (LayerProgram::PassProgram& pass : program->passes)
|
||||||
|
{
|
||||||
|
if (pass.renderer)
|
||||||
|
pass.renderer->UpdateArtifactState(layer.artifact);
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
if (program->pendingFingerprint == fingerprint)
|
if (program->pendingFingerprint == fingerprint)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|||||||
@@ -82,7 +82,10 @@ std::vector<unsigned char> BuildRuntimeShaderGlobalParamsStd140(
|
|||||||
|
|
||||||
for (const ShaderParameterDefinition& definition : artifact.parameterDefinitions)
|
for (const ShaderParameterDefinition& definition : artifact.parameterDefinitions)
|
||||||
{
|
{
|
||||||
const ShaderParameterValue value = DefaultValueForDefinition(definition);
|
const auto valueIt = artifact.parameterValues.find(definition.id);
|
||||||
|
const ShaderParameterValue value = valueIt == artifact.parameterValues.end()
|
||||||
|
? DefaultValueForDefinition(definition)
|
||||||
|
: valueIt->second;
|
||||||
switch (definition.type)
|
switch (definition.type)
|
||||||
{
|
{
|
||||||
case ShaderParameterType::Float:
|
case ShaderParameterType::Float:
|
||||||
|
|||||||
@@ -94,6 +94,13 @@ bool RuntimeShaderRenderer::CommitPreparedProgram(RuntimePreparedShaderProgram&
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RuntimeShaderRenderer::UpdateArtifactState(const RuntimeShaderArtifact& artifact)
|
||||||
|
{
|
||||||
|
mArtifact.parameterDefinitions = artifact.parameterDefinitions;
|
||||||
|
mArtifact.parameterValues = artifact.parameterValues;
|
||||||
|
mArtifact.message = artifact.message;
|
||||||
|
}
|
||||||
|
|
||||||
bool RuntimeShaderRenderer::BuildPreparedProgram(
|
bool RuntimeShaderRenderer::BuildPreparedProgram(
|
||||||
const std::string& layerId,
|
const std::string& layerId,
|
||||||
const std::string& sourceFingerprint,
|
const std::string& sourceFingerprint,
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ public:
|
|||||||
bool CommitShaderArtifact(const RuntimeShaderArtifact& artifact, std::string& error);
|
bool CommitShaderArtifact(const RuntimeShaderArtifact& artifact, std::string& error);
|
||||||
bool CommitPreparedProgram(RuntimePreparedShaderProgram& preparedProgram, std::string& error);
|
bool CommitPreparedProgram(RuntimePreparedShaderProgram& preparedProgram, std::string& error);
|
||||||
bool HasProgram() const { return mProgram != 0; }
|
bool HasProgram() const { return mProgram != 0; }
|
||||||
|
void UpdateArtifactState(const RuntimeShaderArtifact& artifact);
|
||||||
void RenderFrame(uint64_t frameIndex, unsigned width, unsigned height, GLuint sourceTexture = 0, GLuint layerInputTexture = 0);
|
void RenderFrame(uint64_t frameIndex, unsigned width, unsigned height, GLuint sourceTexture = 0, GLuint layerInputTexture = 0);
|
||||||
void ShutdownGl();
|
void ShutdownGl();
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
#include "RuntimeLayerModel.h"
|
#include "RuntimeLayerModel.h"
|
||||||
|
|
||||||
|
#include "RuntimeParameterUtils.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace RenderCadenceCompositor
|
namespace RenderCadenceCompositor
|
||||||
@@ -26,6 +29,7 @@ bool RuntimeLayerModel::InitializeSingleLayer(const SupportedShaderCatalog& shad
|
|||||||
layer.shaderName = shaderPackage->displayName.empty() ? shaderPackage->id : shaderPackage->displayName;
|
layer.shaderName = shaderPackage->displayName.empty() ? shaderPackage->id : shaderPackage->displayName;
|
||||||
layer.buildState = RuntimeLayerBuildState::Pending;
|
layer.buildState = RuntimeLayerBuildState::Pending;
|
||||||
layer.message = "Runtime Slang build is waiting to start.";
|
layer.message = "Runtime Slang build is waiting to start.";
|
||||||
|
InitializeDefaultParameterValues(layer, *shaderPackage);
|
||||||
mLayers.push_back(std::move(layer));
|
mLayers.push_back(std::move(layer));
|
||||||
error.clear();
|
error.clear();
|
||||||
return true;
|
return true;
|
||||||
@@ -46,6 +50,7 @@ bool RuntimeLayerModel::AddLayer(const SupportedShaderCatalog& shaderCatalog, co
|
|||||||
layer.shaderName = shaderPackage->displayName.empty() ? shaderPackage->id : shaderPackage->displayName;
|
layer.shaderName = shaderPackage->displayName.empty() ? shaderPackage->id : shaderPackage->displayName;
|
||||||
layer.buildState = RuntimeLayerBuildState::Pending;
|
layer.buildState = RuntimeLayerBuildState::Pending;
|
||||||
layer.message = "Runtime Slang build is waiting to start.";
|
layer.message = "Runtime Slang build is waiting to start.";
|
||||||
|
InitializeDefaultParameterValues(layer, *shaderPackage);
|
||||||
layerId = layer.id;
|
layerId = layer.id;
|
||||||
mLayers.push_back(std::move(layer));
|
mLayers.push_back(std::move(layer));
|
||||||
error.clear();
|
error.clear();
|
||||||
@@ -68,6 +73,117 @@ bool RuntimeLayerModel::RemoveLayer(const std::string& layerId, std::string& err
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RuntimeLayerModel::ReorderLayer(const std::string& layerId, int targetIndex, std::string& error)
|
||||||
|
{
|
||||||
|
auto layerIt = std::find_if(mLayers.begin(), mLayers.end(), [&layerId](const Layer& layer) {
|
||||||
|
return layer.id == layerId;
|
||||||
|
});
|
||||||
|
if (layerIt == mLayers.end())
|
||||||
|
{
|
||||||
|
error = "Unknown runtime layer id: " + layerId;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetIndex < 0)
|
||||||
|
targetIndex = 0;
|
||||||
|
if (targetIndex >= static_cast<int>(mLayers.size()))
|
||||||
|
targetIndex = static_cast<int>(mLayers.size()) - 1;
|
||||||
|
|
||||||
|
Layer layer = std::move(*layerIt);
|
||||||
|
mLayers.erase(layerIt);
|
||||||
|
std::size_t destinationIndex = static_cast<std::size_t>(targetIndex);
|
||||||
|
if (destinationIndex > mLayers.size())
|
||||||
|
destinationIndex = mLayers.size();
|
||||||
|
mLayers.insert(mLayers.begin() + static_cast<std::ptrdiff_t>(destinationIndex), std::move(layer));
|
||||||
|
error.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RuntimeLayerModel::SetLayerBypass(const std::string& layerId, bool bypass, std::string& error)
|
||||||
|
{
|
||||||
|
Layer* layer = FindLayer(layerId);
|
||||||
|
if (!layer)
|
||||||
|
{
|
||||||
|
error = "Unknown runtime layer id: " + layerId;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
layer->bypass = bypass;
|
||||||
|
error.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RuntimeLayerModel::SetLayerShader(const SupportedShaderCatalog& shaderCatalog, const std::string& layerId, const std::string& shaderId, std::string& error)
|
||||||
|
{
|
||||||
|
Layer* layer = FindLayer(layerId);
|
||||||
|
if (!layer)
|
||||||
|
{
|
||||||
|
error = "Unknown runtime layer id: " + layerId;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ShaderPackage* shaderPackage = shaderCatalog.FindPackage(shaderId);
|
||||||
|
if (!shaderPackage)
|
||||||
|
{
|
||||||
|
error = "Shader '" + shaderId + "' is not in the supported shader catalog.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
layer->shaderId = shaderPackage->id;
|
||||||
|
layer->shaderName = shaderPackage->displayName.empty() ? shaderPackage->id : shaderPackage->displayName;
|
||||||
|
layer->buildState = RuntimeLayerBuildState::Pending;
|
||||||
|
layer->message = "Runtime Slang build is waiting to start.";
|
||||||
|
layer->renderReady = false;
|
||||||
|
layer->artifact = RuntimeShaderArtifact();
|
||||||
|
InitializeDefaultParameterValues(*layer, *shaderPackage);
|
||||||
|
error.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RuntimeLayerModel::UpdateParameter(const std::string& layerId, const std::string& parameterId, const JsonValue& value, std::string& error)
|
||||||
|
{
|
||||||
|
Layer* layer = FindLayer(layerId);
|
||||||
|
if (!layer)
|
||||||
|
{
|
||||||
|
error = "Unknown runtime layer id: " + layerId;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ShaderParameterDefinition* definition = FindParameterDefinition(*layer, parameterId);
|
||||||
|
if (!definition)
|
||||||
|
{
|
||||||
|
error = "Unknown parameter id '" + parameterId + "' for layer " + layerId + ".";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderParameterValue normalizedValue;
|
||||||
|
if (!NormalizeAndValidateParameterValue(*definition, value, normalizedValue, error))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
layer->parameterValues[parameterId] = normalizedValue;
|
||||||
|
if (layer->renderReady)
|
||||||
|
layer->artifact.parameterValues = layer->parameterValues;
|
||||||
|
error.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RuntimeLayerModel::ResetParameters(const std::string& layerId, std::string& error)
|
||||||
|
{
|
||||||
|
Layer* layer = FindLayer(layerId);
|
||||||
|
if (!layer)
|
||||||
|
{
|
||||||
|
error = "Unknown runtime layer id: " + layerId;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
layer->parameterValues.clear();
|
||||||
|
for (const ShaderParameterDefinition& definition : layer->parameterDefinitions)
|
||||||
|
layer->parameterValues[definition.id] = DefaultValueForDefinition(definition);
|
||||||
|
if (layer->renderReady)
|
||||||
|
layer->artifact.parameterValues = layer->parameterValues;
|
||||||
|
error.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void RuntimeLayerModel::Clear()
|
void RuntimeLayerModel::Clear()
|
||||||
{
|
{
|
||||||
mLayers.clear();
|
mLayers.clear();
|
||||||
@@ -106,6 +222,7 @@ bool RuntimeLayerModel::MarkBuildReady(const RuntimeShaderArtifact& artifact, st
|
|||||||
layer->message = artifact.message;
|
layer->message = artifact.message;
|
||||||
layer->renderReady = true;
|
layer->renderReady = true;
|
||||||
layer->artifact = artifact;
|
layer->artifact = artifact;
|
||||||
|
layer->artifact.parameterValues = layer->parameterValues;
|
||||||
error.clear();
|
error.clear();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -159,7 +276,9 @@ RuntimeLayerModelSnapshot RuntimeLayerModel::Snapshot() const
|
|||||||
RuntimeRenderLayerModel renderLayer;
|
RuntimeRenderLayerModel renderLayer;
|
||||||
renderLayer.id = layer.id;
|
renderLayer.id = layer.id;
|
||||||
renderLayer.shaderId = layer.shaderId;
|
renderLayer.shaderId = layer.shaderId;
|
||||||
|
renderLayer.bypass = layer.bypass;
|
||||||
renderLayer.artifact = layer.artifact;
|
renderLayer.artifact = layer.artifact;
|
||||||
|
renderLayer.artifact.parameterValues = layer.parameterValues;
|
||||||
snapshot.renderLayers.push_back(std::move(renderLayer));
|
snapshot.renderLayers.push_back(std::move(renderLayer));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -204,6 +323,24 @@ RuntimeLayerModel::Layer* RuntimeLayerModel::FindFirstLayerForShader(const std::
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RuntimeLayerModel::InitializeDefaultParameterValues(Layer& layer, const ShaderPackage& shaderPackage)
|
||||||
|
{
|
||||||
|
layer.parameterDefinitions = shaderPackage.parameters;
|
||||||
|
layer.parameterValues.clear();
|
||||||
|
for (const ShaderParameterDefinition& definition : layer.parameterDefinitions)
|
||||||
|
layer.parameterValues[definition.id] = DefaultValueForDefinition(definition);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ShaderParameterDefinition* RuntimeLayerModel::FindParameterDefinition(const Layer& layer, const std::string& parameterId)
|
||||||
|
{
|
||||||
|
for (const ShaderParameterDefinition& definition : layer.parameterDefinitions)
|
||||||
|
{
|
||||||
|
if (definition.id == parameterId)
|
||||||
|
return &definition;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
std::string RuntimeLayerModel::AllocateLayerId()
|
std::string RuntimeLayerModel::AllocateLayerId()
|
||||||
{
|
{
|
||||||
return "runtime-layer-" + std::to_string(mNextLayerNumber++);
|
return "runtime-layer-" + std::to_string(mNextLayerNumber++);
|
||||||
@@ -219,6 +356,8 @@ RuntimeLayerReadModel RuntimeLayerModel::ToReadModel(const Layer& layer)
|
|||||||
readModel.buildState = layer.buildState;
|
readModel.buildState = layer.buildState;
|
||||||
readModel.message = layer.message;
|
readModel.message = layer.message;
|
||||||
readModel.renderReady = layer.renderReady;
|
readModel.renderReady = layer.renderReady;
|
||||||
|
readModel.parameterDefinitions = layer.parameterDefinitions;
|
||||||
|
readModel.parameterValues = layer.parameterValues;
|
||||||
return readModel;
|
return readModel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "RuntimeJson.h"
|
||||||
#include "RuntimeShaderArtifact.h"
|
#include "RuntimeShaderArtifact.h"
|
||||||
#include "SupportedShaderCatalog.h"
|
#include "SupportedShaderCatalog.h"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -25,12 +27,15 @@ struct RuntimeLayerReadModel
|
|||||||
RuntimeLayerBuildState buildState = RuntimeLayerBuildState::Pending;
|
RuntimeLayerBuildState buildState = RuntimeLayerBuildState::Pending;
|
||||||
std::string message;
|
std::string message;
|
||||||
bool renderReady = false;
|
bool renderReady = false;
|
||||||
|
std::vector<ShaderParameterDefinition> parameterDefinitions;
|
||||||
|
std::map<std::string, ShaderParameterValue> parameterValues;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RuntimeRenderLayerModel
|
struct RuntimeRenderLayerModel
|
||||||
{
|
{
|
||||||
std::string id;
|
std::string id;
|
||||||
std::string shaderId;
|
std::string shaderId;
|
||||||
|
bool bypass = false;
|
||||||
RuntimeShaderArtifact artifact;
|
RuntimeShaderArtifact artifact;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -50,6 +55,11 @@ public:
|
|||||||
|
|
||||||
bool AddLayer(const SupportedShaderCatalog& shaderCatalog, const std::string& shaderId, std::string& layerId, std::string& error);
|
bool AddLayer(const SupportedShaderCatalog& shaderCatalog, const std::string& shaderId, std::string& layerId, std::string& error);
|
||||||
bool RemoveLayer(const std::string& layerId, std::string& error);
|
bool RemoveLayer(const std::string& layerId, std::string& error);
|
||||||
|
bool ReorderLayer(const std::string& layerId, int targetIndex, std::string& error);
|
||||||
|
bool SetLayerBypass(const std::string& layerId, bool bypass, std::string& error);
|
||||||
|
bool SetLayerShader(const SupportedShaderCatalog& shaderCatalog, const std::string& layerId, const std::string& shaderId, std::string& error);
|
||||||
|
bool UpdateParameter(const std::string& layerId, const std::string& parameterId, const JsonValue& value, std::string& error);
|
||||||
|
bool ResetParameters(const std::string& layerId, std::string& error);
|
||||||
bool MarkBuildStarted(const std::string& layerId, const std::string& message, std::string& error);
|
bool MarkBuildStarted(const std::string& layerId, const std::string& message, std::string& error);
|
||||||
bool MarkBuildReady(const RuntimeShaderArtifact& artifact, std::string& error);
|
bool MarkBuildReady(const RuntimeShaderArtifact& artifact, std::string& error);
|
||||||
bool MarkBuildFailedForShader(const std::string& shaderId, const std::string& message);
|
bool MarkBuildFailedForShader(const std::string& shaderId, const std::string& message);
|
||||||
@@ -69,12 +79,16 @@ private:
|
|||||||
RuntimeLayerBuildState buildState = RuntimeLayerBuildState::Pending;
|
RuntimeLayerBuildState buildState = RuntimeLayerBuildState::Pending;
|
||||||
std::string message;
|
std::string message;
|
||||||
bool renderReady = false;
|
bool renderReady = false;
|
||||||
|
std::vector<ShaderParameterDefinition> parameterDefinitions;
|
||||||
|
std::map<std::string, ShaderParameterValue> parameterValues;
|
||||||
RuntimeShaderArtifact artifact;
|
RuntimeShaderArtifact artifact;
|
||||||
};
|
};
|
||||||
|
|
||||||
Layer* FindLayer(const std::string& layerId);
|
Layer* FindLayer(const std::string& layerId);
|
||||||
const Layer* FindLayer(const std::string& layerId) const;
|
const Layer* FindLayer(const std::string& layerId) const;
|
||||||
Layer* FindFirstLayerForShader(const std::string& shaderId);
|
Layer* FindFirstLayerForShader(const std::string& shaderId);
|
||||||
|
static void InitializeDefaultParameterValues(Layer& layer, const ShaderPackage& shaderPackage);
|
||||||
|
static const ShaderParameterDefinition* FindParameterDefinition(const Layer& layer, const std::string& parameterId);
|
||||||
std::string AllocateLayerId();
|
std::string AllocateLayerId();
|
||||||
static RuntimeLayerReadModel ToReadModel(const Layer& layer);
|
static RuntimeLayerReadModel ToReadModel(const Layer& layer);
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "ShaderTypes.h"
|
#include "ShaderTypes.h"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -22,4 +23,5 @@ struct RuntimeShaderArtifact
|
|||||||
std::vector<RuntimeShaderPassArtifact> passes;
|
std::vector<RuntimeShaderPassArtifact> passes;
|
||||||
std::string message;
|
std::string message;
|
||||||
std::vector<ShaderParameterDefinition> parameterDefinitions;
|
std::vector<ShaderParameterDefinition> parameterDefinitions;
|
||||||
|
std::map<std::string, ShaderParameterValue> parameterValues;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -147,6 +147,29 @@ void TestLayerPostEndpointsUseCallbacks()
|
|||||||
Expect(removeResponse.body.find("Unknown layer id.") != std::string::npos, "remove layer callback returns diagnostic");
|
Expect(removeResponse.body.find("Unknown layer id.") != std::string::npos, "remove layer callback returns diagnostic");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestGenericPostCallbackHandlesControlRoutes()
|
||||||
|
{
|
||||||
|
using namespace RenderCadenceCompositor;
|
||||||
|
|
||||||
|
HttpControlServer server;
|
||||||
|
HttpControlServerCallbacks callbacks;
|
||||||
|
callbacks.executePost = [](const std::string& path, const std::string& body) {
|
||||||
|
ExpectEquals(path, "/api/layers/set-bypass", "generic callback receives route path");
|
||||||
|
Expect(body.find("runtime-layer-1") != std::string::npos, "generic callback receives request body");
|
||||||
|
return ControlActionResult{ true, std::string() };
|
||||||
|
};
|
||||||
|
server.SetCallbacksForTest(callbacks);
|
||||||
|
|
||||||
|
HttpControlServer::HttpRequest request;
|
||||||
|
request.method = "POST";
|
||||||
|
request.path = "/api/layers/set-bypass";
|
||||||
|
request.body = "{\"layerId\":\"runtime-layer-1\",\"bypass\":true}";
|
||||||
|
|
||||||
|
const HttpControlServer::HttpResponse response = server.RouteRequestForTest(request);
|
||||||
|
ExpectEquals(response.status, "200 OK", "generic control callback success returns 200");
|
||||||
|
Expect(response.body.find("\"ok\":true") != std::string::npos, "generic control callback returns action success");
|
||||||
|
}
|
||||||
|
|
||||||
void TestUnknownEndpointReturns404()
|
void TestUnknownEndpointReturns404()
|
||||||
{
|
{
|
||||||
using namespace RenderCadenceCompositor;
|
using namespace RenderCadenceCompositor;
|
||||||
@@ -169,6 +192,7 @@ int main()
|
|||||||
TestRootServesUiIndex();
|
TestRootServesUiIndex();
|
||||||
TestKnownPostEndpointReturnsActionError();
|
TestKnownPostEndpointReturnsActionError();
|
||||||
TestLayerPostEndpointsUseCallbacks();
|
TestLayerPostEndpointsUseCallbacks();
|
||||||
|
TestGenericPostCallbackHandlesControlRoutes();
|
||||||
TestUnknownEndpointReturns404();
|
TestUnknownEndpointReturns404();
|
||||||
|
|
||||||
if (gFailures != 0)
|
if (gFailures != 0)
|
||||||
|
|||||||
@@ -143,6 +143,49 @@ void TestAddAndRemoveLayers()
|
|||||||
|
|
||||||
std::filesystem::remove_all(root);
|
std::filesystem::remove_all(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestLayerControlsUpdateDisplayAndRenderModels()
|
||||||
|
{
|
||||||
|
std::filesystem::path root;
|
||||||
|
RenderCadenceCompositor::SupportedShaderCatalog catalog = MakeCatalog(root);
|
||||||
|
|
||||||
|
RenderCadenceCompositor::RuntimeLayerModel model;
|
||||||
|
std::string error;
|
||||||
|
std::string firstLayerId;
|
||||||
|
std::string secondLayerId;
|
||||||
|
Expect(model.AddLayer(catalog, "solid", firstLayerId, error), "first control layer can be added");
|
||||||
|
Expect(model.AddLayer(catalog, "solid", secondLayerId, error), "second control layer can be added");
|
||||||
|
|
||||||
|
Expect(model.SetLayerBypass(firstLayerId, true, error), "bypass can be set");
|
||||||
|
Expect(model.ReorderLayer(firstLayerId, 1, error), "layer can be reordered");
|
||||||
|
RenderCadenceCompositor::RuntimeLayerModelSnapshot snapshot = model.Snapshot();
|
||||||
|
Expect(snapshot.displayLayers[1].id == firstLayerId, "reordered layer moves to requested index");
|
||||||
|
Expect(snapshot.displayLayers[1].bypass, "bypass state is visible in read model");
|
||||||
|
|
||||||
|
JsonValue gainValue(0.75);
|
||||||
|
Expect(model.UpdateParameter(firstLayerId, "gain", gainValue, error), "parameter value can be updated");
|
||||||
|
snapshot = model.Snapshot();
|
||||||
|
Expect(snapshot.displayLayers[1].parameterValues.at("gain").numberValues.front() == 0.75, "updated parameter value is visible");
|
||||||
|
|
||||||
|
RuntimeShaderArtifact artifact;
|
||||||
|
artifact.layerId = firstLayerId;
|
||||||
|
artifact.shaderId = "solid";
|
||||||
|
artifact.displayName = "Solid";
|
||||||
|
artifact.fragmentShaderSource = "void main(){}";
|
||||||
|
artifact.parameterDefinitions = snapshot.displayLayers[1].parameterDefinitions;
|
||||||
|
artifact.message = "build ready";
|
||||||
|
Expect(model.MarkBuildReady(artifact, error), "ready artifact keeps layer parameter state");
|
||||||
|
snapshot = model.Snapshot();
|
||||||
|
Expect(snapshot.renderLayers.size() == 1, "ready layer produces render model");
|
||||||
|
Expect(snapshot.renderLayers[0].bypass, "render model carries bypass state");
|
||||||
|
Expect(snapshot.renderLayers[0].artifact.parameterValues.at("gain").numberValues.front() == 0.75, "render artifact carries updated parameter value");
|
||||||
|
|
||||||
|
Expect(model.ResetParameters(firstLayerId, error), "parameters can reset to defaults");
|
||||||
|
snapshot = model.Snapshot();
|
||||||
|
Expect(snapshot.displayLayers[1].parameterValues.at("gain").numberValues.front() == 0.5, "reset restores default value");
|
||||||
|
|
||||||
|
std::filesystem::remove_all(root);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
@@ -151,6 +194,7 @@ int main()
|
|||||||
TestRejectsUnsupportedStartupShader();
|
TestRejectsUnsupportedStartupShader();
|
||||||
TestBuildFailureStaysDisplaySide();
|
TestBuildFailureStaysDisplaySide();
|
||||||
TestAddAndRemoveLayers();
|
TestAddAndRemoveLayers();
|
||||||
|
TestLayerControlsUpdateDisplayAndRenderModels();
|
||||||
|
|
||||||
if (gFailures != 0)
|
if (gFailures != 0)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user