diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1fa9a22..9f506e6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -56,8 +56,12 @@ set(APP_SOURCES
"${APP_DIR}/gl/OpenGLComposite.cpp"
"${APP_DIR}/gl/OpenGLComposite.h"
"${APP_DIR}/gl/OpenGLCompositeRuntimeControls.cpp"
+ "${APP_DIR}/gl/OpenGLRenderPass.cpp"
+ "${APP_DIR}/gl/OpenGLRenderPass.h"
"${APP_DIR}/gl/OpenGLRenderer.cpp"
"${APP_DIR}/gl/OpenGLRenderer.h"
+ "${APP_DIR}/gl/OpenGLShaderPrograms.cpp"
+ "${APP_DIR}/gl/OpenGLShaderPrograms.h"
"${APP_DIR}/gl/Std140Buffer.h"
"${APP_DIR}/gl/TextRasterizer.cpp"
"${APP_DIR}/gl/TextRasterizer.h"
diff --git a/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj b/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj
index fed6c09..15a8caf 100644
--- a/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj
+++ b/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj
@@ -195,7 +195,9 @@
+
+
Create
@@ -210,7 +212,9 @@
+
+
diff --git a/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj.filters b/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj.filters
index 3867475..3917cbe 100644
--- a/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj.filters
+++ b/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj.filters
@@ -27,9 +27,15 @@
Source Files
+
+ Source Files
+
Source Files
+
+ Source Files
+
Source Files
@@ -53,9 +59,15 @@
Header Files
+
+ Header Files
+
Header Files
+
+ Header Files
+
Header Files
diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/GLExtensions.h b/apps/LoopThroughWithOpenGLCompositing/gl/GLExtensions.h
index 7aafdee..e89b969 100644
--- a/apps/LoopThroughWithOpenGLCompositing/gl/GLExtensions.h
+++ b/apps/LoopThroughWithOpenGLCompositing/gl/GLExtensions.h
@@ -63,10 +63,12 @@
#define GL_ARRAY_BUFFER 0x8892
#define GL_PIXEL_PACK_BUFFER 0x88EB
#define GL_PIXEL_UNPACK_BUFFER 0x88EC
+#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF
#define GL_FRAGMENT_SHADER 0x8B30
#define GL_VERTEX_SHADER 0x8B31
#define GL_COMPILE_STATUS 0x8B81
#define GL_LINK_STATUS 0x8B82
+#define GL_INVALID_INDEX 0xFFFFFFFFu
#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872
#define GL_RENDERBUFFER_EXT 0x8D41
#define GL_FRAMEBUFFER_EXT 0x8D40
diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp
index 6ec8a44..e475d69 100644
--- a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp
+++ b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp
@@ -44,62 +44,15 @@
#include "OpenGLComposite.h"
#include "GLExtensions.h"
#include "GlRenderConstants.h"
-#include "GlScopedObjects.h"
-#include "GlShaderSources.h"
+#include "OpenGLRenderPass.h"
+#include "OpenGLShaderPrograms.h"
#include "OscServer.h"
#include "RuntimeControlBridge.h"
-#include "Std140Buffer.h"
-#include "TextRasterizer.h"
-#include "TextureAssetLoader.h"
-#include
-#include
-#include
-#include
-#include
#include
#include
#include
-namespace
-{
-void CopyErrorMessage(const std::string& message, int errorMessageSize, char* errorMessage)
-{
- if (!errorMessage || errorMessageSize <= 0)
- return;
-
- strncpy_s(errorMessage, errorMessageSize, message.c_str(), _TRUNCATE);
-}
-
-std::string TextValueForBinding(const RuntimeRenderState& state, const std::string& parameterId)
-{
- auto valueIt = state.parameterValues.find(parameterId);
- return valueIt == state.parameterValues.end() ? std::string() : valueIt->second.textValue;
-}
-
-const ShaderFontAsset* FindFontAssetForParameter(const RuntimeRenderState& state, const ShaderParameterDefinition& definition)
-{
- if (!definition.fontId.empty())
- {
- for (const ShaderFontAsset& fontAsset : state.fontAssets)
- {
- if (fontAsset.id == definition.fontId)
- return &fontAsset;
- }
- }
- return state.fontAssets.empty() ? nullptr : &state.fontAssets.front();
-}
-
-GLint FindSamplerUniformLocation(GLuint program, const std::string& samplerName)
-{
- GLint location = glGetUniformLocation(program, samplerName.c_str());
- if (location >= 0)
- return location;
- return glGetUniformLocation(program, (samplerName + "_0").c_str());
-}
-
-}
-
OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) :
hGLWnd(hWnd), hGLDC(hDC), hGLRC(hRC),
mCaptureDelegate(NULL), mPlayoutDelegate(NULL),
@@ -118,6 +71,8 @@ OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) :
{
InitializeCriticalSection(&pMutex);
mRuntimeHost = std::make_unique();
+ mRenderPass = std::make_unique(*mRenderer);
+ mShaderPrograms = std::make_unique(*mRenderer, *mRuntimeHost);
mControlServer = std::make_unique();
mOscServer = std::make_unique();
}
@@ -648,18 +603,18 @@ bool OpenGLComposite::InitOpenGLState()
// Prepare the runtime shader program generated from the active shader package.
char compilerErrorMessage[1024];
- if (! compileDecodeShader(sizeof(compilerErrorMessage), compilerErrorMessage))
+ if (!mShaderPrograms->CompileDecodeShader(sizeof(compilerErrorMessage), compilerErrorMessage))
{
MessageBoxA(NULL, compilerErrorMessage, "OpenGL decode shader failed to load or compile", MB_OK);
return false;
}
- if (! compileLayerPrograms(sizeof(compilerErrorMessage), compilerErrorMessage))
+ if (!mShaderPrograms->CompileLayerPrograms(mInputFrameWidth, mInputFrameHeight, sizeof(compilerErrorMessage), compilerErrorMessage))
{
MessageBoxA(NULL, compilerErrorMessage, "OpenGL shader failed to load or compile", MB_OK);
return false;
}
- resetTemporalHistoryState();
+ mShaderPrograms->ResetTemporalHistoryState();
std::string rendererError;
if (!mRenderer->InitializeResources(mInputFrameWidth, mInputFrameHeight, mOutputFrameWidth, mOutputFrameHeight, rendererError))
@@ -943,7 +898,7 @@ bool OpenGLComposite::ReloadShader()
EnterCriticalSection(&pMutex);
wglMakeCurrent(hGLDC, hGLRC);
- bool success = compileLayerPrograms(sizeof(compilerErrorMessage), compilerErrorMessage);
+ bool success = mShaderPrograms->CompileLayerPrograms(mInputFrameWidth, mInputFrameHeight, sizeof(compilerErrorMessage), compilerErrorMessage);
if (mRuntimeHost)
mRuntimeHost->ClearReloadRequest();
@@ -966,455 +921,25 @@ bool OpenGLComposite::ReloadShader()
return success;
}
-bool OpenGLComposite::compileSingleLayerProgram(const RuntimeRenderState& state, LayerProgram& layerProgram, int errorMessageSize, char* errorMessage)
-{
- GLsizei errorBufferSize = 0;
- GLint compileResult = GL_FALSE;
- GLint linkResult = GL_FALSE;
- std::string fragmentShaderSource;
- std::string loadError;
- std::vector textureBindings;
- const char* vertexSource = kFullscreenTriangleVertexShaderSource;
-
- if (!mRuntimeHost->BuildLayerFragmentShaderSource(state.layerId, fragmentShaderSource, loadError))
- {
- CopyErrorMessage(loadError, errorMessageSize, errorMessage);
- return false;
- }
-
- const char* fragmentSource = fragmentShaderSource.c_str();
-
- ScopedGlShader newVertexShader(glCreateShader(GL_VERTEX_SHADER));
- glShaderSource(newVertexShader.get(), 1, (const GLchar**)&vertexSource, NULL);
- glCompileShader(newVertexShader.get());
- glGetShaderiv(newVertexShader.get(), GL_COMPILE_STATUS, &compileResult);
- if (compileResult == GL_FALSE)
- {
- glGetShaderInfoLog(newVertexShader.get(), errorMessageSize, &errorBufferSize, errorMessage);
- return false;
- }
-
- ScopedGlShader newFragmentShader(glCreateShader(GL_FRAGMENT_SHADER));
- glShaderSource(newFragmentShader.get(), 1, (const GLchar**)&fragmentSource, NULL);
- glCompileShader(newFragmentShader.get());
- glGetShaderiv(newFragmentShader.get(), GL_COMPILE_STATUS, &compileResult);
- if (compileResult == GL_FALSE)
- {
- glGetShaderInfoLog(newFragmentShader.get(), errorMessageSize, &errorBufferSize, errorMessage);
- return false;
- }
-
- ScopedGlProgram newProgram(glCreateProgram());
- glAttachShader(newProgram.get(), newVertexShader.get());
- glAttachShader(newProgram.get(), newFragmentShader.get());
- glLinkProgram(newProgram.get());
- glGetProgramiv(newProgram.get(), GL_LINK_STATUS, &linkResult);
- if (linkResult == GL_FALSE)
- {
- glGetProgramInfoLog(newProgram.get(), errorMessageSize, &errorBufferSize, errorMessage);
- return false;
- }
-
- for (const ShaderTextureAsset& textureAsset : state.textureAssets)
- {
- LayerProgram::TextureBinding textureBinding;
- textureBinding.samplerName = textureAsset.id;
- textureBinding.sourcePath = textureAsset.path;
- if (!loadTextureAsset(textureAsset, textureBinding.texture, loadError))
- {
- for (LayerProgram::TextureBinding& loadedTexture : textureBindings)
- {
- if (loadedTexture.texture != 0)
- glDeleteTextures(1, &loadedTexture.texture);
- }
- CopyErrorMessage(loadError, errorMessageSize, errorMessage);
- return false;
- }
- textureBindings.push_back(textureBinding);
- }
- std::vector textBindings;
- for (const ShaderParameterDefinition& definition : state.parameterDefinitions)
- {
- if (definition.type != ShaderParameterType::Text)
- continue;
- LayerProgram::TextBinding textBinding;
- textBinding.parameterId = definition.id;
- textBinding.samplerName = definition.id + "Texture";
- textBinding.fontId = definition.fontId;
- glGenTextures(1, &textBinding.texture);
- glBindTexture(GL_TEXTURE_2D, textBinding.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);
- std::vector empty(static_cast(kTextTextureWidth) * kTextTextureHeight * 4, 0);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kTextTextureWidth, kTextTextureHeight, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, empty.data());
- glBindTexture(GL_TEXTURE_2D, 0);
- textBindings.push_back(textBinding);
- }
-
- const GLuint globalParamsIndex = glGetUniformBlockIndex(newProgram.get(), "GlobalParams");
- if (globalParamsIndex != GL_INVALID_INDEX)
- glUniformBlockBinding(newProgram.get(), globalParamsIndex, kGlobalParamsBindingPoint);
-
- const unsigned historyCap = mRuntimeHost ? mRuntimeHost->GetMaxTemporalHistoryFrames() : 0;
- const GLuint shaderTextureBase = state.isTemporal ? kSourceHistoryTextureUnitBase + historyCap + historyCap : kSourceHistoryTextureUnitBase;
- glUseProgram(newProgram.get());
- const GLint videoInputLocation = glGetUniformLocation(newProgram.get(), "gVideoInput");
- if (videoInputLocation >= 0)
- glUniform1i(videoInputLocation, static_cast(kDecodedVideoTextureUnit));
- for (unsigned index = 0; index < historyCap; ++index)
- {
- const std::string sourceSamplerName = "gSourceHistory" + std::to_string(index);
- const GLint sourceSamplerLocation = glGetUniformLocation(newProgram.get(), sourceSamplerName.c_str());
- if (sourceSamplerLocation >= 0)
- glUniform1i(sourceSamplerLocation, static_cast(kSourceHistoryTextureUnitBase + index));
-
- const std::string temporalSamplerName = "gTemporalHistory" + std::to_string(index);
- const GLint temporalSamplerLocation = glGetUniformLocation(newProgram.get(), temporalSamplerName.c_str());
- if (temporalSamplerLocation >= 0)
- glUniform1i(temporalSamplerLocation, static_cast(kSourceHistoryTextureUnitBase + historyCap + index));
- }
- for (std::size_t index = 0; index < textureBindings.size(); ++index)
- {
- const GLint textureSamplerLocation = FindSamplerUniformLocation(newProgram.get(), textureBindings[index].samplerName);
- if (textureSamplerLocation >= 0)
- glUniform1i(textureSamplerLocation, static_cast(shaderTextureBase + static_cast(index)));
- }
- const GLuint textTextureBase = shaderTextureBase + static_cast(textureBindings.size());
- for (std::size_t index = 0; index < textBindings.size(); ++index)
- {
- const GLint textSamplerLocation = FindSamplerUniformLocation(newProgram.get(), textBindings[index].samplerName);
- if (textSamplerLocation >= 0)
- glUniform1i(textSamplerLocation, static_cast(textTextureBase + static_cast(index)));
- }
- glUseProgram(0);
-
- layerProgram.layerId = state.layerId;
- layerProgram.shaderId = state.shaderId;
- layerProgram.shaderTextureBase = shaderTextureBase;
- layerProgram.program = newProgram.release();
- layerProgram.vertexShader = newVertexShader.release();
- layerProgram.fragmentShader = newFragmentShader.release();
- layerProgram.textureBindings.swap(textureBindings);
- layerProgram.textBindings.swap(textBindings);
- return true;
-}
-
-bool OpenGLComposite::compileLayerPrograms(int errorMessageSize, char* errorMessage)
-{
- const std::vector layerStates = mRuntimeHost ? mRuntimeHost->GetLayerRenderStates(mInputFrameWidth, mInputFrameHeight) : std::vector();
- std::string temporalError;
- if (!validateTemporalTextureUnitBudget(layerStates, temporalError))
- {
- CopyErrorMessage(temporalError, errorMessageSize, errorMessage);
- return false;
- }
- if (!ensureTemporalHistoryResources(layerStates, temporalError))
- {
- CopyErrorMessage(temporalError, errorMessageSize, errorMessage);
- return false;
- }
- std::vector newPrograms;
- newPrograms.reserve(layerStates.size());
-
- for (const RuntimeRenderState& state : layerStates)
- {
- LayerProgram layerProgram;
- if (!compileSingleLayerProgram(state, layerProgram, errorMessageSize, errorMessage))
- {
- for (LayerProgram& program : newPrograms)
- destroySingleLayerProgram(program);
- return false;
- }
- newPrograms.push_back(layerProgram);
- }
-
- destroyLayerPrograms();
- mRenderer->mLayerPrograms.swap(newPrograms);
-
- if (mRuntimeHost)
- {
- mRuntimeHost->SetCompileStatus(true, "Shader layers compiled successfully.");
- mRuntimeHost->ClearReloadRequest();
- }
-
- return true;
-}
-
-bool OpenGLComposite::compileDecodeShader(int errorMessageSize, char* errorMessage)
-{
- GLsizei errorBufferSize = 0;
- GLint compileResult = GL_FALSE;
- GLint linkResult = GL_FALSE;
- const char* vertexSource = kFullscreenTriangleVertexShaderSource;
- const char* fragmentSource = kDecodeFragmentShaderSource;
-
- ScopedGlShader newVertexShader(glCreateShader(GL_VERTEX_SHADER));
- glShaderSource(newVertexShader.get(), 1, (const GLchar**)&vertexSource, NULL);
- glCompileShader(newVertexShader.get());
- glGetShaderiv(newVertexShader.get(), GL_COMPILE_STATUS, &compileResult);
- if (compileResult == GL_FALSE)
- {
- glGetShaderInfoLog(newVertexShader.get(), errorMessageSize, &errorBufferSize, errorMessage);
- return false;
- }
-
- ScopedGlShader newFragmentShader(glCreateShader(GL_FRAGMENT_SHADER));
- glShaderSource(newFragmentShader.get(), 1, (const GLchar**)&fragmentSource, NULL);
- glCompileShader(newFragmentShader.get());
- glGetShaderiv(newFragmentShader.get(), GL_COMPILE_STATUS, &compileResult);
- if (compileResult == GL_FALSE)
- {
- glGetShaderInfoLog(newFragmentShader.get(), errorMessageSize, &errorBufferSize, errorMessage);
- return false;
- }
-
- ScopedGlProgram newProgram(glCreateProgram());
- glAttachShader(newProgram.get(), newVertexShader.get());
- glAttachShader(newProgram.get(), newFragmentShader.get());
- glLinkProgram(newProgram.get());
- glGetProgramiv(newProgram.get(), GL_LINK_STATUS, &linkResult);
- if (linkResult == GL_FALSE)
- {
- glGetProgramInfoLog(newProgram.get(), errorMessageSize, &errorBufferSize, errorMessage);
- return false;
- }
-
- destroyDecodeShaderProgram();
- mRenderer->mDecodeProgram = newProgram.release();
- mRenderer->mDecodeVertexShader = newVertexShader.release();
- mRenderer->mDecodeFragmentShader = newFragmentShader.release();
- return true;
-}
-
-void OpenGLComposite::destroySingleLayerProgram(LayerProgram& layerProgram)
-{
- mRenderer->DestroySingleLayerProgram(layerProgram);
-}
-
-void OpenGLComposite::destroyLayerPrograms()
-{
- mRenderer->DestroyLayerPrograms();
-}
-
-bool OpenGLComposite::loadTextureAsset(const ShaderTextureAsset& textureAsset, GLuint& textureId, std::string& error)
-{
- return LoadTextureAsset(textureAsset, textureId, error);
-}
-
-bool OpenGLComposite::renderTextBindingTexture(const RuntimeRenderState& state, LayerProgram::TextBinding& textBinding, std::string& error)
-{
- const std::string text = TextValueForBinding(state, textBinding.parameterId);
- if (text == textBinding.renderedText && textBinding.renderedWidth == kTextTextureWidth && textBinding.renderedHeight == kTextTextureHeight)
- return true;
-
- auto definitionIt = std::find_if(state.parameterDefinitions.begin(), state.parameterDefinitions.end(),
- [&textBinding](const ShaderParameterDefinition& definition) { return definition.id == textBinding.parameterId; });
- if (definitionIt == state.parameterDefinitions.end())
- return true;
-
- const ShaderFontAsset* fontAsset = FindFontAssetForParameter(state, *definitionIt);
- std::filesystem::path fontPath;
- if (fontAsset)
- fontPath = fontAsset->path;
-
- std::vector sdf;
- if (!RasterizeTextSdf(text, fontPath, sdf, error))
- return false;
-
- GLint previousActiveTexture = 0;
- GLint previousUnpackBuffer = 0;
- glGetIntegerv(GL_ACTIVE_TEXTURE, &previousActiveTexture);
- glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, &previousUnpackBuffer);
- glActiveTexture(GL_TEXTURE0);
- glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
- glBindTexture(GL_TEXTURE_2D, textBinding.texture);
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, kTextTextureWidth, kTextTextureHeight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, sdf.data());
- glBindTexture(GL_TEXTURE_2D, 0);
- glBindBuffer(GL_PIXEL_UNPACK_BUFFER, static_cast(previousUnpackBuffer));
- glActiveTexture(static_cast(previousActiveTexture));
-
- textBinding.renderedText = text;
- textBinding.renderedWidth = kTextTextureWidth;
- textBinding.renderedHeight = kTextTextureHeight;
- return true;
-}
-
-void OpenGLComposite::bindLayerTextureAssets(const LayerProgram& layerProgram)
-{
- const GLuint shaderTextureBase = layerProgram.shaderTextureBase != 0 ? layerProgram.shaderTextureBase : kSourceHistoryTextureUnitBase;
- for (std::size_t index = 0; index < layerProgram.textureBindings.size(); ++index)
- {
- glActiveTexture(GL_TEXTURE0 + shaderTextureBase + static_cast(index));
- glBindTexture(GL_TEXTURE_2D, layerProgram.textureBindings[index].texture);
- }
- const GLuint textTextureBase = shaderTextureBase + static_cast(layerProgram.textureBindings.size());
- for (std::size_t index = 0; index < layerProgram.textBindings.size(); ++index)
- {
- glActiveTexture(GL_TEXTURE0 + textTextureBase + static_cast(index));
- glBindTexture(GL_TEXTURE_2D, layerProgram.textBindings[index].texture);
- }
- glActiveTexture(GL_TEXTURE0);
-}
-
-void OpenGLComposite::destroyDecodeShaderProgram()
-{
- mRenderer->DestroyDecodeShaderProgram();
-}
-
-bool OpenGLComposite::validateTemporalTextureUnitBudget(const std::vector& layerStates, std::string& error) const
-{
- const unsigned historyCap = mRuntimeHost ? mRuntimeHost->GetMaxTemporalHistoryFrames() : 0;
- return mRenderer->mTemporalHistory.ValidateTextureUnitBudget(layerStates, historyCap, error);
-}
-
-void OpenGLComposite::resetTemporalHistoryState()
-{
- mRenderer->mTemporalHistory.ResetState();
-}
-
-bool OpenGLComposite::ensureTemporalHistoryResources(const std::vector& layerStates, std::string& error)
-{
- const unsigned historyCap = mRuntimeHost ? mRuntimeHost->GetMaxTemporalHistoryFrames() : 0;
- return mRenderer->mTemporalHistory.EnsureResources(layerStates, historyCap, mInputFrameWidth, mInputFrameHeight, error);
-}
-
-unsigned OpenGLComposite::sourceHistoryAvailableCount() const
-{
- return mRenderer->mTemporalHistory.SourceAvailableCount();
-}
-
-unsigned OpenGLComposite::temporalHistoryAvailableCountForLayer(const std::string& layerId) const
-{
- return mRenderer->mTemporalHistory.AvailableCountForLayer(layerId);
-}
-
-void OpenGLComposite::bindHistorySamplers(const RuntimeRenderState& state, GLuint currentSourceTexture)
-{
- const unsigned historyCap = mRuntimeHost ? mRuntimeHost->GetMaxTemporalHistoryFrames() : 0;
- mRenderer->mTemporalHistory.BindSamplers(state, currentSourceTexture, historyCap);
-}
-
void OpenGLComposite::renderEffect()
{
PollRuntimeChanges();
const bool hasInputSource = !mHasNoInputSource;
- if (hasInputSource && mRenderer->mFastTransferExtensionAvailable)
- {
- // Signal that we're about to draw using mRenderer->mCaptureTexture onto mRenderer->mFBOTexture.
- VideoFrameTransfer::beginTextureInUse(VideoFrameTransfer::CPUtoGPU);
- }
-
- glDisable(GL_BLEND);
- glDisable(GL_DEPTH_TEST);
- if (hasInputSource)
- {
- renderDecodePass();
- }
- else
- {
- glBindFramebuffer(GL_FRAMEBUFFER, mRenderer->mDecodeFrameBuf);
- glViewport(0, 0, mInputFrameWidth, mInputFrameHeight);
- glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
- glClear(GL_COLOR_BUFFER_BIT);
- }
-
const std::vector layerStates = mRuntimeHost ? mRuntimeHost->GetLayerRenderStates(mInputFrameWidth, mInputFrameHeight) : std::vector();
- if (layerStates.empty() || mRenderer->mLayerPrograms.empty())
- {
- glBindFramebuffer(GL_READ_FRAMEBUFFER, mRenderer->mDecodeFrameBuf);
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mRenderer->mIdFrameBuf);
- glBlitFramebuffer(0, 0, mInputFrameWidth, mInputFrameHeight, 0, 0, mInputFrameWidth, mInputFrameHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
- glBindFramebuffer(GL_FRAMEBUFFER, mRenderer->mIdFrameBuf);
- }
- else
- {
- GLuint sourceTexture = mRenderer->mDecodedTexture;
- GLuint sourceFrameBuffer = mRenderer->mDecodeFrameBuf;
- for (std::size_t index = 0; index < layerStates.size() && index < mRenderer->mLayerPrograms.size(); ++index)
- {
- const std::size_t remaining = layerStates.size() - index;
- const bool writeToMain = (remaining % 2) == 1;
- renderShaderProgram(sourceTexture, writeToMain ? mRenderer->mIdFrameBuf : mRenderer->mLayerTempFrameBuf, mRenderer->mLayerPrograms[index], layerStates[index]);
- if (layerStates[index].temporalHistorySource == TemporalHistorySource::PreLayerInput)
- mRenderer->mTemporalHistory.PushPreLayerFramebuffer(layerStates[index].layerId, sourceFrameBuffer, mInputFrameWidth, mInputFrameHeight);
- sourceTexture = writeToMain ? mRenderer->mFBOTexture : mRenderer->mLayerTempTexture;
- sourceFrameBuffer = writeToMain ? mRenderer->mIdFrameBuf : mRenderer->mLayerTempFrameBuf;
- }
- }
-
- mRenderer->mTemporalHistory.PushSourceFramebuffer(mRenderer->mDecodeFrameBuf, mInputFrameWidth, mInputFrameHeight);
-
- if (hasInputSource && mRenderer->mFastTransferExtensionAvailable)
- VideoFrameTransfer::endTextureInUse(VideoFrameTransfer::CPUtoGPU);
-}
-
-void OpenGLComposite::renderShaderProgram(GLuint sourceTexture, GLuint destinationFrameBuffer, LayerProgram& layerProgram, const RuntimeRenderState& state)
-{
- for (LayerProgram::TextBinding& textBinding : layerProgram.textBindings)
- {
- std::string textError;
- if (!renderTextBindingTexture(state, textBinding, textError))
- OutputDebugStringA((textError + "\n").c_str());
- }
-
- glBindFramebuffer(GL_FRAMEBUFFER, destinationFrameBuffer);
- glViewport(0, 0, mInputFrameWidth, mInputFrameHeight);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- glActiveTexture(GL_TEXTURE0 + kDecodedVideoTextureUnit);
- glBindTexture(GL_TEXTURE_2D, sourceTexture);
- bindHistorySamplers(state, sourceTexture);
- bindLayerTextureAssets(layerProgram);
- glBindVertexArray(mRenderer->mFullscreenVAO);
- glUseProgram(layerProgram.program);
- 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);
- }
- const GLuint shaderTextureBase = layerProgram.shaderTextureBase != 0 ? layerProgram.shaderTextureBase : kSourceHistoryTextureUnitBase;
- for (std::size_t index = 0; index < layerProgram.textureBindings.size() + layerProgram.textBindings.size(); ++index)
- {
- glActiveTexture(GL_TEXTURE0 + shaderTextureBase + static_cast(index));
- glBindTexture(GL_TEXTURE_2D, 0);
- }
- glActiveTexture(GL_TEXTURE0 + kDecodedVideoTextureUnit);
- glBindTexture(GL_TEXTURE_2D, 0);
- glActiveTexture(GL_TEXTURE0);
-}
-
-void OpenGLComposite::renderDecodePass()
-{
- glBindFramebuffer(GL_FRAMEBUFFER, mRenderer->mDecodeFrameBuf);
- glViewport(0, 0, mInputFrameWidth, mInputFrameHeight);
- glClear(GL_COLOR_BUFFER_BIT);
- glActiveTexture(GL_TEXTURE0 + kPackedVideoTextureUnit);
- glBindTexture(GL_TEXTURE_2D, mRenderer->mCaptureTexture);
- glBindVertexArray(mRenderer->mFullscreenVAO);
- glUseProgram(mRenderer->mDecodeProgram);
-
- const GLint packedResolutionLocation = glGetUniformLocation(mRenderer->mDecodeProgram, "uPackedVideoResolution");
- const GLint decodedResolutionLocation = glGetUniformLocation(mRenderer->mDecodeProgram, "uDecodedVideoResolution");
- if (packedResolutionLocation >= 0)
- glUniform2f(packedResolutionLocation, static_cast(mInputFrameWidth / 2), static_cast(mInputFrameHeight));
- if (decodedResolutionLocation >= 0)
- glUniform2f(decodedResolutionLocation, static_cast(mInputFrameWidth), static_cast(mInputFrameHeight));
-
- glDrawArrays(GL_TRIANGLES, 0, 3);
-
- glUseProgram(0);
- glBindVertexArray(0);
- glBindTexture(GL_TEXTURE_2D, 0);
- glActiveTexture(GL_TEXTURE0);
+ mRenderPass->Render(
+ hasInputSource,
+ layerStates,
+ mInputFrameWidth,
+ mInputFrameHeight,
+ 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) {
+ return mShaderPrograms->UpdateGlobalParamsBuffer(state, availableSourceHistoryLength, availableTemporalHistoryLength);
+ });
}
bool OpenGLComposite::PollRuntimeChanges()
@@ -1439,7 +964,7 @@ bool OpenGLComposite::PollRuntimeChanges()
return true;
char compilerErrorMessage[1024] = {};
- if (!compileLayerPrograms(sizeof(compilerErrorMessage), compilerErrorMessage))
+ if (!mShaderPrograms->CompileLayerPrograms(mInputFrameWidth, mInputFrameHeight, sizeof(compilerErrorMessage), compilerErrorMessage))
{
mRuntimeHost->SetCompileStatus(false, compilerErrorMessage);
mRuntimeHost->ClearReloadRequest();
@@ -1447,7 +972,7 @@ bool OpenGLComposite::PollRuntimeChanges()
return false;
}
- resetTemporalHistoryState();
+ mShaderPrograms->ResetTemporalHistoryState();
broadcastRuntimeState();
return true;
}
@@ -1458,88 +983,9 @@ void OpenGLComposite::broadcastRuntimeState()
mControlServer->BroadcastState();
}
-bool OpenGLComposite::updateGlobalParamsBuffer(const RuntimeRenderState& state, unsigned availableSourceHistoryLength, unsigned availableTemporalHistoryLength)
+void OpenGLComposite::resetTemporalHistoryState()
{
- std::vector buffer;
- buffer.reserve(512);
-
- AppendStd140Float(buffer, static_cast(state.timeSeconds));
- AppendStd140Vec2(buffer, static_cast(state.inputWidth), static_cast(state.inputHeight));
- AppendStd140Vec2(buffer, static_cast(state.outputWidth), static_cast(state.outputHeight));
- AppendStd140Float(buffer, static_cast(state.frameCount));
- AppendStd140Float(buffer, static_cast(state.mixAmount));
- AppendStd140Float(buffer, static_cast(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(effectiveSourceHistoryLength));
- AppendStd140Int(buffer, static_cast(effectiveTemporalHistoryLength));
-
- for (const ShaderParameterDefinition& definition : state.parameterDefinitions)
- {
- auto valueIt = state.parameterValues.find(definition.id);
- const ShaderParameterValue value = valueIt != state.parameterValues.end()
- ? valueIt->second
- : ShaderParameterValue();
-
- switch (definition.type)
- {
- case ShaderParameterType::Float:
- AppendStd140Float(buffer, value.numberValues.empty() ? 0.0f : static_cast(value.numberValues[0]));
- break;
- case ShaderParameterType::Vec2:
- AppendStd140Vec2(buffer,
- value.numberValues.size() > 0 ? static_cast(value.numberValues[0]) : 0.0f,
- value.numberValues.size() > 1 ? static_cast(value.numberValues[1]) : 0.0f);
- break;
- case ShaderParameterType::Color:
- AppendStd140Vec4(buffer,
- value.numberValues.size() > 0 ? static_cast(value.numberValues[0]) : 1.0f,
- value.numberValues.size() > 1 ? static_cast(value.numberValues[1]) : 1.0f,
- value.numberValues.size() > 2 ? static_cast(value.numberValues[2]) : 1.0f,
- value.numberValues.size() > 3 ? static_cast(value.numberValues[3]) : 1.0f);
- break;
- case ShaderParameterType::Boolean:
- AppendStd140Int(buffer, value.booleanValue ? 1 : 0);
- break;
- case ShaderParameterType::Enum:
- {
- int selectedIndex = 0;
- for (std::size_t optionIndex = 0; optionIndex < definition.enumOptions.size(); ++optionIndex)
- {
- if (definition.enumOptions[optionIndex].value == value.enumValue)
- {
- selectedIndex = static_cast(optionIndex);
- break;
- }
- }
- AppendStd140Int(buffer, selectedIndex);
- break;
- }
- case ShaderParameterType::Text:
- break;
- }
- }
-
- buffer.resize(AlignStd140(buffer.size(), 16), 0);
-
- glBindBuffer(GL_UNIFORM_BUFFER, mRenderer->mGlobalParamsUBO);
- if (mRenderer->mGlobalParamsUBOSize != static_cast(buffer.size()))
- {
- glBufferData(GL_UNIFORM_BUFFER, static_cast(buffer.size()), buffer.data(), GL_DYNAMIC_DRAW);
- mRenderer->mGlobalParamsUBOSize = static_cast(buffer.size());
- }
- else
- {
- glBufferSubData(GL_UNIFORM_BUFFER, 0, static_cast(buffer.size()), buffer.data());
- }
- glBindBufferBase(GL_UNIFORM_BUFFER, kGlobalParamsBindingPoint, mRenderer->mGlobalParamsUBO);
- glBindBuffer(GL_UNIFORM_BUFFER, 0);
-
- return true;
+ mShaderPrograms->ResetTemporalHistoryState();
}
bool OpenGLComposite::CheckOpenGLExtensions()
diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.h b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.h
index c2afd3a..c359b93 100644
--- a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.h
+++ b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.h
@@ -68,6 +68,8 @@ class CaptureDelegate;
class PinnedMemoryAllocator;
class ControlServer;
class OscServer;
+class OpenGLRenderPass;
+class OpenGLShaderPrograms;
class OpenGLComposite
@@ -136,31 +138,16 @@ private:
std::unique_ptr mRenderer;
std::unique_ptr mRuntimeHost;
+ std::unique_ptr mRenderPass;
+ std::unique_ptr mShaderPrograms;
std::unique_ptr mControlServer;
std::unique_ptr mOscServer;
bool InitOpenGLState();
- bool compileLayerPrograms(int errorMessageSize, char* errorMessage);
- bool compileSingleLayerProgram(const RuntimeRenderState& state, LayerProgram& layerProgram, int errorMessageSize, char* errorMessage);
- bool compileDecodeShader(int errorMessageSize, char* errorMessage);
- void destroyLayerPrograms();
- void destroySingleLayerProgram(LayerProgram& layerProgram);
- void destroyDecodeShaderProgram();
- void renderDecodePass();
- void renderShaderProgram(GLuint sourceTexture, GLuint destinationFrameBuffer, LayerProgram& layerProgram, const RuntimeRenderState& state);
- bool loadTextureAsset(const ShaderTextureAsset& textureAsset, GLuint& textureId, std::string& error);
- bool renderTextBindingTexture(const RuntimeRenderState& state, LayerProgram::TextBinding& textBinding, std::string& error);
- void bindLayerTextureAssets(const LayerProgram& layerProgram);
void renderEffect();
bool PollRuntimeChanges();
void broadcastRuntimeState();
- bool updateGlobalParamsBuffer(const RuntimeRenderState& state, unsigned availableSourceHistoryLength, unsigned availableTemporalHistoryLength);
- bool validateTemporalTextureUnitBudget(const std::vector& layerStates, std::string& error) const;
- bool ensureTemporalHistoryResources(const std::vector& layerStates, std::string& error);
void resetTemporalHistoryState();
- void bindHistorySamplers(const RuntimeRenderState& state, GLuint currentSourceTexture);
- unsigned sourceHistoryAvailableCount() const;
- unsigned temporalHistoryAvailableCountForLayer(const std::string& layerId) const;
};
#endif // __OPENGL_COMPOSITE_H__
diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLRenderPass.cpp b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLRenderPass.cpp
new file mode 100644
index 0000000..9af1e7f
--- /dev/null
+++ b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLRenderPass.cpp
@@ -0,0 +1,172 @@
+#include "OpenGLRenderPass.h"
+
+#include "GlRenderConstants.h"
+#include "VideoFrameTransfer.h"
+
+OpenGLRenderPass::OpenGLRenderPass(OpenGLRenderer& renderer) :
+ mRenderer(renderer)
+{
+}
+
+void OpenGLRenderPass::Render(
+ bool hasInputSource,
+ const std::vector& layerStates,
+ unsigned inputFrameWidth,
+ unsigned inputFrameHeight,
+ unsigned historyCap,
+ const TextBindingUpdater& updateTextBinding,
+ const GlobalParamsUpdater& updateGlobalParams)
+{
+ if (hasInputSource && mRenderer.mFastTransferExtensionAvailable)
+ {
+ // Signal that we're about to draw using mCaptureTexture onto mFBOTexture.
+ VideoFrameTransfer::beginTextureInUse(VideoFrameTransfer::CPUtoGPU);
+ }
+
+ glDisable(GL_BLEND);
+ glDisable(GL_DEPTH_TEST);
+ if (hasInputSource)
+ {
+ RenderDecodePass(inputFrameWidth, inputFrameHeight);
+ }
+ else
+ {
+ glBindFramebuffer(GL_FRAMEBUFFER, mRenderer.mDecodeFrameBuf);
+ glViewport(0, 0, inputFrameWidth, inputFrameHeight);
+ glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
+
+ if (layerStates.empty() || mRenderer.mLayerPrograms.empty())
+ {
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, mRenderer.mDecodeFrameBuf);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mRenderer.mIdFrameBuf);
+ glBlitFramebuffer(0, 0, inputFrameWidth, inputFrameHeight, 0, 0, inputFrameWidth, inputFrameHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
+ glBindFramebuffer(GL_FRAMEBUFFER, mRenderer.mIdFrameBuf);
+ }
+ else
+ {
+ GLuint sourceTexture = mRenderer.mDecodedTexture;
+ GLuint sourceFrameBuffer = mRenderer.mDecodeFrameBuf;
+ for (std::size_t index = 0; index < layerStates.size() && index < mRenderer.mLayerPrograms.size(); ++index)
+ {
+ const std::size_t remaining = layerStates.size() - index;
+ const bool writeToMain = (remaining % 2) == 1;
+ RenderShaderProgram(
+ sourceTexture,
+ writeToMain ? mRenderer.mIdFrameBuf : mRenderer.mLayerTempFrameBuf,
+ mRenderer.mLayerPrograms[index],
+ layerStates[index],
+ inputFrameWidth,
+ inputFrameHeight,
+ historyCap,
+ updateTextBinding,
+ updateGlobalParams);
+ if (layerStates[index].temporalHistorySource == TemporalHistorySource::PreLayerInput)
+ mRenderer.mTemporalHistory.PushPreLayerFramebuffer(layerStates[index].layerId, sourceFrameBuffer, inputFrameWidth, inputFrameHeight);
+ sourceTexture = writeToMain ? mRenderer.mFBOTexture : mRenderer.mLayerTempTexture;
+ sourceFrameBuffer = writeToMain ? mRenderer.mIdFrameBuf : mRenderer.mLayerTempFrameBuf;
+ }
+ }
+
+ mRenderer.mTemporalHistory.PushSourceFramebuffer(mRenderer.mDecodeFrameBuf, inputFrameWidth, inputFrameHeight);
+
+ if (hasInputSource && mRenderer.mFastTransferExtensionAvailable)
+ VideoFrameTransfer::endTextureInUse(VideoFrameTransfer::CPUtoGPU);
+}
+
+void OpenGLRenderPass::RenderDecodePass(unsigned inputFrameWidth, unsigned inputFrameHeight)
+{
+ glBindFramebuffer(GL_FRAMEBUFFER, mRenderer.mDecodeFrameBuf);
+ glViewport(0, 0, inputFrameWidth, inputFrameHeight);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glActiveTexture(GL_TEXTURE0 + kPackedVideoTextureUnit);
+ glBindTexture(GL_TEXTURE_2D, mRenderer.mCaptureTexture);
+ glBindVertexArray(mRenderer.mFullscreenVAO);
+ glUseProgram(mRenderer.mDecodeProgram);
+
+ const GLint packedResolutionLocation = glGetUniformLocation(mRenderer.mDecodeProgram, "uPackedVideoResolution");
+ const GLint decodedResolutionLocation = glGetUniformLocation(mRenderer.mDecodeProgram, "uDecodedVideoResolution");
+ if (packedResolutionLocation >= 0)
+ glUniform2f(packedResolutionLocation, static_cast(inputFrameWidth / 2), static_cast(inputFrameHeight));
+ if (decodedResolutionLocation >= 0)
+ glUniform2f(decodedResolutionLocation, static_cast(inputFrameWidth), static_cast(inputFrameHeight));
+
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+
+ glUseProgram(0);
+ glBindVertexArray(0);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glActiveTexture(GL_TEXTURE0);
+}
+
+void OpenGLRenderPass::RenderShaderProgram(
+ GLuint sourceTexture,
+ GLuint destinationFrameBuffer,
+ LayerProgram& layerProgram,
+ const RuntimeRenderState& state,
+ unsigned inputFrameWidth,
+ unsigned inputFrameHeight,
+ unsigned historyCap,
+ const TextBindingUpdater& updateTextBinding,
+ const GlobalParamsUpdater& updateGlobalParams)
+{
+ for (LayerProgram::TextBinding& textBinding : layerProgram.textBindings)
+ {
+ std::string textError;
+ if (!updateTextBinding(state, textBinding, textError))
+ OutputDebugStringA((textError + "\n").c_str());
+ }
+
+ glBindFramebuffer(GL_FRAMEBUFFER, destinationFrameBuffer);
+ glViewport(0, 0, inputFrameWidth, inputFrameHeight);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glActiveTexture(GL_TEXTURE0 + kDecodedVideoTextureUnit);
+ glBindTexture(GL_TEXTURE_2D, sourceTexture);
+ mRenderer.mTemporalHistory.BindSamplers(state, sourceTexture, historyCap);
+ BindLayerTextureAssets(layerProgram);
+ glBindVertexArray(mRenderer.mFullscreenVAO);
+ glUseProgram(layerProgram.program);
+ updateGlobalParams(state, mRenderer.mTemporalHistory.SourceAvailableCount(), mRenderer.mTemporalHistory.AvailableCountForLayer(state.layerId));
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+ glUseProgram(0);
+ glBindVertexArray(0);
+ UnbindLayerTextureAssets(layerProgram, historyCap);
+}
+
+void OpenGLRenderPass::BindLayerTextureAssets(const LayerProgram& layerProgram)
+{
+ const GLuint shaderTextureBase = layerProgram.shaderTextureBase != 0 ? layerProgram.shaderTextureBase : kSourceHistoryTextureUnitBase;
+ for (std::size_t index = 0; index < layerProgram.textureBindings.size(); ++index)
+ {
+ glActiveTexture(GL_TEXTURE0 + shaderTextureBase + static_cast(index));
+ glBindTexture(GL_TEXTURE_2D, layerProgram.textureBindings[index].texture);
+ }
+ const GLuint textTextureBase = shaderTextureBase + static_cast(layerProgram.textureBindings.size());
+ for (std::size_t index = 0; index < layerProgram.textBindings.size(); ++index)
+ {
+ glActiveTexture(GL_TEXTURE0 + textTextureBase + static_cast(index));
+ glBindTexture(GL_TEXTURE_2D, layerProgram.textBindings[index].texture);
+ }
+ glActiveTexture(GL_TEXTURE0);
+}
+
+void OpenGLRenderPass::UnbindLayerTextureAssets(const LayerProgram& layerProgram, unsigned historyCap)
+{
+ 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);
+ }
+ const GLuint shaderTextureBase = layerProgram.shaderTextureBase != 0 ? layerProgram.shaderTextureBase : kSourceHistoryTextureUnitBase;
+ for (std::size_t index = 0; index < layerProgram.textureBindings.size() + layerProgram.textBindings.size(); ++index)
+ {
+ glActiveTexture(GL_TEXTURE0 + shaderTextureBase + static_cast(index));
+ glBindTexture(GL_TEXTURE_2D, 0);
+ }
+ glActiveTexture(GL_TEXTURE0 + kDecodedVideoTextureUnit);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glActiveTexture(GL_TEXTURE0);
+}
diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLRenderPass.h b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLRenderPass.h
new file mode 100644
index 0000000..97ba462
--- /dev/null
+++ b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLRenderPass.h
@@ -0,0 +1,44 @@
+#pragma once
+
+#include "OpenGLRenderer.h"
+#include "ShaderTypes.h"
+
+#include
+#include
+#include
+
+class OpenGLRenderPass
+{
+public:
+ using LayerProgram = OpenGLRenderer::LayerProgram;
+ using TextBindingUpdater = std::function;
+ using GlobalParamsUpdater = std::function;
+
+ explicit OpenGLRenderPass(OpenGLRenderer& renderer);
+
+ void Render(
+ bool hasInputSource,
+ const std::vector& layerStates,
+ unsigned inputFrameWidth,
+ unsigned inputFrameHeight,
+ unsigned historyCap,
+ const TextBindingUpdater& updateTextBinding,
+ const GlobalParamsUpdater& updateGlobalParams);
+
+private:
+ void RenderDecodePass(unsigned inputFrameWidth, unsigned inputFrameHeight);
+ void RenderShaderProgram(
+ GLuint sourceTexture,
+ GLuint destinationFrameBuffer,
+ LayerProgram& layerProgram,
+ const RuntimeRenderState& state,
+ unsigned inputFrameWidth,
+ unsigned inputFrameHeight,
+ unsigned historyCap,
+ const TextBindingUpdater& updateTextBinding,
+ const GlobalParamsUpdater& updateGlobalParams);
+ void BindLayerTextureAssets(const LayerProgram& layerProgram);
+ void UnbindLayerTextureAssets(const LayerProgram& layerProgram, unsigned historyCap);
+
+ OpenGLRenderer& mRenderer;
+};
diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLShaderPrograms.cpp b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLShaderPrograms.cpp
new file mode 100644
index 0000000..0b3ed87
--- /dev/null
+++ b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLShaderPrograms.cpp
@@ -0,0 +1,427 @@
+#include "OpenGLShaderPrograms.h"
+
+#include "GlRenderConstants.h"
+#include "GlScopedObjects.h"
+#include "GlShaderSources.h"
+#include "Std140Buffer.h"
+#include "TextRasterizer.h"
+#include "TextureAssetLoader.h"
+
+#include
+#include
+#include
+#include
+#include
+
+namespace
+{
+void CopyErrorMessage(const std::string& message, int errorMessageSize, char* errorMessage)
+{
+ if (!errorMessage || errorMessageSize <= 0)
+ return;
+
+ strncpy_s(errorMessage, errorMessageSize, message.c_str(), _TRUNCATE);
+}
+
+std::string TextValueForBinding(const RuntimeRenderState& state, const std::string& parameterId)
+{
+ auto valueIt = state.parameterValues.find(parameterId);
+ return valueIt == state.parameterValues.end() ? std::string() : valueIt->second.textValue;
+}
+
+const ShaderFontAsset* FindFontAssetForParameter(const RuntimeRenderState& state, const ShaderParameterDefinition& definition)
+{
+ if (!definition.fontId.empty())
+ {
+ for (const ShaderFontAsset& fontAsset : state.fontAssets)
+ {
+ if (fontAsset.id == definition.fontId)
+ return &fontAsset;
+ }
+ }
+ return state.fontAssets.empty() ? nullptr : &state.fontAssets.front();
+}
+
+GLint FindSamplerUniformLocation(GLuint program, const std::string& samplerName)
+{
+ GLint location = glGetUniformLocation(program, samplerName.c_str());
+ if (location >= 0)
+ return location;
+ return glGetUniformLocation(program, (samplerName + "_0").c_str());
+}
+}
+
+OpenGLShaderPrograms::OpenGLShaderPrograms(OpenGLRenderer& renderer, RuntimeHost& runtimeHost) :
+ mRenderer(renderer),
+ mRuntimeHost(runtimeHost)
+{
+}
+
+bool OpenGLShaderPrograms::CompileLayerPrograms(unsigned inputFrameWidth, unsigned inputFrameHeight, int errorMessageSize, char* errorMessage)
+{
+ const std::vector layerStates = mRuntimeHost.GetLayerRenderStates(inputFrameWidth, inputFrameHeight);
+ std::string temporalError;
+ const unsigned historyCap = mRuntimeHost.GetMaxTemporalHistoryFrames();
+ if (!mRenderer.mTemporalHistory.ValidateTextureUnitBudget(layerStates, historyCap, temporalError))
+ {
+ CopyErrorMessage(temporalError, errorMessageSize, errorMessage);
+ return false;
+ }
+ if (!mRenderer.mTemporalHistory.EnsureResources(layerStates, historyCap, inputFrameWidth, inputFrameHeight, temporalError))
+ {
+ CopyErrorMessage(temporalError, errorMessageSize, errorMessage);
+ return false;
+ }
+
+ std::vector newPrograms;
+ newPrograms.reserve(layerStates.size());
+
+ for (const RuntimeRenderState& state : layerStates)
+ {
+ LayerProgram layerProgram;
+ if (!CompileSingleLayerProgram(state, layerProgram, errorMessageSize, errorMessage))
+ {
+ for (LayerProgram& program : newPrograms)
+ DestroySingleLayerProgram(program);
+ return false;
+ }
+ newPrograms.push_back(layerProgram);
+ }
+
+ DestroyLayerPrograms();
+ mRenderer.mLayerPrograms.swap(newPrograms);
+
+ mRuntimeHost.SetCompileStatus(true, "Shader layers compiled successfully.");
+ mRuntimeHost.ClearReloadRequest();
+
+ return true;
+}
+
+bool OpenGLShaderPrograms::CompileSingleLayerProgram(const RuntimeRenderState& state, LayerProgram& layerProgram, int errorMessageSize, char* errorMessage)
+{
+ GLsizei errorBufferSize = 0;
+ GLint compileResult = GL_FALSE;
+ GLint linkResult = GL_FALSE;
+ std::string fragmentShaderSource;
+ std::string loadError;
+ std::vector textureBindings;
+ const char* vertexSource = kFullscreenTriangleVertexShaderSource;
+
+ if (!mRuntimeHost.BuildLayerFragmentShaderSource(state.layerId, fragmentShaderSource, loadError))
+ {
+ CopyErrorMessage(loadError, errorMessageSize, errorMessage);
+ return false;
+ }
+
+ const char* fragmentSource = fragmentShaderSource.c_str();
+
+ ScopedGlShader newVertexShader(glCreateShader(GL_VERTEX_SHADER));
+ glShaderSource(newVertexShader.get(), 1, (const GLchar**)&vertexSource, NULL);
+ glCompileShader(newVertexShader.get());
+ glGetShaderiv(newVertexShader.get(), GL_COMPILE_STATUS, &compileResult);
+ if (compileResult == GL_FALSE)
+ {
+ glGetShaderInfoLog(newVertexShader.get(), errorMessageSize, &errorBufferSize, errorMessage);
+ return false;
+ }
+
+ ScopedGlShader newFragmentShader(glCreateShader(GL_FRAGMENT_SHADER));
+ glShaderSource(newFragmentShader.get(), 1, (const GLchar**)&fragmentSource, NULL);
+ glCompileShader(newFragmentShader.get());
+ glGetShaderiv(newFragmentShader.get(), GL_COMPILE_STATUS, &compileResult);
+ if (compileResult == GL_FALSE)
+ {
+ glGetShaderInfoLog(newFragmentShader.get(), errorMessageSize, &errorBufferSize, errorMessage);
+ return false;
+ }
+
+ ScopedGlProgram newProgram(glCreateProgram());
+ glAttachShader(newProgram.get(), newVertexShader.get());
+ glAttachShader(newProgram.get(), newFragmentShader.get());
+ glLinkProgram(newProgram.get());
+ glGetProgramiv(newProgram.get(), GL_LINK_STATUS, &linkResult);
+ if (linkResult == GL_FALSE)
+ {
+ glGetProgramInfoLog(newProgram.get(), errorMessageSize, &errorBufferSize, errorMessage);
+ return false;
+ }
+
+ for (const ShaderTextureAsset& textureAsset : state.textureAssets)
+ {
+ LayerProgram::TextureBinding textureBinding;
+ textureBinding.samplerName = textureAsset.id;
+ textureBinding.sourcePath = textureAsset.path;
+ if (!LoadTextureAsset(textureAsset, textureBinding.texture, loadError))
+ {
+ for (LayerProgram::TextureBinding& loadedTexture : textureBindings)
+ {
+ if (loadedTexture.texture != 0)
+ glDeleteTextures(1, &loadedTexture.texture);
+ }
+ CopyErrorMessage(loadError, errorMessageSize, errorMessage);
+ return false;
+ }
+ textureBindings.push_back(textureBinding);
+ }
+
+ std::vector textBindings;
+ for (const ShaderParameterDefinition& definition : state.parameterDefinitions)
+ {
+ if (definition.type != ShaderParameterType::Text)
+ continue;
+ LayerProgram::TextBinding textBinding;
+ textBinding.parameterId = definition.id;
+ textBinding.samplerName = definition.id + "Texture";
+ textBinding.fontId = definition.fontId;
+ glGenTextures(1, &textBinding.texture);
+ glBindTexture(GL_TEXTURE_2D, textBinding.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);
+ std::vector empty(static_cast(kTextTextureWidth) * kTextTextureHeight * 4, 0);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kTextTextureWidth, kTextTextureHeight, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, empty.data());
+ glBindTexture(GL_TEXTURE_2D, 0);
+ textBindings.push_back(textBinding);
+ }
+
+ const GLuint globalParamsIndex = glGetUniformBlockIndex(newProgram.get(), "GlobalParams");
+ if (globalParamsIndex != GL_INVALID_INDEX)
+ glUniformBlockBinding(newProgram.get(), globalParamsIndex, kGlobalParamsBindingPoint);
+
+ const unsigned historyCap = mRuntimeHost.GetMaxTemporalHistoryFrames();
+ const GLuint shaderTextureBase = state.isTemporal ? kSourceHistoryTextureUnitBase + historyCap + historyCap : kSourceHistoryTextureUnitBase;
+ glUseProgram(newProgram.get());
+ const GLint videoInputLocation = glGetUniformLocation(newProgram.get(), "gVideoInput");
+ if (videoInputLocation >= 0)
+ glUniform1i(videoInputLocation, static_cast(kDecodedVideoTextureUnit));
+ for (unsigned index = 0; index < historyCap; ++index)
+ {
+ const std::string sourceSamplerName = "gSourceHistory" + std::to_string(index);
+ const GLint sourceSamplerLocation = glGetUniformLocation(newProgram.get(), sourceSamplerName.c_str());
+ if (sourceSamplerLocation >= 0)
+ glUniform1i(sourceSamplerLocation, static_cast(kSourceHistoryTextureUnitBase + index));
+
+ const std::string temporalSamplerName = "gTemporalHistory" + std::to_string(index);
+ const GLint temporalSamplerLocation = glGetUniformLocation(newProgram.get(), temporalSamplerName.c_str());
+ if (temporalSamplerLocation >= 0)
+ glUniform1i(temporalSamplerLocation, static_cast(kSourceHistoryTextureUnitBase + historyCap + index));
+ }
+ for (std::size_t index = 0; index < textureBindings.size(); ++index)
+ {
+ const GLint textureSamplerLocation = FindSamplerUniformLocation(newProgram.get(), textureBindings[index].samplerName);
+ if (textureSamplerLocation >= 0)
+ glUniform1i(textureSamplerLocation, static_cast(shaderTextureBase + static_cast(index)));
+ }
+ const GLuint textTextureBase = shaderTextureBase + static_cast(textureBindings.size());
+ for (std::size_t index = 0; index < textBindings.size(); ++index)
+ {
+ const GLint textSamplerLocation = FindSamplerUniformLocation(newProgram.get(), textBindings[index].samplerName);
+ if (textSamplerLocation >= 0)
+ glUniform1i(textSamplerLocation, static_cast(textTextureBase + static_cast(index)));
+ }
+ glUseProgram(0);
+
+ layerProgram.layerId = state.layerId;
+ layerProgram.shaderId = state.shaderId;
+ layerProgram.shaderTextureBase = shaderTextureBase;
+ layerProgram.program = newProgram.release();
+ layerProgram.vertexShader = newVertexShader.release();
+ layerProgram.fragmentShader = newFragmentShader.release();
+ layerProgram.textureBindings.swap(textureBindings);
+ layerProgram.textBindings.swap(textBindings);
+ return true;
+}
+
+bool OpenGLShaderPrograms::CompileDecodeShader(int errorMessageSize, char* errorMessage)
+{
+ GLsizei errorBufferSize = 0;
+ GLint compileResult = GL_FALSE;
+ GLint linkResult = GL_FALSE;
+ const char* vertexSource = kFullscreenTriangleVertexShaderSource;
+ const char* fragmentSource = kDecodeFragmentShaderSource;
+
+ ScopedGlShader newVertexShader(glCreateShader(GL_VERTEX_SHADER));
+ glShaderSource(newVertexShader.get(), 1, (const GLchar**)&vertexSource, NULL);
+ glCompileShader(newVertexShader.get());
+ glGetShaderiv(newVertexShader.get(), GL_COMPILE_STATUS, &compileResult);
+ if (compileResult == GL_FALSE)
+ {
+ glGetShaderInfoLog(newVertexShader.get(), errorMessageSize, &errorBufferSize, errorMessage);
+ return false;
+ }
+
+ ScopedGlShader newFragmentShader(glCreateShader(GL_FRAGMENT_SHADER));
+ glShaderSource(newFragmentShader.get(), 1, (const GLchar**)&fragmentSource, NULL);
+ glCompileShader(newFragmentShader.get());
+ glGetShaderiv(newFragmentShader.get(), GL_COMPILE_STATUS, &compileResult);
+ if (compileResult == GL_FALSE)
+ {
+ glGetShaderInfoLog(newFragmentShader.get(), errorMessageSize, &errorBufferSize, errorMessage);
+ return false;
+ }
+
+ ScopedGlProgram newProgram(glCreateProgram());
+ glAttachShader(newProgram.get(), newVertexShader.get());
+ glAttachShader(newProgram.get(), newFragmentShader.get());
+ glLinkProgram(newProgram.get());
+ glGetProgramiv(newProgram.get(), GL_LINK_STATUS, &linkResult);
+ if (linkResult == GL_FALSE)
+ {
+ glGetProgramInfoLog(newProgram.get(), errorMessageSize, &errorBufferSize, errorMessage);
+ return false;
+ }
+
+ DestroyDecodeShaderProgram();
+ mRenderer.mDecodeProgram = newProgram.release();
+ mRenderer.mDecodeVertexShader = newVertexShader.release();
+ mRenderer.mDecodeFragmentShader = newFragmentShader.release();
+ return true;
+}
+
+void OpenGLShaderPrograms::DestroySingleLayerProgram(LayerProgram& layerProgram)
+{
+ mRenderer.DestroySingleLayerProgram(layerProgram);
+}
+
+void OpenGLShaderPrograms::DestroyLayerPrograms()
+{
+ mRenderer.DestroyLayerPrograms();
+}
+
+void OpenGLShaderPrograms::DestroyDecodeShaderProgram()
+{
+ mRenderer.DestroyDecodeShaderProgram();
+}
+
+void OpenGLShaderPrograms::ResetTemporalHistoryState()
+{
+ mRenderer.mTemporalHistory.ResetState();
+}
+
+bool OpenGLShaderPrograms::LoadTextureAsset(const ShaderTextureAsset& textureAsset, GLuint& textureId, std::string& error)
+{
+ return ::LoadTextureAsset(textureAsset, textureId, error);
+}
+
+bool OpenGLShaderPrograms::UpdateTextBindingTexture(const RuntimeRenderState& state, LayerProgram::TextBinding& textBinding, std::string& error)
+{
+ const std::string text = TextValueForBinding(state, textBinding.parameterId);
+ if (text == textBinding.renderedText && textBinding.renderedWidth == kTextTextureWidth && textBinding.renderedHeight == kTextTextureHeight)
+ return true;
+
+ auto definitionIt = std::find_if(state.parameterDefinitions.begin(), state.parameterDefinitions.end(),
+ [&textBinding](const ShaderParameterDefinition& definition) { return definition.id == textBinding.parameterId; });
+ if (definitionIt == state.parameterDefinitions.end())
+ return true;
+
+ const ShaderFontAsset* fontAsset = FindFontAssetForParameter(state, *definitionIt);
+ std::filesystem::path fontPath;
+ if (fontAsset)
+ fontPath = fontAsset->path;
+
+ std::vector sdf;
+ if (!RasterizeTextSdf(text, fontPath, sdf, error))
+ return false;
+
+ GLint previousActiveTexture = 0;
+ GLint previousUnpackBuffer = 0;
+ glGetIntegerv(GL_ACTIVE_TEXTURE, &previousActiveTexture);
+ glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, &previousUnpackBuffer);
+ glActiveTexture(GL_TEXTURE0);
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+ glBindTexture(GL_TEXTURE_2D, textBinding.texture);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, kTextTextureWidth, kTextTextureHeight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, sdf.data());
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, static_cast(previousUnpackBuffer));
+ glActiveTexture(static_cast(previousActiveTexture));
+
+ textBinding.renderedText = text;
+ textBinding.renderedWidth = kTextTextureWidth;
+ textBinding.renderedHeight = kTextTextureHeight;
+ return true;
+}
+
+bool OpenGLShaderPrograms::UpdateGlobalParamsBuffer(const RuntimeRenderState& state, unsigned availableSourceHistoryLength, unsigned availableTemporalHistoryLength)
+{
+ std::vector buffer;
+ buffer.reserve(512);
+
+ AppendStd140Float(buffer, static_cast(state.timeSeconds));
+ AppendStd140Vec2(buffer, static_cast(state.inputWidth), static_cast(state.inputHeight));
+ AppendStd140Vec2(buffer, static_cast(state.outputWidth), static_cast(state.outputHeight));
+ AppendStd140Float(buffer, static_cast(state.frameCount));
+ AppendStd140Float(buffer, static_cast(state.mixAmount));
+ AppendStd140Float(buffer, static_cast(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(effectiveSourceHistoryLength));
+ AppendStd140Int(buffer, static_cast(effectiveTemporalHistoryLength));
+
+ for (const ShaderParameterDefinition& definition : state.parameterDefinitions)
+ {
+ auto valueIt = state.parameterValues.find(definition.id);
+ const ShaderParameterValue value = valueIt != state.parameterValues.end()
+ ? valueIt->second
+ : ShaderParameterValue();
+
+ switch (definition.type)
+ {
+ case ShaderParameterType::Float:
+ AppendStd140Float(buffer, value.numberValues.empty() ? 0.0f : static_cast(value.numberValues[0]));
+ break;
+ case ShaderParameterType::Vec2:
+ AppendStd140Vec2(buffer,
+ value.numberValues.size() > 0 ? static_cast(value.numberValues[0]) : 0.0f,
+ value.numberValues.size() > 1 ? static_cast(value.numberValues[1]) : 0.0f);
+ break;
+ case ShaderParameterType::Color:
+ AppendStd140Vec4(buffer,
+ value.numberValues.size() > 0 ? static_cast(value.numberValues[0]) : 1.0f,
+ value.numberValues.size() > 1 ? static_cast(value.numberValues[1]) : 1.0f,
+ value.numberValues.size() > 2 ? static_cast(value.numberValues[2]) : 1.0f,
+ value.numberValues.size() > 3 ? static_cast(value.numberValues[3]) : 1.0f);
+ break;
+ case ShaderParameterType::Boolean:
+ AppendStd140Int(buffer, value.booleanValue ? 1 : 0);
+ break;
+ case ShaderParameterType::Enum:
+ {
+ int selectedIndex = 0;
+ for (std::size_t optionIndex = 0; optionIndex < definition.enumOptions.size(); ++optionIndex)
+ {
+ if (definition.enumOptions[optionIndex].value == value.enumValue)
+ {
+ selectedIndex = static_cast(optionIndex);
+ break;
+ }
+ }
+ AppendStd140Int(buffer, selectedIndex);
+ break;
+ }
+ case ShaderParameterType::Text:
+ break;
+ }
+ }
+
+ buffer.resize(AlignStd140(buffer.size(), 16), 0);
+
+ glBindBuffer(GL_UNIFORM_BUFFER, mRenderer.mGlobalParamsUBO);
+ if (mRenderer.mGlobalParamsUBOSize != static_cast(buffer.size()))
+ {
+ glBufferData(GL_UNIFORM_BUFFER, static_cast(buffer.size()), buffer.data(), GL_DYNAMIC_DRAW);
+ mRenderer.mGlobalParamsUBOSize = static_cast(buffer.size());
+ }
+ else
+ {
+ glBufferSubData(GL_UNIFORM_BUFFER, 0, static_cast(buffer.size()), buffer.data());
+ }
+ glBindBufferBase(GL_UNIFORM_BUFFER, kGlobalParamsBindingPoint, mRenderer.mGlobalParamsUBO);
+ glBindBuffer(GL_UNIFORM_BUFFER, 0);
+
+ return true;
+}
diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLShaderPrograms.h b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLShaderPrograms.h
new file mode 100644
index 0000000..c0cd226
--- /dev/null
+++ b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLShaderPrograms.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include "OpenGLRenderer.h"
+#include "RuntimeHost.h"
+#include "ShaderTypes.h"
+
+#include
+
+class OpenGLShaderPrograms
+{
+public:
+ using LayerProgram = OpenGLRenderer::LayerProgram;
+
+ OpenGLShaderPrograms(OpenGLRenderer& renderer, RuntimeHost& runtimeHost);
+
+ bool CompileLayerPrograms(unsigned inputFrameWidth, unsigned inputFrameHeight, int errorMessageSize, char* errorMessage);
+ bool CompileDecodeShader(int errorMessageSize, char* errorMessage);
+ void DestroyLayerPrograms();
+ void DestroySingleLayerProgram(LayerProgram& layerProgram);
+ void DestroyDecodeShaderProgram();
+ void ResetTemporalHistoryState();
+ bool UpdateTextBindingTexture(const RuntimeRenderState& state, LayerProgram::TextBinding& textBinding, std::string& error);
+ bool UpdateGlobalParamsBuffer(const RuntimeRenderState& state, unsigned availableSourceHistoryLength, unsigned availableTemporalHistoryLength);
+
+private:
+ bool CompileSingleLayerProgram(const RuntimeRenderState& state, LayerProgram& layerProgram, int errorMessageSize, char* errorMessage);
+ bool LoadTextureAsset(const ShaderTextureAsset& textureAsset, GLuint& textureId, std::string& error);
+
+ OpenGLRenderer& mRenderer;
+ RuntimeHost& mRuntimeHost;
+};