Shader compile thread seperation
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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
|
||||
- move to MSDF, typography rasterisation
|
||||
@@ -198,6 +198,7 @@
|
||||
<ClCompile Include="gl\OpenGLRenderPass.cpp" />
|
||||
<ClCompile Include="gl\OpenGLRenderer.cpp" />
|
||||
<ClCompile Include="gl\OpenGLShaderPrograms.cpp" />
|
||||
<ClCompile Include="gl\ShaderBuildQueue.cpp" />
|
||||
<ClCompile Include="gl\TemporalHistoryBuffers.cpp" />
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
@@ -218,6 +219,7 @@
|
||||
<ClInclude Include="gl\OpenGLRenderPass.h" />
|
||||
<ClInclude Include="gl\OpenGLRenderer.h" />
|
||||
<ClInclude Include="gl\OpenGLShaderPrograms.h" />
|
||||
<ClInclude Include="gl\ShaderBuildQueue.h" />
|
||||
<ClInclude Include="gl\TemporalHistoryBuffers.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="stdafx.h" />
|
||||
|
||||
@@ -36,6 +36,9 @@
|
||||
<ClCompile Include="gl\OpenGLShaderPrograms.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="gl\ShaderBuildQueue.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="gl\TemporalHistoryBuffers.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
@@ -77,6 +80,9 @@
|
||||
<ClInclude Include="gl\OpenGLShaderPrograms.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="gl\ShaderBuildQueue.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="gl\TemporalHistoryBuffers.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "OpenGLRenderPass.h"
|
||||
#include "OpenGLShaderPrograms.h"
|
||||
#include "RuntimeServices.h"
|
||||
#include "ShaderBuildQueue.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -16,7 +17,8 @@
|
||||
OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) :
|
||||
hGLWnd(hWnd), hGLDC(hDC), hGLRC(hRC),
|
||||
mDeckLink(std::make_unique<DeckLinkSession>()),
|
||||
mRenderer(std::make_unique<OpenGLRenderer>())
|
||||
mRenderer(std::make_unique<OpenGLRenderer>()),
|
||||
mUseCommittedLayerStates(false)
|
||||
{
|
||||
InitializeCriticalSection(&pMutex);
|
||||
mRuntimeHost = std::make_unique<RuntimeHost>();
|
||||
@@ -31,6 +33,7 @@ OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) :
|
||||
[this]() { paintGL(); });
|
||||
mRenderPass = std::make_unique<OpenGLRenderPass>(*mRenderer);
|
||||
mShaderPrograms = std::make_unique<OpenGLShaderPrograms>(*mRenderer, *mRuntimeHost);
|
||||
mShaderBuildQueue = std::make_unique<ShaderBuildQueue>(*mRuntimeHost);
|
||||
mRuntimeServices = std::make_unique<RuntimeServices>();
|
||||
}
|
||||
|
||||
@@ -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<RuntimeRenderState> 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)
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "RuntimeHost.h"
|
||||
|
||||
#include <functional>
|
||||
#include <atomic>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -28,6 +29,7 @@ class OpenGLDeckLinkBridge;
|
||||
class OpenGLRenderPass;
|
||||
class OpenGLShaderPrograms;
|
||||
class RuntimeServices;
|
||||
class ShaderBuildQueue;
|
||||
|
||||
|
||||
class OpenGLComposite
|
||||
@@ -81,12 +83,15 @@ private:
|
||||
std::unique_ptr<OpenGLDeckLinkBridge> mDeckLinkBridge;
|
||||
std::unique_ptr<OpenGLRenderPass> mRenderPass;
|
||||
std::unique_ptr<OpenGLShaderPrograms> mShaderPrograms;
|
||||
std::unique_ptr<ShaderBuildQueue> mShaderBuildQueue;
|
||||
std::unique_ptr<RuntimeServices> mRuntimeServices;
|
||||
std::vector<RuntimeRenderState> mCachedLayerRenderStates;
|
||||
std::atomic<bool> mUseCommittedLayerStates;
|
||||
|
||||
bool InitOpenGLState();
|
||||
void renderEffect();
|
||||
bool ProcessRuntimePollResults();
|
||||
void RequestShaderBuild();
|
||||
void broadcastRuntimeState();
|
||||
void resetTemporalHistoryState();
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<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.ClearReloadRequest();
|
||||
|
||||
@@ -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<RuntimeRenderState>& 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<RuntimeRenderState> mCommittedLayerStates;
|
||||
};
|
||||
|
||||
134
apps/LoopThroughWithOpenGLCompositing/gl/ShaderBuildQueue.cpp
Normal file
134
apps/LoopThroughWithOpenGLCompositing/gl/ShaderBuildQueue.cpp
Normal 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;
|
||||
}
|
||||
57
apps/LoopThroughWithOpenGLCompositing/gl/ShaderBuildQueue.h
Normal file
57
apps/LoopThroughWithOpenGLCompositing/gl/ShaderBuildQueue.h
Normal 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;
|
||||
};
|
||||
@@ -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<LayerProgram::TextureBinding> 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<LayerProgram::TextureBinding> textureBindings;
|
||||
const char* vertexSource = kFullscreenTriangleVertexShaderSource;
|
||||
const char* fragmentSource = fragmentShaderSource.c_str();
|
||||
|
||||
ScopedGlShader newVertexShader(glCreateShader(GL_VERTEX_SHADER));
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include "RuntimeHost.h"
|
||||
#include "ShaderTextureBindings.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
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:
|
||||
|
||||
@@ -5,10 +5,8 @@
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <gdiplus.h>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -163,61 +161,6 @@ std::vector<unsigned char> BuildTextSdfTexture(const std::vector<unsigned char>&
|
||||
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)
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user