135 lines
3.0 KiB
C++
135 lines
3.0 KiB
C++
#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.BuildLayerPassFragmentShaderSources(state.layerId, layer.passes, 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;
|
|
}
|