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; +};