Shader compile thread seperation
This commit is contained in:
@@ -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"
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -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" />
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
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)
|
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));
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user