#include "RuntimeShaderPrepareWorker.h" #include "../platform/HiddenGlWindow.h" #include "RuntimeShaderRenderer.h" #include #include #include #include RuntimeShaderPrepareWorker::~RuntimeShaderPrepareWorker() { Stop(); } bool RuntimeShaderPrepareWorker::Start(std::unique_ptr sharedWindow, std::string& error) { if (mThread.joinable()) return true; if (!sharedWindow || sharedWindow->DeviceContext() == nullptr || sharedWindow->Context() == nullptr) { error = "Runtime shader prepare worker needs an existing shared GL context."; return false; } mWindow = std::move(sharedWindow); mStopping.store(false, std::memory_order_release); mStarted.store(false, std::memory_order_release); { std::lock_guard lock(mMutex); mStartupReady = false; mStartupError.clear(); } mThread = std::thread([this]() { ThreadMain(); }); std::unique_lock lock(mMutex); if (!mStartupCondition.wait_for(lock, std::chrono::seconds(3), [this]() { return mStartupReady || !mStartupError.empty(); })) { error = "Timed out starting runtime shader prepare worker."; lock.unlock(); Stop(); return false; } if (!mStartupError.empty()) { error = mStartupError; lock.unlock(); Stop(); return false; } return true; } void RuntimeShaderPrepareWorker::Stop() { mStopping.store(true, std::memory_order_release); mCondition.notify_all(); if (mThread.joinable()) mThread.join(); std::deque completed; { std::lock_guard lock(mMutex); mRequests.clear(); completed.swap(mCompleted); } for (RuntimePreparedShaderProgram& program : completed) program.ReleaseGl(); mWindow.reset(); mStarted.store(false, std::memory_order_release); } void RuntimeShaderPrepareWorker::Submit(const std::vector& layers) { std::lock_guard lock(mMutex); for (const RenderCadenceCompositor::RuntimeRenderLayerModel& layer : layers) { if (layer.artifact.fragmentShaderSource.empty()) continue; PrepareRequest request; request.layerId = layer.id; request.shaderId = layer.shaderId; request.sourceFingerprint = Fingerprint(layer.artifact); request.artifact = layer.artifact; auto sameLayer = [&request](const PrepareRequest& existing) { return existing.layerId == request.layerId; }; mRequests.erase(std::remove_if(mRequests.begin(), mRequests.end(), sameLayer), mRequests.end()); mRequests.push_back(std::move(request)); } mCondition.notify_one(); } bool RuntimeShaderPrepareWorker::TryConsume(RuntimePreparedShaderProgram& preparedProgram) { std::lock_guard lock(mMutex); if (mCompleted.empty()) return false; preparedProgram = std::move(mCompleted.front()); mCompleted.pop_front(); return true; } void RuntimeShaderPrepareWorker::ThreadMain() { if (!mWindow || !mWindow->MakeCurrent()) { std::lock_guard lock(mMutex); mStartupError = "Runtime shader prepare worker could not make shared GL context current."; mStartupCondition.notify_all(); return; } { std::lock_guard lock(mMutex); mStartupReady = true; } mStarted.store(true, std::memory_order_release); mStartupCondition.notify_all(); while (!mStopping.load(std::memory_order_acquire)) { PrepareRequest request; { std::unique_lock lock(mMutex); mCondition.wait(lock, [this]() { return mStopping.load(std::memory_order_acquire) || !mRequests.empty(); }); if (mStopping.load(std::memory_order_acquire)) break; request = std::move(mRequests.front()); mRequests.pop_front(); } RuntimePreparedShaderProgram preparedProgram; RuntimeShaderRenderer::BuildPreparedProgram( request.layerId, request.sourceFingerprint, request.artifact, preparedProgram); glFlush(); std::lock_guard lock(mMutex); mCompleted.push_back(std::move(preparedProgram)); } mWindow->ClearCurrent(); } std::string RuntimeShaderPrepareWorker::Fingerprint(const RuntimeShaderArtifact& artifact) { const std::hash hasher; return artifact.shaderId + ":" + std::to_string(artifact.fragmentShaderSource.size()) + ":" + std::to_string(hasher(artifact.fragmentShaderSource)); }