From 41075bbc610c9b763962821502e7f1addb776cf7 Mon Sep 17 00:00:00 2001
From: Aiden <68633820+awils27@users.noreply.github.com>
Date: Sun, 10 May 2026 23:53:27 +1000
Subject: [PATCH] more seperation
---
CMakeLists.txt | 2 +
.../LoopThroughWithOpenGLCompositing.sln | 28 --
.../LoopThroughWithOpenGLCompositing.vcxproj | 299 ----------------
...roughWithOpenGLCompositing.vcxproj.filters | 338 ------------------
.../gl/OpenGLComposite.cpp | 227 +++++-------
.../gl/OpenGLComposite.h | 12 +-
.../gl/OpenGLCompositeRuntimeControls.cpp | 89 ++---
.../gl/RenderEngine.cpp | 139 ++++++-
.../gl/RenderEngine.h | 30 +-
.../gl/pipeline/OpenGLRenderPipeline.cpp | 13 +-
.../gl/pipeline/OpenGLRenderPipeline.h | 9 +-
.../gl/pipeline/OpenGLVideoIOBridge.cpp | 64 +---
.../gl/pipeline/OpenGLVideoIOBridge.h | 24 +-
.../gl/shader/OpenGLShaderPrograms.cpp | 15 +-
.../gl/shader/OpenGLShaderPrograms.h | 4 +-
.../gl/shader/ShaderProgramCompiler.cpp | 10 +-
.../gl/shader/ShaderProgramCompiler.h | 6 +-
.../runtime/RuntimeCoordinator.cpp | 173 +++++++++
.../runtime/RuntimeCoordinator.h | 70 ++++
.../runtime/RuntimeSnapshotProvider.cpp | 15 +
.../runtime/RuntimeSnapshotProvider.h | 3 +
.../videoio/VideoBackend.cpp | 71 +++-
.../videoio/VideoBackend.h | 14 +
.../videoio/decklink/DeckLinkSession.cpp | 103 +++---
.../videoio/decklink/DeckLinkSession.h | 6 +
25 files changed, 733 insertions(+), 1031 deletions(-)
delete mode 100644 apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.sln
delete mode 100644 apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj
delete mode 100644 apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj.filters
create mode 100644 apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeCoordinator.cpp
create mode 100644 apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeCoordinator.h
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9a50320..8e17a13 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -104,6 +104,8 @@ set(APP_SOURCES
"${APP_DIR}/runtime/RuntimeHost.h"
"${APP_DIR}/runtime/HealthTelemetry.cpp"
"${APP_DIR}/runtime/HealthTelemetry.h"
+ "${APP_DIR}/runtime/RuntimeCoordinator.cpp"
+ "${APP_DIR}/runtime/RuntimeCoordinator.h"
"${APP_DIR}/runtime/RuntimeSnapshotProvider.cpp"
"${APP_DIR}/runtime/RuntimeSnapshotProvider.h"
"${APP_DIR}/runtime/RuntimeClock.cpp"
diff --git a/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.sln b/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.sln
deleted file mode 100644
index e4f57c3..0000000
--- a/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.sln
+++ /dev/null
@@ -1,28 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2013
-VisualStudioVersion = 12.0.21005.1
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LoopThroughWithOpenGLCompositing", "LoopThroughWithOpenGLCompositing.vcxproj", "{92C79085-CA51-4008-95DB-5403D2E19885}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Win32 = Debug|Win32
- Debug|x64 = Debug|x64
- Release|Win32 = Release|Win32
- Release|x64 = Release|x64
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {92C79085-CA51-4008-95DB-5403D2E19885}.Debug|Win32.ActiveCfg = Debug|Win32
- {92C79085-CA51-4008-95DB-5403D2E19885}.Debug|Win32.Build.0 = Debug|Win32
- {92C79085-CA51-4008-95DB-5403D2E19885}.Debug|x64.ActiveCfg = Debug|x64
- {92C79085-CA51-4008-95DB-5403D2E19885}.Debug|x64.Build.0 = Debug|x64
- {92C79085-CA51-4008-95DB-5403D2E19885}.Release|Win32.ActiveCfg = Release|Win32
- {92C79085-CA51-4008-95DB-5403D2E19885}.Release|Win32.Build.0 = Release|Win32
- {92C79085-CA51-4008-95DB-5403D2E19885}.Release|x64.ActiveCfg = Release|x64
- {92C79085-CA51-4008-95DB-5403D2E19885}.Release|x64.Build.0 = Release|x64
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
-EndGlobal
diff --git a/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj b/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj
deleted file mode 100644
index e4e4868..0000000
--- a/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj
+++ /dev/null
@@ -1,299 +0,0 @@
-
-
-
-
- Debug
- Win32
-
-
- Debug
- x64
-
-
- Release
- Win32
-
-
- Release
- x64
-
-
-
- {92C79085-CA51-4008-95DB-5403D2E19885}
- LoopThroughWithOpenGLCompositing
- Win32Proj
- 10.0.26100.0
-
-
-
- Application
- v143
- MultiByte
- true
-
-
- Application
- v143
- MultiByte
-
-
- Application
- v143
- MultiByte
- true
-
-
- Application
- v143
- MultiByte
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <_ProjectFileVersion>12.0.21005.1
-
-
- $(SolutionDir)$(Configuration)\
- $(Configuration)\
- true
-
-
- $(SolutionDir)$(Platform)\$(Configuration)\
- $(Platform)\$(Configuration)\
- true
-
-
- $(SolutionDir)$(Configuration)\
- $(Configuration)\
- false
-
-
- $(SolutionDir)$(Platform)\$(Configuration)\
- $(Platform)\$(Configuration)\
- false
-
-
-
- Disabled
- .;control;gl;gl\pipeline;gl\renderer;gl\shader;platform;runtime;shader;videoio;videoio\decklink;%(AdditionalIncludeDirectories)
- WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)
- EnableFastChecks
- MultiThreadedDebugDLL
-
- Level3
- EditAndContinue
- stdcpp17
-
-
- opengl32.lib;Glu32.lib;Ws2_32.lib;Crypt32.lib;Advapi32.lib;Gdiplus.lib;Ole32.lib;Windowscodecs.lib;%(AdditionalDependencies)
- true
- Windows
- MachineX86
-
-
-
-
- X64
-
-
- Disabled
- .;control;gl;gl\pipeline;gl\renderer;gl\shader;platform;runtime;shader;videoio;videoio\decklink;%(AdditionalIncludeDirectories)
- WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)
- EnableFastChecks
- MultiThreadedDebugDLL
-
- Level3
- ProgramDatabase
- stdcpp17
-
-
- opengl32.lib;Glu32.lib;Ws2_32.lib;Crypt32.lib;Advapi32.lib;Gdiplus.lib;Ole32.lib;Windowscodecs.lib;%(AdditionalDependencies)
- true
- Windows
- MachineX64
-
-
-
-
- MaxSpeed
- true
- .;control;gl;gl\pipeline;gl\renderer;gl\shader;platform;runtime;shader;videoio;videoio\decklink;%(AdditionalIncludeDirectories)
- WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)
- MultiThreadedDLL
- true
-
- Level3
- ProgramDatabase
- stdcpp17
-
-
- opengl32.lib;Glu32.lib;Ws2_32.lib;Crypt32.lib;Advapi32.lib;Gdiplus.lib;Ole32.lib;Windowscodecs.lib;%(AdditionalDependencies)
- true
- Windows
- true
- true
- MachineX86
-
-
-
-
- X64
-
-
- MaxSpeed
- true
- .;control;gl;gl\pipeline;gl\renderer;gl\shader;platform;runtime;shader;videoio;videoio\decklink;%(AdditionalIncludeDirectories)
- WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)
- MultiThreadedDLL
- true
-
- Level3
- ProgramDatabase
- stdcpp17
-
-
- opengl32.lib;Glu32.lib;Ws2_32.lib;Crypt32.lib;Advapi32.lib;Gdiplus.lib;Ole32.lib;Windowscodecs.lib;%(AdditionalDependencies)
- true
- Windows
- true
- true
- MachineX64
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Create
- Create
- Create
- Create
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj.filters b/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj.filters
deleted file mode 100644
index b745b94..0000000
--- a/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj.filters
+++ /dev/null
@@ -1,338 +0,0 @@
-
-
-
-
- {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
- cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
-
-
- {93995380-89BD-4b04-88EB-625FBE52EBFB}
- h;hpp;hxx;hm;inl;inc;xsd
-
-
- {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
- rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav
-
-
- {1eab21d6-58f8-49e0-929b-8a4482e04756}
-
-
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- DeckLink API
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
-
-
- Resource Files
-
-
- Resource Files
-
-
-
-
- Resource Files
-
-
-
-
- DeckLink API
-
-
-
-
- Resource Files
-
-
-
diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp
index d24f81e..bcdad27 100644
--- a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp
+++ b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp
@@ -101,9 +101,10 @@ OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) :
mRuntimeHost = std::make_unique();
mRuntimeStore = std::make_unique(*mRuntimeHost);
mRuntimeSnapshotProvider = std::make_unique(*mRuntimeHost);
+ mRuntimeCoordinator = std::make_unique(*mRuntimeStore);
mRenderEngine = std::make_unique(
- *mRuntimeHost,
*mRuntimeSnapshotProvider,
+ mRuntimeHost->GetHealthTelemetry(),
pMutex,
hGLDC,
hGLRC,
@@ -202,7 +203,11 @@ bool OpenGLComposite::InitVideoIO()
}
if (!mVideoBackend->HasInputDevice() && mRuntimeHost)
{
- mRuntimeHost->SetSignalStatus(false, mVideoBackend->InputFrameWidth(), mVideoBackend->InputFrameHeight(), mVideoBackend->InputDisplayModeName());
+ mRuntimeHost->GetHealthTelemetry().ReportSignalStatus(
+ false,
+ mVideoBackend->InputFrameWidth(),
+ mVideoBackend->InputFrameHeight(),
+ mVideoBackend->InputDisplayModeName());
}
if (!mVideoBackend->ConfigureOutput(videoModes.output, mRuntimeStore && mRuntimeStore->IsExternalKeyingConfigured(), initFailureReason))
@@ -227,27 +232,15 @@ void OpenGLComposite::paintGL(bool force)
{
if (IsIconic(hGLWnd))
return;
-
- const unsigned previewFps = mRuntimeStore ? mRuntimeStore->GetConfiguredPreviewFps() : 30u;
- if (previewFps == 0)
- return;
-
- const auto now = std::chrono::steady_clock::now();
- const auto minimumInterval = std::chrono::microseconds(1000000 / (previewFps == 0 ? 1u : previewFps));
- if (mLastPreviewPresentTime != std::chrono::steady_clock::time_point() &&
- now - mLastPreviewPresentTime < minimumInterval)
- {
- return;
- }
}
- if (!mRenderEngine->TryPresentToWindow(mVideoBackend->OutputFrameWidth(), mVideoBackend->OutputFrameHeight()))
+ const unsigned previewFps = mRuntimeStore ? mRuntimeStore->GetConfiguredPreviewFps() : 30u;
+ if (!mRenderEngine->TryPresentPreview(force, previewFps, mVideoBackend->OutputFrameWidth(), mVideoBackend->OutputFrameHeight()))
{
ValidateRect(hGLWnd, NULL);
return;
}
- mLastPreviewPresentTime = std::chrono::steady_clock::now();
ValidateRect(hGLWnd, NULL);
}
@@ -336,7 +329,7 @@ bool OpenGLComposite::InitOpenGLState()
MessageBoxA(NULL, compilerErrorMessage, "OpenGL shader failed to load or compile", MB_OK);
return false;
}
- mCachedLayerRenderStates = mRenderEngine->CommittedLayerStates();
+ mRuntimeStore->SetCompileStatus(true, "Shader layers compiled successfully.");
mUseCommittedLayerStates = false;
mRenderEngine->ResetTemporalHistoryState();
@@ -367,15 +360,8 @@ bool OpenGLComposite::Stop()
bool OpenGLComposite::ReloadShader(bool preserveFeedbackState)
{
- mPreserveFeedbackOnNextShaderBuild = preserveFeedbackState;
- if (mRuntimeHost)
- {
- mRuntimeStore->SetCompileStatus(true, "Shader rebuild queued.");
- mRuntimeStore->ClearReloadRequest();
- }
- RequestShaderBuild();
- broadcastRuntimeState();
- return true;
+ return mRuntimeCoordinator &&
+ ApplyRuntimeCoordinatorResult(mRuntimeCoordinator->RequestShaderReload(preserveFeedbackState));
}
bool OpenGLComposite::RequestScreenshot(std::string& error)
@@ -442,7 +428,7 @@ void OpenGLComposite::renderEffect()
const auto applyOscOverlays = [&](std::vector& states, bool allowCommit)
{
- if (states.empty() || mOscOverlayStates.empty() || !mRuntimeHost)
+ if (states.empty() || mOscOverlayStates.empty())
return;
const double smoothing = ClampOscAlpha(mRuntimeStore ? mRuntimeStore->GetConfiguredOscSmoothing() : 0.0);
@@ -576,66 +562,12 @@ void OpenGLComposite::renderEffect()
const bool hasInputSource = mVideoBackend->HasInputSource();
std::vector layerStates;
- if (mUseCommittedLayerStates)
- {
- layerStates = mRenderEngine->CommittedLayerStates();
- applyOscOverlays(layerStates, false);
- if (mRuntimeSnapshotProvider)
- mRuntimeSnapshotProvider->RefreshDynamicRenderStateFields(layerStates);
- }
- else if (mRuntimeSnapshotProvider)
- {
- const unsigned renderWidth = mVideoBackend->InputFrameWidth();
- const unsigned renderHeight = mVideoBackend->InputFrameHeight();
- 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;
-
- applyOscOverlays(renderSnapshot.states, true);
- if (mCachedParameterStateVersion != versions.parameterStateVersion &&
- mRuntimeSnapshotProvider->TryRefreshSnapshotParameters(renderSnapshot))
- {
- mCachedParameterStateVersion = renderSnapshot.versions.parameterStateVersion;
- applyOscOverlays(renderSnapshot.states, true);
- }
-
- mCachedLayerRenderStates = renderSnapshot.states;
- layerStates = renderSnapshot.states;
- mRuntimeSnapshotProvider->RefreshDynamicRenderStateFields(layerStates);
- }
- else
- {
- RuntimeRenderStateSnapshot renderSnapshot;
- if (mRuntimeSnapshotProvider->TryGetRenderStateSnapshot(renderWidth, renderHeight, renderSnapshot))
- {
- mCachedLayerRenderStates = renderSnapshot.states;
- mCachedRenderStateVersion = renderSnapshot.versions.renderStateVersion;
- mCachedParameterStateVersion = renderSnapshot.versions.parameterStateVersion;
- mCachedRenderStateWidth = renderSnapshot.outputWidth;
- mCachedRenderStateHeight = renderSnapshot.outputHeight;
- applyOscOverlays(mCachedLayerRenderStates, true);
- layerStates = mCachedLayerRenderStates;
- }
- else
- {
- applyOscOverlays(mCachedLayerRenderStates, true);
- layerStates = mCachedLayerRenderStates;
- mRuntimeSnapshotProvider->RefreshDynamicRenderStateFields(layerStates);
- }
- }
- }
+ mRenderEngine->ResolveRenderLayerStates(
+ mUseCommittedLayerStates.load(),
+ mVideoBackend->InputFrameWidth(),
+ mVideoBackend->InputFrameHeight(),
+ applyOscOverlays,
+ layerStates);
const unsigned historyCap = mRuntimeStore ? mRuntimeStore->GetConfiguredMaxTemporalHistoryFrames() : 0;
mRenderEngine->RenderLayerStack(
hasInputSource,
@@ -657,20 +589,9 @@ void OpenGLComposite::ProcessScreenshotRequest()
if (width == 0 || height == 0)
return;
- std::vector bottomUpPixels;
- if (!mRenderEngine->ReadOutputFrameRgba(width, height, bottomUpPixels))
+ std::vector topDownPixels;
+ if (!mRenderEngine->CaptureOutputFrameRgbaTopDown(width, height, topDownPixels))
return;
- std::vector topDownPixels(bottomUpPixels.size());
-
- const std::size_t rowBytes = static_cast(width) * 4;
- for (unsigned y = 0; y < height; ++y)
- {
- const unsigned sourceY = height - 1 - y;
- std::copy(
- bottomUpPixels.begin() + static_cast(sourceY * rowBytes),
- bottomUpPixels.begin() + static_cast((sourceY + 1) * rowBytes),
- topDownPixels.begin() + static_cast(y * rowBytes));
- }
try
{
@@ -707,14 +628,13 @@ std::filesystem::path OpenGLComposite::BuildScreenshotPath() const
bool OpenGLComposite::ProcessRuntimePollResults()
{
- if (!mRuntimeHost || !mRuntimeServices)
+ if (!mRuntimeServices)
return true;
const RuntimePollEvents events = mRuntimeServices->ConsumePollEvents();
if (events.failed)
{
- mRuntimeStore->SetCompileStatus(false, events.error);
- broadcastRuntimeState();
+ ApplyRuntimeCoordinatorResult(mRuntimeCoordinator->HandleRuntimePollFailure(events.error));
return false;
}
@@ -728,29 +648,23 @@ bool OpenGLComposite::ProcessRuntimePollResults()
return true;
char compilerErrorMessage[1024] = {};
- if (!mRenderEngine->CommitPreparedLayerPrograms(readyBuild, mVideoBackend->InputFrameWidth(), mVideoBackend->InputFrameHeight(), sizeof(compilerErrorMessage), compilerErrorMessage))
+ if (!mRenderEngine->ApplyPreparedShaderBuild(
+ readyBuild,
+ mVideoBackend->InputFrameWidth(),
+ mVideoBackend->InputFrameHeight(),
+ mRuntimeCoordinator && mRuntimeCoordinator->PreserveFeedbackOnNextShaderBuild(),
+ sizeof(compilerErrorMessage),
+ compilerErrorMessage))
{
- mRuntimeStore->SetCompileStatus(false, compilerErrorMessage);
- mUseCommittedLayerStates = true;
- mPreserveFeedbackOnNextShaderBuild = false;
- broadcastRuntimeState();
+ ApplyRuntimeCoordinatorResult(mRuntimeCoordinator->HandlePreparedShaderBuildFailure(compilerErrorMessage));
return false;
}
- mUseCommittedLayerStates = false;
- mCachedLayerRenderStates = mRenderEngine->CommittedLayerStates();
- mRenderEngine->ResetTemporalHistoryState();
- if (!mPreserveFeedbackOnNextShaderBuild)
- mRenderEngine->ResetShaderFeedbackState();
- mPreserveFeedbackOnNextShaderBuild = false;
- broadcastRuntimeState();
+ ApplyRuntimeCoordinatorResult(mRuntimeCoordinator->HandlePreparedShaderBuildSuccess());
return true;
}
- mRuntimeStore->SetCompileStatus(true, "Shader rebuild queued.");
- mPreserveFeedbackOnNextShaderBuild = false;
- RequestShaderBuild();
- broadcastRuntimeState();
+ ApplyRuntimeCoordinatorResult(mRuntimeCoordinator->HandleRuntimeReloadRequest());
return true;
}
@@ -759,24 +673,81 @@ void OpenGLComposite::RequestShaderBuild()
if (!mShaderBuildQueue || !mVideoBackend)
return;
- mUseCommittedLayerStates = true;
- if (mRuntimeHost)
- mRuntimeStore->ClearReloadRequest();
mShaderBuildQueue->RequestBuild(mVideoBackend->InputFrameWidth(), mVideoBackend->InputFrameHeight());
}
+bool OpenGLComposite::ApplyRuntimeCoordinatorResult(const RuntimeCoordinatorResult& result, std::string* error)
+{
+ if (!result.accepted)
+ {
+ if (error)
+ *error = result.errorMessage;
+ return false;
+ }
+
+ if (result.compileStatusChanged && mRuntimeStore)
+ mRuntimeStore->SetCompileStatus(result.compileStatusSucceeded, result.compileStatusMessage);
+
+ if (result.clearReloadRequest && mRuntimeStore)
+ mRuntimeStore->ClearReloadRequest();
+
+ switch (result.committedStateMode)
+ {
+ case RuntimeCoordinatorCommittedStateMode::UseCommittedStates:
+ mUseCommittedLayerStates = true;
+ break;
+ case RuntimeCoordinatorCommittedStateMode::UseLiveSnapshots:
+ mUseCommittedLayerStates = false;
+ break;
+ case RuntimeCoordinatorCommittedStateMode::Unchanged:
+ default:
+ break;
+ }
+
+ if (result.clearTransientOscState)
+ {
+ mOscOverlayStates.clear();
+ if (mRuntimeServices)
+ mRuntimeServices->ClearOscState();
+ }
+
+ ApplyRuntimeCoordinatorRenderReset(result.renderResetScope);
+
+ if (result.shaderBuildRequested)
+ RequestShaderBuild();
+
+ if (result.runtimeStateBroadcastRequired)
+ broadcastRuntimeState();
+
+ return true;
+}
+
+void OpenGLComposite::ApplyRuntimeCoordinatorRenderReset(RuntimeCoordinatorRenderResetScope resetScope)
+{
+ if (!mRenderEngine)
+ return;
+
+ switch (resetScope)
+ {
+ case RuntimeCoordinatorRenderResetScope::TemporalHistoryOnly:
+ mRenderEngine->ResetTemporalHistoryState();
+ break;
+ case RuntimeCoordinatorRenderResetScope::TemporalHistoryAndFeedback:
+ mRenderEngine->ResetTemporalHistoryState();
+ mRenderEngine->ResetShaderFeedbackState();
+ break;
+ case RuntimeCoordinatorRenderResetScope::None:
+ default:
+ break;
+ }
+}
+
void OpenGLComposite::broadcastRuntimeState()
{
if (mRuntimeServices)
mRuntimeServices->BroadcastState();
}
-void OpenGLComposite::resetTemporalHistoryState()
-{
- mRenderEngine->ResetTemporalHistoryState();
- mRenderEngine->ResetShaderFeedbackState();
-}
-
bool OpenGLComposite::CheckOpenGLExtensions()
{
return true;
diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.h b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.h
index a50c5a8..a12bdac 100644
--- a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.h
+++ b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.h
@@ -12,6 +12,7 @@
#include
#include "GLExtensions.h"
+#include "RuntimeCoordinator.h"
#include "RuntimeHost.h"
#include "RuntimeSnapshotProvider.h"
#include "RuntimeStore.h"
@@ -91,30 +92,25 @@ private:
std::unique_ptr mRuntimeHost;
std::unique_ptr mRuntimeStore;
+ std::unique_ptr mRuntimeCoordinator;
std::unique_ptr mRuntimeSnapshotProvider;
std::unique_ptr mRenderEngine;
std::unique_ptr mShaderBuildQueue;
std::unique_ptr mRuntimeServices;
std::unique_ptr mVideoBackend;
- std::vector mCachedLayerRenderStates;
- uint64_t mCachedRenderStateVersion = 0;
- uint64_t mCachedParameterStateVersion = 0;
- unsigned mCachedRenderStateWidth = 0;
- unsigned mCachedRenderStateHeight = 0;
std::map mOscOverlayStates;
std::atomic mUseCommittedLayerStates;
std::atomic mScreenshotRequested;
- std::chrono::steady_clock::time_point mLastPreviewPresentTime;
- bool mPreserveFeedbackOnNextShaderBuild = false;
bool InitOpenGLState();
void renderEffect();
bool ProcessRuntimePollResults();
void RequestShaderBuild();
+ bool ApplyRuntimeCoordinatorResult(const RuntimeCoordinatorResult& result, std::string* error = nullptr);
+ void ApplyRuntimeCoordinatorRenderReset(RuntimeCoordinatorRenderResetScope resetScope);
void ProcessScreenshotRequest();
std::filesystem::path BuildScreenshotPath() const;
void broadcastRuntimeState();
- void resetTemporalHistoryState();
};
#endif // __OPENGL_COMPOSITE_H__
diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLCompositeRuntimeControls.cpp b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLCompositeRuntimeControls.cpp
index 970c710..6a1b804 100644
--- a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLCompositeRuntimeControls.cpp
+++ b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLCompositeRuntimeControls.cpp
@@ -38,62 +38,38 @@ std::string OpenGLComposite::GetOscAddress() const
bool OpenGLComposite::AddLayer(const std::string& shaderId, std::string& error)
{
- if (!mRuntimeStore->CreateStoredLayer(shaderId, error))
- return false;
-
- ReloadShader(true);
- broadcastRuntimeState();
- return true;
+ return mRuntimeCoordinator &&
+ ApplyRuntimeCoordinatorResult(mRuntimeCoordinator->AddLayer(shaderId), &error);
}
bool OpenGLComposite::RemoveLayer(const std::string& layerId, std::string& error)
{
- if (!mRuntimeStore->DeleteStoredLayer(layerId, error))
- return false;
-
- ReloadShader(true);
- broadcastRuntimeState();
- return true;
+ return mRuntimeCoordinator &&
+ ApplyRuntimeCoordinatorResult(mRuntimeCoordinator->RemoveLayer(layerId), &error);
}
bool OpenGLComposite::MoveLayer(const std::string& layerId, int direction, std::string& error)
{
- if (!mRuntimeStore->MoveStoredLayer(layerId, direction, error))
- return false;
-
- ReloadShader(true);
- broadcastRuntimeState();
- return true;
+ return mRuntimeCoordinator &&
+ ApplyRuntimeCoordinatorResult(mRuntimeCoordinator->MoveLayer(layerId, direction), &error);
}
bool OpenGLComposite::MoveLayerToIndex(const std::string& layerId, std::size_t targetIndex, std::string& error)
{
- if (!mRuntimeStore->MoveStoredLayerToIndex(layerId, targetIndex, error))
- return false;
-
- ReloadShader(true);
- broadcastRuntimeState();
- return true;
+ return mRuntimeCoordinator &&
+ ApplyRuntimeCoordinatorResult(mRuntimeCoordinator->MoveLayerToIndex(layerId, targetIndex), &error);
}
bool OpenGLComposite::SetLayerBypass(const std::string& layerId, bool bypassed, std::string& error)
{
- if (!mRuntimeStore->SetStoredLayerBypassState(layerId, bypassed, error))
- return false;
-
- ReloadShader();
- broadcastRuntimeState();
- return true;
+ return mRuntimeCoordinator &&
+ ApplyRuntimeCoordinatorResult(mRuntimeCoordinator->SetLayerBypass(layerId, bypassed), &error);
}
bool OpenGLComposite::SetLayerShader(const std::string& layerId, const std::string& shaderId, std::string& error)
{
- if (!mRuntimeStore->SetStoredLayerShaderSelection(layerId, shaderId, error))
- return false;
-
- ReloadShader();
- broadcastRuntimeState();
- return true;
+ return mRuntimeCoordinator &&
+ ApplyRuntimeCoordinatorResult(mRuntimeCoordinator->SetLayerShader(layerId, shaderId), &error);
}
bool OpenGLComposite::UpdateLayerParameterJson(const std::string& layerId, const std::string& parameterId, const std::string& valueJson, std::string& error)
@@ -102,11 +78,8 @@ bool OpenGLComposite::UpdateLayerParameterJson(const std::string& layerId, const
if (!ParseJson(valueJson, parsedValue, error))
return false;
- if (!mRuntimeStore->SetStoredParameterValue(layerId, parameterId, parsedValue, error))
- return false;
-
- broadcastRuntimeState();
- return true;
+ return mRuntimeCoordinator &&
+ ApplyRuntimeCoordinatorResult(mRuntimeCoordinator->UpdateLayerParameter(layerId, parameterId, parsedValue), &error);
}
bool OpenGLComposite::UpdateLayerParameterByControlKeyJson(const std::string& layerKey, const std::string& parameterKey, const std::string& valueJson, std::string& error)
@@ -115,42 +88,24 @@ bool OpenGLComposite::UpdateLayerParameterByControlKeyJson(const std::string& la
if (!ParseJson(valueJson, parsedValue, error))
return false;
- if (!mRuntimeStore->SetStoredParameterValueByControlKey(layerKey, parameterKey, parsedValue, error))
- return false;
-
- broadcastRuntimeState();
- return true;
+ return mRuntimeCoordinator &&
+ ApplyRuntimeCoordinatorResult(mRuntimeCoordinator->UpdateLayerParameterByControlKey(layerKey, parameterKey, parsedValue), &error);
}
bool OpenGLComposite::ResetLayerParameters(const std::string& layerId, std::string& error)
{
- if (!mRuntimeStore->ResetStoredLayerParameterValues(layerId, error))
- return false;
-
- mOscOverlayStates.clear();
- if (mRuntimeServices)
- mRuntimeServices->ClearOscState();
- resetTemporalHistoryState();
-
- broadcastRuntimeState();
- return true;
+ return mRuntimeCoordinator &&
+ ApplyRuntimeCoordinatorResult(mRuntimeCoordinator->ResetLayerParameters(layerId), &error);
}
bool OpenGLComposite::SaveStackPreset(const std::string& presetName, std::string& error)
{
- if (!mRuntimeStore->SaveStackPresetSnapshot(presetName, error))
- return false;
-
- broadcastRuntimeState();
- return true;
+ return mRuntimeCoordinator &&
+ ApplyRuntimeCoordinatorResult(mRuntimeCoordinator->SaveStackPreset(presetName), &error);
}
bool OpenGLComposite::LoadStackPreset(const std::string& presetName, std::string& error)
{
- if (!mRuntimeStore->LoadStackPresetSnapshot(presetName, error))
- return false;
-
- ReloadShader();
- broadcastRuntimeState();
- return true;
+ return mRuntimeCoordinator &&
+ ApplyRuntimeCoordinatorResult(mRuntimeCoordinator->LoadStackPreset(presetName), &error);
}
diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/RenderEngine.cpp b/apps/LoopThroughWithOpenGLCompositing/gl/RenderEngine.cpp
index 0e836d4..948a56a 100644
--- a/apps/LoopThroughWithOpenGLCompositing/gl/RenderEngine.cpp
+++ b/apps/LoopThroughWithOpenGLCompositing/gl/RenderEngine.cpp
@@ -2,9 +2,12 @@
#include
+#include
+#include
+
RenderEngine::RenderEngine(
- RuntimeHost& runtimeHost,
RuntimeSnapshotProvider& runtimeSnapshotProvider,
+ HealthTelemetry& healthTelemetry,
CRITICAL_SECTION& mutex,
HDC hdc,
HGLRC hglrc,
@@ -13,8 +16,9 @@ RenderEngine::RenderEngine(
PreviewPaintCallback previewPaint) :
mRenderer(),
mRenderPass(mRenderer),
- mRenderPipeline(mRenderer, runtimeHost, std::move(renderEffect), std::move(screenshotReady), std::move(previewPaint)),
- mShaderPrograms(mRenderer, runtimeHost, runtimeSnapshotProvider),
+ mRenderPipeline(mRenderer, runtimeSnapshotProvider, healthTelemetry, std::move(renderEffect), std::move(screenshotReady), std::move(previewPaint)),
+ mShaderPrograms(mRenderer, runtimeSnapshotProvider),
+ mRuntimeSnapshotProvider(runtimeSnapshotProvider),
mMutex(mutex),
mHdc(hdc),
mHglrc(hglrc)
@@ -65,6 +69,28 @@ bool RenderEngine::CommitPreparedLayerPrograms(const PreparedShaderBuild& prepar
return mShaderPrograms.CommitPreparedLayerPrograms(preparedBuild, inputFrameWidth, inputFrameHeight, errorMessageSize, errorMessage);
}
+bool RenderEngine::ApplyPreparedShaderBuild(
+ const PreparedShaderBuild& preparedBuild,
+ unsigned inputFrameWidth,
+ unsigned inputFrameHeight,
+ bool preserveFeedbackState,
+ int errorMessageSize,
+ char* errorMessage)
+{
+ if (!CommitPreparedLayerPrograms(preparedBuild, inputFrameWidth, inputFrameHeight, errorMessageSize, errorMessage))
+ return false;
+
+ mCachedLayerRenderStates = mShaderPrograms.CommittedLayerStates();
+ mCachedRenderStateVersion = preparedBuild.renderSnapshot.versions.renderStateVersion;
+ mCachedParameterStateVersion = preparedBuild.renderSnapshot.versions.parameterStateVersion;
+ mCachedRenderStateWidth = preparedBuild.renderSnapshot.outputWidth;
+ mCachedRenderStateHeight = preparedBuild.renderSnapshot.outputHeight;
+ ResetTemporalHistoryState();
+ if (!preserveFeedbackState)
+ ResetShaderFeedbackState();
+ return true;
+}
+
const std::vector& RenderEngine::CommittedLayerStates() const
{
return mShaderPrograms.CommittedLayerStates();
@@ -85,12 +111,27 @@ void RenderEngine::ResizeView(int width, int height)
mRenderer.ResizeView(width, height);
}
-bool RenderEngine::TryPresentToWindow(unsigned outputFrameWidth, unsigned outputFrameHeight)
+bool RenderEngine::TryPresentPreview(bool force, unsigned previewFps, unsigned outputFrameWidth, unsigned outputFrameHeight)
{
+ if (!force)
+ {
+ if (previewFps == 0)
+ return false;
+
+ const auto now = std::chrono::steady_clock::now();
+ const auto minimumInterval = std::chrono::microseconds(1000000 / (previewFps == 0 ? 1u : previewFps));
+ if (mLastPreviewPresentTime != std::chrono::steady_clock::time_point() &&
+ now - mLastPreviewPresentTime < minimumInterval)
+ {
+ return false;
+ }
+ }
+
if (!TryEnterCriticalSection(&mMutex))
return false;
mRenderer.PresentToWindow(mHdc, outputFrameWidth, outputFrameHeight);
+ mLastPreviewPresentTime = std::chrono::steady_clock::now();
LeaveCriticalSection(&mMutex);
return true;
}
@@ -133,6 +174,76 @@ bool RenderEngine::RenderOutputFrame(const RenderPipelineFrameContext& context,
return rendered;
}
+bool RenderEngine::ResolveRenderLayerStates(
+ bool useCommittedLayerStates,
+ unsigned renderWidth,
+ unsigned renderHeight,
+ OverlayApplier overlayApplier,
+ std::vector& layerStates)
+{
+ layerStates.clear();
+ if (useCommittedLayerStates)
+ {
+ layerStates = mShaderPrograms.CommittedLayerStates();
+ if (overlayApplier)
+ overlayApplier(layerStates, false);
+ mRuntimeSnapshotProvider.RefreshDynamicRenderStateFields(layerStates);
+ return true;
+ }
+
+ 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;
+
+ if (overlayApplier)
+ overlayApplier(renderSnapshot.states, true);
+ if (mCachedParameterStateVersion != versions.parameterStateVersion &&
+ mRuntimeSnapshotProvider.TryRefreshSnapshotParameters(renderSnapshot))
+ {
+ mCachedParameterStateVersion = renderSnapshot.versions.parameterStateVersion;
+ if (overlayApplier)
+ overlayApplier(renderSnapshot.states, true);
+ }
+
+ mCachedLayerRenderStates = renderSnapshot.states;
+ layerStates = renderSnapshot.states;
+ mRuntimeSnapshotProvider.RefreshDynamicRenderStateFields(layerStates);
+ return true;
+ }
+
+ RuntimeRenderStateSnapshot renderSnapshot;
+ if (mRuntimeSnapshotProvider.TryGetRenderStateSnapshot(renderWidth, renderHeight, renderSnapshot))
+ {
+ mCachedLayerRenderStates = renderSnapshot.states;
+ mCachedRenderStateVersion = renderSnapshot.versions.renderStateVersion;
+ mCachedParameterStateVersion = renderSnapshot.versions.parameterStateVersion;
+ mCachedRenderStateWidth = renderSnapshot.outputWidth;
+ mCachedRenderStateHeight = renderSnapshot.outputHeight;
+ if (overlayApplier)
+ overlayApplier(mCachedLayerRenderStates, true);
+ layerStates = mCachedLayerRenderStates;
+ return true;
+ }
+
+ if (overlayApplier)
+ overlayApplier(mCachedLayerRenderStates, true);
+ layerStates = mCachedLayerRenderStates;
+ mRuntimeSnapshotProvider.RefreshDynamicRenderStateFields(layerStates);
+ return !layerStates.empty();
+}
+
void RenderEngine::RenderLayerStack(
bool hasInputSource,
const std::vector& layerStates,
@@ -178,3 +289,23 @@ bool RenderEngine::ReadOutputFrameRgba(unsigned width, unsigned height, std::vec
LeaveCriticalSection(&mMutex);
return true;
}
+
+bool RenderEngine::CaptureOutputFrameRgbaTopDown(unsigned width, unsigned height, std::vector& topDownPixels)
+{
+ std::vector bottomUpPixels;
+ if (!ReadOutputFrameRgba(width, height, bottomUpPixels))
+ return false;
+
+ topDownPixels.resize(bottomUpPixels.size());
+ const std::size_t rowBytes = static_cast(width) * 4;
+ for (unsigned y = 0; y < height; ++y)
+ {
+ const unsigned sourceY = height - 1 - y;
+ std::copy(
+ bottomUpPixels.begin() + static_cast(sourceY * rowBytes),
+ bottomUpPixels.begin() + static_cast((sourceY + 1) * rowBytes),
+ topDownPixels.begin() + static_cast(y * rowBytes));
+ }
+
+ return true;
+}
diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/RenderEngine.h b/apps/LoopThroughWithOpenGLCompositing/gl/RenderEngine.h
index f9007f3..f6b0c4b 100644
--- a/apps/LoopThroughWithOpenGLCompositing/gl/RenderEngine.h
+++ b/apps/LoopThroughWithOpenGLCompositing/gl/RenderEngine.h
@@ -4,11 +4,13 @@
#include "OpenGLRenderPipeline.h"
#include "OpenGLRenderer.h"
#include "OpenGLShaderPrograms.h"
-#include "RuntimeHost.h"
+#include "HealthTelemetry.h"
#include "RuntimeSnapshotProvider.h"
#include
+#include
+#include
#include
#include
#include
@@ -19,10 +21,11 @@ public:
using RenderEffectCallback = std::function;
using ScreenshotCallback = std::function;
using PreviewPaintCallback = std::function;
+ using OverlayApplier = std::function& states, bool allowCommit)>;
RenderEngine(
- RuntimeHost& runtimeHost,
RuntimeSnapshotProvider& runtimeSnapshotProvider,
+ HealthTelemetry& healthTelemetry,
CRITICAL_SECTION& mutex,
HDC hdc,
HGLRC hglrc,
@@ -43,14 +46,27 @@ public:
std::string& error);
bool CompileLayerPrograms(unsigned inputFrameWidth, unsigned inputFrameHeight, int errorMessageSize, char* errorMessage);
bool CommitPreparedLayerPrograms(const PreparedShaderBuild& preparedBuild, unsigned inputFrameWidth, unsigned inputFrameHeight, int errorMessageSize, char* errorMessage);
+ bool ApplyPreparedShaderBuild(
+ const PreparedShaderBuild& preparedBuild,
+ unsigned inputFrameWidth,
+ unsigned inputFrameHeight,
+ bool preserveFeedbackState,
+ int errorMessageSize,
+ char* errorMessage);
const std::vector& CommittedLayerStates() const;
void ResetTemporalHistoryState();
void ResetShaderFeedbackState();
void ResizeView(int width, int height);
- bool TryPresentToWindow(unsigned outputFrameWidth, unsigned outputFrameHeight);
+ bool TryPresentPreview(bool force, unsigned previewFps, unsigned outputFrameWidth, unsigned outputFrameHeight);
bool TryUploadInputFrame(const VideoIOFrame& inputFrame, const VideoIOState& videoState);
bool RenderOutputFrame(const RenderPipelineFrameContext& context, VideoIOOutputFrame& outputFrame);
+ bool ResolveRenderLayerStates(
+ bool useCommittedLayerStates,
+ unsigned renderWidth,
+ unsigned renderHeight,
+ OverlayApplier overlayApplier,
+ std::vector& layerStates);
void RenderLayerStack(
bool hasInputSource,
const std::vector& layerStates,
@@ -60,13 +76,21 @@ public:
VideoIOPixelFormat inputPixelFormat,
unsigned historyCap);
bool ReadOutputFrameRgba(unsigned width, unsigned height, std::vector& bottomUpPixels);
+ bool CaptureOutputFrameRgbaTopDown(unsigned width, unsigned height, std::vector& topDownPixels);
private:
OpenGLRenderer mRenderer;
OpenGLRenderPass mRenderPass;
OpenGLRenderPipeline mRenderPipeline;
OpenGLShaderPrograms mShaderPrograms;
+ RuntimeSnapshotProvider& mRuntimeSnapshotProvider;
CRITICAL_SECTION& mMutex;
HDC mHdc;
HGLRC mHglrc;
+ std::vector mCachedLayerRenderStates;
+ uint64_t mCachedRenderStateVersion = 0;
+ uint64_t mCachedParameterStateVersion = 0;
+ unsigned mCachedRenderStateWidth = 0;
+ unsigned mCachedRenderStateHeight = 0;
+ std::chrono::steady_clock::time_point mLastPreviewPresentTime;
};
diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLRenderPipeline.cpp b/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLRenderPipeline.cpp
index 7328f9d..3eb5061 100644
--- a/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLRenderPipeline.cpp
+++ b/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLRenderPipeline.cpp
@@ -1,7 +1,8 @@
#include "OpenGLRenderPipeline.h"
+#include "HealthTelemetry.h"
#include "OpenGLRenderer.h"
-#include "RuntimeHost.h"
+#include "RuntimeSnapshotProvider.h"
#include "VideoIOFormat.h"
#include
@@ -11,12 +12,14 @@
OpenGLRenderPipeline::OpenGLRenderPipeline(
OpenGLRenderer& renderer,
- RuntimeHost& runtimeHost,
+ RuntimeSnapshotProvider& runtimeSnapshotProvider,
+ HealthTelemetry& healthTelemetry,
RenderEffectCallback renderEffect,
OutputReadyCallback outputReady,
PaintCallback paint) :
mRenderer(renderer),
- mRuntimeHost(runtimeHost),
+ mRuntimeSnapshotProvider(runtimeSnapshotProvider),
+ mHealthTelemetry(healthTelemetry),
mRenderEffect(renderEffect),
mOutputReady(outputReady),
mPaint(paint)
@@ -47,8 +50,8 @@ bool OpenGLRenderPipeline::RenderFrame(const RenderPipelineFrameContext& context
const auto renderEndTime = std::chrono::steady_clock::now();
const double renderMilliseconds = std::chrono::duration_cast>(renderEndTime - renderStartTime).count();
- mRuntimeHost.GetHealthTelemetry().TryRecordPerformanceStats(state.frameBudgetMilliseconds, renderMilliseconds);
- mRuntimeHost.TryAdvanceFrame();
+ mHealthTelemetry.TryRecordPerformanceStats(state.frameBudgetMilliseconds, renderMilliseconds);
+ mRuntimeSnapshotProvider.TryAdvanceFrame();
ReadOutputFrame(state, outputFrame);
if (mPaint)
diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLRenderPipeline.h b/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLRenderPipeline.h
index 070c541..4fc6cce 100644
--- a/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLRenderPipeline.h
+++ b/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLRenderPipeline.h
@@ -8,7 +8,8 @@
#include
class OpenGLRenderer;
-class RuntimeHost;
+class HealthTelemetry;
+class RuntimeSnapshotProvider;
struct RenderPipelineFrameContext
{
@@ -25,7 +26,8 @@ public:
OpenGLRenderPipeline(
OpenGLRenderer& renderer,
- RuntimeHost& runtimeHost,
+ RuntimeSnapshotProvider& runtimeSnapshotProvider,
+ HealthTelemetry& healthTelemetry,
RenderEffectCallback renderEffect,
OutputReadyCallback outputReady,
PaintCallback paint);
@@ -53,7 +55,8 @@ private:
void ReadOutputFrame(const VideoIOState& state, VideoIOOutputFrame& outputFrame);
OpenGLRenderer& mRenderer;
- RuntimeHost& mRuntimeHost;
+ RuntimeSnapshotProvider& mRuntimeSnapshotProvider;
+ HealthTelemetry& mHealthTelemetry;
RenderEffectCallback mRenderEffect;
OutputReadyCallback mOutputReady;
PaintCallback mPaint;
diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLVideoIOBridge.cpp b/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLVideoIOBridge.cpp
index c819d08..ccba9a2 100644
--- a/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLVideoIOBridge.cpp
+++ b/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLVideoIOBridge.cpp
@@ -1,81 +1,25 @@
#include "OpenGLVideoIOBridge.h"
-#include "HealthTelemetry.h"
#include "RenderEngine.h"
-#include
-
-OpenGLVideoIOBridge::OpenGLVideoIOBridge(
- VideoIODevice& videoIO,
- RenderEngine& renderEngine,
- HealthTelemetry& healthTelemetry) :
- mVideoIO(videoIO),
- mRenderEngine(renderEngine),
- mHealthTelemetry(healthTelemetry)
+OpenGLVideoIOBridge::OpenGLVideoIOBridge(RenderEngine& renderEngine) :
+ mRenderEngine(renderEngine)
{
}
-void OpenGLVideoIOBridge::RecordFramePacing(VideoIOCompletionResult completionResult)
+void OpenGLVideoIOBridge::UploadInputFrame(const VideoIOFrame& inputFrame, const VideoIOState& state)
{
- const auto now = std::chrono::steady_clock::now();
- if (mLastPlayoutCompletionTime != std::chrono::steady_clock::time_point())
- {
- mCompletionIntervalMilliseconds = std::chrono::duration_cast>(now - mLastPlayoutCompletionTime).count();
- if (mSmoothedCompletionIntervalMilliseconds <= 0.0)
- mSmoothedCompletionIntervalMilliseconds = mCompletionIntervalMilliseconds;
- else
- mSmoothedCompletionIntervalMilliseconds = mSmoothedCompletionIntervalMilliseconds * 0.9 + mCompletionIntervalMilliseconds * 0.1;
- if (mCompletionIntervalMilliseconds > mMaxCompletionIntervalMilliseconds)
- mMaxCompletionIntervalMilliseconds = mCompletionIntervalMilliseconds;
- }
- mLastPlayoutCompletionTime = now;
-
- if (completionResult == VideoIOCompletionResult::DisplayedLate)
- ++mLateFrameCount;
- else if (completionResult == VideoIOCompletionResult::Dropped)
- ++mDroppedFrameCount;
- else if (completionResult == VideoIOCompletionResult::Flushed)
- ++mFlushedFrameCount;
-
- mHealthTelemetry.TryRecordFramePacingStats(
- mCompletionIntervalMilliseconds,
- mSmoothedCompletionIntervalMilliseconds,
- mMaxCompletionIntervalMilliseconds,
- mLateFrameCount,
- mDroppedFrameCount,
- mFlushedFrameCount);
-}
-
-void OpenGLVideoIOBridge::VideoFrameArrived(const VideoIOFrame& inputFrame)
-{
- const VideoIOState& state = mVideoIO.State();
- mHealthTelemetry.TryReportSignalStatus(!inputFrame.hasNoInputSource, state.inputFrameSize.width, state.inputFrameSize.height, state.inputDisplayModeName);
-
if (inputFrame.hasNoInputSource || inputFrame.bytes == nullptr)
return; // don't transfer texture when there's no input
mRenderEngine.TryUploadInputFrame(inputFrame, state);
}
-void OpenGLVideoIOBridge::PlayoutFrameCompleted(const VideoIOCompletion& completion)
+void OpenGLVideoIOBridge::RenderScheduledFrame(const VideoIOState& state, const VideoIOCompletion& completion, VideoIOOutputFrame& outputFrame)
{
- RecordFramePacing(completion.result);
-
- VideoIOOutputFrame outputFrame;
- if (!mVideoIO.BeginOutputFrame(outputFrame))
- return;
- const VideoIOState& state = mVideoIO.State();
RenderPipelineFrameContext frameContext;
frameContext.videoState = state;
frameContext.completion = completion;
mRenderEngine.RenderOutputFrame(frameContext, outputFrame);
-
- mVideoIO.EndOutputFrame(outputFrame);
-
- mVideoIO.AccountForCompletionResult(completion.result);
-
- // Schedule the next frame for playout after the GL bridge is released so
- // input uploads are not blocked by non-GL output bookkeeping.
- mVideoIO.ScheduleOutputFrame(outputFrame);
}
diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLVideoIOBridge.h b/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLVideoIOBridge.h
index 9f1db55..94525c9 100644
--- a/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLVideoIOBridge.h
+++ b/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLVideoIOBridge.h
@@ -2,34 +2,16 @@
#include "OpenGLRenderPipeline.h"
-#include
-#include
-
-class HealthTelemetry;
class RenderEngine;
class OpenGLVideoIOBridge
{
public:
- OpenGLVideoIOBridge(
- VideoIODevice& videoIO,
- RenderEngine& renderEngine,
- HealthTelemetry& healthTelemetry);
+ explicit OpenGLVideoIOBridge(RenderEngine& renderEngine);
- void VideoFrameArrived(const VideoIOFrame& inputFrame);
- void PlayoutFrameCompleted(const VideoIOCompletion& completion);
+ void UploadInputFrame(const VideoIOFrame& inputFrame, const VideoIOState& state);
+ void RenderScheduledFrame(const VideoIOState& state, const VideoIOCompletion& completion, VideoIOOutputFrame& outputFrame);
private:
- void RecordFramePacing(VideoIOCompletionResult completionResult);
-
- VideoIODevice& mVideoIO;
RenderEngine& mRenderEngine;
- HealthTelemetry& mHealthTelemetry;
- std::chrono::steady_clock::time_point mLastPlayoutCompletionTime;
- double mCompletionIntervalMilliseconds = 0.0;
- double mSmoothedCompletionIntervalMilliseconds = 0.0;
- double mMaxCompletionIntervalMilliseconds = 0.0;
- uint64_t mLateFrameCount = 0;
- uint64_t mDroppedFrameCount = 0;
- uint64_t mFlushedFrameCount = 0;
};
diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/shader/OpenGLShaderPrograms.cpp b/apps/LoopThroughWithOpenGLCompositing/gl/shader/OpenGLShaderPrograms.cpp
index 894ecfe..31525e4 100644
--- a/apps/LoopThroughWithOpenGLCompositing/gl/shader/OpenGLShaderPrograms.cpp
+++ b/apps/LoopThroughWithOpenGLCompositing/gl/shader/OpenGLShaderPrograms.cpp
@@ -29,12 +29,11 @@ std::size_t RequiredTemporaryRenderTargets(const std::vector& layerStates = renderSnapshot.states;
std::string temporalError;
- const unsigned historyCap = mRuntimeHost.GetMaxTemporalHistoryFrames();
+ const unsigned historyCap = mRuntimeSnapshotProvider.GetMaxTemporalHistoryFrames();
if (!mRenderer.TemporalHistory().ValidateTextureUnitBudget(layerStates, historyCap, temporalError))
{
CopyErrorMessage(temporalError, errorMessageSize, errorMessage);
@@ -92,9 +91,6 @@ bool OpenGLShaderPrograms::CompileLayerPrograms(unsigned inputFrameWidth, unsign
mRenderer.ReplaceLayerPrograms(newPrograms);
mCommittedLayerStates = renderSnapshot.states;
- mRuntimeHost.SetCompileStatus(true, "Shader layers compiled successfully.");
- mRuntimeHost.ClearReloadRequest();
-
return true;
}
@@ -107,7 +103,7 @@ bool OpenGLShaderPrograms::CommitPreparedLayerPrograms(const PreparedShaderBuild
}
std::string temporalError;
- const unsigned historyCap = mRuntimeHost.GetMaxTemporalHistoryFrames();
+ const unsigned historyCap = mRuntimeSnapshotProvider.GetMaxTemporalHistoryFrames();
if (!mRenderer.TemporalHistory().ValidateTextureUnitBudget(preparedBuild.renderSnapshot.states, historyCap, temporalError))
{
CopyErrorMessage(temporalError, errorMessageSize, errorMessage);
@@ -155,9 +151,6 @@ bool OpenGLShaderPrograms::CommitPreparedLayerPrograms(const PreparedShaderBuild
mRenderer.ReplaceLayerPrograms(newPrograms);
mCommittedLayerStates = preparedBuild.renderSnapshot.states;
- mRuntimeHost.SetCompileStatus(true, "Shader layers compiled successfully.");
- mRuntimeHost.ClearReloadRequest();
-
return true;
}
diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/shader/OpenGLShaderPrograms.h b/apps/LoopThroughWithOpenGLCompositing/gl/shader/OpenGLShaderPrograms.h
index 20b1f16..b1458da 100644
--- a/apps/LoopThroughWithOpenGLCompositing/gl/shader/OpenGLShaderPrograms.h
+++ b/apps/LoopThroughWithOpenGLCompositing/gl/shader/OpenGLShaderPrograms.h
@@ -2,7 +2,6 @@
#include "GlobalParamsBuffer.h"
#include "OpenGLRenderer.h"
-#include "RuntimeHost.h"
#include "RuntimeSnapshotProvider.h"
#include "ShaderBuildQueue.h"
#include "ShaderTypes.h"
@@ -16,7 +15,7 @@ class OpenGLShaderPrograms
public:
using LayerProgram = OpenGLRenderer::LayerProgram;
- OpenGLShaderPrograms(OpenGLRenderer& renderer, RuntimeHost& runtimeHost, RuntimeSnapshotProvider& runtimeSnapshotProvider);
+ OpenGLShaderPrograms(OpenGLRenderer& renderer, RuntimeSnapshotProvider& runtimeSnapshotProvider);
bool CompileLayerPrograms(unsigned inputFrameWidth, unsigned inputFrameHeight, int errorMessageSize, char* errorMessage);
bool CommitPreparedLayerPrograms(const PreparedShaderBuild& preparedBuild, unsigned inputFrameWidth, unsigned inputFrameHeight, int errorMessageSize, char* errorMessage);
@@ -33,7 +32,6 @@ public:
private:
OpenGLRenderer& mRenderer;
- RuntimeHost& mRuntimeHost;
RuntimeSnapshotProvider& mRuntimeSnapshotProvider;
ShaderTextureBindings mTextureBindings;
GlobalParamsBuffer mGlobalParamsBuffer;
diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/shader/ShaderProgramCompiler.cpp b/apps/LoopThroughWithOpenGLCompositing/gl/shader/ShaderProgramCompiler.cpp
index 9d80e97..0be6d62 100644
--- a/apps/LoopThroughWithOpenGLCompositing/gl/shader/ShaderProgramCompiler.cpp
+++ b/apps/LoopThroughWithOpenGLCompositing/gl/shader/ShaderProgramCompiler.cpp
@@ -19,9 +19,9 @@ void CopyErrorMessage(const std::string& message, int errorMessageSize, char* er
}
}
-ShaderProgramCompiler::ShaderProgramCompiler(OpenGLRenderer& renderer, RuntimeHost& runtimeHost, ShaderTextureBindings& textureBindings) :
+ShaderProgramCompiler::ShaderProgramCompiler(OpenGLRenderer& renderer, RuntimeSnapshotProvider& runtimeSnapshotProvider, ShaderTextureBindings& textureBindings) :
mRenderer(renderer),
- mRuntimeHost(runtimeHost),
+ mRuntimeSnapshotProvider(runtimeSnapshotProvider),
mTextureBindings(textureBindings)
{
}
@@ -31,7 +31,7 @@ bool ShaderProgramCompiler::CompileLayerProgram(const RuntimeRenderState& state,
std::vector passSources;
std::string loadError;
- if (!mRuntimeHost.BuildLayerPassFragmentShaderSources(state.layerId, passSources, loadError))
+ if (!mRuntimeSnapshotProvider.BuildLayerPassFragmentShaderSources(state.layerId, passSources, loadError))
{
CopyErrorMessage(loadError, errorMessageSize, errorMessage);
return false;
@@ -117,7 +117,7 @@ bool ShaderProgramCompiler::CompilePreparedLayerProgram(const RuntimeRenderState
passProgram.passId = passSource.passId;
passProgram.inputNames = passSource.inputNames;
passProgram.outputName = passSource.outputName;
- passProgram.shaderTextureBase = mTextureBindings.ResolveShaderTextureBase(state, mRuntimeHost.GetMaxTemporalHistoryFrames());
+ passProgram.shaderTextureBase = mTextureBindings.ResolveShaderTextureBase(state, mRuntimeSnapshotProvider.GetMaxTemporalHistoryFrames());
passProgram.textureBindings.swap(textureBindings);
passProgram.textBindings.swap(textBindings);
@@ -125,7 +125,7 @@ bool ShaderProgramCompiler::CompilePreparedLayerProgram(const RuntimeRenderState
if (globalParamsIndex != GL_INVALID_INDEX)
glUniformBlockBinding(newProgram.get(), globalParamsIndex, kGlobalParamsBindingPoint);
- const unsigned historyCap = mRuntimeHost.GetMaxTemporalHistoryFrames();
+ const unsigned historyCap = mRuntimeSnapshotProvider.GetMaxTemporalHistoryFrames();
glUseProgram(newProgram.get());
mTextureBindings.AssignLayerSamplerUniforms(newProgram.get(), state, passProgram, historyCap);
glUseProgram(0);
diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/shader/ShaderProgramCompiler.h b/apps/LoopThroughWithOpenGLCompositing/gl/shader/ShaderProgramCompiler.h
index db2e38f..82e05af 100644
--- a/apps/LoopThroughWithOpenGLCompositing/gl/shader/ShaderProgramCompiler.h
+++ b/apps/LoopThroughWithOpenGLCompositing/gl/shader/ShaderProgramCompiler.h
@@ -1,7 +1,7 @@
#pragma once
#include "OpenGLRenderer.h"
-#include "RuntimeHost.h"
+#include "RuntimeSnapshotProvider.h"
#include "ShaderTextureBindings.h"
#include
@@ -13,7 +13,7 @@ public:
using LayerProgram = OpenGLRenderer::LayerProgram;
using PassProgram = OpenGLRenderer::LayerProgram::PassProgram;
- ShaderProgramCompiler(OpenGLRenderer& renderer, RuntimeHost& runtimeHost, ShaderTextureBindings& textureBindings);
+ ShaderProgramCompiler(OpenGLRenderer& renderer, RuntimeSnapshotProvider& runtimeSnapshotProvider, ShaderTextureBindings& textureBindings);
bool CompileLayerProgram(const RuntimeRenderState& state, LayerProgram& layerProgram, int errorMessageSize, char* errorMessage);
bool CompilePreparedLayerProgram(const RuntimeRenderState& state, const std::vector& passSources, LayerProgram& layerProgram, int errorMessageSize, char* errorMessage);
@@ -22,6 +22,6 @@ public:
private:
OpenGLRenderer& mRenderer;
- RuntimeHost& mRuntimeHost;
+ RuntimeSnapshotProvider& mRuntimeSnapshotProvider;
ShaderTextureBindings& mTextureBindings;
};
diff --git a/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeCoordinator.cpp b/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeCoordinator.cpp
new file mode 100644
index 0000000..52f273f
--- /dev/null
+++ b/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeCoordinator.cpp
@@ -0,0 +1,173 @@
+#include "RuntimeCoordinator.h"
+
+#include "RuntimeStore.h"
+
+RuntimeCoordinator::RuntimeCoordinator(RuntimeStore& runtimeStore) :
+ mRuntimeStore(runtimeStore)
+{
+}
+
+RuntimeCoordinatorResult RuntimeCoordinator::AddLayer(const std::string& shaderId)
+{
+ std::string error;
+ return ApplyStoreMutation(mRuntimeStore.CreateStoredLayer(shaderId, error), error, true, true);
+}
+
+RuntimeCoordinatorResult RuntimeCoordinator::RemoveLayer(const std::string& layerId)
+{
+ std::string error;
+ return ApplyStoreMutation(mRuntimeStore.DeleteStoredLayer(layerId, error), error, true, true);
+}
+
+RuntimeCoordinatorResult RuntimeCoordinator::MoveLayer(const std::string& layerId, int direction)
+{
+ std::string error;
+ return ApplyStoreMutation(mRuntimeStore.MoveStoredLayer(layerId, direction, error), error, true, true);
+}
+
+RuntimeCoordinatorResult RuntimeCoordinator::MoveLayerToIndex(const std::string& layerId, std::size_t targetIndex)
+{
+ std::string error;
+ return ApplyStoreMutation(mRuntimeStore.MoveStoredLayerToIndex(layerId, targetIndex, error), error, true, true);
+}
+
+RuntimeCoordinatorResult RuntimeCoordinator::SetLayerBypass(const std::string& layerId, bool bypassed)
+{
+ std::string error;
+ return ApplyStoreMutation(mRuntimeStore.SetStoredLayerBypassState(layerId, bypassed, error), error, true, false);
+}
+
+RuntimeCoordinatorResult RuntimeCoordinator::SetLayerShader(const std::string& layerId, const std::string& shaderId)
+{
+ std::string error;
+ return ApplyStoreMutation(mRuntimeStore.SetStoredLayerShaderSelection(layerId, shaderId, error), error, true, false);
+}
+
+RuntimeCoordinatorResult RuntimeCoordinator::UpdateLayerParameter(const std::string& layerId, const std::string& parameterId, const JsonValue& newValue)
+{
+ std::string error;
+ return ApplyStoreMutation(mRuntimeStore.SetStoredParameterValue(layerId, parameterId, newValue, error), error, false, false);
+}
+
+RuntimeCoordinatorResult RuntimeCoordinator::UpdateLayerParameterByControlKey(const std::string& layerKey, const std::string& parameterKey, const JsonValue& newValue)
+{
+ std::string error;
+ return ApplyStoreMutation(mRuntimeStore.SetStoredParameterValueByControlKey(layerKey, parameterKey, newValue, error), error, false, false);
+}
+
+RuntimeCoordinatorResult RuntimeCoordinator::ResetLayerParameters(const std::string& layerId)
+{
+ std::string error;
+ RuntimeCoordinatorResult result = ApplyStoreMutation(mRuntimeStore.ResetStoredLayerParameterValues(layerId, error), error, false, false);
+ if (!result.accepted)
+ return result;
+
+ result.clearTransientOscState = true;
+ result.renderResetScope = RuntimeCoordinatorRenderResetScope::TemporalHistoryAndFeedback;
+ return result;
+}
+
+RuntimeCoordinatorResult RuntimeCoordinator::SaveStackPreset(const std::string& presetName)
+{
+ std::string error;
+ return ApplyStoreMutation(mRuntimeStore.SaveStackPresetSnapshot(presetName, error), error, false, false);
+}
+
+RuntimeCoordinatorResult RuntimeCoordinator::LoadStackPreset(const std::string& presetName)
+{
+ std::string error;
+ return ApplyStoreMutation(mRuntimeStore.LoadStackPresetSnapshot(presetName, error), error, true, false);
+}
+
+RuntimeCoordinatorResult RuntimeCoordinator::RequestShaderReload(bool preserveFeedbackState)
+{
+ return BuildQueuedReloadResult(preserveFeedbackState);
+}
+
+RuntimeCoordinatorResult RuntimeCoordinator::HandleRuntimePollFailure(const std::string& error)
+{
+ RuntimeCoordinatorResult result;
+ result.accepted = true;
+ result.runtimeStateBroadcastRequired = true;
+ result.compileStatusChanged = true;
+ result.compileStatusSucceeded = false;
+ result.compileStatusMessage = error;
+ return result;
+}
+
+RuntimeCoordinatorResult RuntimeCoordinator::HandlePreparedShaderBuildFailure(const std::string& error)
+{
+ mPreserveFeedbackOnNextShaderBuild = false;
+
+ RuntimeCoordinatorResult result;
+ result.accepted = true;
+ result.runtimeStateBroadcastRequired = true;
+ result.compileStatusChanged = true;
+ result.compileStatusSucceeded = false;
+ result.compileStatusMessage = error;
+ result.committedStateMode = RuntimeCoordinatorCommittedStateMode::UseCommittedStates;
+ return result;
+}
+
+RuntimeCoordinatorResult RuntimeCoordinator::HandlePreparedShaderBuildSuccess()
+{
+ RuntimeCoordinatorResult result;
+ result.accepted = true;
+ result.runtimeStateBroadcastRequired = true;
+ result.compileStatusChanged = true;
+ result.compileStatusSucceeded = true;
+ result.compileStatusMessage = "Shader layers compiled successfully.";
+ result.committedStateMode = RuntimeCoordinatorCommittedStateMode::UseLiveSnapshots;
+ mPreserveFeedbackOnNextShaderBuild = false;
+ return result;
+}
+
+RuntimeCoordinatorResult RuntimeCoordinator::HandleRuntimeReloadRequest()
+{
+ return BuildQueuedReloadResult(false);
+}
+
+bool RuntimeCoordinator::PreserveFeedbackOnNextShaderBuild() const
+{
+ return mPreserveFeedbackOnNextShaderBuild;
+}
+
+RuntimeCoordinatorResult RuntimeCoordinator::ApplyStoreMutation(bool succeeded, const std::string& errorMessage, bool reloadRequired, bool preserveFeedbackState)
+{
+ if (!succeeded)
+ {
+ RuntimeCoordinatorResult result;
+ result.accepted = false;
+ result.errorMessage = errorMessage;
+ return result;
+ }
+
+ if (reloadRequired)
+ return BuildQueuedReloadResult(preserveFeedbackState);
+
+ return BuildAcceptedNoReloadResult();
+}
+
+RuntimeCoordinatorResult RuntimeCoordinator::BuildQueuedReloadResult(bool preserveFeedbackState)
+{
+ mPreserveFeedbackOnNextShaderBuild = preserveFeedbackState;
+
+ RuntimeCoordinatorResult result;
+ result.accepted = true;
+ result.runtimeStateBroadcastRequired = true;
+ result.shaderBuildRequested = true;
+ result.compileStatusChanged = true;
+ result.compileStatusSucceeded = true;
+ result.compileStatusMessage = "Shader rebuild queued.";
+ result.clearReloadRequest = true;
+ result.committedStateMode = RuntimeCoordinatorCommittedStateMode::UseCommittedStates;
+ return result;
+}
+
+RuntimeCoordinatorResult RuntimeCoordinator::BuildAcceptedNoReloadResult() const
+{
+ RuntimeCoordinatorResult result;
+ result.accepted = true;
+ result.runtimeStateBroadcastRequired = true;
+ return result;
+}
diff --git a/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeCoordinator.h b/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeCoordinator.h
new file mode 100644
index 0000000..476bed9
--- /dev/null
+++ b/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeCoordinator.h
@@ -0,0 +1,70 @@
+#pragma once
+
+#include "RuntimeJson.h"
+
+#include
+#include
+
+class RuntimeStore;
+
+enum class RuntimeCoordinatorCommittedStateMode
+{
+ Unchanged,
+ UseCommittedStates,
+ UseLiveSnapshots
+};
+
+enum class RuntimeCoordinatorRenderResetScope
+{
+ None,
+ TemporalHistoryOnly,
+ TemporalHistoryAndFeedback
+};
+
+struct RuntimeCoordinatorResult
+{
+ bool accepted = false;
+ bool runtimeStateBroadcastRequired = false;
+ bool shaderBuildRequested = false;
+ bool clearTransientOscState = false;
+ bool compileStatusChanged = false;
+ bool compileStatusSucceeded = false;
+ bool clearReloadRequest = false;
+ RuntimeCoordinatorCommittedStateMode committedStateMode = RuntimeCoordinatorCommittedStateMode::Unchanged;
+ RuntimeCoordinatorRenderResetScope renderResetScope = RuntimeCoordinatorRenderResetScope::None;
+ std::string compileStatusMessage;
+ std::string errorMessage;
+};
+
+class RuntimeCoordinator
+{
+public:
+ explicit RuntimeCoordinator(RuntimeStore& runtimeStore);
+
+ RuntimeCoordinatorResult AddLayer(const std::string& shaderId);
+ RuntimeCoordinatorResult RemoveLayer(const std::string& layerId);
+ RuntimeCoordinatorResult MoveLayer(const std::string& layerId, int direction);
+ RuntimeCoordinatorResult MoveLayerToIndex(const std::string& layerId, std::size_t targetIndex);
+ RuntimeCoordinatorResult SetLayerBypass(const std::string& layerId, bool bypassed);
+ RuntimeCoordinatorResult SetLayerShader(const std::string& layerId, const std::string& shaderId);
+ RuntimeCoordinatorResult UpdateLayerParameter(const std::string& layerId, const std::string& parameterId, const JsonValue& newValue);
+ RuntimeCoordinatorResult UpdateLayerParameterByControlKey(const std::string& layerKey, const std::string& parameterKey, const JsonValue& newValue);
+ RuntimeCoordinatorResult ResetLayerParameters(const std::string& layerId);
+ RuntimeCoordinatorResult SaveStackPreset(const std::string& presetName);
+ RuntimeCoordinatorResult LoadStackPreset(const std::string& presetName);
+
+ RuntimeCoordinatorResult RequestShaderReload(bool preserveFeedbackState = false);
+ RuntimeCoordinatorResult HandleRuntimePollFailure(const std::string& error);
+ RuntimeCoordinatorResult HandlePreparedShaderBuildFailure(const std::string& error);
+ RuntimeCoordinatorResult HandlePreparedShaderBuildSuccess();
+ RuntimeCoordinatorResult HandleRuntimeReloadRequest();
+ bool PreserveFeedbackOnNextShaderBuild() const;
+
+private:
+ RuntimeCoordinatorResult ApplyStoreMutation(bool succeeded, const std::string& errorMessage, bool reloadRequired, bool preserveFeedbackState);
+ RuntimeCoordinatorResult BuildQueuedReloadResult(bool preserveFeedbackState);
+ RuntimeCoordinatorResult BuildAcceptedNoReloadResult() const;
+
+ RuntimeStore& mRuntimeStore;
+ bool mPreserveFeedbackOnNextShaderBuild = false;
+};
diff --git a/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeSnapshotProvider.cpp b/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeSnapshotProvider.cpp
index e4d590d..559277a 100644
--- a/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeSnapshotProvider.cpp
+++ b/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeSnapshotProvider.cpp
@@ -12,6 +12,11 @@ bool RuntimeSnapshotProvider::BuildLayerPassFragmentShaderSources(const std::str
return mRuntimeHost.BuildLayerPassFragmentShaderSources(layerId, passSources, error);
}
+unsigned RuntimeSnapshotProvider::GetMaxTemporalHistoryFrames() const
+{
+ return mRuntimeHost.GetMaxTemporalHistoryFrames();
+}
+
RuntimeSnapshotVersions RuntimeSnapshotProvider::GetVersions() const
{
RuntimeSnapshotVersions versions;
@@ -35,6 +40,16 @@ RuntimeRenderFrameContext RuntimeSnapshotProvider::GetFrameContext() const
return frameContext;
}
+void RuntimeSnapshotProvider::AdvanceFrame()
+{
+ mRuntimeHost.AdvanceFrame();
+}
+
+bool RuntimeSnapshotProvider::TryAdvanceFrame()
+{
+ return mRuntimeHost.TryAdvanceFrame();
+}
+
RuntimeRenderStateSnapshot RuntimeSnapshotProvider::GetRenderStateSnapshot(unsigned outputWidth, unsigned outputHeight) const
{
for (;;)
diff --git a/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeSnapshotProvider.h b/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeSnapshotProvider.h
index d791bfa..252cf42 100644
--- a/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeSnapshotProvider.h
+++ b/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeSnapshotProvider.h
@@ -35,8 +35,11 @@ public:
explicit RuntimeSnapshotProvider(RuntimeHost& runtimeHost);
bool BuildLayerPassFragmentShaderSources(const std::string& layerId, std::vector& passSources, std::string& error) const;
+ unsigned GetMaxTemporalHistoryFrames() const;
RuntimeSnapshotVersions GetVersions() const;
RuntimeRenderFrameContext GetFrameContext() const;
+ void AdvanceFrame();
+ bool TryAdvanceFrame();
RuntimeRenderStateSnapshot GetRenderStateSnapshot(unsigned outputWidth, unsigned outputHeight) const;
bool TryGetRenderStateSnapshot(unsigned outputWidth, unsigned outputHeight, RuntimeRenderStateSnapshot& snapshot) const;
bool TryRefreshSnapshotParameters(RuntimeRenderStateSnapshot& snapshot) const;
diff --git a/apps/LoopThroughWithOpenGLCompositing/videoio/VideoBackend.cpp b/apps/LoopThroughWithOpenGLCompositing/videoio/VideoBackend.cpp
index 8b0a3ec..d2f517f 100644
--- a/apps/LoopThroughWithOpenGLCompositing/videoio/VideoBackend.cpp
+++ b/apps/LoopThroughWithOpenGLCompositing/videoio/VideoBackend.cpp
@@ -2,12 +2,15 @@
#include "DeckLinkSession.h"
#include "OpenGLVideoIOBridge.h"
-#include "RenderEngine.h"
#include "HealthTelemetry.h"
+#include "RenderEngine.h"
+
+#include
VideoBackend::VideoBackend(RenderEngine& renderEngine, HealthTelemetry& healthTelemetry) :
+ mHealthTelemetry(healthTelemetry),
mVideoIODevice(std::make_unique()),
- mBridge(std::make_unique(*mVideoIODevice, renderEngine, healthTelemetry))
+ mBridge(std::make_unique(renderEngine))
{
}
@@ -35,7 +38,7 @@ bool VideoBackend::SelectPreferredFormats(const VideoFormatSelection& videoModes
bool VideoBackend::ConfigureInput(const VideoFormat& inputVideoMode, std::string& error)
{
return mVideoIODevice->ConfigureInput(
- [this](const VideoIOFrame& frame) { mBridge->VideoFrameArrived(frame); },
+ [this](const VideoIOFrame& frame) { HandleInputFrame(frame); },
inputVideoMode,
error);
}
@@ -43,7 +46,7 @@ bool VideoBackend::ConfigureInput(const VideoFormat& inputVideoMode, std::string
bool VideoBackend::ConfigureOutput(const VideoFormat& outputVideoMode, bool externalKeyingEnabled, std::string& error)
{
return mVideoIODevice->ConfigureOutput(
- [this](const VideoIOCompletion& completion) { mBridge->PlayoutFrameCompleted(completion); },
+ [this](const VideoIOCompletion& completion) { HandleOutputFrameCompletion(completion); },
outputVideoMode,
externalKeyingEnabled,
error);
@@ -173,3 +176,63 @@ void VideoBackend::SetStatusMessage(const std::string& message)
{
mVideoIODevice->SetStatusMessage(message);
}
+
+void VideoBackend::HandleInputFrame(const VideoIOFrame& frame)
+{
+ const VideoIOState& state = mVideoIODevice->State();
+ mHealthTelemetry.TryReportSignalStatus(!frame.hasNoInputSource, state.inputFrameSize.width, state.inputFrameSize.height, state.inputDisplayModeName);
+
+ if (mBridge)
+ mBridge->UploadInputFrame(frame, state);
+}
+
+void VideoBackend::HandleOutputFrameCompletion(const VideoIOCompletion& completion)
+{
+ RecordFramePacing(completion.result);
+
+ VideoIOOutputFrame outputFrame;
+ if (!BeginOutputFrame(outputFrame))
+ return;
+
+ const VideoIOState& state = mVideoIODevice->State();
+ if (mBridge)
+ mBridge->RenderScheduledFrame(state, completion, outputFrame);
+
+ EndOutputFrame(outputFrame);
+ AccountForCompletionResult(completion.result);
+
+ // Schedule the next frame after render work is complete so device-side
+ // bookkeeping stays with the backend seam and the bridge stays render-only.
+ ScheduleOutputFrame(outputFrame);
+}
+
+void VideoBackend::RecordFramePacing(VideoIOCompletionResult completionResult)
+{
+ const auto now = std::chrono::steady_clock::now();
+ if (mLastPlayoutCompletionTime != std::chrono::steady_clock::time_point())
+ {
+ mCompletionIntervalMilliseconds = std::chrono::duration_cast>(now - mLastPlayoutCompletionTime).count();
+ if (mSmoothedCompletionIntervalMilliseconds <= 0.0)
+ mSmoothedCompletionIntervalMilliseconds = mCompletionIntervalMilliseconds;
+ else
+ mSmoothedCompletionIntervalMilliseconds = mSmoothedCompletionIntervalMilliseconds * 0.9 + mCompletionIntervalMilliseconds * 0.1;
+ if (mCompletionIntervalMilliseconds > mMaxCompletionIntervalMilliseconds)
+ mMaxCompletionIntervalMilliseconds = mCompletionIntervalMilliseconds;
+ }
+ mLastPlayoutCompletionTime = now;
+
+ if (completionResult == VideoIOCompletionResult::DisplayedLate)
+ ++mLateFrameCount;
+ else if (completionResult == VideoIOCompletionResult::Dropped)
+ ++mDroppedFrameCount;
+ else if (completionResult == VideoIOCompletionResult::Flushed)
+ ++mFlushedFrameCount;
+
+ mHealthTelemetry.TryRecordFramePacingStats(
+ mCompletionIntervalMilliseconds,
+ mSmoothedCompletionIntervalMilliseconds,
+ mMaxCompletionIntervalMilliseconds,
+ mLateFrameCount,
+ mDroppedFrameCount,
+ mFlushedFrameCount);
+}
diff --git a/apps/LoopThroughWithOpenGLCompositing/videoio/VideoBackend.h b/apps/LoopThroughWithOpenGLCompositing/videoio/VideoBackend.h
index d5a1702..0484875 100644
--- a/apps/LoopThroughWithOpenGLCompositing/videoio/VideoBackend.h
+++ b/apps/LoopThroughWithOpenGLCompositing/videoio/VideoBackend.h
@@ -2,6 +2,8 @@
#include "VideoIOTypes.h"
+#include
+#include
#include
#include
@@ -50,6 +52,18 @@ public:
void SetStatusMessage(const std::string& message);
private:
+ void HandleInputFrame(const VideoIOFrame& frame);
+ void HandleOutputFrameCompletion(const VideoIOCompletion& completion);
+ void RecordFramePacing(VideoIOCompletionResult completionResult);
+
+ HealthTelemetry& mHealthTelemetry;
std::unique_ptr mVideoIODevice;
std::unique_ptr mBridge;
+ std::chrono::steady_clock::time_point mLastPlayoutCompletionTime;
+ double mCompletionIntervalMilliseconds = 0.0;
+ double mSmoothedCompletionIntervalMilliseconds = 0.0;
+ double mMaxCompletionIntervalMilliseconds = 0.0;
+ uint64_t mLateFrameCount = 0;
+ uint64_t mDroppedFrameCount = 0;
+ uint64_t mFlushedFrameCount = 0;
};
diff --git a/apps/LoopThroughWithOpenGLCompositing/videoio/decklink/DeckLinkSession.cpp b/apps/LoopThroughWithOpenGLCompositing/videoio/decklink/DeckLinkSession.cpp
index 4c8e4fc..2a29432 100644
--- a/apps/LoopThroughWithOpenGLCompositing/videoio/decklink/DeckLinkSession.cpp
+++ b/apps/LoopThroughWithOpenGLCompositing/videoio/decklink/DeckLinkSession.cpp
@@ -417,11 +417,21 @@ double DeckLinkSession::FrameBudgetMilliseconds() const
return mScheduler.FrameBudgetMilliseconds();
}
-bool DeckLinkSession::BeginOutputFrame(VideoIOOutputFrame& frame)
+bool DeckLinkSession::AcquireNextOutputVideoFrame(CComPtr& outputVideoFrame)
{
- CComPtr outputVideoFrame = outputVideoFrameQueue.front();
+ if (outputVideoFrameQueue.empty())
+ return false;
+
+ outputVideoFrame = outputVideoFrameQueue.front();
outputVideoFrameQueue.push_back(outputVideoFrame);
outputVideoFrameQueue.pop_front();
+ return outputVideoFrame != nullptr;
+}
+
+bool DeckLinkSession::PopulateOutputFrame(IDeckLinkMutableVideoFrame* outputVideoFrame, VideoIOOutputFrame& frame)
+{
+ if (outputVideoFrame == nullptr)
+ return false;
CComPtr outputVideoFrameBuffer;
if (outputVideoFrame->QueryInterface(IID_IDeckLinkVideoBuffer, (void**)&outputVideoFrameBuffer) != S_OK)
@@ -438,11 +448,44 @@ bool DeckLinkSession::BeginOutputFrame(VideoIOOutputFrame& frame)
frame.width = mState.outputFrameSize.width;
frame.height = mState.outputFrameSize.height;
frame.pixelFormat = mState.outputPixelFormat;
- frame.nativeFrame = outputVideoFrame.p;
+ frame.nativeFrame = outputVideoFrame;
frame.nativeBuffer = outputVideoFrameBuffer.Detach();
return true;
}
+bool DeckLinkSession::ScheduleFrame(IDeckLinkMutableVideoFrame* outputVideoFrame)
+{
+ const VideoIOScheduleTime scheduleTime = mScheduler.NextScheduleTime();
+ return outputVideoFrame != nullptr &&
+ output->ScheduleVideoFrame(outputVideoFrame, scheduleTime.streamTime, scheduleTime.duration, scheduleTime.timeScale) == S_OK;
+}
+
+bool DeckLinkSession::ScheduleBlackFrame(IDeckLinkMutableVideoFrame* outputVideoFrame)
+{
+ if (outputVideoFrame == nullptr)
+ return false;
+
+ CComPtr outputVideoFrameBuffer;
+ if (outputVideoFrame->QueryInterface(IID_IDeckLinkVideoBuffer, (void**)&outputVideoFrameBuffer) != S_OK)
+ return false;
+
+ if (outputVideoFrameBuffer->StartAccess(bmdBufferAccessWrite) != S_OK)
+ return false;
+
+ void* pFrame = nullptr;
+ outputVideoFrameBuffer->GetBytes((void**)&pFrame);
+ memset(pFrame, 0, outputVideoFrame->GetRowBytes() * mState.outputFrameSize.height);
+
+ outputVideoFrameBuffer->EndAccess(bmdBufferAccessWrite);
+ return ScheduleFrame(outputVideoFrame);
+}
+
+bool DeckLinkSession::BeginOutputFrame(VideoIOOutputFrame& frame)
+{
+ CComPtr outputVideoFrame;
+ return AcquireNextOutputVideoFrame(outputVideoFrame) && PopulateOutputFrame(outputVideoFrame, frame);
+}
+
void DeckLinkSession::EndOutputFrame(VideoIOOutputFrame& frame)
{
IDeckLinkVideoBuffer* outputVideoFrameBuffer = static_cast(frame.nativeBuffer);
@@ -463,11 +506,7 @@ void DeckLinkSession::AccountForCompletionResult(VideoIOCompletionResult complet
bool DeckLinkSession::ScheduleOutputFrame(const VideoIOOutputFrame& frame)
{
IDeckLinkMutableVideoFrame* outputVideoFrame = static_cast(frame.nativeFrame);
- const VideoIOScheduleTime scheduleTime = mScheduler.NextScheduleTime();
- if (outputVideoFrame == nullptr || output->ScheduleVideoFrame(outputVideoFrame, scheduleTime.streamTime, scheduleTime.duration, scheduleTime.timeScale) != S_OK)
- return false;
-
- return true;
+ return ScheduleFrame(outputVideoFrame);
}
bool DeckLinkSession::Start()
@@ -486,31 +525,13 @@ bool DeckLinkSession::Start()
for (unsigned i = 0; i < kPrerollFrameCount; i++)
{
- CComPtr outputVideoFrame = outputVideoFrameQueue.front();
- outputVideoFrameQueue.push_back(outputVideoFrame);
- outputVideoFrameQueue.pop_front();
-
- CComPtr outputVideoFrameBuffer;
- if (outputVideoFrame->QueryInterface(IID_IDeckLinkVideoBuffer, (void**)&outputVideoFrameBuffer) != S_OK)
+ CComPtr outputVideoFrame;
+ if (!AcquireNextOutputVideoFrame(outputVideoFrame))
{
- MessageBoxA(NULL, "Could not query the preroll output frame buffer.", "DeckLink start failed", MB_OK | MB_ICONERROR);
+ MessageBoxA(NULL, "Could not acquire a preroll output frame.", "DeckLink start failed", MB_OK | MB_ICONERROR);
return false;
}
-
- if (outputVideoFrameBuffer->StartAccess(bmdBufferAccessWrite) != S_OK)
- {
- MessageBoxA(NULL, "Could not write to the preroll output frame buffer.", "DeckLink start failed", MB_OK | MB_ICONERROR);
- return false;
- }
-
- void* pFrame = nullptr;
- outputVideoFrameBuffer->GetBytes((void**)&pFrame);
- memset(pFrame, 0, outputVideoFrame->GetRowBytes() * mState.outputFrameSize.height);
-
- outputVideoFrameBuffer->EndAccess(bmdBufferAccessWrite);
-
- const VideoIOScheduleTime scheduleTime = mScheduler.NextScheduleTime();
- if (output->ScheduleVideoFrame(outputVideoFrame, scheduleTime.streamTime, scheduleTime.duration, scheduleTime.timeScale) != S_OK)
+ if (!ScheduleBlackFrame(outputVideoFrame))
{
MessageBoxA(NULL, "Could not schedule a preroll output frame.", "DeckLink start failed", MB_OK | MB_ICONERROR);
return false;
@@ -599,23 +620,23 @@ void DeckLinkSession::HandlePlayoutFrameCompleted(IDeckLinkVideoFrame*, BMDOutpu
return;
VideoIOCompletion completion;
+ completion.result = TranslateCompletionResult(completionResult);
+ mOutputFrameCallback(completion);
+}
+
+VideoIOCompletionResult DeckLinkSession::TranslateCompletionResult(BMDOutputFrameCompletionResult completionResult)
+{
switch (completionResult)
{
case bmdOutputFrameDisplayedLate:
- completion.result = VideoIOCompletionResult::DisplayedLate;
- break;
+ return VideoIOCompletionResult::DisplayedLate;
case bmdOutputFrameDropped:
- completion.result = VideoIOCompletionResult::Dropped;
- break;
+ return VideoIOCompletionResult::Dropped;
case bmdOutputFrameFlushed:
- completion.result = VideoIOCompletionResult::Flushed;
- break;
+ return VideoIOCompletionResult::Flushed;
case bmdOutputFrameCompleted:
- completion.result = VideoIOCompletionResult::Completed;
- break;
+ return VideoIOCompletionResult::Completed;
default:
- completion.result = VideoIOCompletionResult::Unknown;
- break;
+ return VideoIOCompletionResult::Unknown;
}
- mOutputFrameCallback(completion);
}
diff --git a/apps/LoopThroughWithOpenGLCompositing/videoio/decklink/DeckLinkSession.h b/apps/LoopThroughWithOpenGLCompositing/videoio/decklink/DeckLinkSession.h
index 211914d..5d695e8 100644
--- a/apps/LoopThroughWithOpenGLCompositing/videoio/decklink/DeckLinkSession.h
+++ b/apps/LoopThroughWithOpenGLCompositing/videoio/decklink/DeckLinkSession.h
@@ -66,6 +66,12 @@ public:
void HandlePlayoutFrameCompleted(IDeckLinkVideoFrame* completedFrame, BMDOutputFrameCompletionResult completionResult);
private:
+ bool AcquireNextOutputVideoFrame(CComPtr& outputVideoFrame);
+ bool PopulateOutputFrame(IDeckLinkMutableVideoFrame* outputVideoFrame, VideoIOOutputFrame& frame);
+ bool ScheduleFrame(IDeckLinkMutableVideoFrame* outputVideoFrame);
+ bool ScheduleBlackFrame(IDeckLinkMutableVideoFrame* outputVideoFrame);
+ static VideoIOCompletionResult TranslateCompletionResult(BMDOutputFrameCompletionResult completionResult);
+
CComPtr captureDelegate;
CComPtr playoutDelegate;
CComPtr input;