temporal effects

This commit is contained in:
2026-05-02 19:23:03 +10:00
parent 2a0eea936d
commit 1d9bf151ce
13 changed files with 786 additions and 7 deletions

View File

@@ -45,6 +45,8 @@
#include <algorithm>
#include <cstdint>
#include <cstring>
#include <set>
#include <sstream>
#include <string>
#include <vector>
@@ -55,6 +57,7 @@ DEFINE_GUID(IID_PinnedMemoryAllocator,
namespace
{
constexpr GLuint kDecodedVideoTextureUnit = 1;
constexpr GLuint kSourceHistoryTextureUnitBase = 2;
constexpr GLuint kPackedVideoTextureUnit = 2;
constexpr GLuint kGlobalParamsBindingPoint = 0;
const char* kDisplayModeName = "1080p59.94";
@@ -149,23 +152,34 @@ void AppendStd140Vec4(std::vector<unsigned char>& buffer, float x, float y, floa
OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) :
hGLWnd(hWnd), hGLDC(hDC), hGLRC(hRC),
mCaptureDelegate(NULL), mPlayoutDelegate(NULL),
mDLInput(NULL), mDLOutput(NULL),
mDLInput(NULL), mDLOutput(NULL), mDLKeyer(NULL),
mPlayoutAllocator(NULL),
mFrameWidth(0), mFrameHeight(0),
mHasNoInputSource(true),
mDeckLinkSupportsInternalKeying(false),
mDeckLinkSupportsExternalKeying(false),
mDeckLinkKeyerInterfaceAvailable(false),
mDeckLinkExternalKeyingActive(false),
mFastTransferExtensionAvailable(false),
mCaptureTexture(0),
mDecodedTexture(0),
mLayerTempTexture(0),
mFBOTexture(0),
mUnpinnedTextureBuffer(0),
mDecodeFrameBuf(0),
mLayerTempFrameBuf(0),
mIdFrameBuf(0),
mIdColorBuf(0),
mIdDepthBuf(0),
mFullscreenVAO(0),
mGlobalParamsUBO(0),
mDecodeProgram(0),
mDecodeVertexShader(0),
mDecodeFragmentShader(0),
mGlobalParamsUBOSize(0)
mGlobalParamsUBOSize(0),
mViewWidth(0),
mViewHeight(0),
mTemporalHistoryNeedsReset(true)
{
InitializeCriticalSection(&pMutex);
mRuntimeHost = std::make_unique<RuntimeHost>();
@@ -203,6 +217,13 @@ OpenGLComposite::~OpenGLComposite()
if (mDLOutput != NULL)
{
if (mDLKeyer != NULL)
{
mDLKeyer->Disable();
mDLKeyer->Release();
mDLKeyer = NULL;
}
mDLOutput->SetScheduledFrameCompletionCallback(NULL);
mDLOutput->Release();
@@ -246,6 +267,7 @@ OpenGLComposite::~OpenGLComposite()
if (mUnpinnedTextureBuffer != 0)
glDeleteBuffers(1, &mUnpinnedTextureBuffer);
destroyTemporalHistoryResources();
destroyLayerPrograms();
destroyDecodeShaderProgram();
if (mControlServer)
@@ -276,6 +298,9 @@ bool OpenGLComposite::InitDeckLink()
while (pDLIterator->Next(&pDL) == S_OK)
{
int64_t duplexMode;
bool supportsInternalKeying = false;
bool supportsExternalKeying = false;
std::string modelName;
if (result = pDL->QueryInterface(IID_IDeckLinkProfileAttributes, (void**)&deckLinkAttributes) != S_OK)
{
@@ -286,6 +311,20 @@ bool OpenGLComposite::InitDeckLink()
}
result = deckLinkAttributes->GetInt(BMDDeckLinkDuplex, &duplexMode);
BOOL attributeFlag = FALSE;
if (deckLinkAttributes->GetFlag(BMDDeckLinkSupportsInternalKeying, &attributeFlag) == S_OK)
supportsInternalKeying = (attributeFlag != FALSE);
attributeFlag = FALSE;
if (deckLinkAttributes->GetFlag(BMDDeckLinkSupportsExternalKeying, &attributeFlag) == S_OK)
supportsExternalKeying = (attributeFlag != FALSE);
BSTR modelNameBstr = NULL;
if (deckLinkAttributes->GetString(BMDDeckLinkModelName, &modelNameBstr) == S_OK && modelNameBstr != NULL)
{
_bstr_t modelNameWrapper(modelNameBstr, false);
const char* modelNameChars = modelNameWrapper;
if (modelNameChars != NULL)
modelName = modelNameChars;
}
deckLinkAttributes->Release();
deckLinkAttributes = NULL;
@@ -306,6 +345,12 @@ bool OpenGLComposite::InitDeckLink()
{
if (pDL->QueryInterface(IID_IDeckLinkOutput, (void**)&mDLOutput) != S_OK)
mDLOutput = NULL;
else
{
mDeckLinkOutputModelName = modelName;
mDeckLinkSupportsInternalKeying = supportsInternalKeying;
mDeckLinkSupportsExternalKeying = supportsExternalKeying;
}
}
pDL->Release();
@@ -353,6 +398,21 @@ bool OpenGLComposite::InitDeckLink()
if (! InitOpenGLState())
goto error;
if (mRuntimeHost)
{
mDeckLinkStatusMessage = mDeckLinkOutputModelName.empty()
? "DeckLink output device selected."
: ("Selected output device: " + mDeckLinkOutputModelName);
mRuntimeHost->SetDeckLinkOutputStatus(
mDeckLinkOutputModelName,
mDeckLinkSupportsInternalKeying,
mDeckLinkSupportsExternalKeying,
mDeckLinkKeyerInterfaceAvailable,
mRuntimeHost->ExternalKeyingEnabled(),
mDeckLinkExternalKeyingActive,
mDeckLinkStatusMessage);
}
pDLDisplayMode->GetFrameRate(&mFrameDuration, &mFrameTimescale);
// Resize window to match video frame, but scale large formats down by half for viewing
@@ -392,6 +452,46 @@ bool OpenGLComposite::InitDeckLink()
if (mDLOutput->EnableVideoOutput(displayMode, bmdVideoOutputFlagDefault) != S_OK)
goto error;
if (mDLOutput->QueryInterface(IID_IDeckLinkKeyer, (void**)&mDLKeyer) == S_OK && mDLKeyer != NULL)
mDeckLinkKeyerInterfaceAvailable = true;
if (mRuntimeHost && mRuntimeHost->ExternalKeyingEnabled())
{
if (!mDeckLinkSupportsExternalKeying)
{
mDeckLinkStatusMessage = "External keying was requested, but the selected DeckLink output does not report external keying support.";
}
else if (!mDeckLinkKeyerInterfaceAvailable)
{
mDeckLinkStatusMessage = "External keying was requested, but the selected DeckLink output does not expose the IDeckLinkKeyer interface.";
}
else if (mDLKeyer->Enable(TRUE) != S_OK || mDLKeyer->SetLevel(255) != S_OK)
{
mDeckLinkStatusMessage = "External keying was requested, but enabling the DeckLink keyer failed.";
}
else
{
mDeckLinkExternalKeyingActive = true;
mDeckLinkStatusMessage = "External keying is active on the selected DeckLink output.";
}
}
else if (mDeckLinkSupportsExternalKeying)
{
mDeckLinkStatusMessage = "Selected DeckLink output supports external keying. Set enableExternalKeying to true in runtime-host.json to request it.";
}
if (mRuntimeHost)
{
mRuntimeHost->SetDeckLinkOutputStatus(
mDeckLinkOutputModelName,
mDeckLinkSupportsInternalKeying,
mDeckLinkSupportsExternalKeying,
mDeckLinkKeyerInterfaceAvailable,
mRuntimeHost->ExternalKeyingEnabled(),
mDeckLinkExternalKeyingActive,
mDeckLinkStatusMessage);
}
// Create a queue of 10 IDeckLinkMutableVideoFrame objects to use for scheduling output video frames.
// The ScheduledFrameCompleted() callback will immediately schedule a new frame using the next video frame from this queue.
for (int i = 0; i < 10; i++)
@@ -580,6 +680,7 @@ bool OpenGLComposite::InitOpenGLState()
MessageBoxA(NULL, compilerErrorMessage, "OpenGL shader failed to load or compile", MB_OK);
return false;
}
resetTemporalHistoryState();
glClearColor( 0.0f, 0.0f, 0.0f, 0.5f ); // Black background
glDisable(GL_DEPTH_TEST);
@@ -769,8 +870,13 @@ void OpenGLComposite::PlayoutFrameCompleted(IDeckLinkVideoFrame* completedFrame,
// Draw the effect output to the off-screen framebuffer.
const auto renderStartTime = std::chrono::steady_clock::now();
if (mFastTransferExtensionAvailable)
VideoFrameTransfer::beginTextureInUse(VideoFrameTransfer::GPUtoCPU);
glBindFramebuffer(GL_FRAMEBUFFER, mIdFrameBuf);
renderEffect();
glFlush();
if (mFastTransferExtensionAvailable)
VideoFrameTransfer::endTextureInUse(VideoFrameTransfer::GPUtoCPU);
const auto renderEndTime = std::chrono::steady_clock::now();
if (mRuntimeHost)
{
@@ -815,6 +921,7 @@ void OpenGLComposite::PlayoutFrameCompleted(IDeckLinkVideoFrame* completedFrame,
}
else
{
glBindFramebuffer(GL_READ_FRAMEBUFFER, mIdFrameBuf);
glReadPixels(0, 0, mFrameWidth, mFrameHeight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pFrame);
paintGL();
}
@@ -976,6 +1083,29 @@ bool OpenGLComposite::compileSingleLayerProgram(const RuntimeRenderState& state,
return false;
}
const GLuint globalParamsIndex = glGetUniformBlockIndex(newProgram, "GlobalParams");
if (globalParamsIndex != GL_INVALID_INDEX)
glUniformBlockBinding(newProgram, globalParamsIndex, kGlobalParamsBindingPoint);
const unsigned historyCap = mRuntimeHost ? mRuntimeHost->GetMaxTemporalHistoryFrames() : 0;
glUseProgram(newProgram);
const GLint videoInputLocation = glGetUniformLocation(newProgram, "gVideoInput");
if (videoInputLocation >= 0)
glUniform1i(videoInputLocation, static_cast<GLint>(kDecodedVideoTextureUnit));
for (unsigned index = 0; index < historyCap; ++index)
{
const std::string sourceSamplerName = "gSourceHistory" + std::to_string(index);
const GLint sourceSamplerLocation = glGetUniformLocation(newProgram, sourceSamplerName.c_str());
if (sourceSamplerLocation >= 0)
glUniform1i(sourceSamplerLocation, static_cast<GLint>(kSourceHistoryTextureUnitBase + index));
const std::string temporalSamplerName = "gTemporalHistory" + std::to_string(index);
const GLint temporalSamplerLocation = glGetUniformLocation(newProgram, temporalSamplerName.c_str());
if (temporalSamplerLocation >= 0)
glUniform1i(temporalSamplerLocation, static_cast<GLint>(kSourceHistoryTextureUnitBase + historyCap + index));
}
glUseProgram(0);
layerProgram.layerId = state.layerId;
layerProgram.shaderId = state.shaderId;
layerProgram.program = newProgram;
@@ -987,6 +1117,17 @@ bool OpenGLComposite::compileSingleLayerProgram(const RuntimeRenderState& state,
bool OpenGLComposite::compileLayerPrograms(int errorMessageSize, char* errorMessage)
{
const std::vector<RuntimeRenderState> layerStates = mRuntimeHost ? mRuntimeHost->GetLayerRenderStates(mFrameWidth, mFrameHeight) : std::vector<RuntimeRenderState>();
std::string temporalError;
if (!validateTemporalTextureUnitBudget(temporalError))
{
CopyErrorMessage(temporalError, errorMessageSize, errorMessage);
return false;
}
if (!ensureTemporalHistoryResources(layerStates, temporalError))
{
CopyErrorMessage(temporalError, errorMessageSize, errorMessage);
return false;
}
std::vector<LayerProgram> newPrograms;
newPrograms.reserve(layerStates.size());
@@ -1115,6 +1256,211 @@ void OpenGLComposite::destroyDecodeShaderProgram()
}
}
bool OpenGLComposite::validateTemporalTextureUnitBudget(std::string& error) const
{
const unsigned historyCap = mRuntimeHost ? mRuntimeHost->GetMaxTemporalHistoryFrames() : 0;
GLint maxTextureUnits = 0;
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
const unsigned requiredUnits = kSourceHistoryTextureUnitBase + historyCap + historyCap;
const unsigned availableUnits = maxTextureUnits > 0 ? static_cast<unsigned>(maxTextureUnits) : 0u;
if (requiredUnits > availableUnits)
{
std::ostringstream message;
message << "Temporal history cap requires " << requiredUnits
<< " fragment texture units, but only " << maxTextureUnits << " are available.";
error = message.str();
return false;
}
return true;
}
bool OpenGLComposite::createHistoryRing(HistoryRing& ring, unsigned effectiveLength, TemporalHistorySource historySource, std::string& error)
{
destroyHistoryRing(ring);
ring.effectiveLength = effectiveLength;
ring.historySource = historySource;
if (effectiveLength == 0)
return true;
ring.slots.resize(effectiveLength);
for (HistorySlot& slot : ring.slots)
{
glGenTextures(1, &slot.texture);
glBindTexture(GL_TEXTURE_2D, slot.texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, mFrameWidth, mFrameHeight, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
glGenFramebuffers(1, &slot.framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, slot.framebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, slot.texture, 0);
const GLenum framebufferStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (framebufferStatus != GL_FRAMEBUFFER_COMPLETE)
{
error = "Failed to initialize a temporal history framebuffer.";
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);
destroyHistoryRing(ring);
return false;
}
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);
return true;
}
void OpenGLComposite::destroyHistoryRing(HistoryRing& ring)
{
for (HistorySlot& slot : ring.slots)
{
if (slot.framebuffer != 0)
glDeleteFramebuffers(1, &slot.framebuffer);
if (slot.texture != 0)
glDeleteTextures(1, &slot.texture);
slot.framebuffer = 0;
slot.texture = 0;
}
ring.slots.clear();
ring.nextWriteIndex = 0;
ring.filledCount = 0;
ring.effectiveLength = 0;
ring.historySource = TemporalHistorySource::None;
}
void OpenGLComposite::destroyTemporalHistoryResources()
{
destroyHistoryRing(mSourceHistoryRing);
for (auto& historyEntry : mPreLayerHistoryByLayerId)
destroyHistoryRing(historyEntry.second);
mPreLayerHistoryByLayerId.clear();
}
void OpenGLComposite::resetTemporalHistoryState()
{
mSourceHistoryRing.nextWriteIndex = 0;
mSourceHistoryRing.filledCount = 0;
for (auto& historyEntry : mPreLayerHistoryByLayerId)
{
historyEntry.second.nextWriteIndex = 0;
historyEntry.second.filledCount = 0;
}
mTemporalHistoryNeedsReset = false;
}
bool OpenGLComposite::ensureTemporalHistoryResources(const std::vector<RuntimeRenderState>& layerStates, std::string& error)
{
const unsigned historyCap = mRuntimeHost ? mRuntimeHost->GetMaxTemporalHistoryFrames() : 0;
const bool sourceHistoryNeeded = std::any_of(layerStates.begin(), layerStates.end(),
[](const RuntimeRenderState& state) { return state.isTemporal && state.effectiveTemporalHistoryLength > 0; });
const unsigned sourceHistoryLength = sourceHistoryNeeded ? historyCap : 0;
if (mSourceHistoryRing.effectiveLength != sourceHistoryLength)
{
if (!createHistoryRing(mSourceHistoryRing, sourceHistoryLength, TemporalHistorySource::Source, error))
return false;
mTemporalHistoryNeedsReset = true;
}
std::set<std::string> requiredPreLayerIds;
for (const RuntimeRenderState& state : layerStates)
{
if (!state.isTemporal || state.temporalHistorySource != TemporalHistorySource::PreLayerInput)
continue;
requiredPreLayerIds.insert(state.layerId);
auto historyIt = mPreLayerHistoryByLayerId.find(state.layerId);
if (historyIt == mPreLayerHistoryByLayerId.end() || historyIt->second.effectiveLength != state.effectiveTemporalHistoryLength)
{
HistoryRing replacement;
if (!createHistoryRing(replacement, state.effectiveTemporalHistoryLength, TemporalHistorySource::PreLayerInput, error))
return false;
mPreLayerHistoryByLayerId[state.layerId] = std::move(replacement);
mTemporalHistoryNeedsReset = true;
}
}
for (auto it = mPreLayerHistoryByLayerId.begin(); it != mPreLayerHistoryByLayerId.end();)
{
if (requiredPreLayerIds.find(it->first) == requiredPreLayerIds.end())
{
destroyHistoryRing(it->second);
it = mPreLayerHistoryByLayerId.erase(it);
mTemporalHistoryNeedsReset = true;
}
else
{
++it;
}
}
if (mTemporalHistoryNeedsReset)
resetTemporalHistoryState();
return true;
}
void OpenGLComposite::pushFramebufferToHistoryRing(GLuint sourceFramebuffer, HistoryRing& ring)
{
if (ring.effectiveLength == 0 || ring.slots.empty())
return;
HistorySlot& targetSlot = ring.slots[ring.nextWriteIndex];
glBindFramebuffer(GL_READ_FRAMEBUFFER, sourceFramebuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, targetSlot.framebuffer);
glBlitFramebuffer(0, 0, mFrameWidth, mFrameHeight, 0, 0, mFrameWidth, mFrameHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
ring.nextWriteIndex = (ring.nextWriteIndex + 1) % ring.slots.size();
ring.filledCount = std::min<std::size_t>(ring.filledCount + 1, ring.slots.size());
}
GLuint OpenGLComposite::resolveHistoryTexture(const HistoryRing& ring, GLuint fallbackTexture, std::size_t framesAgo) const
{
if (ring.filledCount == 0 || ring.slots.empty())
return fallbackTexture;
const std::size_t clampedOffset = std::min<std::size_t>(framesAgo, ring.filledCount - 1);
const std::size_t newestIndex = (ring.nextWriteIndex + ring.slots.size() - 1) % ring.slots.size();
const std::size_t slotIndex = (newestIndex + ring.slots.size() - clampedOffset) % ring.slots.size();
return ring.slots[slotIndex].texture != 0 ? ring.slots[slotIndex].texture : fallbackTexture;
}
unsigned OpenGLComposite::sourceHistoryAvailableCount() const
{
return static_cast<unsigned>(mSourceHistoryRing.filledCount);
}
unsigned OpenGLComposite::temporalHistoryAvailableCountForLayer(const std::string& layerId) const
{
auto it = mPreLayerHistoryByLayerId.find(layerId);
if (it == mPreLayerHistoryByLayerId.end())
return 0;
return static_cast<unsigned>(it->second.filledCount);
}
void OpenGLComposite::bindHistorySamplers(const RuntimeRenderState& state, GLuint currentSourceTexture)
{
const unsigned historyCap = mRuntimeHost ? mRuntimeHost->GetMaxTemporalHistoryFrames() : 0;
for (unsigned index = 0; index < historyCap; ++index)
{
glActiveTexture(GL_TEXTURE0 + kSourceHistoryTextureUnitBase + index);
glBindTexture(GL_TEXTURE_2D, resolveHistoryTexture(mSourceHistoryRing, currentSourceTexture, index));
}
const GLuint temporalBase = kSourceHistoryTextureUnitBase + historyCap;
const HistoryRing* temporalRing = nullptr;
auto it = mPreLayerHistoryByLayerId.find(state.layerId);
if (it != mPreLayerHistoryByLayerId.end())
temporalRing = &it->second;
for (unsigned index = 0; index < historyCap; ++index)
{
glActiveTexture(GL_TEXTURE0 + temporalBase + index);
glBindTexture(GL_TEXTURE_2D, temporalRing ? resolveHistoryTexture(*temporalRing, currentSourceTexture, index) : currentSourceTexture);
}
glActiveTexture(GL_TEXTURE0);
}
void OpenGLComposite::renderEffect()
{
PollRuntimeChanges();
@@ -1143,15 +1489,25 @@ void OpenGLComposite::renderEffect()
else
{
GLuint sourceTexture = mDecodedTexture;
GLuint sourceFrameBuffer = mDecodeFrameBuf;
for (std::size_t index = 0; index < layerStates.size() && index < mLayerPrograms.size(); ++index)
{
const std::size_t remaining = layerStates.size() - index;
const bool writeToMain = (remaining % 2) == 1;
renderShaderProgram(sourceTexture, writeToMain ? mIdFrameBuf : mLayerTempFrameBuf, mLayerPrograms[index], layerStates[index]);
if (layerStates[index].temporalHistorySource == TemporalHistorySource::PreLayerInput)
{
auto historyIt = mPreLayerHistoryByLayerId.find(layerStates[index].layerId);
if (historyIt != mPreLayerHistoryByLayerId.end())
pushFramebufferToHistoryRing(sourceFrameBuffer, historyIt->second);
}
sourceTexture = writeToMain ? mFBOTexture : mLayerTempTexture;
sourceFrameBuffer = writeToMain ? mIdFrameBuf : mLayerTempFrameBuf;
}
}
pushFramebufferToHistoryRing(mDecodeFrameBuf, mSourceHistoryRing);
if (mFastTransferExtensionAvailable)
VideoFrameTransfer::endTextureInUse(VideoFrameTransfer::CPUtoGPU);
}
@@ -1163,12 +1519,22 @@ void OpenGLComposite::renderShaderProgram(GLuint sourceTexture, GLuint destinati
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0 + kDecodedVideoTextureUnit);
glBindTexture(GL_TEXTURE_2D, sourceTexture);
bindHistorySamplers(state, sourceTexture);
glBindVertexArray(mFullscreenVAO);
glUseProgram(layerProgram.program);
updateGlobalParamsBuffer(state);
updateGlobalParamsBuffer(state, sourceHistoryAvailableCount(), temporalHistoryAvailableCountForLayer(state.layerId));
glDrawArrays(GL_TRIANGLES, 0, 3);
glUseProgram(0);
glBindVertexArray(0);
const unsigned historyCap = mRuntimeHost ? mRuntimeHost->GetMaxTemporalHistoryFrames() : 0;
for (unsigned index = 0; index < historyCap; ++index)
{
glActiveTexture(GL_TEXTURE0 + kSourceHistoryTextureUnitBase + index);
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE0 + kSourceHistoryTextureUnitBase + historyCap + index);
glBindTexture(GL_TEXTURE_2D, 0);
}
glActiveTexture(GL_TEXTURE0 + kDecodedVideoTextureUnit);
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE0);
}
@@ -1228,6 +1594,7 @@ bool OpenGLComposite::PollRuntimeChanges()
return false;
}
resetTemporalHistoryState();
broadcastRuntimeState();
return true;
}
@@ -1238,7 +1605,7 @@ void OpenGLComposite::broadcastRuntimeState()
mControlServer->BroadcastState();
}
bool OpenGLComposite::updateGlobalParamsBuffer(const RuntimeRenderState& state)
bool OpenGLComposite::updateGlobalParamsBuffer(const RuntimeRenderState& state, unsigned availableSourceHistoryLength, unsigned availableTemporalHistoryLength)
{
std::vector<unsigned char> buffer;
buffer.reserve(512);
@@ -1249,6 +1616,14 @@ bool OpenGLComposite::updateGlobalParamsBuffer(const RuntimeRenderState& state)
AppendStd140Float(buffer, static_cast<float>(state.frameCount));
AppendStd140Float(buffer, static_cast<float>(state.mixAmount));
AppendStd140Float(buffer, static_cast<float>(state.bypass));
const unsigned effectiveSourceHistoryLength = availableSourceHistoryLength < state.effectiveTemporalHistoryLength
? availableSourceHistoryLength
: state.effectiveTemporalHistoryLength;
const unsigned effectiveTemporalHistoryLength = (state.temporalHistorySource == TemporalHistorySource::PreLayerInput)
? (availableTemporalHistoryLength < state.effectiveTemporalHistoryLength ? availableTemporalHistoryLength : state.effectiveTemporalHistoryLength)
: 0u;
AppendStd140Int(buffer, static_cast<int>(effectiveSourceHistoryLength));
AppendStd140Int(buffer, static_cast<int>(effectiveTemporalHistoryLength));
for (const ShaderParameterDefinition& definition : state.parameterDefinitions)
{
@@ -1323,6 +1698,7 @@ bool OpenGLComposite::AddLayer(const std::string& shaderId, std::string& error)
return false;
ReloadShader();
resetTemporalHistoryState();
broadcastRuntimeState();
return true;
}
@@ -1333,6 +1709,7 @@ bool OpenGLComposite::RemoveLayer(const std::string& layerId, std::string& error
return false;
ReloadShader();
resetTemporalHistoryState();
broadcastRuntimeState();
return true;
}
@@ -1343,6 +1720,7 @@ bool OpenGLComposite::MoveLayer(const std::string& layerId, int direction, std::
return false;
ReloadShader();
resetTemporalHistoryState();
broadcastRuntimeState();
return true;
}
@@ -1353,6 +1731,7 @@ bool OpenGLComposite::MoveLayerToIndex(const std::string& layerId, std::size_t t
return false;
ReloadShader();
resetTemporalHistoryState();
broadcastRuntimeState();
return true;
}
@@ -1363,6 +1742,7 @@ bool OpenGLComposite::SetLayerBypass(const std::string& layerId, bool bypassed,
return false;
ReloadShader();
resetTemporalHistoryState();
broadcastRuntimeState();
return true;
}
@@ -1373,6 +1753,7 @@ bool OpenGLComposite::SetLayerShader(const std::string& layerId, const std::stri
return false;
ReloadShader();
resetTemporalHistoryState();
broadcastRuntimeState();
return true;
}
@@ -1414,6 +1795,7 @@ bool OpenGLComposite::LoadStackPreset(const std::string& presetName, std::string
return false;
ReloadShader();
resetTemporalHistoryState();
broadcastRuntimeState();
return true;
}