From d88341f9db5d2cc09036bab5461d6bcb1e60c7e6 Mon Sep 17 00:00:00 2001 From: Mike Solar Date: Fri, 3 Jul 2026 02:46:56 +0800 Subject: [PATCH] Revert OCIO LUT processor generation to synchronous Background generation caused the UI to freeze indefinitely when switching LUT files, likely due to a deadlock between the worker process, the preview autocacher, and the asynchronous set_processor path. Synchronous generation is fast enough for typical 33^3 .cube files and keeps the cache invalidation logic simple and safe. The passthrough fix in OCIOBaseNode::Value() is retained so the viewer shows the input frame while a processor is being created. --- app/node/color/ociolut/ociolut.cpp | 99 ++++-------------------------- app/node/color/ociolut/ociolut.h | 29 --------- 2 files changed, 11 insertions(+), 117 deletions(-) diff --git a/app/node/color/ociolut/ociolut.cpp b/app/node/color/ociolut/ociolut.cpp index aea41b933..ed025aa7c 100644 --- a/app/node/color/ociolut/ociolut.cpp +++ b/app/node/color/ociolut/ociolut.cpp @@ -155,92 +155,10 @@ void OCIOLutNode::GenerateProcessor() pending_direction_ = direction; pending_generation_++; - MaybeStartNextTask(); -} - -void OCIOLutNode::MaybeStartNextTask() -{ - if (task_running_) { - return; - } - - if (pending_path_.isEmpty()) { - return; - } - - if (pending_path_ == last_path_ && pending_direction_ == last_direction_ && - last_processor_) { - return; - } - - task_running_ = true; - const int generation = pending_generation_; - const QString path = pending_path_; - const int direction = pending_direction_; - ColorManager *manager = this->manager(); - - QThreadPool::globalInstance()->start( - new GenerateProcessorTask(this, path, direction, generation, manager)); -} - -void OCIOLutNode::SetProcessorResult(ColorProcessorPtr processor, - const QString &path, int direction, - int generation) -{ - QMutexLocker locker(&gen_mutex_); - - task_running_ = false; - - if (generation != pending_generation_) { - // A newer request was issued while this one was in flight; restart. - MaybeStartNextTask(); - return; - } - - if (path != pending_path_ || direction != pending_direction_) { - MaybeStartNextTask(); - return; - } - - last_path_ = path; - last_direction_ = direction; - last_processor_ = processor; - set_processor(processor); - - // NOTE: We intentionally do NOT call InvalidateAll() here. A full cache - // invalidation of the entire timeline freezes the UI while the preview - // autocacher re-renders every frame. The new processor will be picked up - // naturally on the next render request (e.g. scrubbing or playback). -} - -OCIOLutNode::GenerateProcessorTask::GenerateProcessorTask( - OCIOLutNode *node, const QString &path, int direction, int generation, - ColorManager *manager) - : node_(node), path_(path), direction_(direction), generation_(generation), - manager_(manager) -{ - setAutoDelete(true); -} - -void OCIOLutNode::GenerateProcessorTask::run() -{ - ColorProcessorPtr processor = CreateProcessor(path_, direction_, manager_); - - if (node_) { - QMetaObject::invokeMethod( - node_, "SetProcessorResult", Qt::QueuedConnection, - Q_ARG(olive::ColorProcessorPtr, processor), Q_ARG(QString, path_), - Q_ARG(int, direction_), Q_ARG(int, generation_)); - } -} - -ColorProcessorPtr OCIOLutNode::GenerateProcessorTask::CreateProcessor( - const QString &path, int direction, ColorManager *manager) -{ - if (!manager) { - return nullptr; - } - + // Synchronous generation: for typical 33^3 .cube files this is well under + // 100ms and avoids the complexity and potential deadlocks of background + // generation + cache invalidation. + ColorProcessorPtr processor; try { OCIO::FileTransformRcPtr transform = OCIO::FileTransform::Create(); transform->setSrc(path.toUtf8().constData()); @@ -251,11 +169,16 @@ ColorProcessorPtr OCIOLutNode::GenerateProcessorTask::CreateProcessor( ? OCIO::TRANSFORM_DIR_FORWARD : OCIO::TRANSFORM_DIR_INVERSE); - return ColorProcessor::Create(manager->GetConfig()->getProcessor(transform)); + processor = ColorProcessor::Create(manager()->GetConfig()->getProcessor(transform)); } catch (const std::exception &e) { qWarning() << "OCIO LUT processor error:" << e.what(); - return nullptr; + processor = nullptr; } + + last_path_ = path; + last_direction_ = direction; + last_processor_ = processor; + set_processor(processor); } } // namespace olive diff --git a/app/node/color/ociolut/ociolut.h b/app/node/color/ociolut/ociolut.h index 01048ea0c..bf1f9baf7 100644 --- a/app/node/color/ociolut/ociolut.h +++ b/app/node/color/ociolut/ociolut.h @@ -22,9 +22,6 @@ #define OCIOLUTNODE_H #include -#include -#include -#include #include "node/color/ociobase/ociobase.h" #include "render/colorprocessor.h" @@ -54,33 +51,8 @@ public: protected slots: virtual void ConfigChanged() override; -private slots: - void SetProcessorResult(olive::ColorProcessorPtr processor, - const QString &path, int direction, int generation); - private: - class GenerateProcessorTask : public QRunnable { - public: - GenerateProcessorTask(OCIOLutNode *node, const QString &path, - int direction, int generation, - ColorManager *manager); - - void run() override; - - private: - static ColorProcessorPtr CreateProcessor(const QString &path, - int direction, - ColorManager *manager); - - QPointer node_; - QString path_; - int direction_; - int generation_; - ColorManager *manager_; - }; - void GenerateProcessor(); - void MaybeStartNextTask(); QMutex gen_mutex_; QString last_path_; @@ -90,7 +62,6 @@ private: QString pending_path_; int pending_direction_ = -1; int pending_generation_ = 0; - bool task_running_ = false; }; } // namespace olive