shader control
All checks were successful
CI / React UI Build (push) Successful in 11s
CI / Native Windows Build And Tests (push) Successful in 3m3s
CI / Windows Release Package (push) Has been skipped

This commit is contained in:
Aiden
2026-05-12 17:52:55 +10:00
parent dfd49fd0e3
commit f43b6f6519
19 changed files with 562 additions and 9 deletions

View 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;
}
}

View 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);
}

View File

@@ -93,6 +93,31 @@ inline void WriteDefaultParameterValue(JsonWriter& writer, const ShaderParameter
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)
{
writer.BeginObject();
@@ -122,7 +147,7 @@ inline const char* RuntimeLayerBuildStateName(RuntimeLayerBuildState state)
return "unknown";
}
inline void WriteParameterDefinitionJson(JsonWriter& writer, const ShaderParameterDefinition& parameter)
inline void WriteParameterDefinitionJson(JsonWriter& writer, const ShaderParameterDefinition& parameter, const ShaderParameterValue* value)
{
writer.BeginObject();
writer.KeyString("id", parameter.id);
@@ -132,7 +157,10 @@ inline void WriteParameterDefinitionJson(JsonWriter& writer, const ShaderParamet
writer.Key("defaultValue");
WriteDefaultParameterValue(writer, parameter);
writer.Key("value");
WriteDefaultParameterValue(writer, parameter);
if (value)
WriteParameterValue(writer, parameter, *value);
else
WriteDefaultParameterValue(writer, parameter);
if (!parameter.minNumbers.empty())
{
@@ -197,10 +225,10 @@ inline void WriteLayersJson(JsonWriter& writer, const RuntimeStateJsonInput& inp
WriteFeedbackJson(writer, FeedbackSettings());
writer.Key("parameters");
writer.BeginArray();
if (shaderPackage)
for (const ShaderParameterDefinition& parameter : layer.parameterDefinitions)
{
for (const ShaderParameterDefinition& parameter : shaderPackage->parameters)
WriteParameterDefinitionJson(writer, parameter);
const auto valueIt = layer.parameterValues.find(parameter.id);
WriteParameterDefinitionJson(writer, parameter, valueIt == layer.parameterValues.end() ? nullptr : &valueIt->second);
}
writer.EndArray();
writer.EndObject();

View File

@@ -286,6 +286,12 @@ HttpControlServer::HttpResponse HttpControlServer::ServePost(const HttpRequest&
if (!IsKnownPostEndpoint(request.path))
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)
{
const ControlActionResult result = mCallbacks.addLayer(request.body);

View File

@@ -28,6 +28,7 @@ struct HttpControlServerCallbacks
std::function<std::string()> getStateJson;
std::function<ControlActionResult(const std::string&)> addLayer;
std::function<ControlActionResult(const std::string&)> removeLayer;
std::function<ControlActionResult(const std::string&, const std::string&)> executePost;
};
class UniqueSocket