Shader compile thread seperation
All checks were successful
CI / React UI Build (push) Successful in 11s
CI / Native Windows Build And Tests (push) Successful in 1m31s
CI / Windows Release Package (push) Successful in 2m6s

This commit is contained in:
2026-05-06 14:11:18 +10:00
parent 6502344d0a
commit 08e039aebe
14 changed files with 321 additions and 107 deletions

View File

@@ -72,6 +72,8 @@ set(APP_SOURCES
"${APP_DIR}/gl/OpenGLShaderPrograms.h" "${APP_DIR}/gl/OpenGLShaderPrograms.h"
"${APP_DIR}/gl/ShaderProgramCompiler.cpp" "${APP_DIR}/gl/ShaderProgramCompiler.cpp"
"${APP_DIR}/gl/ShaderProgramCompiler.h" "${APP_DIR}/gl/ShaderProgramCompiler.h"
"${APP_DIR}/gl/ShaderBuildQueue.cpp"
"${APP_DIR}/gl/ShaderBuildQueue.h"
"${APP_DIR}/gl/ShaderTextureBindings.cpp" "${APP_DIR}/gl/ShaderTextureBindings.cpp"
"${APP_DIR}/gl/ShaderTextureBindings.h" "${APP_DIR}/gl/ShaderTextureBindings.h"
"${APP_DIR}/gl/Std140Buffer.h" "${APP_DIR}/gl/Std140Buffer.h"

View File

@@ -247,12 +247,10 @@ If neither variable is set, the workflow falls back to the repo-local defaults u
## Still Todo ## Still Todo
- Audio. - Audio.
- Improve text rendering.
- Genlock. - Genlock.
- Find a better UI library for react. - Find a better UI library for react.
- Logs. - 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) - Support a separate sound shader `.slang` file in shader packages. (https://www.shadertoy.com/view/XsBXWt)
- Add WebView2 - Add WebView2
- move to MSDF, typography rasterisation - move to MSDF, typography rasterisation
- abritary function/triggers for the shader

View File

@@ -198,6 +198,7 @@
<ClCompile Include="gl\OpenGLRenderPass.cpp" /> <ClCompile Include="gl\OpenGLRenderPass.cpp" />
<ClCompile Include="gl\OpenGLRenderer.cpp" /> <ClCompile Include="gl\OpenGLRenderer.cpp" />
<ClCompile Include="gl\OpenGLShaderPrograms.cpp" /> <ClCompile Include="gl\OpenGLShaderPrograms.cpp" />
<ClCompile Include="gl\ShaderBuildQueue.cpp" />
<ClCompile Include="gl\TemporalHistoryBuffers.cpp" /> <ClCompile Include="gl\TemporalHistoryBuffers.cpp" />
<ClCompile Include="stdafx.cpp"> <ClCompile Include="stdafx.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader> <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
@@ -218,6 +219,7 @@
<ClInclude Include="gl\OpenGLRenderPass.h" /> <ClInclude Include="gl\OpenGLRenderPass.h" />
<ClInclude Include="gl\OpenGLRenderer.h" /> <ClInclude Include="gl\OpenGLRenderer.h" />
<ClInclude Include="gl\OpenGLShaderPrograms.h" /> <ClInclude Include="gl\OpenGLShaderPrograms.h" />
<ClInclude Include="gl\ShaderBuildQueue.h" />
<ClInclude Include="gl\TemporalHistoryBuffers.h" /> <ClInclude Include="gl\TemporalHistoryBuffers.h" />
<ClInclude Include="resource.h" /> <ClInclude Include="resource.h" />
<ClInclude Include="stdafx.h" /> <ClInclude Include="stdafx.h" />

View File

@@ -36,6 +36,9 @@
<ClCompile Include="gl\OpenGLShaderPrograms.cpp"> <ClCompile Include="gl\OpenGLShaderPrograms.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="gl\ShaderBuildQueue.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="gl\TemporalHistoryBuffers.cpp"> <ClCompile Include="gl\TemporalHistoryBuffers.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
@@ -77,6 +80,9 @@
<ClInclude Include="gl\OpenGLShaderPrograms.h"> <ClInclude Include="gl\OpenGLShaderPrograms.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="gl\ShaderBuildQueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="gl\TemporalHistoryBuffers.h"> <ClInclude Include="gl\TemporalHistoryBuffers.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>

View File

@@ -8,6 +8,7 @@
#include "OpenGLRenderPass.h" #include "OpenGLRenderPass.h"
#include "OpenGLShaderPrograms.h" #include "OpenGLShaderPrograms.h"
#include "RuntimeServices.h" #include "RuntimeServices.h"
#include "ShaderBuildQueue.h"
#include <memory> #include <memory>
#include <string> #include <string>
@@ -16,7 +17,8 @@
OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) : OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) :
hGLWnd(hWnd), hGLDC(hDC), hGLRC(hRC), hGLWnd(hWnd), hGLDC(hDC), hGLRC(hRC),
mDeckLink(std::make_unique<DeckLinkSession>()), mDeckLink(std::make_unique<DeckLinkSession>()),
mRenderer(std::make_unique<OpenGLRenderer>()) mRenderer(std::make_unique<OpenGLRenderer>()),
mUseCommittedLayerStates(false)
{ {
InitializeCriticalSection(&pMutex); InitializeCriticalSection(&pMutex);
mRuntimeHost = std::make_unique<RuntimeHost>(); mRuntimeHost = std::make_unique<RuntimeHost>();
@@ -31,6 +33,7 @@ OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) :
[this]() { paintGL(); }); [this]() { paintGL(); });
mRenderPass = std::make_unique<OpenGLRenderPass>(*mRenderer); mRenderPass = std::make_unique<OpenGLRenderPass>(*mRenderer);
mShaderPrograms = std::make_unique<OpenGLShaderPrograms>(*mRenderer, *mRuntimeHost); mShaderPrograms = std::make_unique<OpenGLShaderPrograms>(*mRenderer, *mRuntimeHost);
mShaderBuildQueue = std::make_unique<ShaderBuildQueue>(*mRuntimeHost);
mRuntimeServices = std::make_unique<RuntimeServices>(); mRuntimeServices = std::make_unique<RuntimeServices>();
} }
@@ -38,6 +41,8 @@ OpenGLComposite::~OpenGLComposite()
{ {
if (mRuntimeServices) if (mRuntimeServices)
mRuntimeServices->Stop(); mRuntimeServices->Stop();
if (mShaderBuildQueue)
mShaderBuildQueue->Stop();
mDeckLink->ReleaseResources(); mDeckLink->ReleaseResources();
mRenderer->DestroyResources(); mRenderer->DestroyResources();
@@ -223,6 +228,8 @@ bool OpenGLComposite::InitOpenGLState()
MessageBoxA(NULL, compilerErrorMessage, "OpenGL shader failed to load or compile", MB_OK); MessageBoxA(NULL, compilerErrorMessage, "OpenGL shader failed to load or compile", MB_OK);
return false; return false;
} }
mCachedLayerRenderStates = mShaderPrograms->CommittedLayerStates();
mUseCommittedLayerStates = false;
mShaderPrograms->ResetTemporalHistoryState(); mShaderPrograms->ResetTemporalHistoryState();
std::string rendererError; std::string rendererError;
@@ -268,32 +275,14 @@ bool OpenGLComposite::Stop()
bool OpenGLComposite::ReloadShader() bool OpenGLComposite::ReloadShader()
{ {
char compilerErrorMessage[1024];
EnterCriticalSection(&pMutex);
wglMakeCurrent(hGLDC, hGLRC);
bool success = mShaderPrograms->CompileLayerPrograms(mDeckLink->InputFrameWidth(), mDeckLink->InputFrameHeight(), sizeof(compilerErrorMessage), compilerErrorMessage);
if (mRuntimeHost) if (mRuntimeHost)
{
mRuntimeHost->SetCompileStatus(true, "Shader rebuild queued.");
mRuntimeHost->ClearReloadRequest(); 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 RequestShaderBuild();
{
if (mRuntimeHost)
mRuntimeHost->SetCompileStatus(true, "Shader compiled successfully.");
broadcastRuntimeState(); broadcastRuntimeState();
} return true;
return success;
} }
void OpenGLComposite::renderEffect() void OpenGLComposite::renderEffect()
@@ -302,7 +291,11 @@ void OpenGLComposite::renderEffect()
const bool hasInputSource = mDeckLink->HasInputSource(); const bool hasInputSource = mDeckLink->HasInputSource();
std::vector<RuntimeRenderState> layerStates; std::vector<RuntimeRenderState> layerStates;
if (mRuntimeHost) if (mUseCommittedLayerStates)
{
layerStates = mShaderPrograms->CommittedLayerStates();
}
else if (mRuntimeHost)
{ {
if (mRuntimeHost->TryGetLayerRenderStates(mDeckLink->InputFrameWidth(), mDeckLink->InputFrameHeight(), layerStates)) if (mRuntimeHost->TryGetLayerRenderStates(mDeckLink->InputFrameWidth(), mDeckLink->InputFrameHeight(), layerStates))
mCachedLayerRenderStates = layerStates; mCachedLayerRenderStates = layerStates;
@@ -341,20 +334,42 @@ bool OpenGLComposite::ProcessRuntimePollResults()
broadcastRuntimeState(); broadcastRuntimeState();
if (!events.reloadRequested) if (!events.reloadRequested)
{
PreparedShaderBuild readyBuild;
if (!mShaderBuildQueue || !mShaderBuildQueue->TryConsumeReadyBuild(readyBuild))
return true; return true;
char compilerErrorMessage[1024] = {}; char compilerErrorMessage[1024] = {};
if (!mShaderPrograms->CompileLayerPrograms(mDeckLink->InputFrameWidth(), mDeckLink->InputFrameHeight(), sizeof(compilerErrorMessage), compilerErrorMessage)) if (!mShaderPrograms->CommitPreparedLayerPrograms(readyBuild, mDeckLink->InputFrameWidth(), mDeckLink->InputFrameHeight(), sizeof(compilerErrorMessage), compilerErrorMessage))
{ {
mRuntimeHost->SetCompileStatus(false, compilerErrorMessage); mRuntimeHost->SetCompileStatus(false, compilerErrorMessage);
mRuntimeHost->ClearReloadRequest(); mUseCommittedLayerStates = true;
broadcastRuntimeState(); broadcastRuntimeState();
return false; return false;
} }
mUseCommittedLayerStates = false;
mCachedLayerRenderStates = mShaderPrograms->CommittedLayerStates();
mShaderPrograms->ResetTemporalHistoryState(); mShaderPrograms->ResetTemporalHistoryState();
broadcastRuntimeState(); broadcastRuntimeState();
return true; return true;
}
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() void OpenGLComposite::broadcastRuntimeState()

View File

@@ -17,6 +17,7 @@
#include "RuntimeHost.h" #include "RuntimeHost.h"
#include <functional> #include <functional>
#include <atomic>
#include <map> #include <map>
#include <memory> #include <memory>
#include <string> #include <string>
@@ -28,6 +29,7 @@ class OpenGLDeckLinkBridge;
class OpenGLRenderPass; class OpenGLRenderPass;
class OpenGLShaderPrograms; class OpenGLShaderPrograms;
class RuntimeServices; class RuntimeServices;
class ShaderBuildQueue;
class OpenGLComposite class OpenGLComposite
@@ -81,12 +83,15 @@ private:
std::unique_ptr<OpenGLDeckLinkBridge> mDeckLinkBridge; std::unique_ptr<OpenGLDeckLinkBridge> mDeckLinkBridge;
std::unique_ptr<OpenGLRenderPass> mRenderPass; std::unique_ptr<OpenGLRenderPass> mRenderPass;
std::unique_ptr<OpenGLShaderPrograms> mShaderPrograms; std::unique_ptr<OpenGLShaderPrograms> mShaderPrograms;
std::unique_ptr<ShaderBuildQueue> mShaderBuildQueue;
std::unique_ptr<RuntimeServices> mRuntimeServices; std::unique_ptr<RuntimeServices> mRuntimeServices;
std::vector<RuntimeRenderState> mCachedLayerRenderStates; std::vector<RuntimeRenderState> mCachedLayerRenderStates;
std::atomic<bool> mUseCommittedLayerStates;
bool InitOpenGLState(); bool InitOpenGLState();
void renderEffect(); void renderEffect();
bool ProcessRuntimePollResults(); bool ProcessRuntimePollResults();
void RequestShaderBuild();
void broadcastRuntimeState(); void broadcastRuntimeState();
void resetTemporalHistoryState(); void resetTemporalHistoryState();
}; };

View File

@@ -36,7 +36,6 @@ bool OpenGLComposite::AddLayer(const std::string& shaderId, std::string& error)
return false; return false;
ReloadShader(); ReloadShader();
resetTemporalHistoryState();
broadcastRuntimeState(); broadcastRuntimeState();
return true; return true;
} }
@@ -47,7 +46,6 @@ bool OpenGLComposite::RemoveLayer(const std::string& layerId, std::string& error
return false; return false;
ReloadShader(); ReloadShader();
resetTemporalHistoryState();
broadcastRuntimeState(); broadcastRuntimeState();
return true; return true;
} }
@@ -58,7 +56,6 @@ bool OpenGLComposite::MoveLayer(const std::string& layerId, int direction, std::
return false; return false;
ReloadShader(); ReloadShader();
resetTemporalHistoryState();
broadcastRuntimeState(); broadcastRuntimeState();
return true; return true;
} }
@@ -69,7 +66,6 @@ bool OpenGLComposite::MoveLayerToIndex(const std::string& layerId, std::size_t t
return false; return false;
ReloadShader(); ReloadShader();
resetTemporalHistoryState();
broadcastRuntimeState(); broadcastRuntimeState();
return true; return true;
} }
@@ -80,7 +76,6 @@ bool OpenGLComposite::SetLayerBypass(const std::string& layerId, bool bypassed,
return false; return false;
ReloadShader(); ReloadShader();
resetTemporalHistoryState();
broadcastRuntimeState(); broadcastRuntimeState();
return true; return true;
} }
@@ -91,7 +86,6 @@ bool OpenGLComposite::SetLayerShader(const std::string& layerId, const std::stri
return false; return false;
ReloadShader(); ReloadShader();
resetTemporalHistoryState();
broadcastRuntimeState(); broadcastRuntimeState();
return true; return true;
} }
@@ -146,7 +140,6 @@ bool OpenGLComposite::LoadStackPreset(const std::string& presetName, std::string
return false; return false;
ReloadShader(); ReloadShader();
resetTemporalHistoryState();
broadcastRuntimeState(); broadcastRuntimeState();
return true; return true;
} }

View File

@@ -56,6 +56,53 @@ bool OpenGLShaderPrograms::CompileLayerPrograms(unsigned inputFrameWidth, unsign
DestroyLayerPrograms(); DestroyLayerPrograms();
mRenderer.ReplaceLayerPrograms(newPrograms); 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<LayerProgram> 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.SetCompileStatus(true, "Shader layers compiled successfully.");
mRuntimeHost.ClearReloadRequest(); mRuntimeHost.ClearReloadRequest();

View File

@@ -3,6 +3,7 @@
#include "GlobalParamsBuffer.h" #include "GlobalParamsBuffer.h"
#include "OpenGLRenderer.h" #include "OpenGLRenderer.h"
#include "RuntimeHost.h" #include "RuntimeHost.h"
#include "ShaderBuildQueue.h"
#include "ShaderTypes.h" #include "ShaderTypes.h"
#include "ShaderProgramCompiler.h" #include "ShaderProgramCompiler.h"
#include "ShaderTextureBindings.h" #include "ShaderTextureBindings.h"
@@ -17,11 +18,13 @@ public:
OpenGLShaderPrograms(OpenGLRenderer& renderer, RuntimeHost& runtimeHost); OpenGLShaderPrograms(OpenGLRenderer& renderer, RuntimeHost& runtimeHost);
bool CompileLayerPrograms(unsigned inputFrameWidth, unsigned inputFrameHeight, int errorMessageSize, char* errorMessage); 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); bool CompileDecodeShader(int errorMessageSize, char* errorMessage);
void DestroyLayerPrograms(); void DestroyLayerPrograms();
void DestroySingleLayerProgram(LayerProgram& layerProgram); void DestroySingleLayerProgram(LayerProgram& layerProgram);
void DestroyDecodeShaderProgram(); void DestroyDecodeShaderProgram();
void ResetTemporalHistoryState(); void ResetTemporalHistoryState();
const std::vector<RuntimeRenderState>& CommittedLayerStates() const { return mCommittedLayerStates; }
bool UpdateTextBindingTexture(const RuntimeRenderState& state, LayerProgram::TextBinding& textBinding, std::string& error); bool UpdateTextBindingTexture(const RuntimeRenderState& state, LayerProgram::TextBinding& textBinding, std::string& error);
bool UpdateGlobalParamsBuffer(const RuntimeRenderState& state, unsigned availableSourceHistoryLength, unsigned availableTemporalHistoryLength); bool UpdateGlobalParamsBuffer(const RuntimeRenderState& state, unsigned availableSourceHistoryLength, unsigned availableTemporalHistoryLength);
@@ -31,4 +34,5 @@ private:
ShaderTextureBindings mTextureBindings; ShaderTextureBindings mTextureBindings;
GlobalParamsBuffer mGlobalParamsBuffer; GlobalParamsBuffer mGlobalParamsBuffer;
ShaderProgramCompiler mCompiler; ShaderProgramCompiler mCompiler;
std::vector<RuntimeRenderState> mCommittedLayerStates;
}; };

View File

@@ -0,0 +1,134 @@
#include "ShaderBuildQueue.h"
#include "RuntimeHost.h"
#include <chrono>
#include <utility>
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<std::mutex> lock(mMutex);
mHasRequest = true;
++mRequestedGeneration;
mRequestedOutputWidth = outputWidth;
mRequestedOutputHeight = outputHeight;
mHasReadyBuild = false;
}
mCondition.notify_one();
}
bool ShaderBuildQueue::TryConsumeReadyBuild(PreparedShaderBuild& build)
{
std::lock_guard<std::mutex> lock(mMutex);
if (!mHasReadyBuild)
return false;
build = std::move(mReadyBuild);
mReadyBuild = PreparedShaderBuild();
mHasReadyBuild = false;
return true;
}
void ShaderBuildQueue::Stop()
{
{
std::lock_guard<std::mutex> 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<std::mutex> lock(mMutex);
mCondition.wait(lock, [this]() { return mStopping || mHasRequest; });
if (mStopping)
return;
generation = mRequestedGeneration;
outputWidth = mRequestedOutputWidth;
outputHeight = mRequestedOutputHeight;
mHasRequest = false;
}
for (;;)
{
std::unique_lock<std::mutex> 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<std::mutex> 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;
}

View File

@@ -0,0 +1,57 @@
#pragma once
#include "ShaderTypes.h"
#include <condition_variable>
#include <cstdint>
#include <mutex>
#include <string>
#include <thread>
#include <vector>
class RuntimeHost;
struct PreparedLayerShader
{
RuntimeRenderState state;
std::string fragmentShaderSource;
};
struct PreparedShaderBuild
{
uint64_t generation = 0;
bool succeeded = false;
std::string message;
std::vector<RuntimeRenderState> layerStates;
std::vector<PreparedLayerShader> 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;
};

View File

@@ -27,13 +27,8 @@ ShaderProgramCompiler::ShaderProgramCompiler(OpenGLRenderer& renderer, RuntimeHo
bool ShaderProgramCompiler::CompileLayerProgram(const RuntimeRenderState& state, LayerProgram& layerProgram, int errorMessageSize, char* errorMessage) 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 fragmentShaderSource;
std::string loadError; std::string loadError;
std::vector<LayerProgram::TextureBinding> textureBindings;
const char* vertexSource = kFullscreenTriangleVertexShaderSource;
if (!mRuntimeHost.BuildLayerFragmentShaderSource(state.layerId, fragmentShaderSource, loadError)) if (!mRuntimeHost.BuildLayerFragmentShaderSource(state.layerId, fragmentShaderSource, loadError))
{ {
@@ -41,6 +36,17 @@ bool ShaderProgramCompiler::CompileLayerProgram(const RuntimeRenderState& state,
return false; 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<LayerProgram::TextureBinding> textureBindings;
const char* vertexSource = kFullscreenTriangleVertexShaderSource;
const char* fragmentSource = fragmentShaderSource.c_str(); const char* fragmentSource = fragmentShaderSource.c_str();
ScopedGlShader newVertexShader(glCreateShader(GL_VERTEX_SHADER)); ScopedGlShader newVertexShader(glCreateShader(GL_VERTEX_SHADER));

View File

@@ -4,6 +4,8 @@
#include "RuntimeHost.h" #include "RuntimeHost.h"
#include "ShaderTextureBindings.h" #include "ShaderTextureBindings.h"
#include <string>
class ShaderProgramCompiler class ShaderProgramCompiler
{ {
public: public:
@@ -12,6 +14,7 @@ public:
ShaderProgramCompiler(OpenGLRenderer& renderer, RuntimeHost& runtimeHost, ShaderTextureBindings& textureBindings); ShaderProgramCompiler(OpenGLRenderer& renderer, RuntimeHost& runtimeHost, ShaderTextureBindings& textureBindings);
bool CompileLayerProgram(const RuntimeRenderState& state, LayerProgram& layerProgram, int errorMessageSize, char* errorMessage); 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); bool CompileDecodeShader(int errorMessageSize, char* errorMessage);
private: private:

View File

@@ -5,10 +5,8 @@
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include <cstring> #include <cstring>
#include <fstream>
#include <gdiplus.h> #include <gdiplus.h>
#include <memory> #include <memory>
#include <sstream>
namespace namespace
{ {
@@ -163,61 +161,6 @@ std::vector<unsigned char> BuildTextSdfTexture(const std::vector<unsigned char>&
return sdf; return sdf;
} }
void WriteTextMaskDebugDump(const std::string& text, const std::vector<unsigned char>& alpha, const std::vector<unsigned char>& 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<unsigned char>& 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<char>(gray[(static_cast<std::size_t>(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<unsigned>(alphaMin, value);
alphaMax = std::max<unsigned>(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<unsigned>(sdfMin, value);
sdfMax = std::max<unsigned>(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<unsigned char>& sdf, std::string& error) bool RasterizeTextSdf(const std::string& text, const std::filesystem::path& fontPath, std::vector<unsigned char>& sdf, std::string& error)
@@ -296,6 +239,5 @@ bool RasterizeTextSdf(const std::string& text, const std::filesystem::path& font
} }
} }
sdf = BuildTextSdfTexture(alpha, kTextTextureWidth, kTextTextureHeight); sdf = BuildTextSdfTexture(alpha, kTextTextureWidth, kTextTextureHeight);
WriteTextMaskDebugDump(text, alpha, sdf, kTextTextureWidth, kTextTextureHeight);
return true; return true;
} }