#include "ShaderBuildQueue.h" #include "RuntimeHost.h" #include #include namespace { constexpr auto kShaderBuildDebounce = std::chrono::milliseconds(400); } ShaderBuildQueue::ShaderBuildQueue(RuntimeHost& runtimeHost) : mRuntimeHost(runtimeHost), mWorkerThread([this]() { WorkerLoop(); }) { } ShaderBuildQueue::~ShaderBuildQueue() { Stop(); } void ShaderBuildQueue::RequestBuild(unsigned outputWidth, unsigned outputHeight) { { std::lock_guard lock(mMutex); mHasRequest = true; ++mRequestedGeneration; mRequestedOutputWidth = outputWidth; mRequestedOutputHeight = outputHeight; mHasReadyBuild = false; } mCondition.notify_one(); } bool ShaderBuildQueue::TryConsumeReadyBuild(PreparedShaderBuild& build) { std::lock_guard lock(mMutex); if (!mHasReadyBuild) return false; build = std::move(mReadyBuild); mReadyBuild = PreparedShaderBuild(); mHasReadyBuild = false; return true; } void ShaderBuildQueue::Stop() { { std::lock_guard lock(mMutex); if (mStopping) return; mStopping = true; } mCondition.notify_one(); if (mWorkerThread.joinable()) mWorkerThread.join(); } void ShaderBuildQueue::WorkerLoop() { for (;;) { uint64_t generation = 0; unsigned outputWidth = 0; unsigned outputHeight = 0; { std::unique_lock lock(mMutex); mCondition.wait(lock, [this]() { return mStopping || mHasRequest; }); if (mStopping) return; generation = mRequestedGeneration; outputWidth = mRequestedOutputWidth; outputHeight = mRequestedOutputHeight; mHasRequest = false; } for (;;) { std::unique_lock lock(mMutex); if (mCondition.wait_for(lock, kShaderBuildDebounce, [this, generation]() { return mStopping || (mHasRequest && mRequestedGeneration != generation); })) { if (mStopping) return; generation = mRequestedGeneration; outputWidth = mRequestedOutputWidth; outputHeight = mRequestedOutputHeight; mHasRequest = false; continue; } break; } PreparedShaderBuild build = Build(generation, outputWidth, outputHeight); std::lock_guard lock(mMutex); if (mStopping) return; if (generation != mRequestedGeneration) continue; mReadyBuild = std::move(build); mHasReadyBuild = true; } } PreparedShaderBuild ShaderBuildQueue::Build(uint64_t generation, unsigned outputWidth, unsigned outputHeight) { PreparedShaderBuild build; build.generation = generation; build.layerStates = mRuntimeHost.GetLayerRenderStates(outputWidth, outputHeight); build.layers.reserve(build.layerStates.size()); for (const RuntimeRenderState& state : build.layerStates) { PreparedLayerShader layer; layer.state = state; if (!mRuntimeHost.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; }