diff --git a/CMakeLists.txt b/CMakeLists.txt index e99203d..e543f7b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,6 +72,8 @@ set(APP_SOURCES "${APP_DIR}/gl/OpenGLShaderPrograms.h" "${APP_DIR}/gl/ShaderProgramCompiler.cpp" "${APP_DIR}/gl/ShaderProgramCompiler.h" + "${APP_DIR}/gl/ShaderBuildQueue.cpp" + "${APP_DIR}/gl/ShaderBuildQueue.h" "${APP_DIR}/gl/ShaderTextureBindings.cpp" "${APP_DIR}/gl/ShaderTextureBindings.h" "${APP_DIR}/gl/Std140Buffer.h" diff --git a/README.md b/README.md index da14a24..3cd6252 100644 --- a/README.md +++ b/README.md @@ -247,12 +247,10 @@ If neither variable is set, the workflow falls back to the repo-local defaults u ## Still Todo - Audio. -- Improve text rendering. - Genlock. - Find a better UI library for react. - Logs. -- Continue source cleanup/refactoring. Pass 1 done +- Continue source cleanup/refactoring. Pass 2 done - Support a separate sound shader `.slang` file in shader packages. (https://www.shadertoy.com/view/XsBXWt) - Add WebView2 -- move to MSDF, typography rasterisation -- abritary function/triggers for the shader \ No newline at end of file +- move to MSDF, typography rasterisation \ No newline at end of file diff --git a/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj b/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj index bead286..1eb62d8 100644 --- a/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj +++ b/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj @@ -198,6 +198,7 @@ + Create @@ -218,6 +219,7 @@ + diff --git a/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj.filters b/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj.filters index 35ec3e1..d8ae771 100644 --- a/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj.filters +++ b/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj.filters @@ -36,6 +36,9 @@ Source Files + + Source Files + Source Files @@ -77,6 +80,9 @@ Header Files + + Header Files + Header Files diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp index 2d10b12..4c37e9c 100644 --- a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp +++ b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp @@ -8,6 +8,7 @@ #include "OpenGLRenderPass.h" #include "OpenGLShaderPrograms.h" #include "RuntimeServices.h" +#include "ShaderBuildQueue.h" #include #include @@ -16,7 +17,8 @@ OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) : hGLWnd(hWnd), hGLDC(hDC), hGLRC(hRC), mDeckLink(std::make_unique()), - mRenderer(std::make_unique()) + mRenderer(std::make_unique()), + mUseCommittedLayerStates(false) { InitializeCriticalSection(&pMutex); mRuntimeHost = std::make_unique(); @@ -31,6 +33,7 @@ OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) : [this]() { paintGL(); }); mRenderPass = std::make_unique(*mRenderer); mShaderPrograms = std::make_unique(*mRenderer, *mRuntimeHost); + mShaderBuildQueue = std::make_unique(*mRuntimeHost); mRuntimeServices = std::make_unique(); } @@ -38,6 +41,8 @@ OpenGLComposite::~OpenGLComposite() { if (mRuntimeServices) mRuntimeServices->Stop(); + if (mShaderBuildQueue) + mShaderBuildQueue->Stop(); mDeckLink->ReleaseResources(); mRenderer->DestroyResources(); @@ -223,6 +228,8 @@ bool OpenGLComposite::InitOpenGLState() MessageBoxA(NULL, compilerErrorMessage, "OpenGL shader failed to load or compile", MB_OK); return false; } + mCachedLayerRenderStates = mShaderPrograms->CommittedLayerStates(); + mUseCommittedLayerStates = false; mShaderPrograms->ResetTemporalHistoryState(); std::string rendererError; @@ -268,32 +275,14 @@ bool OpenGLComposite::Stop() bool OpenGLComposite::ReloadShader() { - char compilerErrorMessage[1024]; - - EnterCriticalSection(&pMutex); - wglMakeCurrent(hGLDC, hGLRC); - - bool success = mShaderPrograms->CompileLayerPrograms(mDeckLink->InputFrameWidth(), mDeckLink->InputFrameHeight(), sizeof(compilerErrorMessage), compilerErrorMessage); if (mRuntimeHost) + { + mRuntimeHost->SetCompileStatus(true, "Shader rebuild queued."); mRuntimeHost->ClearReloadRequest(); - - wglMakeCurrent(NULL, NULL); - LeaveCriticalSection(&pMutex); - - if (!success) - { - if (mRuntimeHost) - mRuntimeHost->SetCompileStatus(false, compilerErrorMessage); - MessageBoxA(NULL, compilerErrorMessage, "Slang shader reload failed", MB_OK); } - else - { - if (mRuntimeHost) - mRuntimeHost->SetCompileStatus(true, "Shader compiled successfully."); - broadcastRuntimeState(); - } - - return success; + RequestShaderBuild(); + broadcastRuntimeState(); + return true; } void OpenGLComposite::renderEffect() @@ -302,7 +291,11 @@ void OpenGLComposite::renderEffect() const bool hasInputSource = mDeckLink->HasInputSource(); std::vector layerStates; - if (mRuntimeHost) + if (mUseCommittedLayerStates) + { + layerStates = mShaderPrograms->CommittedLayerStates(); + } + else if (mRuntimeHost) { if (mRuntimeHost->TryGetLayerRenderStates(mDeckLink->InputFrameWidth(), mDeckLink->InputFrameHeight(), layerStates)) mCachedLayerRenderStates = layerStates; @@ -341,22 +334,44 @@ bool OpenGLComposite::ProcessRuntimePollResults() broadcastRuntimeState(); if (!events.reloadRequested) - return true; - - char compilerErrorMessage[1024] = {}; - if (!mShaderPrograms->CompileLayerPrograms(mDeckLink->InputFrameWidth(), mDeckLink->InputFrameHeight(), sizeof(compilerErrorMessage), compilerErrorMessage)) { - mRuntimeHost->SetCompileStatus(false, compilerErrorMessage); - mRuntimeHost->ClearReloadRequest(); + PreparedShaderBuild readyBuild; + if (!mShaderBuildQueue || !mShaderBuildQueue->TryConsumeReadyBuild(readyBuild)) + return true; + + char compilerErrorMessage[1024] = {}; + if (!mShaderPrograms->CommitPreparedLayerPrograms(readyBuild, mDeckLink->InputFrameWidth(), mDeckLink->InputFrameHeight(), sizeof(compilerErrorMessage), compilerErrorMessage)) + { + mRuntimeHost->SetCompileStatus(false, compilerErrorMessage); + mUseCommittedLayerStates = true; + broadcastRuntimeState(); + return false; + } + + mUseCommittedLayerStates = false; + mCachedLayerRenderStates = mShaderPrograms->CommittedLayerStates(); + mShaderPrograms->ResetTemporalHistoryState(); broadcastRuntimeState(); - return false; + return true; } - mShaderPrograms->ResetTemporalHistoryState(); + mRuntimeHost->SetCompileStatus(true, "Shader rebuild queued."); + RequestShaderBuild(); broadcastRuntimeState(); return true; } +void OpenGLComposite::RequestShaderBuild() +{ + if (!mShaderBuildQueue || !mDeckLink) + return; + + mUseCommittedLayerStates = true; + if (mRuntimeHost) + mRuntimeHost->ClearReloadRequest(); + mShaderBuildQueue->RequestBuild(mDeckLink->InputFrameWidth(), mDeckLink->InputFrameHeight()); +} + void OpenGLComposite::broadcastRuntimeState() { if (mRuntimeServices) diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.h b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.h index 0b9e025..5db2c94 100644 --- a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.h +++ b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.h @@ -17,6 +17,7 @@ #include "RuntimeHost.h" #include +#include #include #include #include @@ -28,6 +29,7 @@ class OpenGLDeckLinkBridge; class OpenGLRenderPass; class OpenGLShaderPrograms; class RuntimeServices; +class ShaderBuildQueue; class OpenGLComposite @@ -81,12 +83,15 @@ private: std::unique_ptr mDeckLinkBridge; std::unique_ptr mRenderPass; std::unique_ptr mShaderPrograms; + std::unique_ptr mShaderBuildQueue; std::unique_ptr mRuntimeServices; std::vector mCachedLayerRenderStates; + std::atomic mUseCommittedLayerStates; bool InitOpenGLState(); void renderEffect(); bool ProcessRuntimePollResults(); + void RequestShaderBuild(); void broadcastRuntimeState(); void resetTemporalHistoryState(); }; diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLCompositeRuntimeControls.cpp b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLCompositeRuntimeControls.cpp index ac01bfd..38344ca 100644 --- a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLCompositeRuntimeControls.cpp +++ b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLCompositeRuntimeControls.cpp @@ -36,7 +36,6 @@ bool OpenGLComposite::AddLayer(const std::string& shaderId, std::string& error) return false; ReloadShader(); - resetTemporalHistoryState(); broadcastRuntimeState(); return true; } @@ -47,7 +46,6 @@ bool OpenGLComposite::RemoveLayer(const std::string& layerId, std::string& error return false; ReloadShader(); - resetTemporalHistoryState(); broadcastRuntimeState(); return true; } @@ -58,7 +56,6 @@ bool OpenGLComposite::MoveLayer(const std::string& layerId, int direction, std:: return false; ReloadShader(); - resetTemporalHistoryState(); broadcastRuntimeState(); return true; } @@ -69,7 +66,6 @@ bool OpenGLComposite::MoveLayerToIndex(const std::string& layerId, std::size_t t return false; ReloadShader(); - resetTemporalHistoryState(); broadcastRuntimeState(); return true; } @@ -80,7 +76,6 @@ bool OpenGLComposite::SetLayerBypass(const std::string& layerId, bool bypassed, return false; ReloadShader(); - resetTemporalHistoryState(); broadcastRuntimeState(); return true; } @@ -91,7 +86,6 @@ bool OpenGLComposite::SetLayerShader(const std::string& layerId, const std::stri return false; ReloadShader(); - resetTemporalHistoryState(); broadcastRuntimeState(); return true; } @@ -146,7 +140,6 @@ bool OpenGLComposite::LoadStackPreset(const std::string& presetName, std::string return false; ReloadShader(); - resetTemporalHistoryState(); broadcastRuntimeState(); return true; } diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLShaderPrograms.cpp b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLShaderPrograms.cpp index 5204e60..2b50d14 100644 --- a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLShaderPrograms.cpp +++ b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLShaderPrograms.cpp @@ -56,6 +56,53 @@ bool OpenGLShaderPrograms::CompileLayerPrograms(unsigned inputFrameWidth, unsign DestroyLayerPrograms(); mRenderer.ReplaceLayerPrograms(newPrograms); + mCommittedLayerStates = layerStates; + + mRuntimeHost.SetCompileStatus(true, "Shader layers compiled successfully."); + mRuntimeHost.ClearReloadRequest(); + + return true; +} + +bool OpenGLShaderPrograms::CommitPreparedLayerPrograms(const PreparedShaderBuild& preparedBuild, unsigned inputFrameWidth, unsigned inputFrameHeight, int errorMessageSize, char* errorMessage) +{ + if (!preparedBuild.succeeded) + { + CopyErrorMessage(preparedBuild.message, errorMessageSize, errorMessage); + return false; + } + + std::string temporalError; + const unsigned historyCap = mRuntimeHost.GetMaxTemporalHistoryFrames(); + if (!mRenderer.TemporalHistory().ValidateTextureUnitBudget(preparedBuild.layerStates, historyCap, temporalError)) + { + CopyErrorMessage(temporalError, errorMessageSize, errorMessage); + return false; + } + if (!mRenderer.TemporalHistory().EnsureResources(preparedBuild.layerStates, historyCap, inputFrameWidth, inputFrameHeight, temporalError)) + { + CopyErrorMessage(temporalError, errorMessageSize, errorMessage); + return false; + } + + std::vector newPrograms; + newPrograms.reserve(preparedBuild.layers.size()); + + for (const PreparedLayerShader& preparedLayer : preparedBuild.layers) + { + LayerProgram layerProgram; + if (!mCompiler.CompilePreparedLayerProgram(preparedLayer.state, preparedLayer.fragmentShaderSource, layerProgram, errorMessageSize, errorMessage)) + { + for (LayerProgram& program : newPrograms) + DestroySingleLayerProgram(program); + return false; + } + newPrograms.push_back(layerProgram); + } + + DestroyLayerPrograms(); + mRenderer.ReplaceLayerPrograms(newPrograms); + mCommittedLayerStates = preparedBuild.layerStates; mRuntimeHost.SetCompileStatus(true, "Shader layers compiled successfully."); mRuntimeHost.ClearReloadRequest(); diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLShaderPrograms.h b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLShaderPrograms.h index f4746ea..a69611d 100644 --- a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLShaderPrograms.h +++ b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLShaderPrograms.h @@ -3,6 +3,7 @@ #include "GlobalParamsBuffer.h" #include "OpenGLRenderer.h" #include "RuntimeHost.h" +#include "ShaderBuildQueue.h" #include "ShaderTypes.h" #include "ShaderProgramCompiler.h" #include "ShaderTextureBindings.h" @@ -17,11 +18,13 @@ public: OpenGLShaderPrograms(OpenGLRenderer& renderer, RuntimeHost& runtimeHost); bool CompileLayerPrograms(unsigned inputFrameWidth, unsigned inputFrameHeight, int errorMessageSize, char* errorMessage); + bool CommitPreparedLayerPrograms(const PreparedShaderBuild& preparedBuild, unsigned inputFrameWidth, unsigned inputFrameHeight, int errorMessageSize, char* errorMessage); bool CompileDecodeShader(int errorMessageSize, char* errorMessage); void DestroyLayerPrograms(); void DestroySingleLayerProgram(LayerProgram& layerProgram); void DestroyDecodeShaderProgram(); void ResetTemporalHistoryState(); + const std::vector& CommittedLayerStates() const { return mCommittedLayerStates; } bool UpdateTextBindingTexture(const RuntimeRenderState& state, LayerProgram::TextBinding& textBinding, std::string& error); bool UpdateGlobalParamsBuffer(const RuntimeRenderState& state, unsigned availableSourceHistoryLength, unsigned availableTemporalHistoryLength); @@ -31,4 +34,5 @@ private: ShaderTextureBindings mTextureBindings; GlobalParamsBuffer mGlobalParamsBuffer; ShaderProgramCompiler mCompiler; + std::vector mCommittedLayerStates; }; diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/ShaderBuildQueue.cpp b/apps/LoopThroughWithOpenGLCompositing/gl/ShaderBuildQueue.cpp new file mode 100644 index 0000000..f69b72b --- /dev/null +++ b/apps/LoopThroughWithOpenGLCompositing/gl/ShaderBuildQueue.cpp @@ -0,0 +1,134 @@ +#include "ShaderBuildQueue.h" + +#include "RuntimeHost.h" + +#include +#include + +namespace +{ +constexpr auto kShaderBuildDebounce = std::chrono::milliseconds(400); +} + +ShaderBuildQueue::ShaderBuildQueue(RuntimeHost& runtimeHost) : + mRuntimeHost(runtimeHost), + mWorkerThread([this]() { WorkerLoop(); }) +{ +} + +ShaderBuildQueue::~ShaderBuildQueue() +{ + Stop(); +} + +void ShaderBuildQueue::RequestBuild(unsigned outputWidth, unsigned outputHeight) +{ + { + std::lock_guard lock(mMutex); + mHasRequest = true; + ++mRequestedGeneration; + mRequestedOutputWidth = outputWidth; + mRequestedOutputHeight = outputHeight; + mHasReadyBuild = false; + } + mCondition.notify_one(); +} + +bool ShaderBuildQueue::TryConsumeReadyBuild(PreparedShaderBuild& build) +{ + std::lock_guard lock(mMutex); + if (!mHasReadyBuild) + return false; + + build = std::move(mReadyBuild); + mReadyBuild = PreparedShaderBuild(); + mHasReadyBuild = false; + return true; +} + +void ShaderBuildQueue::Stop() +{ + { + std::lock_guard lock(mMutex); + if (mStopping) + return; + mStopping = true; + } + mCondition.notify_one(); + if (mWorkerThread.joinable()) + mWorkerThread.join(); +} + +void ShaderBuildQueue::WorkerLoop() +{ + for (;;) + { + uint64_t generation = 0; + unsigned outputWidth = 0; + unsigned outputHeight = 0; + { + std::unique_lock lock(mMutex); + mCondition.wait(lock, [this]() { return mStopping || mHasRequest; }); + if (mStopping) + return; + + generation = mRequestedGeneration; + outputWidth = mRequestedOutputWidth; + outputHeight = mRequestedOutputHeight; + mHasRequest = false; + } + + for (;;) + { + std::unique_lock lock(mMutex); + if (mCondition.wait_for(lock, kShaderBuildDebounce, [this, generation]() { + return mStopping || (mHasRequest && mRequestedGeneration != generation); + })) + { + if (mStopping) + return; + + generation = mRequestedGeneration; + outputWidth = mRequestedOutputWidth; + outputHeight = mRequestedOutputHeight; + mHasRequest = false; + continue; + } + break; + } + + PreparedShaderBuild build = Build(generation, outputWidth, outputHeight); + + std::lock_guard lock(mMutex); + if (mStopping) + return; + if (generation != mRequestedGeneration) + continue; + mReadyBuild = std::move(build); + mHasReadyBuild = true; + } +} + +PreparedShaderBuild ShaderBuildQueue::Build(uint64_t generation, unsigned outputWidth, unsigned outputHeight) +{ + PreparedShaderBuild build; + build.generation = generation; + build.layerStates = mRuntimeHost.GetLayerRenderStates(outputWidth, outputHeight); + build.layers.reserve(build.layerStates.size()); + + for (const RuntimeRenderState& state : build.layerStates) + { + PreparedLayerShader layer; + layer.state = state; + if (!mRuntimeHost.BuildLayerFragmentShaderSource(state.layerId, layer.fragmentShaderSource, build.message)) + { + build.succeeded = false; + return build; + } + build.layers.push_back(std::move(layer)); + } + + build.succeeded = true; + build.message = "Shader layers prepared successfully."; + return build; +} diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/ShaderBuildQueue.h b/apps/LoopThroughWithOpenGLCompositing/gl/ShaderBuildQueue.h new file mode 100644 index 0000000..6975962 --- /dev/null +++ b/apps/LoopThroughWithOpenGLCompositing/gl/ShaderBuildQueue.h @@ -0,0 +1,57 @@ +#pragma once + +#include "ShaderTypes.h" + +#include +#include +#include +#include +#include +#include + +class RuntimeHost; + +struct PreparedLayerShader +{ + RuntimeRenderState state; + std::string fragmentShaderSource; +}; + +struct PreparedShaderBuild +{ + uint64_t generation = 0; + bool succeeded = false; + std::string message; + std::vector layerStates; + std::vector layers; +}; + +class ShaderBuildQueue +{ +public: + explicit ShaderBuildQueue(RuntimeHost& runtimeHost); + ~ShaderBuildQueue(); + + ShaderBuildQueue(const ShaderBuildQueue&) = delete; + ShaderBuildQueue& operator=(const ShaderBuildQueue&) = delete; + + void RequestBuild(unsigned outputWidth, unsigned outputHeight); + bool TryConsumeReadyBuild(PreparedShaderBuild& build); + void Stop(); + +private: + void WorkerLoop(); + PreparedShaderBuild Build(uint64_t generation, unsigned outputWidth, unsigned outputHeight); + + RuntimeHost& mRuntimeHost; + std::thread mWorkerThread; + std::mutex mMutex; + std::condition_variable mCondition; + bool mStopping = false; + bool mHasRequest = false; + uint64_t mRequestedGeneration = 0; + unsigned mRequestedOutputWidth = 0; + unsigned mRequestedOutputHeight = 0; + bool mHasReadyBuild = false; + PreparedShaderBuild mReadyBuild; +}; diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/ShaderProgramCompiler.cpp b/apps/LoopThroughWithOpenGLCompositing/gl/ShaderProgramCompiler.cpp index 70c9ca0..14caf29 100644 --- a/apps/LoopThroughWithOpenGLCompositing/gl/ShaderProgramCompiler.cpp +++ b/apps/LoopThroughWithOpenGLCompositing/gl/ShaderProgramCompiler.cpp @@ -27,13 +27,8 @@ ShaderProgramCompiler::ShaderProgramCompiler(OpenGLRenderer& renderer, RuntimeHo 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)) { @@ -41,6 +36,17 @@ bool ShaderProgramCompiler::CompileLayerProgram(const RuntimeRenderState& state, return false; } + return CompilePreparedLayerProgram(state, fragmentShaderSource, layerProgram, errorMessageSize, errorMessage); +} + +bool ShaderProgramCompiler::CompilePreparedLayerProgram(const RuntimeRenderState& state, const std::string& fragmentShaderSource, LayerProgram& layerProgram, int errorMessageSize, char* errorMessage) +{ + GLsizei errorBufferSize = 0; + GLint compileResult = GL_FALSE; + GLint linkResult = GL_FALSE; + std::string loadError; + std::vector textureBindings; + const char* vertexSource = kFullscreenTriangleVertexShaderSource; const char* fragmentSource = fragmentShaderSource.c_str(); ScopedGlShader newVertexShader(glCreateShader(GL_VERTEX_SHADER)); diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/ShaderProgramCompiler.h b/apps/LoopThroughWithOpenGLCompositing/gl/ShaderProgramCompiler.h index ef4333f..8083a11 100644 --- a/apps/LoopThroughWithOpenGLCompositing/gl/ShaderProgramCompiler.h +++ b/apps/LoopThroughWithOpenGLCompositing/gl/ShaderProgramCompiler.h @@ -4,6 +4,8 @@ #include "RuntimeHost.h" #include "ShaderTextureBindings.h" +#include + class ShaderProgramCompiler { public: @@ -12,6 +14,7 @@ public: ShaderProgramCompiler(OpenGLRenderer& renderer, RuntimeHost& runtimeHost, ShaderTextureBindings& textureBindings); bool CompileLayerProgram(const RuntimeRenderState& state, LayerProgram& layerProgram, int errorMessageSize, char* errorMessage); + bool CompilePreparedLayerProgram(const RuntimeRenderState& state, const std::string& fragmentShaderSource, LayerProgram& layerProgram, int errorMessageSize, char* errorMessage); bool CompileDecodeShader(int errorMessageSize, char* errorMessage); private: diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/TextRasterizer.cpp b/apps/LoopThroughWithOpenGLCompositing/gl/TextRasterizer.cpp index b5e11c9..8f0535b 100644 --- a/apps/LoopThroughWithOpenGLCompositing/gl/TextRasterizer.cpp +++ b/apps/LoopThroughWithOpenGLCompositing/gl/TextRasterizer.cpp @@ -5,10 +5,8 @@ #include #include #include -#include #include #include -#include namespace { @@ -163,61 +161,6 @@ std::vector BuildTextSdfTexture(const std::vector& return sdf; } -void WriteTextMaskDebugDump(const std::string& text, const std::vector& alpha, const std::vector& sdf, unsigned width, unsigned height) -{ - try - { - std::filesystem::path debugDir = std::filesystem::current_path() / "runtime"; - std::filesystem::create_directories(debugDir); - - auto writePgm = [width, height](const std::filesystem::path& path, const std::vector& gray, std::size_t stride) - { - std::ofstream out(path, std::ios::binary); - if (!out) - return; - out << "P5\n" << width << " " << height << "\n255\n"; - for (unsigned y = 0; y < height; ++y) - { - for (unsigned x = 0; x < width; ++x) - out.put(static_cast(gray[(static_cast(y) * width + x) * stride])); - } - }; - - writePgm(debugDir / "text-mask-alpha-debug.pgm", alpha, 1); - writePgm(debugDir / "text-mask-sdf-debug.pgm", sdf, 4); - - unsigned alphaMin = 255; - unsigned alphaMax = 0; - unsigned sdfMin = 255; - unsigned sdfMax = 0; - std::size_t alphaLit = 0; - std::size_t sdfLit = 0; - for (unsigned char value : alpha) - { - alphaMin = std::min(alphaMin, value); - alphaMax = std::max(alphaMax, value); - if (value > 0) - ++alphaLit; - } - for (std::size_t index = 0; index < sdf.size(); index += 4) - { - const unsigned char value = sdf[index]; - sdfMin = std::min(sdfMin, value); - sdfMax = std::max(sdfMax, value); - if (value > 127) - ++sdfLit; - } - - std::ostringstream message; - message << "Text mask debug for '" << text << "': alpha min/max/lit=" << alphaMin << "/" << alphaMax << "/" << alphaLit - << ", sdf min/max/gt127=" << sdfMin << "/" << sdfMax << "/" << sdfLit << "\n"; - OutputDebugStringA(message.str().c_str()); - } - catch (...) - { - OutputDebugStringA("Failed to write text mask debug dump.\n"); - } -} } bool RasterizeTextSdf(const std::string& text, const std::filesystem::path& fontPath, std::vector& sdf, std::string& error) @@ -296,6 +239,5 @@ bool RasterizeTextSdf(const std::string& text, const std::filesystem::path& font } } sdf = BuildTextSdfTexture(alpha, kTextTextureWidth, kTextTextureHeight); - WriteTextMaskDebugDump(text, alpha, sdf, kTextTextureWidth, kTextTextureHeight); return true; }