#include "ShaderBuildQueue.h" #include "RuntimeEventDispatcher.h" #include #include namespace { constexpr auto kShaderBuildDebounce = std::chrono::milliseconds(400); } ShaderBuildQueue::ShaderBuildQueue(RuntimeSnapshotProvider& runtimeSnapshotProvider, RuntimeEventDispatcher& runtimeEventDispatcher) : mRuntimeSnapshotProvider(runtimeSnapshotProvider), mRuntimeEventDispatcher(runtimeEventDispatcher), 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; } bool ShaderBuildQueue::TryConsumeReadyBuild(uint64_t expectedGeneration, PreparedShaderBuild& build) { std::lock_guard lock(mMutex); if (!mHasReadyBuild || mReadyBuild.generation != expectedGeneration) 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); bool shouldPublish = false; { std::lock_guard lock(mMutex); if (mStopping) return; if (generation != mRequestedGeneration) continue; mReadyBuild = build; mHasReadyBuild = true; shouldPublish = true; } if (shouldPublish) PublishBuildLifecycleEvent(build, outputWidth, outputHeight); } } PreparedShaderBuild ShaderBuildQueue::Build(uint64_t generation, unsigned outputWidth, unsigned outputHeight) { PreparedShaderBuild build; build.generation = generation; build.renderSnapshot = mRuntimeSnapshotProvider.PublishRenderStateSnapshot(outputWidth, outputHeight); build.layers.reserve(build.renderSnapshot.states.size()); for (const RuntimeRenderState& state : build.renderSnapshot.states) { PreparedLayerShader layer; layer.state = state; if (!mRuntimeSnapshotProvider.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; } void ShaderBuildQueue::PublishBuildLifecycleEvent(const PreparedShaderBuild& build, unsigned outputWidth, unsigned outputHeight) const { ShaderBuildEvent event; event.phase = build.succeeded ? RuntimeEventShaderBuildPhase::Prepared : RuntimeEventShaderBuildPhase::Failed; event.generation = build.generation; event.inputWidth = outputWidth; event.inputHeight = outputHeight; event.succeeded = build.succeeded; event.message = build.message; mRuntimeEventDispatcher.PublishPayload(event, "ShaderBuildQueue"); }