From 8b9e2916df6a6d1486642513947eb42b2b76cc57 Mon Sep 17 00:00:00 2001 From: Aiden Date: Wed, 6 May 2026 11:03:16 +1000 Subject: [PATCH] Shader ownership --- CMakeLists.txt | 6 + .../gl/GlobalParamsBuffer.cpp | 95 +++++ .../gl/GlobalParamsBuffer.h | 15 + .../gl/OpenGLShaderPrograms.cpp | 342 +----------------- .../gl/OpenGLShaderPrograms.h | 9 +- .../gl/ShaderProgramCompiler.cpp | 188 ++++++++++ .../gl/ShaderProgramCompiler.h | 21 ++ .../gl/ShaderTextureBindings.cpp | 104 ++++++ .../gl/ShaderTextureBindings.h | 18 + 9 files changed, 461 insertions(+), 337 deletions(-) create mode 100644 apps/LoopThroughWithOpenGLCompositing/gl/GlobalParamsBuffer.cpp create mode 100644 apps/LoopThroughWithOpenGLCompositing/gl/GlobalParamsBuffer.h create mode 100644 apps/LoopThroughWithOpenGLCompositing/gl/ShaderProgramCompiler.cpp create mode 100644 apps/LoopThroughWithOpenGLCompositing/gl/ShaderProgramCompiler.h create mode 100644 apps/LoopThroughWithOpenGLCompositing/gl/ShaderTextureBindings.cpp create mode 100644 apps/LoopThroughWithOpenGLCompositing/gl/ShaderTextureBindings.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 6bb6ef3..535d39e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,6 +51,8 @@ set(APP_SOURCES "${APP_DIR}/decklink/DeckLinkSession.h" "${APP_DIR}/gl/GLExtensions.cpp" "${APP_DIR}/gl/GLExtensions.h" + "${APP_DIR}/gl/GlobalParamsBuffer.cpp" + "${APP_DIR}/gl/GlobalParamsBuffer.h" "${APP_DIR}/gl/GlRenderConstants.h" "${APP_DIR}/gl/GlScopedObjects.h" "${APP_DIR}/gl/GlShaderSources.cpp" @@ -66,6 +68,10 @@ set(APP_SOURCES "${APP_DIR}/gl/OpenGLRenderer.h" "${APP_DIR}/gl/OpenGLShaderPrograms.cpp" "${APP_DIR}/gl/OpenGLShaderPrograms.h" + "${APP_DIR}/gl/ShaderProgramCompiler.cpp" + "${APP_DIR}/gl/ShaderProgramCompiler.h" + "${APP_DIR}/gl/ShaderTextureBindings.cpp" + "${APP_DIR}/gl/ShaderTextureBindings.h" "${APP_DIR}/gl/Std140Buffer.h" "${APP_DIR}/gl/TextRasterizer.cpp" "${APP_DIR}/gl/TextRasterizer.h" diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/GlobalParamsBuffer.cpp b/apps/LoopThroughWithOpenGLCompositing/gl/GlobalParamsBuffer.cpp new file mode 100644 index 0000000..1f611a6 --- /dev/null +++ b/apps/LoopThroughWithOpenGLCompositing/gl/GlobalParamsBuffer.cpp @@ -0,0 +1,95 @@ +#include "GlobalParamsBuffer.h" + +#include "GlRenderConstants.h" +#include "Std140Buffer.h" + +#include + +GlobalParamsBuffer::GlobalParamsBuffer(OpenGLRenderer& renderer) : + mRenderer(renderer) +{ +} + +bool GlobalParamsBuffer::Update(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.GlobalParamsUBO()); + if (mRenderer.GlobalParamsUBOSize() != static_cast(buffer.size())) + { + glBufferData(GL_UNIFORM_BUFFER, static_cast(buffer.size()), buffer.data(), GL_DYNAMIC_DRAW); + mRenderer.SetGlobalParamsUBOSize(static_cast(buffer.size())); + } + else + { + glBufferSubData(GL_UNIFORM_BUFFER, 0, static_cast(buffer.size()), buffer.data()); + } + glBindBufferBase(GL_UNIFORM_BUFFER, kGlobalParamsBindingPoint, mRenderer.GlobalParamsUBO()); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + + return true; +} diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/GlobalParamsBuffer.h b/apps/LoopThroughWithOpenGLCompositing/gl/GlobalParamsBuffer.h new file mode 100644 index 0000000..4f38d51 --- /dev/null +++ b/apps/LoopThroughWithOpenGLCompositing/gl/GlobalParamsBuffer.h @@ -0,0 +1,15 @@ +#pragma once + +#include "OpenGLRenderer.h" +#include "ShaderTypes.h" + +class GlobalParamsBuffer +{ +public: + explicit GlobalParamsBuffer(OpenGLRenderer& renderer); + + bool Update(const RuntimeRenderState& state, unsigned availableSourceHistoryLength, unsigned availableTemporalHistoryLength); + +private: + OpenGLRenderer& mRenderer; +}; diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLShaderPrograms.cpp b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLShaderPrograms.cpp index bdedd04..5204e60 100644 --- a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLShaderPrograms.cpp +++ b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLShaderPrograms.cpp @@ -1,16 +1,7 @@ #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 #include namespace @@ -22,38 +13,13 @@ void CopyErrorMessage(const std::string& message, int errorMessageSize, char* er 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) + mRuntimeHost(runtimeHost), + mGlobalParamsBuffer(renderer), + mCompiler(renderer, runtimeHost, mTextureBindings) { } @@ -79,7 +45,7 @@ bool OpenGLShaderPrograms::CompileLayerPrograms(unsigned inputFrameWidth, unsign for (const RuntimeRenderState& state : layerStates) { LayerProgram layerProgram; - if (!CompileSingleLayerProgram(state, layerProgram, errorMessageSize, errorMessage)) + if (!mCompiler.CompileLayerProgram(state, layerProgram, errorMessageSize, errorMessage)) { for (LayerProgram& program : newPrograms) DestroySingleLayerProgram(program); @@ -97,184 +63,9 @@ bool OpenGLShaderPrograms::CompileLayerPrograms(unsigned inputFrameWidth, unsign 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.SetDecodeShaderProgram(newProgram.release(), newVertexShader.release(), newFragmentShader.release()); - return true; + return mCompiler.CompileDecodeShader(errorMessageSize, errorMessage); } void OpenGLShaderPrograms::DestroySingleLayerProgram(LayerProgram& layerProgram) @@ -297,129 +88,12 @@ void OpenGLShaderPrograms::ResetTemporalHistoryState() mRenderer.TemporalHistory().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; + return mTextureBindings.UpdateTextBindingTexture(state, textBinding, error); } 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.GlobalParamsUBO()); - if (mRenderer.GlobalParamsUBOSize() != static_cast(buffer.size())) - { - glBufferData(GL_UNIFORM_BUFFER, static_cast(buffer.size()), buffer.data(), GL_DYNAMIC_DRAW); - mRenderer.SetGlobalParamsUBOSize(static_cast(buffer.size())); - } - else - { - glBufferSubData(GL_UNIFORM_BUFFER, 0, static_cast(buffer.size()), buffer.data()); - } - glBindBufferBase(GL_UNIFORM_BUFFER, kGlobalParamsBindingPoint, mRenderer.GlobalParamsUBO()); - glBindBuffer(GL_UNIFORM_BUFFER, 0); - - return true; + return mGlobalParamsBuffer.Update(state, availableSourceHistoryLength, availableTemporalHistoryLength); } diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLShaderPrograms.h b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLShaderPrograms.h index c0cd226..f4746ea 100644 --- a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLShaderPrograms.h +++ b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLShaderPrograms.h @@ -1,8 +1,11 @@ #pragma once +#include "GlobalParamsBuffer.h" #include "OpenGLRenderer.h" #include "RuntimeHost.h" #include "ShaderTypes.h" +#include "ShaderProgramCompiler.h" +#include "ShaderTextureBindings.h" #include @@ -23,9 +26,9 @@ public: 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; + ShaderTextureBindings mTextureBindings; + GlobalParamsBuffer mGlobalParamsBuffer; + ShaderProgramCompiler mCompiler; }; diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/ShaderProgramCompiler.cpp b/apps/LoopThroughWithOpenGLCompositing/gl/ShaderProgramCompiler.cpp new file mode 100644 index 0000000..70c9ca0 --- /dev/null +++ b/apps/LoopThroughWithOpenGLCompositing/gl/ShaderProgramCompiler.cpp @@ -0,0 +1,188 @@ +#include "ShaderProgramCompiler.h" + +#include "GlRenderConstants.h" +#include "GlScopedObjects.h" +#include "GlShaderSources.h" + +#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); +} +} + +ShaderProgramCompiler::ShaderProgramCompiler(OpenGLRenderer& renderer, RuntimeHost& runtimeHost, ShaderTextureBindings& textureBindings) : + mRenderer(renderer), + mRuntimeHost(runtimeHost), + mTextureBindings(textureBindings) +{ +} + +bool ShaderProgramCompiler::CompileLayerProgram(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 (!mTextureBindings.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; + mTextureBindings.CreateTextBindings(state, textBindings); + + 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 = mTextureBindings.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 = mTextureBindings.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 ShaderProgramCompiler::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; + } + + mRenderer.DestroyDecodeShaderProgram(); + mRenderer.SetDecodeShaderProgram(newProgram.release(), newVertexShader.release(), newFragmentShader.release()); + return true; +} diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/ShaderProgramCompiler.h b/apps/LoopThroughWithOpenGLCompositing/gl/ShaderProgramCompiler.h new file mode 100644 index 0000000..ef4333f --- /dev/null +++ b/apps/LoopThroughWithOpenGLCompositing/gl/ShaderProgramCompiler.h @@ -0,0 +1,21 @@ +#pragma once + +#include "OpenGLRenderer.h" +#include "RuntimeHost.h" +#include "ShaderTextureBindings.h" + +class ShaderProgramCompiler +{ +public: + using LayerProgram = OpenGLRenderer::LayerProgram; + + ShaderProgramCompiler(OpenGLRenderer& renderer, RuntimeHost& runtimeHost, ShaderTextureBindings& textureBindings); + + bool CompileLayerProgram(const RuntimeRenderState& state, LayerProgram& layerProgram, int errorMessageSize, char* errorMessage); + bool CompileDecodeShader(int errorMessageSize, char* errorMessage); + +private: + OpenGLRenderer& mRenderer; + RuntimeHost& mRuntimeHost; + ShaderTextureBindings& mTextureBindings; +}; diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/ShaderTextureBindings.cpp b/apps/LoopThroughWithOpenGLCompositing/gl/ShaderTextureBindings.cpp new file mode 100644 index 0000000..a2e9e76 --- /dev/null +++ b/apps/LoopThroughWithOpenGLCompositing/gl/ShaderTextureBindings.cpp @@ -0,0 +1,104 @@ +#include "ShaderTextureBindings.h" + +#include "GlRenderConstants.h" +#include "TextRasterizer.h" +#include "TextureAssetLoader.h" + +#include +#include + +namespace +{ +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(); +} +} + +bool ShaderTextureBindings::LoadTextureAsset(const ShaderTextureAsset& textureAsset, GLuint& textureId, std::string& error) +{ + return ::LoadTextureAsset(textureAsset, textureId, error); +} + +void ShaderTextureBindings::CreateTextBindings(const RuntimeRenderState& state, 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); + } +} + +bool ShaderTextureBindings::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; +} + +GLint ShaderTextureBindings::FindSamplerUniformLocation(GLuint program, const std::string& samplerName) const +{ + GLint location = glGetUniformLocation(program, samplerName.c_str()); + if (location >= 0) + return location; + return glGetUniformLocation(program, (samplerName + "_0").c_str()); +} diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/ShaderTextureBindings.h b/apps/LoopThroughWithOpenGLCompositing/gl/ShaderTextureBindings.h new file mode 100644 index 0000000..8f98ade --- /dev/null +++ b/apps/LoopThroughWithOpenGLCompositing/gl/ShaderTextureBindings.h @@ -0,0 +1,18 @@ +#pragma once + +#include "OpenGLRenderer.h" +#include "ShaderTypes.h" + +#include +#include + +class ShaderTextureBindings +{ +public: + using LayerProgram = OpenGLRenderer::LayerProgram; + + bool LoadTextureAsset(const ShaderTextureAsset& textureAsset, GLuint& textureId, std::string& error); + void CreateTextBindings(const RuntimeRenderState& state, std::vector& textBindings); + bool UpdateTextBindingTexture(const RuntimeRenderState& state, LayerProgram::TextBinding& textBinding, std::string& error); + GLint FindSamplerUniformLocation(GLuint program, const std::string& samplerName) const; +};