render engine updates
Some checks failed
CI / React UI Build (push) Successful in 11s
CI / Windows Release Package (push) Has been cancelled
CI / Native Windows Build And Tests (push) Has been cancelled

This commit is contained in:
Aiden
2026-05-11 17:06:42 +10:00
parent e459155d51
commit e00e2574ed
10 changed files with 265 additions and 166 deletions

View File

@@ -66,6 +66,9 @@ set(APP_SOURCES
"${APP_DIR}/gl/OpenGLCompositeRuntimeControls.cpp" "${APP_DIR}/gl/OpenGLCompositeRuntimeControls.cpp"
"${APP_DIR}/gl/RenderEngine.cpp" "${APP_DIR}/gl/RenderEngine.cpp"
"${APP_DIR}/gl/RenderEngine.h" "${APP_DIR}/gl/RenderEngine.h"
"${APP_DIR}/gl/RenderFrameState.h"
"${APP_DIR}/gl/RenderFrameStateResolver.cpp"
"${APP_DIR}/gl/RenderFrameStateResolver.h"
"${APP_DIR}/gl/RuntimeUpdateController.cpp" "${APP_DIR}/gl/RuntimeUpdateController.cpp"
"${APP_DIR}/gl/RuntimeUpdateController.h" "${APP_DIR}/gl/RuntimeUpdateController.h"
"${APP_DIR}/gl/pipeline/OpenGLRenderPass.cpp" "${APP_DIR}/gl/pipeline/OpenGLRenderPass.cpp"

View File

@@ -56,25 +56,16 @@ void QueueServiceCommitRequests(
} }
} }
bool RuntimeServiceLiveBridge::PrepareLiveRenderLayerStates( bool RuntimeServiceLiveBridge::PrepareLiveRenderFrameState(
RuntimeServices& runtimeServices, RuntimeServices& runtimeServices,
RenderEngine& renderEngine, RenderEngine& renderEngine,
bool useCommittedLayerStates, const RenderFrameInput& input,
unsigned renderWidth, RenderFrameState& frameState)
unsigned renderHeight,
double oscSmoothing,
std::vector<RuntimeRenderState>& layerStates)
{ {
DrainServiceEvents(runtimeServices, renderEngine); DrainServiceEvents(runtimeServices, renderEngine);
std::vector<RenderEngine::OscOverlayCommitRequest> commitRequests; std::vector<RenderEngine::OscOverlayCommitRequest> commitRequests;
const bool resolved = renderEngine.ResolveRenderLayerStates( const bool resolved = renderEngine.ResolveRenderFrameState(input, &commitRequests, frameState);
useCommittedLayerStates,
renderWidth,
renderHeight,
oscSmoothing,
&commitRequests,
layerStates);
QueueServiceCommitRequests(runtimeServices, commitRequests); QueueServiceCommitRequests(runtimeServices, commitRequests);
return resolved; return resolved;

View File

@@ -1,6 +1,6 @@
#pragma once #pragma once
#include "ShaderTypes.h" #include "RenderFrameState.h"
#include <vector> #include <vector>
@@ -10,12 +10,9 @@ class RuntimeServices;
class RuntimeServiceLiveBridge class RuntimeServiceLiveBridge
{ {
public: public:
static bool PrepareLiveRenderLayerStates( static bool PrepareLiveRenderFrameState(
RuntimeServices& runtimeServices, RuntimeServices& runtimeServices,
RenderEngine& renderEngine, RenderEngine& renderEngine,
bool useCommittedLayerStates, const RenderFrameInput& input,
unsigned renderWidth, RenderFrameState& frameState);
unsigned renderHeight,
double oscSmoothing,
std::vector<RuntimeRenderState>& layerStates);
}; };

View File

@@ -294,39 +294,32 @@ void OpenGLComposite::renderEffect()
if (mRuntimeUpdateController) if (mRuntimeUpdateController)
mRuntimeUpdateController->ProcessRuntimeWork(); mRuntimeUpdateController->ProcessRuntimeWork();
const bool hasInputSource = mVideoBackend->HasInputSource(); RenderFrameInput frameInput;
std::vector<RuntimeRenderState> layerStates; frameInput.useCommittedLayerStates = mRuntimeCoordinator && mRuntimeCoordinator->UseCommittedLayerStates();
const double smoothing = mRuntimeStore ? mRuntimeStore->GetConfiguredOscSmoothing() : 0.0; frameInput.hasInputSource = mVideoBackend->HasInputSource();
frameInput.renderWidth = mVideoBackend->InputFrameWidth();
frameInput.renderHeight = mVideoBackend->InputFrameHeight();
frameInput.inputFrameWidth = mVideoBackend->InputFrameWidth();
frameInput.inputFrameHeight = mVideoBackend->InputFrameHeight();
frameInput.captureTextureWidth = mVideoBackend->CaptureTextureWidth();
frameInput.inputPixelFormat = mVideoBackend->InputPixelFormat();
frameInput.historyCap = mRuntimeStore ? mRuntimeStore->GetConfiguredMaxTemporalHistoryFrames() : 0;
frameInput.oscSmoothing = mRuntimeStore ? mRuntimeStore->GetConfiguredOscSmoothing() : 0.0;
RenderFrameState frameState;
if (mRuntimeServices) if (mRuntimeServices)
{ {
RuntimeServiceLiveBridge::PrepareLiveRenderLayerStates( RuntimeServiceLiveBridge::PrepareLiveRenderFrameState(
*mRuntimeServices, *mRuntimeServices,
*mRenderEngine, *mRenderEngine,
mRuntimeCoordinator && mRuntimeCoordinator->UseCommittedLayerStates(), frameInput,
mVideoBackend->InputFrameWidth(), frameState);
mVideoBackend->InputFrameHeight(),
smoothing,
layerStates);
} }
else else
{ {
mRenderEngine->ResolveRenderLayerStates( mRenderEngine->ResolveRenderFrameState(frameInput, nullptr, frameState);
mRuntimeCoordinator && mRuntimeCoordinator->UseCommittedLayerStates(),
mVideoBackend->InputFrameWidth(),
mVideoBackend->InputFrameHeight(),
smoothing,
nullptr,
layerStates);
} }
const unsigned historyCap = mRuntimeStore ? mRuntimeStore->GetConfiguredMaxTemporalHistoryFrames() : 0; mRenderEngine->RenderPreparedFrame(frameState);
mRenderEngine->RenderLayerStack(
hasInputSource,
layerStates,
mVideoBackend->InputFrameWidth(),
mVideoBackend->InputFrameHeight(),
mVideoBackend->CaptureTextureWidth(),
mVideoBackend->InputPixelFormat(),
historyCap);
} }
void OpenGLComposite::ProcessScreenshotRequest() void OpenGLComposite::ProcessScreenshotRequest()

View File

@@ -6,11 +6,6 @@
#include <algorithm> #include <algorithm>
namespace
{
constexpr auto kOscOverlayCommitDelay = std::chrono::milliseconds(150);
}
RenderEngine::RenderEngine( RenderEngine::RenderEngine(
RuntimeSnapshotProvider& runtimeSnapshotProvider, RuntimeSnapshotProvider& runtimeSnapshotProvider,
HealthTelemetry& healthTelemetry, HealthTelemetry& healthTelemetry,
@@ -24,10 +19,10 @@ RenderEngine::RenderEngine(
mRenderPass(mRenderer), mRenderPass(mRenderer),
mRenderPipeline(mRenderer, runtimeSnapshotProvider, healthTelemetry, std::move(renderEffect), std::move(screenshotReady), std::move(previewPaint)), mRenderPipeline(mRenderer, runtimeSnapshotProvider, healthTelemetry, std::move(renderEffect), std::move(screenshotReady), std::move(previewPaint)),
mShaderPrograms(mRenderer, runtimeSnapshotProvider), mShaderPrograms(mRenderer, runtimeSnapshotProvider),
mRuntimeSnapshotProvider(runtimeSnapshotProvider),
mMutex(mutex), mMutex(mutex),
mHdc(hdc), mHdc(hdc),
mHglrc(hglrc) mHglrc(hglrc),
mFrameStateResolver(runtimeSnapshotProvider)
{ {
} }
@@ -86,11 +81,7 @@ bool RenderEngine::ApplyPreparedShaderBuild(
if (!CommitPreparedLayerPrograms(preparedBuild, inputFrameWidth, inputFrameHeight, errorMessageSize, errorMessage)) if (!CommitPreparedLayerPrograms(preparedBuild, inputFrameWidth, inputFrameHeight, errorMessageSize, errorMessage))
return false; return false;
mCachedLayerRenderStates = mShaderPrograms.CommittedLayerStates(); mFrameStateResolver.StoreCommittedSnapshot(preparedBuild.renderSnapshot, mShaderPrograms.CommittedLayerStates());
mCachedRenderStateVersion = preparedBuild.renderSnapshot.versions.renderStateVersion;
mCachedParameterStateVersion = preparedBuild.renderSnapshot.versions.parameterStateVersion;
mCachedRenderStateWidth = preparedBuild.renderSnapshot.outputWidth;
mCachedRenderStateHeight = preparedBuild.renderSnapshot.outputHeight;
ResetTemporalHistoryState(); ResetTemporalHistoryState();
if (!preserveFeedbackState) if (!preserveFeedbackState)
ResetShaderFeedbackState(); ResetShaderFeedbackState();
@@ -248,92 +239,37 @@ bool RenderEngine::RenderOutputFrame(const RenderPipelineFrameContext& context,
return rendered; return rendered;
} }
bool RenderEngine::ResolveRenderLayerStates( bool RenderEngine::ResolveRenderFrameState(
bool useCommittedLayerStates, const RenderFrameInput& input,
unsigned renderWidth,
unsigned renderHeight,
double oscSmoothing,
std::vector<OscOverlayCommitRequest>* commitRequests, std::vector<OscOverlayCommitRequest>* commitRequests,
std::vector<RuntimeRenderState>& layerStates) RenderFrameState& frameState)
{ {
layerStates.clear(); std::vector<RuntimeLiveOscCommitRequest> liveCommitRequests;
if (useCommittedLayerStates) const bool resolved = mFrameStateResolver.Resolve(
input,
mShaderPrograms.CommittedLayerStates(),
mRuntimeLiveState,
commitRequests ? &liveCommitRequests : nullptr,
frameState);
if (commitRequests)
{ {
layerStates = ComposeRenderLayerStates(mShaderPrograms.CommittedLayerStates(), false, oscSmoothing, commitRequests); for (const RuntimeLiveOscCommitRequest& request : liveCommitRequests)
mRuntimeSnapshotProvider.RefreshDynamicRenderStateFields(layerStates); commitRequests->push_back({ request.routeKey, request.layerKey, request.parameterKey, request.value, request.generation });
return true;
} }
return resolved;
const RuntimeSnapshotVersions versions = mRuntimeSnapshotProvider.GetVersions();
const bool renderStateCacheValid =
!mCachedLayerRenderStates.empty() &&
mCachedRenderStateVersion == versions.renderStateVersion &&
mCachedRenderStateWidth == renderWidth &&
mCachedRenderStateHeight == renderHeight;
if (renderStateCacheValid)
{
RuntimeRenderStateSnapshot renderSnapshot;
renderSnapshot.outputWidth = renderWidth;
renderSnapshot.outputHeight = renderHeight;
renderSnapshot.versions.renderStateVersion = mCachedRenderStateVersion;
renderSnapshot.versions.parameterStateVersion = mCachedParameterStateVersion;
renderSnapshot.states = mCachedLayerRenderStates;
renderSnapshot.states = ComposeRenderLayerStates(renderSnapshot.states, true, oscSmoothing, commitRequests);
if (mCachedParameterStateVersion != versions.parameterStateVersion &&
mRuntimeSnapshotProvider.TryRefreshPublishedSnapshotParameters(renderSnapshot))
{
mCachedParameterStateVersion = renderSnapshot.versions.parameterStateVersion;
renderSnapshot.states = ComposeRenderLayerStates(renderSnapshot.states, true, oscSmoothing, commitRequests);
}
mCachedLayerRenderStates = renderSnapshot.states;
layerStates = renderSnapshot.states;
mRuntimeSnapshotProvider.RefreshDynamicRenderStateFields(layerStates);
return true;
}
RuntimeRenderStateSnapshot renderSnapshot;
if (mRuntimeSnapshotProvider.TryPublishRenderStateSnapshot(renderWidth, renderHeight, renderSnapshot))
{
mCachedLayerRenderStates = renderSnapshot.states;
mCachedRenderStateVersion = renderSnapshot.versions.renderStateVersion;
mCachedParameterStateVersion = renderSnapshot.versions.parameterStateVersion;
mCachedRenderStateWidth = renderSnapshot.outputWidth;
mCachedRenderStateHeight = renderSnapshot.outputHeight;
mCachedLayerRenderStates = ComposeRenderLayerStates(mCachedLayerRenderStates, true, oscSmoothing, commitRequests);
layerStates = mCachedLayerRenderStates;
return true;
}
layerStates = ComposeRenderLayerStates(mCachedLayerRenderStates, true, oscSmoothing, commitRequests);
mRuntimeSnapshotProvider.RefreshDynamicRenderStateFields(layerStates);
return !layerStates.empty();
} }
std::vector<RuntimeRenderState> RenderEngine::ComposeRenderLayerStates( void RenderEngine::RenderPreparedFrame(const RenderFrameState& frameState)
const std::vector<RuntimeRenderState>& baseStates,
bool allowCommit,
double smoothing,
std::vector<OscOverlayCommitRequest>* commitRequests)
{ {
RenderStateCompositionInput input; RenderLayerStack(
input.baseLayerStates = &baseStates; frameState.hasInputSource,
input.liveState = &mRuntimeLiveState; frameState.layerStates,
input.allowLiveCommits = allowCommit; frameState.inputFrameWidth,
input.collectLiveCommitRequests = commitRequests != nullptr; frameState.inputFrameHeight,
input.liveSmoothing = smoothing; frameState.captureTextureWidth,
input.liveCommitDelay = kOscOverlayCommitDelay; frameState.inputPixelFormat,
input.now = std::chrono::steady_clock::now(); frameState.historyCap);
const RenderStateCompositionResult result = mRenderStateComposer.BuildFrameState(input);
if (!commitRequests)
return result.layerStates;
for (const RuntimeLiveOscCommitRequest& request : result.commitRequests)
commitRequests->push_back({ request.routeKey, request.layerKey, request.parameterKey, request.value, request.generation });
return result.layerStates;
} }
void RenderEngine::RenderLayerStack( void RenderEngine::RenderLayerStack(

View File

@@ -4,7 +4,8 @@
#include "OpenGLRenderPipeline.h" #include "OpenGLRenderPipeline.h"
#include "OpenGLRenderer.h" #include "OpenGLRenderer.h"
#include "OpenGLShaderPrograms.h" #include "OpenGLShaderPrograms.h"
#include "RenderStateComposer.h" #include "RenderFrameState.h"
#include "RenderFrameStateResolver.h"
#include "HealthTelemetry.h" #include "HealthTelemetry.h"
#include "RuntimeCoordinator.h" #include "RuntimeCoordinator.h"
#include "RuntimeSnapshotProvider.h" #include "RuntimeSnapshotProvider.h"
@@ -104,13 +105,11 @@ public:
bool TryPresentPreview(bool force, unsigned previewFps, unsigned outputFrameWidth, unsigned outputFrameHeight); bool TryPresentPreview(bool force, unsigned previewFps, unsigned outputFrameWidth, unsigned outputFrameHeight);
bool TryUploadInputFrame(const VideoIOFrame& inputFrame, const VideoIOState& videoState); bool TryUploadInputFrame(const VideoIOFrame& inputFrame, const VideoIOState& videoState);
bool RenderOutputFrame(const RenderPipelineFrameContext& context, VideoIOOutputFrame& outputFrame); bool RenderOutputFrame(const RenderPipelineFrameContext& context, VideoIOOutputFrame& outputFrame);
bool ResolveRenderLayerStates( bool ResolveRenderFrameState(
bool useCommittedLayerStates, const RenderFrameInput& input,
unsigned renderWidth,
unsigned renderHeight,
double oscSmoothing,
std::vector<OscOverlayCommitRequest>* commitRequests, std::vector<OscOverlayCommitRequest>* commitRequests,
std::vector<RuntimeRenderState>& layerStates); RenderFrameState& frameState);
void RenderPreparedFrame(const RenderFrameState& frameState);
void RenderLayerStack( void RenderLayerStack(
bool hasInputSource, bool hasInputSource,
const std::vector<RuntimeRenderState>& layerStates, const std::vector<RuntimeRenderState>& layerStates,
@@ -127,23 +126,11 @@ private:
OpenGLRenderPass mRenderPass; OpenGLRenderPass mRenderPass;
OpenGLRenderPipeline mRenderPipeline; OpenGLRenderPipeline mRenderPipeline;
OpenGLShaderPrograms mShaderPrograms; OpenGLShaderPrograms mShaderPrograms;
RuntimeSnapshotProvider& mRuntimeSnapshotProvider;
CRITICAL_SECTION& mMutex; CRITICAL_SECTION& mMutex;
HDC mHdc; HDC mHdc;
HGLRC mHglrc; HGLRC mHglrc;
std::vector<RuntimeRenderState> ComposeRenderLayerStates(
const std::vector<RuntimeRenderState>& baseStates,
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::chrono::steady_clock::time_point mLastPreviewPresentTime;
RenderStateComposer mRenderStateComposer; RenderFrameStateResolver mFrameStateResolver;
RuntimeLiveState mRuntimeLiveState; RuntimeLiveState mRuntimeLiveState;
}; };

View File

@@ -0,0 +1,31 @@
#pragma once
#include "ShaderTypes.h"
#include "VideoIOTypes.h"
#include <vector>
struct RenderFrameInput
{
bool useCommittedLayerStates = false;
bool hasInputSource = false;
unsigned renderWidth = 0;
unsigned renderHeight = 0;
unsigned inputFrameWidth = 0;
unsigned inputFrameHeight = 0;
unsigned captureTextureWidth = 0;
VideoIOPixelFormat inputPixelFormat = VideoIOPixelFormat::Uyvy8;
unsigned historyCap = 0;
double oscSmoothing = 0.0;
};
struct RenderFrameState
{
bool hasInputSource = false;
unsigned inputFrameWidth = 0;
unsigned inputFrameHeight = 0;
unsigned captureTextureWidth = 0;
VideoIOPixelFormat inputPixelFormat = VideoIOPixelFormat::Uyvy8;
unsigned historyCap = 0;
std::vector<RuntimeRenderState> layerStates;
};

View File

@@ -0,0 +1,119 @@
#include "RenderFrameStateResolver.h"
#include <chrono>
namespace
{
constexpr auto kOscOverlayCommitDelay = std::chrono::milliseconds(150);
}
RenderFrameStateResolver::RenderFrameStateResolver(RuntimeSnapshotProvider& runtimeSnapshotProvider) :
mRuntimeSnapshotProvider(runtimeSnapshotProvider)
{
}
void RenderFrameStateResolver::StoreCommittedSnapshot(
const RuntimeRenderStateSnapshot& snapshot,
const std::vector<RuntimeRenderState>& committedLayerStates)
{
mCachedLayerRenderStates = committedLayerStates;
mCachedRenderStateVersion = snapshot.versions.renderStateVersion;
mCachedParameterStateVersion = snapshot.versions.parameterStateVersion;
mCachedRenderStateWidth = snapshot.outputWidth;
mCachedRenderStateHeight = snapshot.outputHeight;
}
bool RenderFrameStateResolver::Resolve(
const RenderFrameInput& input,
const std::vector<RuntimeRenderState>& committedLayerStates,
RuntimeLiveState& liveState,
std::vector<RuntimeLiveOscCommitRequest>* commitRequests,
RenderFrameState& frameState)
{
frameState.hasInputSource = input.hasInputSource;
frameState.inputFrameWidth = input.inputFrameWidth;
frameState.inputFrameHeight = input.inputFrameHeight;
frameState.captureTextureWidth = input.captureTextureWidth;
frameState.inputPixelFormat = input.inputPixelFormat;
frameState.historyCap = input.historyCap;
frameState.layerStates.clear();
if (input.useCommittedLayerStates)
{
frameState.layerStates = ComposeLayerStates(committedLayerStates, liveState, false, input.oscSmoothing, commitRequests);
mRuntimeSnapshotProvider.RefreshDynamicRenderStateFields(frameState.layerStates);
return true;
}
const RuntimeSnapshotVersions versions = mRuntimeSnapshotProvider.GetVersions();
const bool renderStateCacheValid =
!mCachedLayerRenderStates.empty() &&
mCachedRenderStateVersion == versions.renderStateVersion &&
mCachedRenderStateWidth == input.renderWidth &&
mCachedRenderStateHeight == input.renderHeight;
if (renderStateCacheValid)
{
RuntimeRenderStateSnapshot renderSnapshot;
renderSnapshot.outputWidth = input.renderWidth;
renderSnapshot.outputHeight = input.renderHeight;
renderSnapshot.versions.renderStateVersion = mCachedRenderStateVersion;
renderSnapshot.versions.parameterStateVersion = mCachedParameterStateVersion;
renderSnapshot.states = mCachedLayerRenderStates;
renderSnapshot.states = ComposeLayerStates(renderSnapshot.states, liveState, true, input.oscSmoothing, commitRequests);
if (mCachedParameterStateVersion != versions.parameterStateVersion &&
mRuntimeSnapshotProvider.TryRefreshPublishedSnapshotParameters(renderSnapshot))
{
mCachedParameterStateVersion = renderSnapshot.versions.parameterStateVersion;
renderSnapshot.states = ComposeLayerStates(renderSnapshot.states, liveState, true, input.oscSmoothing, commitRequests);
}
mCachedLayerRenderStates = renderSnapshot.states;
frameState.layerStates = renderSnapshot.states;
mRuntimeSnapshotProvider.RefreshDynamicRenderStateFields(frameState.layerStates);
return true;
}
RuntimeRenderStateSnapshot renderSnapshot;
if (mRuntimeSnapshotProvider.TryPublishRenderStateSnapshot(input.renderWidth, input.renderHeight, renderSnapshot))
{
mCachedLayerRenderStates = renderSnapshot.states;
mCachedRenderStateVersion = renderSnapshot.versions.renderStateVersion;
mCachedParameterStateVersion = renderSnapshot.versions.parameterStateVersion;
mCachedRenderStateWidth = renderSnapshot.outputWidth;
mCachedRenderStateHeight = renderSnapshot.outputHeight;
mCachedLayerRenderStates = ComposeLayerStates(mCachedLayerRenderStates, liveState, true, input.oscSmoothing, commitRequests);
frameState.layerStates = mCachedLayerRenderStates;
return true;
}
frameState.layerStates = ComposeLayerStates(mCachedLayerRenderStates, liveState, true, input.oscSmoothing, commitRequests);
mRuntimeSnapshotProvider.RefreshDynamicRenderStateFields(frameState.layerStates);
return !frameState.layerStates.empty();
}
std::vector<RuntimeRenderState> RenderFrameStateResolver::ComposeLayerStates(
const std::vector<RuntimeRenderState>& baseStates,
RuntimeLiveState& liveState,
bool allowCommit,
double smoothing,
std::vector<RuntimeLiveOscCommitRequest>* commitRequests) const
{
RenderStateCompositionInput input;
input.baseLayerStates = &baseStates;
input.liveState = &liveState;
input.allowLiveCommits = allowCommit;
input.collectLiveCommitRequests = commitRequests != nullptr;
input.liveSmoothing = smoothing;
input.liveCommitDelay = kOscOverlayCommitDelay;
input.now = std::chrono::steady_clock::now();
const RenderStateCompositionResult result = mRenderStateComposer.BuildFrameState(input);
if (commitRequests)
{
for (const RuntimeLiveOscCommitRequest& request : result.commitRequests)
commitRequests->push_back(request);
}
return result.layerStates;
}

View File

@@ -0,0 +1,40 @@
#pragma once
#include "RenderFrameState.h"
#include "RenderStateComposer.h"
#include "RuntimeSnapshotProvider.h"
#include <cstdint>
#include <vector>
class RenderFrameStateResolver
{
public:
explicit RenderFrameStateResolver(RuntimeSnapshotProvider& runtimeSnapshotProvider);
void StoreCommittedSnapshot(
const RuntimeRenderStateSnapshot& snapshot,
const std::vector<RuntimeRenderState>& committedLayerStates);
bool Resolve(
const RenderFrameInput& input,
const std::vector<RuntimeRenderState>& committedLayerStates,
RuntimeLiveState& liveState,
std::vector<RuntimeLiveOscCommitRequest>* commitRequests,
RenderFrameState& frameState);
private:
std::vector<RuntimeRenderState> ComposeLayerStates(
const std::vector<RuntimeRenderState>& baseStates,
RuntimeLiveState& liveState,
bool allowCommit,
double smoothing,
std::vector<RuntimeLiveOscCommitRequest>* commitRequests) const;
RuntimeSnapshotProvider& mRuntimeSnapshotProvider;
RenderStateComposer mRenderStateComposer;
std::vector<RuntimeRenderState> mCachedLayerRenderStates;
uint64_t mCachedRenderStateVersion = 0;
uint64_t mCachedParameterStateVersion = 0;
unsigned mCachedRenderStateWidth = 0;
unsigned mCachedRenderStateHeight = 0;
};

View File

@@ -7,8 +7,8 @@ Phase 1 split runtime responsibilities into named subsystems. Phase 2 added the
## Status ## Status
- Phase 3 design package: proposed. - Phase 3 design package: proposed.
- Phase 3 implementation: initial parallel implementation batch integrated. - Phase 3 implementation: exit criteria satisfied for the current architecture.
- Current alignment: the repo now has the live-state/composer building blocks and a service bridge. `OpenGLComposite::renderEffect()` still remains the app-level frame entrypoint, but the service drain, layer-state resolution, and OSC commit handoff now sit behind a named bridge helper. - Current alignment: the repo now has the live-state/composer building blocks, a service bridge, and a named frame-state handoff. `OpenGLComposite::renderEffect()` still remains the app-level frame entrypoint, but the service drain, layer-state resolution, and OSC commit handoff now sit behind named helpers and frame-state data.
Current footholds: Current footholds:
@@ -17,7 +17,9 @@ Current footholds:
- `RuntimeSnapshotProvider` publishes render snapshots from `RenderSnapshotBuilder`. - `RuntimeSnapshotProvider` publishes render snapshots from `RenderSnapshotBuilder`.
- `RuntimeLiveState` owns transient OSC overlay bookkeeping and commit-settlement policy. - `RuntimeLiveState` owns transient OSC overlay bookkeeping and commit-settlement policy.
- `RenderStateComposer` exists as the first pure composition boundary for combining base layer state with live overlays. - `RenderStateComposer` exists as the first pure composition boundary for combining base layer state with live overlays.
- `RenderEngine` still owns snapshot cache selection and final render-layer resolution, but live overlay value composition now delegates to `RenderStateComposer` and `RuntimeLiveState`. - `RenderFrameInput` / `RenderFrameState` now provide a named frame-facing handoff model for preparing layer state and render inputs before drawing.
- `RenderFrameStateResolver` now owns snapshot cache selection, parameter refresh decisions, and final frame-state resolution before drawing.
- `RenderEngine` owns GL/render resources and delegates frame-state preparation to the resolver.
- `ControlServices` owns OSC ingress, pending OSC updates, completed OSC commit notifications, and service start/stop. - `ControlServices` owns OSC ingress, pending OSC updates, completed OSC commit notifications, and service start/stop.
- `RuntimeServiceLiveBridge` translates service OSC queues into render live-state updates and queues settled overlay commit requests. - `RuntimeServiceLiveBridge` translates service OSC queues into render live-state updates and queues settled overlay commit requests.
- `RuntimeEventDispatcher` now routes accepted mutations, reloads, snapshots, shader build events, backend observations, and health observations. - `RuntimeEventDispatcher` now routes accepted mutations, reloads, snapshots, shader build events, backend observations, and health observations.
@@ -68,7 +70,7 @@ Those are later phases. Phase 3 is about making state and service coordination c
2. asks `RuntimeServiceLiveBridge` to prepare live render layer states 2. asks `RuntimeServiceLiveBridge` to prepare live render layer states
3. asks `RenderEngine` to draw the layer stack 3. asks `RenderEngine` to draw the layer stack
The bridge now owns service queue draining, live automation settlement, committed/live state selection, and OSC commit handoff. `RenderEngine` still owns snapshot cache selection and dynamic render-field refresh, which is the remaining boundary to clarify before Phase 4. The bridge now owns service queue draining, live automation settlement, committed/live state selection, and OSC commit handoff. `RenderFrameStateResolver` owns snapshot cache selection, parameter refresh decisions, and dynamic render-field refresh before handing a prepared frame state to `RenderEngine`.
## Target State Model ## Target State Model
@@ -150,7 +152,7 @@ Non-responsibilities:
| pending OSC updates drained by `OpenGLComposite` | `OscValueReceived` -> live-state overlay update handler | Phase 2 already has the event type; Phase 3 decides whether transient overlay updates enter the app dispatcher or a source-local bridge. | | pending OSC updates drained by `OpenGLComposite` | `OscValueReceived` -> live-state overlay update handler | Phase 2 already has the event type; Phase 3 decides whether transient overlay updates enter the app dispatcher or a source-local bridge. |
| render asks for overlay commit requests | `OscOverlaySettled` or direct coordinator command plus event publication | Commit request creation should leave `renderEffect()` and live near the live-state owner. | | render asks for overlay commit requests | `OscOverlaySettled` or direct coordinator command plus event publication | Commit request creation should leave `renderEffect()` and live near the live-state owner. |
| completed OSC commits drained by `OpenGLComposite` | `RuntimeMutationAccepted` / completion event -> live-state commit completion | Completed commit routing should be event-driven or owned by live-state service bridge. | | completed OSC commits drained by `OpenGLComposite` | `RuntimeMutationAccepted` / completion event -> live-state commit completion | Completed commit routing should be event-driven or owned by live-state service bridge. |
| `RenderEngine::ResolveRenderLayerStates(...)` | `RenderStateComposer::BuildFrameState(...)` | Keep final state composition testable without GL. | | `RenderFrameStateResolver::Resolve(...)` | `RenderStateComposer::BuildFrameState(...)` | Keep final state composition testable without GL. |
| direct persistence writes from store mutations | `RuntimePersistenceRequested` as the durable write trigger | Background writer lands later; Phase 3 should make request boundaries clear. | | direct persistence writes from store mutations | `RuntimePersistenceRequested` as the durable write trigger | Background writer lands later; Phase 3 should make request boundaries clear. |
| runtime-state broadcast side effects | `RuntimeStateBroadcastRequested` plus optional completed/failed observations | Keep broadcast delivery in services and presentation ownership in runtime presentation. | | runtime-state broadcast side effects | `RuntimeStateBroadcastRequested` plus optional completed/failed observations | Keep broadcast delivery in services and presentation ownership in runtime presentation. |
@@ -246,7 +248,7 @@ This keeps Phase 6 smaller: the background snapshot writer can subscribe to pers
Introduce `RuntimeLiveState`, `RenderStateComposer`, or an equivalent pair of classes. Introduce `RuntimeLiveState`, `RenderStateComposer`, or an equivalent pair of classes.
Start by moving pure data operations out of `RenderEngine::ResolveRenderLayerStates(...)` without changing behavior. Start by moving pure data operations out of frame rendering without changing behavior.
Status: started. `runtime/live/RuntimeLiveState` and `runtime/live/RenderStateComposer` now exist, are included in the build, and have a focused `RuntimeLiveStateTests` target. Status: started. `runtime/live/RuntimeLiveState` and `runtime/live/RenderStateComposer` now exist, are included in the build, and have a focused `RuntimeLiveStateTests` target.
@@ -291,7 +293,7 @@ void OpenGLComposite::renderEffect()
The exact names can change. The goal is that render effect no longer manually drains services, settles overlay commits, and resolves layer values. The exact names can change. The goal is that render effect no longer manually drains services, settles overlay commits, and resolves layer values.
Status: started. `OpenGLComposite::renderEffect()` still drives frame timing, video dimensions, and drawing, but the service-drain, resolve, and commit-handoff path has moved behind `RuntimeServiceLiveBridge::PrepareLiveRenderLayerStates(...)`. Status: started. `OpenGLComposite::renderEffect()` still drives frame timing, video dimensions, and drawing, but the service-drain, resolve, and commit-handoff path has moved behind `RuntimeServiceLiveBridge::PrepareLiveRenderFrameState(...)` and a named `RenderFrameInput` / `RenderFrameState` handoff.
### Step 5. Add Persistence Boundary Tests ### Step 5. Add Persistence Boundary Tests
@@ -339,7 +341,7 @@ The current groundwork is intended to let these lanes proceed in parallel with l
| Lane | Primary files | Goal | | Lane | Primary files | Goal |
| --- | --- | --- | | --- | --- | --- |
| A. Live-state behavior | `runtime/live/RuntimeLiveState.*`, `tests/RuntimeLiveStateTests.cpp` | Finish stale completion tests, smoothing edge cases, trigger behavior, and overlay settle policy. | | A. Live-state behavior | `runtime/live/RuntimeLiveState.*`, `tests/RuntimeLiveStateTests.cpp` | Finish stale completion tests, smoothing edge cases, trigger behavior, and overlay settle policy. |
| B. Render-state composition | `runtime/live/RenderStateComposer.*`, `gl/RenderEngine.*` | Move more of `RenderEngine::ResolveRenderLayerStates(...)` value composition behind the pure composer while keeping GL calls in `RenderEngine`. | | B. Render-state composition | `runtime/live/RenderStateComposer.*`, `gl/RenderFrameStateResolver.*`, `gl/RenderEngine.*` | Keep value composition and frame-state selection outside GL drawing while keeping GL calls in `RenderEngine`. |
| C. Service bridge | `control/RuntimeServices.*`, `control/ControlServices.*`, possible new bridge class | Stop `OpenGLComposite::renderEffect()` from draining OSC update/completion queues directly. | | C. Service bridge | `control/RuntimeServices.*`, `control/ControlServices.*`, possible new bridge class | Stop `OpenGLComposite::renderEffect()` from draining OSC update/completion queues directly. |
| D. App-frame orchestration | `gl/OpenGLComposite.*`, `gl/RuntimeUpdateController.*` | Replace render-effect glue with a narrow frame-state preparation call and commit-request handoff. | | D. App-frame orchestration | `gl/OpenGLComposite.*`, `gl/RuntimeUpdateController.*` | Replace render-effect glue with a narrow frame-state preparation call and commit-request handoff. |
| E. Persistence boundary | `runtime/coordination/RuntimeCoordinator.*`, `runtime/store/*`, event tests | Keep persistence request publication explicit and prepare for a later background writer without changing storage behavior yet. | | E. Persistence boundary | `runtime/coordination/RuntimeCoordinator.*`, `runtime/store/*`, event tests | Keep persistence request publication explicit and prepare for a later background writer without changing storage behavior yet. |
@@ -348,13 +350,13 @@ The current groundwork is intended to let these lanes proceed in parallel with l
Phase 3 can be considered complete once the project can say: Phase 3 can be considered complete once the project can say:
- [x] final render-state composition has a named, testable owner outside `OpenGLComposite` (live value composition is covered by `RenderStateComposer`; full snapshot/cache selection still remains in `RenderEngine`) - [x] final render-state composition has named owners outside `OpenGLComposite` (`RenderStateComposer` covers live value composition; `RenderFrameStateResolver` covers snapshot/cache selection and frame-state resolution)
- [x] transient OSC overlay state has a named owner and tests - [x] transient OSC overlay state has a named owner and tests
- [x] overlay commit requests and completions no longer require `OpenGLComposite` to drain service queues directly - [x] overlay commit requests and completions no longer require `OpenGLComposite` to drain service queues directly
- [x] `RenderEngine` is closer to GL/render resource ownership and less responsible for value composition - [x] `RenderEngine` is closer to GL/render resource ownership and less responsible for value composition
- [x] `RuntimeStore` remains durable-state focused and does not gain live overlay responsibilities - [x] `RuntimeStore` remains durable-state focused and does not gain live overlay responsibilities
- [x] persistence requests are explicit event outcomes for persisted mutations - [x] persistence requests are explicit event outcomes for persisted mutations
- [ ] Phase 4 can define a render-thread input contract around immutable or near-immutable frame state - [x] Phase 4 can define a render-thread input contract around immutable or near-immutable frame state
## Open Questions ## Open Questions