Timing and saftey pass
All checks were successful
CI / React UI Build (push) Successful in 11s
CI / Native Windows Build And Tests (push) Successful in 1m33s
CI / Windows Release Package (push) Successful in 2m23s

This commit is contained in:
2026-05-06 14:35:41 +10:00
parent b2f4d6677c
commit 70be7312b8
9 changed files with 149 additions and 14 deletions

View File

@@ -294,13 +294,20 @@ void OpenGLComposite::renderEffect()
if (mUseCommittedLayerStates)
{
layerStates = mShaderPrograms->CommittedLayerStates();
if (mRuntimeHost)
mRuntimeHost->RefreshDynamicRenderStateFields(layerStates);
}
else if (mRuntimeHost)
{
if (mRuntimeHost->TryGetLayerRenderStates(mDeckLink->InputFrameWidth(), mDeckLink->InputFrameHeight(), layerStates))
{
mCachedLayerRenderStates = layerStates;
}
else
{
layerStates = mCachedLayerRenderStates;
mRuntimeHost->RefreshDynamicRenderStateFields(layerStates);
}
}
const unsigned historyCap = mRuntimeHost ? mRuntimeHost->GetMaxTemporalHistoryFrames() : 0;
mRenderPass->Render(

View File

@@ -98,6 +98,8 @@ void OpenGLDeckLinkBridge::VideoFrameArrived(IDeckLinkVideoInputFrame* inputFram
else
{
// Use a straightforward texture buffer
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mRenderer.UnpinnedTextureBuffer());
glBufferData(GL_PIXEL_UNPACK_BUFFER, textureSize, videoPixels, GL_DYNAMIC_DRAW);
glBindTexture(GL_TEXTURE_2D, mRenderer.CaptureTexture());
@@ -174,13 +176,14 @@ void OpenGLDeckLinkBridge::PlayoutFrameCompleted(IDeckLinkVideoFrame* completedF
if (!mDeckLink.TransferPlayoutFrame(pFrame, mRenderer.OutputTexture()))
OutputDebugStringA("Playback: transferFrame() failed\n");
mPaint();
// Wait for transfer to system memory to complete
mDeckLink.WaitForPlayoutTransferComplete(pFrame);
mPaint();
}
else
{
glPixelStorei(GL_PACK_ALIGNMENT, 4);
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
glBindFramebuffer(GL_READ_FRAMEBUFFER, mRenderer.OutputFramebuffer());
glReadPixels(0, 0, mDeckLink.OutputFrameWidth(), mDeckLink.OutputFrameHeight(), GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pFrame);
mPaint();

View File

@@ -23,6 +23,7 @@ void OpenGLRenderPass::Render(
VideoFrameTransfer::beginTextureInUse(VideoFrameTransfer::CPUtoGPU);
}
glDisable(GL_SCISSOR_TEST);
glDisable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
if (hasInputSource)

View File

@@ -143,6 +143,7 @@ void OpenGLRenderer::PresentToWindow(HDC hdc, unsigned outputFrameWidth, unsigne
glBindFramebuffer(GL_READ_FRAMEBUFFER, mOutputFrameBuf);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glDisable(GL_SCISSOR_TEST);
glViewport(0, 0, mViewWidth, mViewHeight);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);

View File

@@ -1274,16 +1274,11 @@ void RuntimeHost::SetFramePacingStatsLocked(double completionIntervalMillisecond
void RuntimeHost::AdvanceFrame()
{
std::lock_guard<std::mutex> lock(mMutex);
++mFrameCounter;
}
bool RuntimeHost::TryAdvanceFrame()
{
std::unique_lock<std::mutex> lock(mMutex, std::try_to_lock);
if (!lock.owns_lock())
return false;
++mFrameCounter;
return true;
}
@@ -1345,9 +1340,23 @@ bool RuntimeHost::TryGetLayerRenderStates(unsigned outputWidth, unsigned outputH
return true;
}
void RuntimeHost::BuildLayerRenderStatesLocked(unsigned outputWidth, unsigned outputHeight, std::vector<RuntimeRenderState>& states) const
void RuntimeHost::RefreshDynamicRenderStateFields(std::vector<RuntimeRenderState>& states) const
{
const RuntimeClockSnapshot clock = GetRuntimeClockSnapshot();
const double timeSeconds = std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::steady_clock::now() - mStartTime).count();
const double frameCount = static_cast<double>(mFrameCounter.load(std::memory_order_relaxed));
for (RuntimeRenderState& state : states)
{
state.timeSeconds = timeSeconds;
state.utcTimeSeconds = clock.utcTimeSeconds;
state.utcOffsetSeconds = clock.utcOffsetSeconds;
state.frameCount = frameCount;
}
}
void RuntimeHost::BuildLayerRenderStatesLocked(unsigned outputWidth, unsigned outputHeight, std::vector<RuntimeRenderState>& states) const
{
for (const LayerPersistentState& layer : mPersistentState.layers)
{
auto shaderIt = mPackagesById.find(layer.shaderId);
@@ -1357,10 +1366,6 @@ void RuntimeHost::BuildLayerRenderStatesLocked(unsigned outputWidth, unsigned ou
RuntimeRenderState state;
state.layerId = layer.id;
state.shaderId = layer.shaderId;
state.timeSeconds = std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::steady_clock::now() - mStartTime).count();
state.utcTimeSeconds = clock.utcTimeSeconds;
state.utcOffsetSeconds = clock.utcOffsetSeconds;
state.frameCount = static_cast<double>(mFrameCounter);
state.mixAmount = 1.0;
state.bypass = layer.bypass ? 1.0 : 0.0;
state.inputWidth = mSignalWidth;
@@ -1386,6 +1391,8 @@ void RuntimeHost::BuildLayerRenderStatesLocked(unsigned outputWidth, unsigned ou
states.push_back(state);
}
RefreshDynamicRenderStateFields(states);
}
std::string RuntimeHost::BuildStateJson() const

View File

@@ -3,6 +3,7 @@
#include "RuntimeJson.h"
#include "ShaderTypes.h"
#include <atomic>
#include <chrono>
#include <filesystem>
#include <map>
@@ -50,6 +51,7 @@ public:
bool BuildLayerFragmentShaderSource(const std::string& layerId, std::string& fragmentShaderSource, std::string& error);
std::vector<RuntimeRenderState> GetLayerRenderStates(unsigned outputWidth, unsigned outputHeight) const;
bool TryGetLayerRenderStates(unsigned outputWidth, unsigned outputHeight, std::vector<RuntimeRenderState>& states) const;
void RefreshDynamicRenderStateFields(std::vector<RuntimeRenderState>& states) const;
std::string BuildStateJson() const;
const std::filesystem::path& GetRepoRoot() const { return mRepoRoot; }
@@ -173,6 +175,6 @@ private:
bool mAutoReloadEnabled;
std::chrono::steady_clock::time_point mStartTime;
std::chrono::steady_clock::time_point mLastScanTime;
uint64_t mFrameCounter;
std::atomic<uint64_t> mFrameCounter;
uint64_t mNextLayerId;
};