Osc orchestration
All checks were successful
CI / React UI Build (push) Successful in 11s
CI / Native Windows Build And Tests (push) Successful in 2m38s
CI / Windows Release Package (push) Successful in 2m42s

This commit is contained in:
Aiden
2026-05-10 23:59:48 +10:00
parent 120f899b0d
commit a24cdc0630
6 changed files with 351 additions and 270 deletions

View File

@@ -24,72 +24,6 @@
namespace
{
constexpr auto kOscOverlayCommitDelay = std::chrono::milliseconds(150);
constexpr double kOscSmoothingReferenceFps = 60.0;
constexpr double kOscSmoothingMaxStepSeconds = 0.25;
std::string SimplifyOscControlKey(const std::string& text)
{
std::string simplified;
for (unsigned char ch : text)
{
if (std::isalnum(ch))
simplified.push_back(static_cast<char>(std::tolower(ch)));
}
return simplified;
}
bool MatchesOscControlKey(const std::string& candidate, const std::string& key)
{
return candidate == key || SimplifyOscControlKey(candidate) == SimplifyOscControlKey(key);
}
double ClampOscAlpha(double value)
{
return (std::max)(0.0, (std::min)(1.0, value));
}
double ComputeTimeBasedOscAlpha(double smoothing, double deltaSeconds)
{
const double clampedSmoothing = ClampOscAlpha(smoothing);
if (clampedSmoothing <= 0.0)
return 0.0;
if (clampedSmoothing >= 1.0)
return 1.0;
const double clampedDeltaSeconds = (std::max)(0.0, (std::min)(kOscSmoothingMaxStepSeconds, deltaSeconds));
if (clampedDeltaSeconds <= 0.0)
return 0.0;
const double frameScale = clampedDeltaSeconds * kOscSmoothingReferenceFps;
return ClampOscAlpha(1.0 - std::pow(1.0 - clampedSmoothing, frameScale));
}
JsonValue BuildOscCommitValue(const ShaderParameterDefinition& definition, const ShaderParameterValue& value)
{
switch (definition.type)
{
case ShaderParameterType::Boolean:
return JsonValue(value.booleanValue);
case ShaderParameterType::Enum:
return JsonValue(value.enumValue);
case ShaderParameterType::Text:
return JsonValue(value.textValue);
case ShaderParameterType::Trigger:
case ShaderParameterType::Float:
return JsonValue(value.numberValues.empty() ? 0.0 : value.numberValues.front());
case ShaderParameterType::Vec2:
case ShaderParameterType::Color:
{
JsonValue array = JsonValue::MakeArray();
for (double number : value.numberValues)
array.pushBack(JsonValue(number));
return array;
}
}
return JsonValue();
}
}
OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) :
@@ -376,7 +310,7 @@ void OpenGLComposite::renderEffect()
ProcessRuntimePollResults();
std::vector<RuntimeServices::AppliedOscUpdate> appliedOscUpdates;
std::vector<RuntimeServices::CompletedOscCommit> completedOscCommits;
if (mRuntimeHost && mRuntimeServices)
if (mRuntimeServices)
{
std::string oscError;
if (!mRuntimeServices->ApplyPendingOscUpdates(appliedOscUpdates, oscError) && !oscError.empty())
@@ -384,190 +318,52 @@ void OpenGLComposite::renderEffect()
mRuntimeServices->ConsumeCompletedOscCommits(completedOscCommits);
}
for (const RuntimeServices::CompletedOscCommit& completedCommit : completedOscCommits)
{
auto overlayIt = mOscOverlayStates.find(completedCommit.routeKey);
if (overlayIt == mOscOverlayStates.end())
continue;
OscOverlayState& overlay = overlayIt->second;
if (overlay.commitQueued &&
overlay.pendingCommitGeneration == completedCommit.generation &&
overlay.generation == completedCommit.generation)
{
mOscOverlayStates.erase(overlayIt);
}
}
std::set<std::string> pendingOscRouteKeys;
const auto oscNow = std::chrono::steady_clock::now();
std::vector<RenderEngine::OscOverlayUpdate> overlayUpdates;
overlayUpdates.reserve(appliedOscUpdates.size());
for (const RuntimeServices::AppliedOscUpdate& update : appliedOscUpdates)
{
const std::string routeKey = update.routeKey;
auto overlayIt = mOscOverlayStates.find(routeKey);
if (overlayIt == mOscOverlayStates.end())
{
OscOverlayState overlay;
overlay.layerKey = update.layerKey;
overlay.parameterKey = update.parameterKey;
overlay.targetValue = update.targetValue;
overlay.lastUpdatedTime = oscNow;
overlay.lastAppliedTime = oscNow;
overlay.generation = 1;
mOscOverlayStates[routeKey] = std::move(overlay);
}
else
{
overlayIt->second.targetValue = update.targetValue;
overlayIt->second.lastUpdatedTime = oscNow;
overlayIt->second.generation += 1;
overlayIt->second.commitQueued = false;
}
pendingOscRouteKeys.insert(routeKey);
overlayUpdates.push_back({ update.routeKey, update.layerKey, update.parameterKey, update.targetValue });
}
const auto applyOscOverlays = [&](std::vector<RuntimeRenderState>& states, bool allowCommit)
std::vector<RenderEngine::OscOverlayCommitCompletion> overlayCommitCompletions;
overlayCommitCompletions.reserve(completedOscCommits.size());
for (const RuntimeServices::CompletedOscCommit& completedCommit : completedOscCommits)
{
if (states.empty() || mOscOverlayStates.empty())
return;
overlayCommitCompletions.push_back({ completedCommit.routeKey, completedCommit.generation });
}
const double smoothing = ClampOscAlpha(mRuntimeStore ? mRuntimeStore->GetConfiguredOscSmoothing() : 0.0);
std::vector<std::string> overlayKeysToRemove;
for (auto& item : mOscOverlayStates)
{
OscOverlayState& overlay = item.second;
auto stateIt = std::find_if(states.begin(), states.end(),
[&overlay](const RuntimeRenderState& state)
{
return MatchesOscControlKey(state.layerId, overlay.layerKey) ||
MatchesOscControlKey(state.shaderId, overlay.layerKey) ||
MatchesOscControlKey(state.shaderName, overlay.layerKey);
});
if (stateIt == states.end())
continue;
auto definitionIt = std::find_if(stateIt->parameterDefinitions.begin(), stateIt->parameterDefinitions.end(),
[&overlay](const ShaderParameterDefinition& definition)
{
return MatchesOscControlKey(definition.id, overlay.parameterKey) ||
MatchesOscControlKey(definition.label, overlay.parameterKey);
});
if (definitionIt == stateIt->parameterDefinitions.end())
continue;
if (definitionIt->type == ShaderParameterType::Trigger)
{
if (pendingOscRouteKeys.find(item.first) == pendingOscRouteKeys.end())
continue;
ShaderParameterValue& value = stateIt->parameterValues[definitionIt->id];
const double previousCount = value.numberValues.empty() ? 0.0 : value.numberValues[0];
const double triggerTime = stateIt->timeSeconds;
value.numberValues = { previousCount + 1.0, triggerTime };
overlayKeysToRemove.push_back(item.first);
continue;
}
ShaderParameterValue targetValue;
std::string normalizeError;
if (!NormalizeAndValidateParameterValue(*definitionIt, overlay.targetValue, targetValue, normalizeError))
continue;
const bool smoothable =
smoothing > 0.0 &&
(definitionIt->type == ShaderParameterType::Float ||
definitionIt->type == ShaderParameterType::Vec2 ||
definitionIt->type == ShaderParameterType::Color);
if (!smoothable)
{
overlay.currentValue = targetValue;
overlay.hasCurrentValue = true;
stateIt->parameterValues[definitionIt->id] = overlay.currentValue;
if (allowCommit &&
!overlay.commitQueued &&
oscNow - overlay.lastUpdatedTime >= kOscOverlayCommitDelay &&
mRuntimeServices)
{
std::string commitError;
if (mRuntimeServices->QueueOscCommit(item.first, overlay.layerKey, overlay.parameterKey, overlay.targetValue, overlay.generation, commitError))
{
overlay.pendingCommitGeneration = overlay.generation;
overlay.commitQueued = true;
}
}
continue;
}
if (!overlay.hasCurrentValue)
{
overlay.currentValue = DefaultValueForDefinition(*definitionIt);
auto currentIt = stateIt->parameterValues.find(definitionIt->id);
if (currentIt != stateIt->parameterValues.end())
overlay.currentValue = currentIt->second;
overlay.hasCurrentValue = true;
}
if (overlay.currentValue.numberValues.size() != targetValue.numberValues.size())
overlay.currentValue.numberValues = targetValue.numberValues;
double smoothingAlpha = smoothing;
if (overlay.lastAppliedTime != std::chrono::steady_clock::time_point())
{
const double deltaSeconds =
std::chrono::duration_cast<std::chrono::duration<double>>(oscNow - overlay.lastAppliedTime).count();
smoothingAlpha = ComputeTimeBasedOscAlpha(smoothing, deltaSeconds);
}
overlay.lastAppliedTime = oscNow;
ShaderParameterValue nextValue = targetValue;
bool converged = true;
for (std::size_t index = 0; index < targetValue.numberValues.size(); ++index)
{
const double currentNumber = overlay.currentValue.numberValues[index];
const double targetNumber = targetValue.numberValues[index];
const double delta = targetNumber - currentNumber;
double nextNumber = currentNumber + delta * smoothingAlpha;
if (std::fabs(delta) <= 0.0005)
nextNumber = targetNumber;
else
converged = false;
nextValue.numberValues[index] = nextNumber;
}
if (converged)
nextValue.numberValues = targetValue.numberValues;
overlay.currentValue = nextValue;
overlay.hasCurrentValue = true;
stateIt->parameterValues[definitionIt->id] = overlay.currentValue;
if (allowCommit &&
converged &&
!overlay.commitQueued &&
oscNow - overlay.lastUpdatedTime >= kOscOverlayCommitDelay &&
mRuntimeServices)
{
std::string commitError;
JsonValue committedValue = BuildOscCommitValue(*definitionIt, overlay.currentValue);
if (mRuntimeServices->QueueOscCommit(item.first, overlay.layerKey, overlay.parameterKey, committedValue, overlay.generation, commitError))
{
overlay.pendingCommitGeneration = overlay.generation;
overlay.commitQueued = true;
}
}
}
for (const std::string& overlayKey : overlayKeysToRemove)
mOscOverlayStates.erase(overlayKey);
};
if (mRenderEngine)
mRenderEngine->UpdateOscOverlayState(overlayUpdates, overlayCommitCompletions);
const bool hasInputSource = mVideoBackend->HasInputSource();
std::vector<RuntimeRenderState> layerStates;
std::vector<RenderEngine::OscOverlayCommitRequest> overlayCommitRequests;
const double smoothing = mRuntimeStore ? mRuntimeStore->GetConfiguredOscSmoothing() : 0.0;
mRenderEngine->ResolveRenderLayerStates(
mUseCommittedLayerStates.load(),
mVideoBackend->InputFrameWidth(),
mVideoBackend->InputFrameHeight(),
applyOscOverlays,
smoothing,
&overlayCommitRequests,
layerStates);
if (mRuntimeServices)
{
for (const RenderEngine::OscOverlayCommitRequest& commitRequest : overlayCommitRequests)
{
std::string commitError;
if (!mRuntimeServices->QueueOscCommit(
commitRequest.routeKey,
commitRequest.layerKey,
commitRequest.parameterKey,
commitRequest.value,
commitRequest.generation,
commitError) &&
!commitError.empty())
{
OutputDebugStringA(("OSC commit queue failed: " + commitError + "\n").c_str());
}
}
}
const unsigned historyCap = mRuntimeStore ? mRuntimeStore->GetConfiguredMaxTemporalHistoryFrames() : 0;
mRenderEngine->RenderLayerStack(
hasInputSource,
@@ -706,7 +502,8 @@ bool OpenGLComposite::ApplyRuntimeCoordinatorResult(const RuntimeCoordinatorResu
if (result.clearTransientOscState)
{
mOscOverlayStates.clear();
if (mRenderEngine)
mRenderEngine->ClearOscOverlayState();
if (mRuntimeServices)
mRuntimeServices->ClearOscState();
}

View File

@@ -20,12 +20,9 @@
#include <functional>
#include <atomic>
#include <filesystem>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include <deque>
#include <chrono>
class RenderEngine;
class RuntimeServices;
@@ -71,19 +68,6 @@ private:
void resizeWindow(int width, int height);
bool CheckOpenGLExtensions();
void PublishVideoIOStatus(const std::string& statusMessage);
struct OscOverlayState
{
std::string layerKey;
std::string parameterKey;
JsonValue targetValue;
ShaderParameterValue currentValue;
bool hasCurrentValue = false;
std::chrono::steady_clock::time_point lastUpdatedTime;
std::chrono::steady_clock::time_point lastAppliedTime;
uint64_t generation = 0;
uint64_t pendingCommitGeneration = 0;
bool commitQueued = false;
};
HWND hGLWnd;
HDC hGLDC;
@@ -98,7 +82,6 @@ private:
std::unique_ptr<ShaderBuildQueue> mShaderBuildQueue;
std::unique_ptr<RuntimeServices> mRuntimeServices;
std::unique_ptr<VideoBackend> mVideoBackend;
std::map<std::string, OscOverlayState> mOscOverlayStates;
std::atomic<bool> mUseCommittedLayerStates;
std::atomic<bool> mScreenshotRequested;

View File

@@ -1,10 +1,84 @@
#include "RenderEngine.h"
#include "RuntimeParameterUtils.h"
#include <gl/gl.h>
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstddef>
namespace
{
constexpr auto kOscOverlayCommitDelay = std::chrono::milliseconds(150);
constexpr double kOscSmoothingReferenceFps = 60.0;
constexpr double kOscSmoothingMaxStepSeconds = 0.25;
std::string SimplifyOscControlKey(const std::string& text)
{
std::string simplified;
for (unsigned char ch : text)
{
if (std::isalnum(ch))
simplified.push_back(static_cast<char>(std::tolower(ch)));
}
return simplified;
}
bool MatchesOscControlKey(const std::string& candidate, const std::string& key)
{
return candidate == key || SimplifyOscControlKey(candidate) == SimplifyOscControlKey(key);
}
double ClampOscAlpha(double value)
{
return (std::max)(0.0, (std::min)(1.0, value));
}
double ComputeTimeBasedOscAlpha(double smoothing, double deltaSeconds)
{
const double clampedSmoothing = ClampOscAlpha(smoothing);
if (clampedSmoothing <= 0.0)
return 0.0;
if (clampedSmoothing >= 1.0)
return 1.0;
const double clampedDeltaSeconds = (std::max)(0.0, (std::min)(kOscSmoothingMaxStepSeconds, deltaSeconds));
if (clampedDeltaSeconds <= 0.0)
return 0.0;
const double frameScale = clampedDeltaSeconds * kOscSmoothingReferenceFps;
return ClampOscAlpha(1.0 - std::pow(1.0 - clampedSmoothing, frameScale));
}
JsonValue BuildOscCommitValue(const ShaderParameterDefinition& definition, const ShaderParameterValue& value)
{
switch (definition.type)
{
case ShaderParameterType::Boolean:
return JsonValue(value.booleanValue);
case ShaderParameterType::Enum:
return JsonValue(value.enumValue);
case ShaderParameterType::Text:
return JsonValue(value.textValue);
case ShaderParameterType::Trigger:
case ShaderParameterType::Float:
return JsonValue(value.numberValues.empty() ? 0.0 : value.numberValues.front());
case ShaderParameterType::Vec2:
case ShaderParameterType::Color:
{
JsonValue array = JsonValue::MakeArray();
for (double number : value.numberValues)
array.pushBack(JsonValue(number));
return array;
}
}
return JsonValue();
}
}
RenderEngine::RenderEngine(
RuntimeSnapshotProvider& runtimeSnapshotProvider,
HealthTelemetry& healthTelemetry,
@@ -106,6 +180,55 @@ void RenderEngine::ResetShaderFeedbackState()
mShaderPrograms.ResetShaderFeedbackState();
}
void RenderEngine::ClearOscOverlayState()
{
mOscOverlayStates.clear();
}
void RenderEngine::UpdateOscOverlayState(
const std::vector<OscOverlayUpdate>& updates,
const std::vector<OscOverlayCommitCompletion>& completedCommits)
{
for (const OscOverlayCommitCompletion& completedCommit : completedCommits)
{
auto overlayIt = mOscOverlayStates.find(completedCommit.routeKey);
if (overlayIt == mOscOverlayStates.end())
continue;
OscOverlayState& overlay = overlayIt->second;
if (overlay.commitQueued &&
overlay.pendingCommitGeneration == completedCommit.generation &&
overlay.generation == completedCommit.generation)
{
mOscOverlayStates.erase(overlayIt);
}
}
const auto now = std::chrono::steady_clock::now();
for (const OscOverlayUpdate& update : updates)
{
auto overlayIt = mOscOverlayStates.find(update.routeKey);
if (overlayIt == mOscOverlayStates.end())
{
OscOverlayState overlay;
overlay.layerKey = update.layerKey;
overlay.parameterKey = update.parameterKey;
overlay.targetValue = update.targetValue;
overlay.lastUpdatedTime = now;
overlay.lastAppliedTime = now;
overlay.generation = 1;
mOscOverlayStates[update.routeKey] = std::move(overlay);
}
else
{
overlayIt->second.targetValue = update.targetValue;
overlayIt->second.lastUpdatedTime = now;
overlayIt->second.generation += 1;
overlayIt->second.commitQueued = false;
}
}
}
void RenderEngine::ResizeView(int width, int height)
{
mRenderer.ResizeView(width, height);
@@ -178,15 +301,15 @@ bool RenderEngine::ResolveRenderLayerStates(
bool useCommittedLayerStates,
unsigned renderWidth,
unsigned renderHeight,
OverlayApplier overlayApplier,
double oscSmoothing,
std::vector<OscOverlayCommitRequest>* commitRequests,
std::vector<RuntimeRenderState>& layerStates)
{
layerStates.clear();
if (useCommittedLayerStates)
{
layerStates = mShaderPrograms.CommittedLayerStates();
if (overlayApplier)
overlayApplier(layerStates, false);
ApplyOscOverlays(layerStates, false, oscSmoothing, commitRequests);
mRuntimeSnapshotProvider.RefreshDynamicRenderStateFields(layerStates);
return true;
}
@@ -207,14 +330,12 @@ bool RenderEngine::ResolveRenderLayerStates(
renderSnapshot.versions.parameterStateVersion = mCachedParameterStateVersion;
renderSnapshot.states = mCachedLayerRenderStates;
if (overlayApplier)
overlayApplier(renderSnapshot.states, true);
ApplyOscOverlays(renderSnapshot.states, true, oscSmoothing, commitRequests);
if (mCachedParameterStateVersion != versions.parameterStateVersion &&
mRuntimeSnapshotProvider.TryRefreshSnapshotParameters(renderSnapshot))
{
mCachedParameterStateVersion = renderSnapshot.versions.parameterStateVersion;
if (overlayApplier)
overlayApplier(renderSnapshot.states, true);
ApplyOscOverlays(renderSnapshot.states, true, oscSmoothing, commitRequests);
}
mCachedLayerRenderStates = renderSnapshot.states;
@@ -231,19 +352,148 @@ bool RenderEngine::ResolveRenderLayerStates(
mCachedParameterStateVersion = renderSnapshot.versions.parameterStateVersion;
mCachedRenderStateWidth = renderSnapshot.outputWidth;
mCachedRenderStateHeight = renderSnapshot.outputHeight;
if (overlayApplier)
overlayApplier(mCachedLayerRenderStates, true);
ApplyOscOverlays(mCachedLayerRenderStates, true, oscSmoothing, commitRequests);
layerStates = mCachedLayerRenderStates;
return true;
}
if (overlayApplier)
overlayApplier(mCachedLayerRenderStates, true);
ApplyOscOverlays(mCachedLayerRenderStates, true, oscSmoothing, commitRequests);
layerStates = mCachedLayerRenderStates;
mRuntimeSnapshotProvider.RefreshDynamicRenderStateFields(layerStates);
return !layerStates.empty();
}
void RenderEngine::ApplyOscOverlays(
std::vector<RuntimeRenderState>& states,
bool allowCommit,
double smoothing,
std::vector<OscOverlayCommitRequest>* commitRequests)
{
if (states.empty() || mOscOverlayStates.empty())
return;
const auto now = std::chrono::steady_clock::now();
const double clampedSmoothing = ClampOscAlpha(smoothing);
std::vector<std::string> overlayKeysToRemove;
for (auto& item : mOscOverlayStates)
{
const std::string& routeKey = item.first;
OscOverlayState& overlay = item.second;
auto stateIt = std::find_if(states.begin(), states.end(),
[&overlay](const RuntimeRenderState& state)
{
return MatchesOscControlKey(state.layerId, overlay.layerKey) ||
MatchesOscControlKey(state.shaderId, overlay.layerKey) ||
MatchesOscControlKey(state.shaderName, overlay.layerKey);
});
if (stateIt == states.end())
continue;
auto definitionIt = std::find_if(stateIt->parameterDefinitions.begin(), stateIt->parameterDefinitions.end(),
[&overlay](const ShaderParameterDefinition& definition)
{
return MatchesOscControlKey(definition.id, overlay.parameterKey) ||
MatchesOscControlKey(definition.label, overlay.parameterKey);
});
if (definitionIt == stateIt->parameterDefinitions.end())
continue;
ShaderParameterValue targetValue;
std::string normalizeError;
if (!NormalizeAndValidateParameterValue(*definitionIt, overlay.targetValue, targetValue, normalizeError))
continue;
if (definitionIt->type == ShaderParameterType::Trigger)
{
ShaderParameterValue& value = stateIt->parameterValues[definitionIt->id];
const double previousCount = value.numberValues.empty() ? 0.0 : value.numberValues[0];
const double triggerTime = stateIt->timeSeconds;
value.numberValues = { previousCount + 1.0, triggerTime };
overlayKeysToRemove.push_back(routeKey);
continue;
}
const bool smoothable =
clampedSmoothing > 0.0 &&
(definitionIt->type == ShaderParameterType::Float ||
definitionIt->type == ShaderParameterType::Vec2 ||
definitionIt->type == ShaderParameterType::Color);
if (!smoothable)
{
overlay.currentValue = targetValue;
overlay.hasCurrentValue = true;
stateIt->parameterValues[definitionIt->id] = overlay.currentValue;
if (allowCommit &&
!overlay.commitQueued &&
now - overlay.lastUpdatedTime >= kOscOverlayCommitDelay &&
commitRequests)
{
commitRequests->push_back({ routeKey, overlay.layerKey, overlay.parameterKey, overlay.targetValue, overlay.generation });
overlay.pendingCommitGeneration = overlay.generation;
overlay.commitQueued = true;
}
continue;
}
if (!overlay.hasCurrentValue)
{
overlay.currentValue = DefaultValueForDefinition(*definitionIt);
auto currentIt = stateIt->parameterValues.find(definitionIt->id);
if (currentIt != stateIt->parameterValues.end())
overlay.currentValue = currentIt->second;
overlay.hasCurrentValue = true;
}
if (overlay.currentValue.numberValues.size() != targetValue.numberValues.size())
overlay.currentValue.numberValues = targetValue.numberValues;
double smoothingAlpha = clampedSmoothing;
if (overlay.lastAppliedTime != std::chrono::steady_clock::time_point())
{
const double deltaSeconds =
std::chrono::duration_cast<std::chrono::duration<double>>(now - overlay.lastAppliedTime).count();
smoothingAlpha = ComputeTimeBasedOscAlpha(clampedSmoothing, deltaSeconds);
}
overlay.lastAppliedTime = now;
ShaderParameterValue nextValue = targetValue;
bool converged = true;
for (std::size_t index = 0; index < targetValue.numberValues.size(); ++index)
{
const double currentNumber = overlay.currentValue.numberValues[index];
const double targetNumber = targetValue.numberValues[index];
const double delta = targetNumber - currentNumber;
double nextNumber = currentNumber + delta * smoothingAlpha;
if (std::fabs(delta) <= 0.0005)
nextNumber = targetNumber;
else
converged = false;
nextValue.numberValues[index] = nextNumber;
}
if (converged)
nextValue.numberValues = targetValue.numberValues;
overlay.currentValue = nextValue;
overlay.hasCurrentValue = true;
stateIt->parameterValues[definitionIt->id] = overlay.currentValue;
if (allowCommit &&
converged &&
!overlay.commitQueued &&
now - overlay.lastUpdatedTime >= kOscOverlayCommitDelay &&
commitRequests)
{
commitRequests->push_back({ routeKey, overlay.layerKey, overlay.parameterKey, BuildOscCommitValue(*definitionIt, overlay.currentValue), overlay.generation });
overlay.pendingCommitGeneration = overlay.generation;
overlay.commitQueued = true;
}
}
for (const std::string& overlayKey : overlayKeysToRemove)
mOscOverlayStates.erase(overlayKey);
}
void RenderEngine::RenderLayerStack(
bool hasInputSource,
const std::vector<RuntimeRenderState>& layerStates,

View File

@@ -12,6 +12,7 @@
#include <cstdint>
#include <chrono>
#include <functional>
#include <map>
#include <string>
#include <vector>
@@ -21,7 +22,29 @@ public:
using RenderEffectCallback = std::function<void()>;
using ScreenshotCallback = std::function<void()>;
using PreviewPaintCallback = std::function<void()>;
using OverlayApplier = std::function<void(std::vector<RuntimeRenderState>& states, bool allowCommit)>;
struct OscOverlayUpdate
{
std::string routeKey;
std::string layerKey;
std::string parameterKey;
JsonValue targetValue;
};
struct OscOverlayCommitCompletion
{
std::string routeKey;
uint64_t generation = 0;
};
struct OscOverlayCommitRequest
{
std::string routeKey;
std::string layerKey;
std::string parameterKey;
JsonValue value;
uint64_t generation = 0;
};
RenderEngine(
RuntimeSnapshotProvider& runtimeSnapshotProvider,
@@ -57,6 +80,10 @@ public:
const std::vector<RuntimeRenderState>& CommittedLayerStates() const;
void ResetTemporalHistoryState();
void ResetShaderFeedbackState();
void ClearOscOverlayState();
void UpdateOscOverlayState(
const std::vector<OscOverlayUpdate>& updates,
const std::vector<OscOverlayCommitCompletion>& completedCommits);
void ResizeView(int width, int height);
bool TryPresentPreview(bool force, unsigned previewFps, unsigned outputFrameWidth, unsigned outputFrameHeight);
bool TryUploadInputFrame(const VideoIOFrame& inputFrame, const VideoIOState& videoState);
@@ -65,7 +92,8 @@ public:
bool useCommittedLayerStates,
unsigned renderWidth,
unsigned renderHeight,
OverlayApplier overlayApplier,
double oscSmoothing,
std::vector<OscOverlayCommitRequest>* commitRequests,
std::vector<RuntimeRenderState>& layerStates);
void RenderLayerStack(
bool hasInputSource,
@@ -87,10 +115,32 @@ private:
CRITICAL_SECTION& mMutex;
HDC mHdc;
HGLRC mHglrc;
struct OscOverlayState
{
std::string layerKey;
std::string parameterKey;
JsonValue targetValue;
ShaderParameterValue currentValue;
bool hasCurrentValue = false;
std::chrono::steady_clock::time_point lastUpdatedTime;
std::chrono::steady_clock::time_point lastAppliedTime;
uint64_t generation = 0;
uint64_t pendingCommitGeneration = 0;
bool commitQueued = false;
};
void ApplyOscOverlays(
std::vector<RuntimeRenderState>& states,
bool allowCommit,
double smoothing,
std::vector<OscOverlayCommitRequest>* commitRequests);
std::vector<RuntimeRenderState> mCachedLayerRenderStates;
uint64_t mCachedRenderStateVersion = 0;
uint64_t mCachedParameterStateVersion = 0;
unsigned mCachedRenderStateWidth = 0;
unsigned mCachedRenderStateHeight = 0;
std::chrono::steady_clock::time_point mLastPreviewPresentTime;
std::map<std::string, OscOverlayState> mOscOverlayStates;
};