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

This commit is contained in:
Aiden
2026-05-10 23:31:45 +10:00
parent 739231d5a1
commit 7f0f60c0e3
11 changed files with 593 additions and 176 deletions

View File

@@ -62,6 +62,8 @@ set(APP_SOURCES
"${APP_DIR}/gl/OpenGLComposite.cpp" "${APP_DIR}/gl/OpenGLComposite.cpp"
"${APP_DIR}/gl/OpenGLComposite.h" "${APP_DIR}/gl/OpenGLComposite.h"
"${APP_DIR}/gl/OpenGLCompositeRuntimeControls.cpp" "${APP_DIR}/gl/OpenGLCompositeRuntimeControls.cpp"
"${APP_DIR}/gl/RenderEngine.cpp"
"${APP_DIR}/gl/RenderEngine.h"
"${APP_DIR}/gl/pipeline/OpenGLRenderPass.cpp" "${APP_DIR}/gl/pipeline/OpenGLRenderPass.cpp"
"${APP_DIR}/gl/pipeline/OpenGLRenderPass.h" "${APP_DIR}/gl/pipeline/OpenGLRenderPass.h"
"${APP_DIR}/gl/pipeline/OpenGLRenderPipeline.cpp" "${APP_DIR}/gl/pipeline/OpenGLRenderPipeline.cpp"
@@ -122,6 +124,8 @@ set(APP_SOURCES
"${APP_DIR}/targetver.h" "${APP_DIR}/targetver.h"
"${APP_DIR}/videoio/VideoIOFormat.cpp" "${APP_DIR}/videoio/VideoIOFormat.cpp"
"${APP_DIR}/videoio/VideoIOFormat.h" "${APP_DIR}/videoio/VideoIOFormat.h"
"${APP_DIR}/videoio/VideoBackend.cpp"
"${APP_DIR}/videoio/VideoBackend.h"
"${APP_DIR}/videoio/VideoIOTypes.h" "${APP_DIR}/videoio/VideoIOTypes.h"
"${APP_DIR}/videoio/VideoPlayoutScheduler.cpp" "${APP_DIR}/videoio/VideoPlayoutScheduler.cpp"
"${APP_DIR}/videoio/VideoPlayoutScheduler.h" "${APP_DIR}/videoio/VideoPlayoutScheduler.h"

View File

@@ -186,6 +186,7 @@
<ClCompile Include="LoopThroughWithOpenGLCompositing.cpp" /> <ClCompile Include="LoopThroughWithOpenGLCompositing.cpp" />
<ClCompile Include="gl\OpenGLComposite.cpp" /> <ClCompile Include="gl\OpenGLComposite.cpp" />
<ClCompile Include="gl\OpenGLCompositeRuntimeControls.cpp" /> <ClCompile Include="gl\OpenGLCompositeRuntimeControls.cpp" />
<ClCompile Include="gl\RenderEngine.cpp" />
<ClCompile Include="gl\pipeline\OpenGLRenderPass.cpp" /> <ClCompile Include="gl\pipeline\OpenGLRenderPass.cpp" />
<ClCompile Include="gl\pipeline\OpenGLRenderPipeline.cpp" /> <ClCompile Include="gl\pipeline\OpenGLRenderPipeline.cpp" />
<ClCompile Include="gl\pipeline\ShaderFeedbackBuffers.cpp" /> <ClCompile Include="gl\pipeline\ShaderFeedbackBuffers.cpp" />
@@ -220,6 +221,7 @@
<ClCompile Include="runtime\RuntimeStore.cpp" /> <ClCompile Include="runtime\RuntimeStore.cpp" />
<ClCompile Include="shader\ShaderCompiler.cpp" /> <ClCompile Include="shader\ShaderCompiler.cpp" />
<ClCompile Include="shader\ShaderPackageRegistry.cpp" /> <ClCompile Include="shader\ShaderPackageRegistry.cpp" />
<ClCompile Include="videoio\VideoBackend.cpp" />
<ClCompile Include="videoio\VideoIOFormat.cpp" /> <ClCompile Include="videoio\VideoIOFormat.cpp" />
<ClCompile Include="videoio\VideoPlayoutScheduler.cpp" /> <ClCompile Include="videoio\VideoPlayoutScheduler.cpp" />
</ItemGroup> </ItemGroup>
@@ -236,6 +238,7 @@
<ClInclude Include="gl\shader\GlShaderSources.h" /> <ClInclude Include="gl\shader\GlShaderSources.h" />
<ClInclude Include="LoopThroughWithOpenGLCompositing.h" /> <ClInclude Include="LoopThroughWithOpenGLCompositing.h" />
<ClInclude Include="gl\OpenGLComposite.h" /> <ClInclude Include="gl\OpenGLComposite.h" />
<ClInclude Include="gl\RenderEngine.h" />
<ClInclude Include="gl\pipeline\OpenGLRenderPass.h" /> <ClInclude Include="gl\pipeline\OpenGLRenderPass.h" />
<ClInclude Include="gl\pipeline\OpenGLRenderPipeline.h" /> <ClInclude Include="gl\pipeline\OpenGLRenderPipeline.h" />
<ClInclude Include="gl\pipeline\RenderPassDescriptor.h" /> <ClInclude Include="gl\pipeline\RenderPassDescriptor.h" />
@@ -272,6 +275,7 @@
<ClInclude Include="videoio\decklink\DeckLinkFrameTransfer.h" /> <ClInclude Include="videoio\decklink\DeckLinkFrameTransfer.h" />
<ClInclude Include="videoio\decklink\DeckLinkSession.h" /> <ClInclude Include="videoio\decklink\DeckLinkSession.h" />
<ClInclude Include="videoio\decklink\DeckLinkVideoIOFormat.h" /> <ClInclude Include="videoio\decklink\DeckLinkVideoIOFormat.h" />
<ClInclude Include="videoio\VideoBackend.h" />
<ClInclude Include="videoio\VideoIOFormat.h" /> <ClInclude Include="videoio\VideoIOFormat.h" />
<ClInclude Include="videoio\VideoIOTypes.h" /> <ClInclude Include="videoio\VideoIOTypes.h" />
<ClInclude Include="videoio\VideoPlayoutScheduler.h" /> <ClInclude Include="videoio\VideoPlayoutScheduler.h" />

View File

@@ -48,6 +48,9 @@
<ClCompile Include="gl\OpenGLCompositeRuntimeControls.cpp"> <ClCompile Include="gl\OpenGLCompositeRuntimeControls.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="gl\RenderEngine.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="gl\pipeline\OpenGLRenderPass.cpp"> <ClCompile Include="gl\pipeline\OpenGLRenderPass.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
@@ -138,6 +141,9 @@
<ClCompile Include="shader\ShaderPackageRegistry.cpp"> <ClCompile Include="shader\ShaderPackageRegistry.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="videoio\VideoBackend.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="videoio\VideoIOFormat.cpp"> <ClCompile Include="videoio\VideoIOFormat.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
@@ -179,6 +185,9 @@
<ClInclude Include="gl\OpenGLComposite.h"> <ClInclude Include="gl\OpenGLComposite.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="gl\RenderEngine.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="gl\pipeline\OpenGLRenderPass.h"> <ClInclude Include="gl\pipeline\OpenGLRenderPass.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
@@ -287,6 +296,9 @@
<ClInclude Include="videoio\decklink\DeckLinkFrameTransfer.h"> <ClInclude Include="videoio\decklink\DeckLinkFrameTransfer.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="videoio\VideoBackend.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="runtime\RuntimeClock.h"> <ClInclude Include="runtime\RuntimeClock.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>

View File

@@ -1,16 +1,13 @@
#include "DeckLinkDisplayMode.h" #include "DeckLinkDisplayMode.h"
#include "DeckLinkSession.h"
#include "OpenGLComposite.h" #include "OpenGLComposite.h"
#include "GLExtensions.h" #include "GLExtensions.h"
#include "GlRenderConstants.h" #include "GlRenderConstants.h"
#include "OpenGLRenderPass.h"
#include "OpenGLRenderPipeline.h"
#include "OpenGLShaderPrograms.h"
#include "OpenGLVideoIOBridge.h"
#include "PngScreenshotWriter.h" #include "PngScreenshotWriter.h"
#include "RenderEngine.h"
#include "RuntimeParameterUtils.h" #include "RuntimeParameterUtils.h"
#include "RuntimeServices.h" #include "RuntimeServices.h"
#include "ShaderBuildQueue.h" #include "ShaderBuildQueue.h"
#include "VideoBackend.h"
#include <algorithm> #include <algorithm>
#include <cctype> #include <cctype>
@@ -97,8 +94,6 @@ JsonValue BuildOscCommitValue(const ShaderParameterDefinition& definition, const
OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) : OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) :
hGLWnd(hWnd), hGLDC(hDC), hGLRC(hRC), hGLWnd(hWnd), hGLDC(hDC), hGLRC(hRC),
mVideoIO(std::make_unique<DeckLinkSession>()),
mRenderer(std::make_unique<OpenGLRenderer>()),
mUseCommittedLayerStates(false), mUseCommittedLayerStates(false),
mScreenshotRequested(false) mScreenshotRequested(false)
{ {
@@ -106,22 +101,16 @@ OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) :
mRuntimeHost = std::make_unique<RuntimeHost>(); mRuntimeHost = std::make_unique<RuntimeHost>();
mRuntimeStore = std::make_unique<RuntimeStore>(*mRuntimeHost); mRuntimeStore = std::make_unique<RuntimeStore>(*mRuntimeHost);
mRuntimeSnapshotProvider = std::make_unique<RuntimeSnapshotProvider>(*mRuntimeHost); mRuntimeSnapshotProvider = std::make_unique<RuntimeSnapshotProvider>(*mRuntimeHost);
mRenderPipeline = std::make_unique<OpenGLRenderPipeline>( mRenderEngine = std::make_unique<RenderEngine>(
*mRenderer,
*mRuntimeHost, *mRuntimeHost,
*mRuntimeSnapshotProvider,
pMutex,
hGLDC,
hGLRC,
[this]() { renderEffect(); }, [this]() { renderEffect(); },
[this]() { ProcessScreenshotRequest(); }, [this]() { ProcessScreenshotRequest(); },
[this]() { paintGL(false); }); [this]() { paintGL(false); });
mVideoIOBridge = std::make_unique<OpenGLVideoIOBridge>( mVideoBackend = std::make_unique<VideoBackend>(*mRenderEngine, mRuntimeHost->GetHealthTelemetry());
*mVideoIO,
*mRenderer,
*mRenderPipeline,
*mRuntimeHost,
pMutex,
hGLDC,
hGLRC);
mRenderPass = std::make_unique<OpenGLRenderPass>(*mRenderer);
mShaderPrograms = std::make_unique<OpenGLShaderPrograms>(*mRenderer, *mRuntimeHost, *mRuntimeSnapshotProvider);
mShaderBuildQueue = std::make_unique<ShaderBuildQueue>(*mRuntimeSnapshotProvider); mShaderBuildQueue = std::make_unique<ShaderBuildQueue>(*mRuntimeSnapshotProvider);
mRuntimeServices = std::make_unique<RuntimeServices>(); mRuntimeServices = std::make_unique<RuntimeServices>();
} }
@@ -132,8 +121,8 @@ OpenGLComposite::~OpenGLComposite()
mRuntimeServices->Stop(); mRuntimeServices->Stop();
if (mShaderBuildQueue) if (mShaderBuildQueue)
mShaderBuildQueue->Stop(); mShaderBuildQueue->Stop();
mVideoIO->ReleaseResources(); if (mVideoBackend)
mRenderer->DestroyResources(); mVideoBackend->ReleaseResources();
DeleteCriticalSection(&pMutex); DeleteCriticalSection(&pMutex);
} }
@@ -173,7 +162,7 @@ bool OpenGLComposite::InitVideoIO()
} }
} }
if (!mVideoIO->DiscoverDevicesAndModes(videoModes, initFailureReason)) if (!mVideoBackend->DiscoverDevicesAndModes(videoModes, initFailureReason))
{ {
const char* title = initFailureReason == "Please install the Blackmagic DeckLink drivers to use the features of this application." const char* title = initFailureReason == "Please install the Blackmagic DeckLink drivers to use the features of this application."
? "This application requires the DeckLink drivers installed." ? "This application requires the DeckLink drivers installed."
@@ -182,7 +171,7 @@ bool OpenGLComposite::InitVideoIO()
return false; return false;
} }
const bool outputAlphaRequired = mRuntimeStore && mRuntimeStore->IsExternalKeyingConfigured(); const bool outputAlphaRequired = mRuntimeStore && mRuntimeStore->IsExternalKeyingConfigured();
if (!mVideoIO->SelectPreferredFormats(videoModes, outputAlphaRequired, initFailureReason)) if (!mVideoBackend->SelectPreferredFormats(videoModes, outputAlphaRequired, initFailureReason))
goto error; goto error;
if (! CheckOpenGLExtensions()) if (! CheckOpenGLExtensions())
@@ -197,38 +186,38 @@ bool OpenGLComposite::InitVideoIO()
goto error; goto error;
} }
PublishVideoIOStatus(mVideoIO->OutputModelName().empty() PublishVideoIOStatus(mVideoBackend->OutputModelName().empty()
? "DeckLink output device selected." ? "DeckLink output device selected."
: ("Selected output device: " + mVideoIO->OutputModelName())); : ("Selected output device: " + mVideoBackend->OutputModelName()));
// Resize window to match output video frame, but scale large formats down by half for viewing. // Resize window to match output video frame, but scale large formats down by half for viewing.
if (mVideoIO->OutputFrameWidth() < 1920) if (mVideoBackend->OutputFrameWidth() < 1920)
resizeWindow(mVideoIO->OutputFrameWidth(), mVideoIO->OutputFrameHeight()); resizeWindow(mVideoBackend->OutputFrameWidth(), mVideoBackend->OutputFrameHeight());
else else
resizeWindow(mVideoIO->OutputFrameWidth() / 2, mVideoIO->OutputFrameHeight() / 2); resizeWindow(mVideoBackend->OutputFrameWidth() / 2, mVideoBackend->OutputFrameHeight() / 2);
if (!mVideoIO->ConfigureInput([this](const VideoIOFrame& frame) { mVideoIOBridge->VideoFrameArrived(frame); }, videoModes.input, initFailureReason)) if (!mVideoBackend->ConfigureInput(videoModes.input, initFailureReason))
{ {
goto error; goto error;
} }
if (!mVideoIO->HasInputDevice() && mRuntimeHost) if (!mVideoBackend->HasInputDevice() && mRuntimeHost)
{ {
mRuntimeHost->SetSignalStatus(false, mVideoIO->InputFrameWidth(), mVideoIO->InputFrameHeight(), mVideoIO->InputDisplayModeName()); mRuntimeHost->SetSignalStatus(false, mVideoBackend->InputFrameWidth(), mVideoBackend->InputFrameHeight(), mVideoBackend->InputDisplayModeName());
} }
if (!mVideoIO->ConfigureOutput([this](const VideoIOCompletion& completion) { mVideoIOBridge->PlayoutFrameCompleted(completion); }, videoModes.output, mRuntimeStore && mRuntimeStore->IsExternalKeyingConfigured(), initFailureReason)) if (!mVideoBackend->ConfigureOutput(videoModes.output, mRuntimeStore && mRuntimeStore->IsExternalKeyingConfigured(), initFailureReason))
{ {
goto error; goto error;
} }
PublishVideoIOStatus(mVideoIO->StatusMessage()); PublishVideoIOStatus(mVideoBackend->StatusMessage());
return true; return true;
error: error:
if (!initFailureReason.empty()) if (!initFailureReason.empty())
MessageBoxA(NULL, initFailureReason.c_str(), "DeckLink initialization failed", MB_OK | MB_ICONERROR); MessageBoxA(NULL, initFailureReason.c_str(), "DeckLink initialization failed", MB_OK | MB_ICONERROR);
mVideoIO->ReleaseResources(); mVideoBackend->ReleaseResources();
return false; return false;
} }
@@ -252,23 +241,21 @@ void OpenGLComposite::paintGL(bool force)
} }
} }
if (!TryEnterCriticalSection(&pMutex)) if (!mRenderEngine->TryPresentToWindow(mVideoBackend->OutputFrameWidth(), mVideoBackend->OutputFrameHeight()))
{ {
ValidateRect(hGLWnd, NULL); ValidateRect(hGLWnd, NULL);
return; return;
} }
mRenderer->PresentToWindow(hGLDC, mVideoIO->OutputFrameWidth(), mVideoIO->OutputFrameHeight());
mLastPreviewPresentTime = std::chrono::steady_clock::now(); mLastPreviewPresentTime = std::chrono::steady_clock::now();
ValidateRect(hGLWnd, NULL); ValidateRect(hGLWnd, NULL);
LeaveCriticalSection(&pMutex);
} }
void OpenGLComposite::resizeGL(WORD width, WORD height) void OpenGLComposite::resizeGL(WORD width, WORD height)
{ {
// We don't set the project or model matrices here since the window data is copied directly from // We don't set the project or model matrices here since the window data is copied directly from
// an off-screen FBO in paintGL(). Just save the width and height for use in paintGL(). // an off-screen FBO in paintGL(). Just save the width and height for use in paintGL().
mRenderer->ResizeView(width, height); mRenderEngine->ResizeView(width, height);
} }
void OpenGLComposite::resizeWindow(int width, int height) void OpenGLComposite::resizeWindow(int width, int height)
@@ -286,17 +273,17 @@ void OpenGLComposite::PublishVideoIOStatus(const std::string& statusMessage)
return; return;
if (!statusMessage.empty()) if (!statusMessage.empty())
mVideoIO->SetStatusMessage(statusMessage); mVideoBackend->SetStatusMessage(statusMessage);
mRuntimeHost->SetVideoIOStatus( mRuntimeHost->SetVideoIOStatus(
"decklink", "decklink",
mVideoIO->OutputModelName(), mVideoBackend->OutputModelName(),
mVideoIO->SupportsInternalKeying(), mVideoBackend->SupportsInternalKeying(),
mVideoIO->SupportsExternalKeying(), mVideoBackend->SupportsExternalKeying(),
mVideoIO->KeyerInterfaceAvailable(), mVideoBackend->KeyerInterfaceAvailable(),
mRuntimeStore ? mRuntimeStore->IsExternalKeyingConfigured() : false, mRuntimeStore ? mRuntimeStore->IsExternalKeyingConfigured() : false,
mVideoIO->ExternalKeyingActive(), mVideoBackend->ExternalKeyingActive(),
mVideoIO->StatusMessage()); mVideoBackend->StatusMessage());
} }
bool OpenGLComposite::InitOpenGLState() bool OpenGLComposite::InitOpenGLState()
@@ -319,41 +306,41 @@ bool OpenGLComposite::InitOpenGLState()
// Prepare the runtime shader program generated from the active shader package. // Prepare the runtime shader program generated from the active shader package.
char compilerErrorMessage[1024]; char compilerErrorMessage[1024];
if (!mShaderPrograms->CompileDecodeShader(sizeof(compilerErrorMessage), compilerErrorMessage)) if (!mRenderEngine->CompileDecodeShader(sizeof(compilerErrorMessage), compilerErrorMessage))
{ {
MessageBoxA(NULL, compilerErrorMessage, "OpenGL decode shader failed to load or compile", MB_OK); MessageBoxA(NULL, compilerErrorMessage, "OpenGL decode shader failed to load or compile", MB_OK);
return false; return false;
} }
if (!mShaderPrograms->CompileOutputPackShader(sizeof(compilerErrorMessage), compilerErrorMessage)) if (!mRenderEngine->CompileOutputPackShader(sizeof(compilerErrorMessage), compilerErrorMessage))
{ {
MessageBoxA(NULL, compilerErrorMessage, "OpenGL output pack shader failed to load or compile", MB_OK); MessageBoxA(NULL, compilerErrorMessage, "OpenGL output pack shader failed to load or compile", MB_OK);
return false; return false;
} }
std::string rendererError; std::string rendererError;
if (!mRenderer->InitializeResources( if (!mRenderEngine->InitializeResources(
mVideoIO->InputFrameWidth(), mVideoBackend->InputFrameWidth(),
mVideoIO->InputFrameHeight(), mVideoBackend->InputFrameHeight(),
mVideoIO->CaptureTextureWidth(), mVideoBackend->CaptureTextureWidth(),
mVideoIO->OutputFrameWidth(), mVideoBackend->OutputFrameWidth(),
mVideoIO->OutputFrameHeight(), mVideoBackend->OutputFrameHeight(),
mVideoIO->OutputPackTextureWidth(), mVideoBackend->OutputPackTextureWidth(),
rendererError)) rendererError))
{ {
MessageBoxA(NULL, rendererError.c_str(), "OpenGL initialization error.", MB_OK); MessageBoxA(NULL, rendererError.c_str(), "OpenGL initialization error.", MB_OK);
return false; return false;
} }
if (!mShaderPrograms->CompileLayerPrograms(mVideoIO->InputFrameWidth(), mVideoIO->InputFrameHeight(), sizeof(compilerErrorMessage), compilerErrorMessage)) if (!mRenderEngine->CompileLayerPrograms(mVideoBackend->InputFrameWidth(), mVideoBackend->InputFrameHeight(), sizeof(compilerErrorMessage), compilerErrorMessage))
{ {
MessageBoxA(NULL, compilerErrorMessage, "OpenGL shader failed to load or compile", MB_OK); MessageBoxA(NULL, compilerErrorMessage, "OpenGL shader failed to load or compile", MB_OK);
return false; return false;
} }
mCachedLayerRenderStates = mShaderPrograms->CommittedLayerStates(); mCachedLayerRenderStates = mRenderEngine->CommittedLayerStates();
mUseCommittedLayerStates = false; mUseCommittedLayerStates = false;
mShaderPrograms->ResetTemporalHistoryState(); mRenderEngine->ResetTemporalHistoryState();
mShaderPrograms->ResetShaderFeedbackState(); mRenderEngine->ResetShaderFeedbackState();
broadcastRuntimeState(); broadcastRuntimeState();
mRuntimeServices->BeginPolling(*mRuntimeHost); mRuntimeServices->BeginPolling(*mRuntimeHost);
@@ -362,7 +349,7 @@ bool OpenGLComposite::InitOpenGLState()
bool OpenGLComposite::Start() bool OpenGLComposite::Start()
{ {
return mVideoIO->Start(); return mVideoBackend->Start();
} }
bool OpenGLComposite::Stop() bool OpenGLComposite::Stop()
@@ -370,8 +357,8 @@ bool OpenGLComposite::Stop()
if (mRuntimeServices) if (mRuntimeServices)
mRuntimeServices->Stop(); mRuntimeServices->Stop();
const bool wasExternalKeyingActive = mVideoIO->ExternalKeyingActive(); const bool wasExternalKeyingActive = mVideoBackend->ExternalKeyingActive();
mVideoIO->Stop(); mVideoBackend->Stop();
if (wasExternalKeyingActive) if (wasExternalKeyingActive)
PublishVideoIOStatus("External keying has been disabled."); PublishVideoIOStatus("External keying has been disabled.");
@@ -587,19 +574,19 @@ void OpenGLComposite::renderEffect()
mOscOverlayStates.erase(overlayKey); mOscOverlayStates.erase(overlayKey);
}; };
const bool hasInputSource = mVideoIO->HasInputSource(); const bool hasInputSource = mVideoBackend->HasInputSource();
std::vector<RuntimeRenderState> layerStates; std::vector<RuntimeRenderState> layerStates;
if (mUseCommittedLayerStates) if (mUseCommittedLayerStates)
{ {
layerStates = mShaderPrograms->CommittedLayerStates(); layerStates = mRenderEngine->CommittedLayerStates();
applyOscOverlays(layerStates, false); applyOscOverlays(layerStates, false);
if (mRuntimeSnapshotProvider) if (mRuntimeSnapshotProvider)
mRuntimeSnapshotProvider->RefreshDynamicRenderStateFields(layerStates); mRuntimeSnapshotProvider->RefreshDynamicRenderStateFields(layerStates);
} }
else if (mRuntimeSnapshotProvider) else if (mRuntimeSnapshotProvider)
{ {
const unsigned renderWidth = mVideoIO->InputFrameWidth(); const unsigned renderWidth = mVideoBackend->InputFrameWidth();
const unsigned renderHeight = mVideoIO->InputFrameHeight(); const unsigned renderHeight = mVideoBackend->InputFrameHeight();
const RuntimeSnapshotVersions versions = mRuntimeSnapshotProvider->GetVersions(); const RuntimeSnapshotVersions versions = mRuntimeSnapshotProvider->GetVersions();
const bool renderStateCacheValid = const bool renderStateCacheValid =
!mCachedLayerRenderStates.empty() && !mCachedLayerRenderStates.empty() &&
@@ -650,20 +637,14 @@ void OpenGLComposite::renderEffect()
} }
} }
const unsigned historyCap = mRuntimeStore ? mRuntimeStore->GetConfiguredMaxTemporalHistoryFrames() : 0; const unsigned historyCap = mRuntimeStore ? mRuntimeStore->GetConfiguredMaxTemporalHistoryFrames() : 0;
mRenderPass->Render( mRenderEngine->RenderLayerStack(
hasInputSource, hasInputSource,
layerStates, layerStates,
mVideoIO->InputFrameWidth(), mVideoBackend->InputFrameWidth(),
mVideoIO->InputFrameHeight(), mVideoBackend->InputFrameHeight(),
mVideoIO->CaptureTextureWidth(), mVideoBackend->CaptureTextureWidth(),
mVideoIO->InputPixelFormat(), mVideoBackend->InputPixelFormat(),
historyCap, historyCap);
[this](const RuntimeRenderState& state, LayerProgram::TextBinding& textBinding, std::string& error) {
return mShaderPrograms->UpdateTextBindingTexture(state, textBinding, error);
},
[this](const RuntimeRenderState& state, unsigned availableSourceHistoryLength, unsigned availableTemporalHistoryLength, bool feedbackAvailable) {
return mShaderPrograms->UpdateGlobalParamsBuffer(state, availableSourceHistoryLength, availableTemporalHistoryLength, feedbackAvailable);
});
} }
void OpenGLComposite::ProcessScreenshotRequest() void OpenGLComposite::ProcessScreenshotRequest()
@@ -671,21 +652,16 @@ void OpenGLComposite::ProcessScreenshotRequest()
if (!mScreenshotRequested.exchange(false)) if (!mScreenshotRequested.exchange(false))
return; return;
const unsigned width = mVideoIO ? mVideoIO->OutputFrameWidth() : 0; const unsigned width = mVideoBackend ? mVideoBackend->OutputFrameWidth() : 0;
const unsigned height = mVideoIO ? mVideoIO->OutputFrameHeight() : 0; const unsigned height = mVideoBackend ? mVideoBackend->OutputFrameHeight() : 0;
if (width == 0 || height == 0) if (width == 0 || height == 0)
return; return;
std::vector<unsigned char> bottomUpPixels(static_cast<std::size_t>(width) * height * 4); std::vector<unsigned char> bottomUpPixels;
if (!mRenderEngine->ReadOutputFrameRgba(width, height, bottomUpPixels))
return;
std::vector<unsigned char> topDownPixels(bottomUpPixels.size()); std::vector<unsigned char> topDownPixels(bottomUpPixels.size());
glBindFramebuffer(GL_READ_FRAMEBUFFER, mRenderer->OutputFramebuffer());
glReadBuffer(GL_COLOR_ATTACHMENT0);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, bottomUpPixels.data());
glPixelStorei(GL_PACK_ALIGNMENT, 4);
const std::size_t rowBytes = static_cast<std::size_t>(width) * 4; const std::size_t rowBytes = static_cast<std::size_t>(width) * 4;
for (unsigned y = 0; y < height; ++y) for (unsigned y = 0; y < height; ++y)
{ {
@@ -752,7 +728,7 @@ bool OpenGLComposite::ProcessRuntimePollResults()
return true; return true;
char compilerErrorMessage[1024] = {}; char compilerErrorMessage[1024] = {};
if (!mShaderPrograms->CommitPreparedLayerPrograms(readyBuild, mVideoIO->InputFrameWidth(), mVideoIO->InputFrameHeight(), sizeof(compilerErrorMessage), compilerErrorMessage)) if (!mRenderEngine->CommitPreparedLayerPrograms(readyBuild, mVideoBackend->InputFrameWidth(), mVideoBackend->InputFrameHeight(), sizeof(compilerErrorMessage), compilerErrorMessage))
{ {
mRuntimeStore->SetCompileStatus(false, compilerErrorMessage); mRuntimeStore->SetCompileStatus(false, compilerErrorMessage);
mUseCommittedLayerStates = true; mUseCommittedLayerStates = true;
@@ -762,10 +738,10 @@ bool OpenGLComposite::ProcessRuntimePollResults()
} }
mUseCommittedLayerStates = false; mUseCommittedLayerStates = false;
mCachedLayerRenderStates = mShaderPrograms->CommittedLayerStates(); mCachedLayerRenderStates = mRenderEngine->CommittedLayerStates();
mShaderPrograms->ResetTemporalHistoryState(); mRenderEngine->ResetTemporalHistoryState();
if (!mPreserveFeedbackOnNextShaderBuild) if (!mPreserveFeedbackOnNextShaderBuild)
mShaderPrograms->ResetShaderFeedbackState(); mRenderEngine->ResetShaderFeedbackState();
mPreserveFeedbackOnNextShaderBuild = false; mPreserveFeedbackOnNextShaderBuild = false;
broadcastRuntimeState(); broadcastRuntimeState();
return true; return true;
@@ -780,13 +756,13 @@ bool OpenGLComposite::ProcessRuntimePollResults()
void OpenGLComposite::RequestShaderBuild() void OpenGLComposite::RequestShaderBuild()
{ {
if (!mShaderBuildQueue || !mVideoIO) if (!mShaderBuildQueue || !mVideoBackend)
return; return;
mUseCommittedLayerStates = true; mUseCommittedLayerStates = true;
if (mRuntimeHost) if (mRuntimeHost)
mRuntimeStore->ClearReloadRequest(); mRuntimeStore->ClearReloadRequest();
mShaderBuildQueue->RequestBuild(mVideoIO->InputFrameWidth(), mVideoIO->InputFrameHeight()); mShaderBuildQueue->RequestBuild(mVideoBackend->InputFrameWidth(), mVideoBackend->InputFrameHeight());
} }
void OpenGLComposite::broadcastRuntimeState() void OpenGLComposite::broadcastRuntimeState()
@@ -797,8 +773,8 @@ void OpenGLComposite::broadcastRuntimeState()
void OpenGLComposite::resetTemporalHistoryState() void OpenGLComposite::resetTemporalHistoryState()
{ {
mShaderPrograms->ResetTemporalHistoryState(); mRenderEngine->ResetTemporalHistoryState();
mShaderPrograms->ResetShaderFeedbackState(); mRenderEngine->ResetShaderFeedbackState();
} }
bool OpenGLComposite::CheckOpenGLExtensions() bool OpenGLComposite::CheckOpenGLExtensions()

View File

@@ -12,7 +12,6 @@
#include <comutil.h> #include <comutil.h>
#include "GLExtensions.h" #include "GLExtensions.h"
#include "OpenGLRenderer.h"
#include "RuntimeHost.h" #include "RuntimeHost.h"
#include "RuntimeSnapshotProvider.h" #include "RuntimeSnapshotProvider.h"
#include "RuntimeStore.h" #include "RuntimeStore.h"
@@ -27,13 +26,10 @@
#include <deque> #include <deque>
#include <chrono> #include <chrono>
class VideoIODevice; class RenderEngine;
class OpenGLVideoIOBridge;
class OpenGLRenderPass;
class OpenGLRenderPipeline;
class OpenGLShaderPrograms;
class RuntimeServices; class RuntimeServices;
class ShaderBuildQueue; class ShaderBuildQueue;
class VideoBackend;
class OpenGLComposite class OpenGLComposite
@@ -74,7 +70,6 @@ private:
void resizeWindow(int width, int height); void resizeWindow(int width, int height);
bool CheckOpenGLExtensions(); bool CheckOpenGLExtensions();
void PublishVideoIOStatus(const std::string& statusMessage); void PublishVideoIOStatus(const std::string& statusMessage);
using LayerProgram = OpenGLRenderer::LayerProgram;
struct OscOverlayState struct OscOverlayState
{ {
std::string layerKey; std::string layerKey;
@@ -94,17 +89,13 @@ private:
HGLRC hGLRC; HGLRC hGLRC;
CRITICAL_SECTION pMutex; CRITICAL_SECTION pMutex;
std::unique_ptr<VideoIODevice> mVideoIO;
std::unique_ptr<OpenGLRenderer> mRenderer;
std::unique_ptr<RuntimeHost> mRuntimeHost; std::unique_ptr<RuntimeHost> mRuntimeHost;
std::unique_ptr<RuntimeStore> mRuntimeStore; std::unique_ptr<RuntimeStore> mRuntimeStore;
std::unique_ptr<RuntimeSnapshotProvider> mRuntimeSnapshotProvider; std::unique_ptr<RuntimeSnapshotProvider> mRuntimeSnapshotProvider;
std::unique_ptr<OpenGLVideoIOBridge> mVideoIOBridge; std::unique_ptr<RenderEngine> mRenderEngine;
std::unique_ptr<OpenGLRenderPass> mRenderPass;
std::unique_ptr<OpenGLRenderPipeline> mRenderPipeline;
std::unique_ptr<OpenGLShaderPrograms> mShaderPrograms;
std::unique_ptr<ShaderBuildQueue> mShaderBuildQueue; std::unique_ptr<ShaderBuildQueue> mShaderBuildQueue;
std::unique_ptr<RuntimeServices> mRuntimeServices; std::unique_ptr<RuntimeServices> mRuntimeServices;
std::unique_ptr<VideoBackend> mVideoBackend;
std::vector<RuntimeRenderState> mCachedLayerRenderStates; std::vector<RuntimeRenderState> mCachedLayerRenderStates;
uint64_t mCachedRenderStateVersion = 0; uint64_t mCachedRenderStateVersion = 0;
uint64_t mCachedParameterStateVersion = 0; uint64_t mCachedParameterStateVersion = 0;

View File

@@ -0,0 +1,180 @@
#include "RenderEngine.h"
#include <gl/gl.h>
RenderEngine::RenderEngine(
RuntimeHost& runtimeHost,
RuntimeSnapshotProvider& runtimeSnapshotProvider,
CRITICAL_SECTION& mutex,
HDC hdc,
HGLRC hglrc,
RenderEffectCallback renderEffect,
ScreenshotCallback screenshotReady,
PreviewPaintCallback previewPaint) :
mRenderer(),
mRenderPass(mRenderer),
mRenderPipeline(mRenderer, runtimeHost, std::move(renderEffect), std::move(screenshotReady), std::move(previewPaint)),
mShaderPrograms(mRenderer, runtimeHost, runtimeSnapshotProvider),
mMutex(mutex),
mHdc(hdc),
mHglrc(hglrc)
{
}
RenderEngine::~RenderEngine()
{
mRenderer.DestroyResources();
}
bool RenderEngine::CompileDecodeShader(int errorMessageSize, char* errorMessage)
{
return mShaderPrograms.CompileDecodeShader(errorMessageSize, errorMessage);
}
bool RenderEngine::CompileOutputPackShader(int errorMessageSize, char* errorMessage)
{
return mShaderPrograms.CompileOutputPackShader(errorMessageSize, errorMessage);
}
bool RenderEngine::InitializeResources(
unsigned inputFrameWidth,
unsigned inputFrameHeight,
unsigned captureTextureWidth,
unsigned outputFrameWidth,
unsigned outputFrameHeight,
unsigned outputPackTextureWidth,
std::string& error)
{
return mRenderer.InitializeResources(
inputFrameWidth,
inputFrameHeight,
captureTextureWidth,
outputFrameWidth,
outputFrameHeight,
outputPackTextureWidth,
error);
}
bool RenderEngine::CompileLayerPrograms(unsigned inputFrameWidth, unsigned inputFrameHeight, int errorMessageSize, char* errorMessage)
{
return mShaderPrograms.CompileLayerPrograms(inputFrameWidth, inputFrameHeight, errorMessageSize, errorMessage);
}
bool RenderEngine::CommitPreparedLayerPrograms(const PreparedShaderBuild& preparedBuild, unsigned inputFrameWidth, unsigned inputFrameHeight, int errorMessageSize, char* errorMessage)
{
return mShaderPrograms.CommitPreparedLayerPrograms(preparedBuild, inputFrameWidth, inputFrameHeight, errorMessageSize, errorMessage);
}
const std::vector<RuntimeRenderState>& RenderEngine::CommittedLayerStates() const
{
return mShaderPrograms.CommittedLayerStates();
}
void RenderEngine::ResetTemporalHistoryState()
{
mShaderPrograms.ResetTemporalHistoryState();
}
void RenderEngine::ResetShaderFeedbackState()
{
mShaderPrograms.ResetShaderFeedbackState();
}
void RenderEngine::ResizeView(int width, int height)
{
mRenderer.ResizeView(width, height);
}
bool RenderEngine::TryPresentToWindow(unsigned outputFrameWidth, unsigned outputFrameHeight)
{
if (!TryEnterCriticalSection(&mMutex))
return false;
mRenderer.PresentToWindow(mHdc, outputFrameWidth, outputFrameHeight);
LeaveCriticalSection(&mMutex);
return true;
}
bool RenderEngine::TryUploadInputFrame(const VideoIOFrame& inputFrame, const VideoIOState& videoState)
{
if (inputFrame.hasNoInputSource || inputFrame.bytes == nullptr)
return true;
const long textureSize = inputFrame.rowBytes * static_cast<long>(inputFrame.height);
if (!TryEnterCriticalSection(&mMutex))
return false;
wglMakeCurrent(mHdc, mHglrc);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mRenderer.TextureUploadBuffer());
glBufferData(GL_PIXEL_UNPACK_BUFFER, textureSize, inputFrame.bytes, GL_DYNAMIC_DRAW);
glBindTexture(GL_TEXTURE_2D, mRenderer.CaptureTexture());
if (inputFrame.pixelFormat == VideoIOPixelFormat::V210)
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, videoState.captureTextureWidth, videoState.inputFrameSize.height, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
else
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, videoState.captureTextureWidth, videoState.inputFrameSize.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
wglMakeCurrent(NULL, NULL);
LeaveCriticalSection(&mMutex);
return true;
}
bool RenderEngine::RenderOutputFrame(const RenderPipelineFrameContext& context, VideoIOOutputFrame& outputFrame)
{
EnterCriticalSection(&mMutex);
wglMakeCurrent(mHdc, mHglrc);
const bool rendered = mRenderPipeline.RenderFrame(context, outputFrame);
wglMakeCurrent(NULL, NULL);
LeaveCriticalSection(&mMutex);
return rendered;
}
void RenderEngine::RenderLayerStack(
bool hasInputSource,
const std::vector<RuntimeRenderState>& layerStates,
unsigned inputFrameWidth,
unsigned inputFrameHeight,
unsigned captureTextureWidth,
VideoIOPixelFormat inputPixelFormat,
unsigned historyCap)
{
mRenderPass.Render(
hasInputSource,
layerStates,
inputFrameWidth,
inputFrameHeight,
captureTextureWidth,
inputPixelFormat,
historyCap,
[this](const RuntimeRenderState& state, OpenGLRenderer::LayerProgram::TextBinding& textBinding, std::string& error) {
return mShaderPrograms.UpdateTextBindingTexture(state, textBinding, error);
},
[this](const RuntimeRenderState& state, unsigned availableSourceHistoryLength, unsigned availableTemporalHistoryLength, bool feedbackAvailable) {
return mShaderPrograms.UpdateGlobalParamsBuffer(state, availableSourceHistoryLength, availableTemporalHistoryLength, feedbackAvailable);
});
}
bool RenderEngine::ReadOutputFrameRgba(unsigned width, unsigned height, std::vector<unsigned char>& bottomUpPixels)
{
if (width == 0 || height == 0)
return false;
EnterCriticalSection(&mMutex);
wglMakeCurrent(mHdc, mHglrc);
bottomUpPixels.resize(static_cast<std::size_t>(width) * height * 4);
glBindFramebuffer(GL_READ_FRAMEBUFFER, mRenderer.OutputFramebuffer());
glReadBuffer(GL_COLOR_ATTACHMENT0);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, bottomUpPixels.data());
glPixelStorei(GL_PACK_ALIGNMENT, 4);
wglMakeCurrent(NULL, NULL);
LeaveCriticalSection(&mMutex);
return true;
}

View File

@@ -0,0 +1,72 @@
#pragma once
#include "OpenGLRenderPass.h"
#include "OpenGLRenderPipeline.h"
#include "OpenGLRenderer.h"
#include "OpenGLShaderPrograms.h"
#include "RuntimeHost.h"
#include "RuntimeSnapshotProvider.h"
#include <windows.h>
#include <functional>
#include <string>
#include <vector>
class RenderEngine
{
public:
using RenderEffectCallback = std::function<void()>;
using ScreenshotCallback = std::function<void()>;
using PreviewPaintCallback = std::function<void()>;
RenderEngine(
RuntimeHost& runtimeHost,
RuntimeSnapshotProvider& runtimeSnapshotProvider,
CRITICAL_SECTION& mutex,
HDC hdc,
HGLRC hglrc,
RenderEffectCallback renderEffect,
ScreenshotCallback screenshotReady,
PreviewPaintCallback previewPaint);
~RenderEngine();
bool CompileDecodeShader(int errorMessageSize, char* errorMessage);
bool CompileOutputPackShader(int errorMessageSize, char* errorMessage);
bool InitializeResources(
unsigned inputFrameWidth,
unsigned inputFrameHeight,
unsigned captureTextureWidth,
unsigned outputFrameWidth,
unsigned outputFrameHeight,
unsigned outputPackTextureWidth,
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);
const std::vector<RuntimeRenderState>& CommittedLayerStates() const;
void ResetTemporalHistoryState();
void ResetShaderFeedbackState();
void ResizeView(int width, int height);
bool TryPresentToWindow(unsigned outputFrameWidth, unsigned outputFrameHeight);
bool TryUploadInputFrame(const VideoIOFrame& inputFrame, const VideoIOState& videoState);
bool RenderOutputFrame(const RenderPipelineFrameContext& context, VideoIOOutputFrame& outputFrame);
void RenderLayerStack(
bool hasInputSource,
const std::vector<RuntimeRenderState>& layerStates,
unsigned inputFrameWidth,
unsigned inputFrameHeight,
unsigned captureTextureWidth,
VideoIOPixelFormat inputPixelFormat,
unsigned historyCap);
bool ReadOutputFrameRgba(unsigned width, unsigned height, std::vector<unsigned char>& bottomUpPixels);
private:
OpenGLRenderer mRenderer;
OpenGLRenderPass mRenderPass;
OpenGLRenderPipeline mRenderPipeline;
OpenGLShaderPrograms mShaderPrograms;
CRITICAL_SECTION& mMutex;
HDC mHdc;
HGLRC mHglrc;
};

View File

@@ -1,26 +1,17 @@
#include "OpenGLVideoIOBridge.h" #include "OpenGLVideoIOBridge.h"
#include "OpenGLRenderer.h" #include "HealthTelemetry.h"
#include "RuntimeHost.h" #include "RenderEngine.h"
#include <chrono> #include <chrono>
#include <gl/gl.h>
OpenGLVideoIOBridge::OpenGLVideoIOBridge( OpenGLVideoIOBridge::OpenGLVideoIOBridge(
VideoIODevice& videoIO, VideoIODevice& videoIO,
OpenGLRenderer& renderer, RenderEngine& renderEngine,
OpenGLRenderPipeline& renderPipeline, HealthTelemetry& healthTelemetry) :
RuntimeHost& runtimeHost,
CRITICAL_SECTION& mutex,
HDC hdc,
HGLRC hglrc) :
mVideoIO(videoIO), mVideoIO(videoIO),
mRenderer(renderer), mRenderEngine(renderEngine),
mRenderPipeline(renderPipeline), mHealthTelemetry(healthTelemetry)
mRuntimeHost(runtimeHost),
mMutex(mutex),
mHdc(hdc),
mHglrc(hglrc)
{ {
} }
@@ -46,7 +37,7 @@ void OpenGLVideoIOBridge::RecordFramePacing(VideoIOCompletionResult completionRe
else if (completionResult == VideoIOCompletionResult::Flushed) else if (completionResult == VideoIOCompletionResult::Flushed)
++mFlushedFrameCount; ++mFlushedFrameCount;
mRuntimeHost.GetHealthTelemetry().TryRecordFramePacingStats( mHealthTelemetry.TryRecordFramePacingStats(
mCompletionIntervalMilliseconds, mCompletionIntervalMilliseconds,
mSmoothedCompletionIntervalMilliseconds, mSmoothedCompletionIntervalMilliseconds,
mMaxCompletionIntervalMilliseconds, mMaxCompletionIntervalMilliseconds,
@@ -58,38 +49,12 @@ void OpenGLVideoIOBridge::RecordFramePacing(VideoIOCompletionResult completionRe
void OpenGLVideoIOBridge::VideoFrameArrived(const VideoIOFrame& inputFrame) void OpenGLVideoIOBridge::VideoFrameArrived(const VideoIOFrame& inputFrame)
{ {
const VideoIOState& state = mVideoIO.State(); const VideoIOState& state = mVideoIO.State();
mRuntimeHost.GetHealthTelemetry().TryReportSignalStatus(!inputFrame.hasNoInputSource, state.inputFrameSize.width, state.inputFrameSize.height, state.inputDisplayModeName); mHealthTelemetry.TryReportSignalStatus(!inputFrame.hasNoInputSource, state.inputFrameSize.width, state.inputFrameSize.height, state.inputDisplayModeName);
if (inputFrame.hasNoInputSource || inputFrame.bytes == nullptr) if (inputFrame.hasNoInputSource || inputFrame.bytes == nullptr)
return; // don't transfer texture when there's no input return; // don't transfer texture when there's no input
const long textureSize = inputFrame.rowBytes * static_cast<long>(inputFrame.height); mRenderEngine.TryUploadInputFrame(inputFrame, state);
// Never let input upload stall the playout/render callback. If the GL bridge
// is busy producing an output frame, skip this upload and use the next input.
if (!TryEnterCriticalSection(&mMutex))
return;
wglMakeCurrent(mHdc, mHglrc); // make OpenGL context current in this thread
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mRenderer.TextureUploadBuffer());
glBufferData(GL_PIXEL_UNPACK_BUFFER, textureSize, inputFrame.bytes, GL_DYNAMIC_DRAW);
glBindTexture(GL_TEXTURE_2D, mRenderer.CaptureTexture());
// NULL for last arg indicates use current GL_PIXEL_UNPACK_BUFFER target as texture data.
if (inputFrame.pixelFormat == VideoIOPixelFormat::V210)
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, state.captureTextureWidth, state.inputFrameSize.height, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
else
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, state.captureTextureWidth, state.inputFrameSize.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
wglMakeCurrent(NULL, NULL);
LeaveCriticalSection(&mMutex);
} }
void OpenGLVideoIOBridge::PlayoutFrameCompleted(const VideoIOCompletion& completion) void OpenGLVideoIOBridge::PlayoutFrameCompleted(const VideoIOCompletion& completion)
@@ -104,15 +69,7 @@ void OpenGLVideoIOBridge::PlayoutFrameCompleted(const VideoIOCompletion& complet
frameContext.videoState = state; frameContext.videoState = state;
frameContext.completion = completion; frameContext.completion = completion;
EnterCriticalSection(&mMutex); mRenderEngine.RenderOutputFrame(frameContext, outputFrame);
// make GL context current in this thread
wglMakeCurrent(mHdc, mHglrc);
mRenderPipeline.RenderFrame(frameContext, outputFrame);
wglMakeCurrent(NULL, NULL);
LeaveCriticalSection(&mMutex);
mVideoIO.EndOutputFrame(outputFrame); mVideoIO.EndOutputFrame(outputFrame);

View File

@@ -2,24 +2,19 @@
#include "OpenGLRenderPipeline.h" #include "OpenGLRenderPipeline.h"
#include <windows.h>
#include <chrono> #include <chrono>
#include <cstdint> #include <cstdint>
class RuntimeHost; class HealthTelemetry;
class RenderEngine;
class OpenGLVideoIOBridge class OpenGLVideoIOBridge
{ {
public: public:
OpenGLVideoIOBridge( OpenGLVideoIOBridge(
VideoIODevice& videoIO, VideoIODevice& videoIO,
OpenGLRenderer& renderer, RenderEngine& renderEngine,
OpenGLRenderPipeline& renderPipeline, HealthTelemetry& healthTelemetry);
RuntimeHost& runtimeHost,
CRITICAL_SECTION& mutex,
HDC hdc,
HGLRC hglrc);
void VideoFrameArrived(const VideoIOFrame& inputFrame); void VideoFrameArrived(const VideoIOFrame& inputFrame);
void PlayoutFrameCompleted(const VideoIOCompletion& completion); void PlayoutFrameCompleted(const VideoIOCompletion& completion);
@@ -28,12 +23,8 @@ private:
void RecordFramePacing(VideoIOCompletionResult completionResult); void RecordFramePacing(VideoIOCompletionResult completionResult);
VideoIODevice& mVideoIO; VideoIODevice& mVideoIO;
OpenGLRenderer& mRenderer; RenderEngine& mRenderEngine;
OpenGLRenderPipeline& mRenderPipeline; HealthTelemetry& mHealthTelemetry;
RuntimeHost& mRuntimeHost;
CRITICAL_SECTION& mMutex;
HDC mHdc;
HGLRC mHglrc;
std::chrono::steady_clock::time_point mLastPlayoutCompletionTime; std::chrono::steady_clock::time_point mLastPlayoutCompletionTime;
double mCompletionIntervalMilliseconds = 0.0; double mCompletionIntervalMilliseconds = 0.0;
double mSmoothedCompletionIntervalMilliseconds = 0.0; double mSmoothedCompletionIntervalMilliseconds = 0.0;

View File

@@ -0,0 +1,175 @@
#include "VideoBackend.h"
#include "DeckLinkSession.h"
#include "OpenGLVideoIOBridge.h"
#include "RenderEngine.h"
#include "HealthTelemetry.h"
VideoBackend::VideoBackend(RenderEngine& renderEngine, HealthTelemetry& healthTelemetry) :
mVideoIODevice(std::make_unique<DeckLinkSession>()),
mBridge(std::make_unique<OpenGLVideoIOBridge>(*mVideoIODevice, renderEngine, healthTelemetry))
{
}
VideoBackend::~VideoBackend()
{
ReleaseResources();
}
void VideoBackend::ReleaseResources()
{
if (mVideoIODevice)
mVideoIODevice->ReleaseResources();
}
bool VideoBackend::DiscoverDevicesAndModes(const VideoFormatSelection& videoModes, std::string& error)
{
return mVideoIODevice->DiscoverDevicesAndModes(videoModes, error);
}
bool VideoBackend::SelectPreferredFormats(const VideoFormatSelection& videoModes, bool outputAlphaRequired, std::string& error)
{
return mVideoIODevice->SelectPreferredFormats(videoModes, outputAlphaRequired, error);
}
bool VideoBackend::ConfigureInput(const VideoFormat& inputVideoMode, std::string& error)
{
return mVideoIODevice->ConfigureInput(
[this](const VideoIOFrame& frame) { mBridge->VideoFrameArrived(frame); },
inputVideoMode,
error);
}
bool VideoBackend::ConfigureOutput(const VideoFormat& outputVideoMode, bool externalKeyingEnabled, std::string& error)
{
return mVideoIODevice->ConfigureOutput(
[this](const VideoIOCompletion& completion) { mBridge->PlayoutFrameCompleted(completion); },
outputVideoMode,
externalKeyingEnabled,
error);
}
bool VideoBackend::Start()
{
return mVideoIODevice->Start();
}
bool VideoBackend::Stop()
{
return mVideoIODevice->Stop();
}
const VideoIOState& VideoBackend::State() const
{
return mVideoIODevice->State();
}
VideoIOState& VideoBackend::MutableState()
{
return mVideoIODevice->MutableState();
}
bool VideoBackend::BeginOutputFrame(VideoIOOutputFrame& frame)
{
return mVideoIODevice->BeginOutputFrame(frame);
}
void VideoBackend::EndOutputFrame(VideoIOOutputFrame& frame)
{
mVideoIODevice->EndOutputFrame(frame);
}
bool VideoBackend::ScheduleOutputFrame(const VideoIOOutputFrame& frame)
{
return mVideoIODevice->ScheduleOutputFrame(frame);
}
void VideoBackend::AccountForCompletionResult(VideoIOCompletionResult result)
{
mVideoIODevice->AccountForCompletionResult(result);
}
bool VideoBackend::HasInputDevice() const
{
return mVideoIODevice->HasInputDevice();
}
bool VideoBackend::HasInputSource() const
{
return mVideoIODevice->HasInputSource();
}
unsigned VideoBackend::InputFrameWidth() const
{
return mVideoIODevice->InputFrameWidth();
}
unsigned VideoBackend::InputFrameHeight() const
{
return mVideoIODevice->InputFrameHeight();
}
unsigned VideoBackend::OutputFrameWidth() const
{
return mVideoIODevice->OutputFrameWidth();
}
unsigned VideoBackend::OutputFrameHeight() const
{
return mVideoIODevice->OutputFrameHeight();
}
unsigned VideoBackend::CaptureTextureWidth() const
{
return mVideoIODevice->CaptureTextureWidth();
}
unsigned VideoBackend::OutputPackTextureWidth() const
{
return mVideoIODevice->OutputPackTextureWidth();
}
VideoIOPixelFormat VideoBackend::InputPixelFormat() const
{
return mVideoIODevice->InputPixelFormat();
}
const std::string& VideoBackend::InputDisplayModeName() const
{
return mVideoIODevice->InputDisplayModeName();
}
const std::string& VideoBackend::OutputModelName() const
{
return mVideoIODevice->OutputModelName();
}
bool VideoBackend::SupportsInternalKeying() const
{
return mVideoIODevice->SupportsInternalKeying();
}
bool VideoBackend::SupportsExternalKeying() const
{
return mVideoIODevice->SupportsExternalKeying();
}
bool VideoBackend::KeyerInterfaceAvailable() const
{
return mVideoIODevice->KeyerInterfaceAvailable();
}
bool VideoBackend::ExternalKeyingActive() const
{
return mVideoIODevice->ExternalKeyingActive();
}
const std::string& VideoBackend::StatusMessage() const
{
return mVideoIODevice->StatusMessage();
}
void VideoBackend::SetStatusMessage(const std::string& message)
{
mVideoIODevice->SetStatusMessage(message);
}

View File

@@ -0,0 +1,55 @@
#pragma once
#include "VideoIOTypes.h"
#include <memory>
#include <string>
class HealthTelemetry;
class OpenGLVideoIOBridge;
class RenderEngine;
class VideoIODevice;
class VideoBackend
{
public:
VideoBackend(RenderEngine& renderEngine, HealthTelemetry& healthTelemetry);
~VideoBackend();
void ReleaseResources();
bool DiscoverDevicesAndModes(const VideoFormatSelection& videoModes, std::string& error);
bool SelectPreferredFormats(const VideoFormatSelection& videoModes, bool outputAlphaRequired, std::string& error);
bool ConfigureInput(const VideoFormat& inputVideoMode, std::string& error);
bool ConfigureOutput(const VideoFormat& outputVideoMode, bool externalKeyingEnabled, std::string& error);
bool Start();
bool Stop();
const VideoIOState& State() const;
VideoIOState& MutableState();
bool BeginOutputFrame(VideoIOOutputFrame& frame);
void EndOutputFrame(VideoIOOutputFrame& frame);
bool ScheduleOutputFrame(const VideoIOOutputFrame& frame);
void AccountForCompletionResult(VideoIOCompletionResult result);
bool HasInputDevice() const;
bool HasInputSource() const;
unsigned InputFrameWidth() const;
unsigned InputFrameHeight() const;
unsigned OutputFrameWidth() const;
unsigned OutputFrameHeight() const;
unsigned CaptureTextureWidth() const;
unsigned OutputPackTextureWidth() const;
VideoIOPixelFormat InputPixelFormat() const;
const std::string& InputDisplayModeName() const;
const std::string& OutputModelName() const;
bool SupportsInternalKeying() const;
bool SupportsExternalKeying() const;
bool KeyerInterfaceAvailable() const;
bool ExternalKeyingActive() const;
const std::string& StatusMessage() const;
void SetStatusMessage(const std::string& message);
private:
std::unique_ptr<VideoIODevice> mVideoIODevice;
std::unique_ptr<OpenGLVideoIOBridge> mBridge;
};