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.
This commit is contained in:
2026-07-13 10:19:30 +08:00
parent 8636ee0dc6
commit d88341f9db
2 changed files with 11 additions and 117 deletions
+11 -88
View File
@@ -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
-29
View File
@@ -22,9 +22,6 @@
#define OCIOLUTNODE_H
#include <QMutex>
#include <QPointer>
#include <QRunnable>
#include <QThreadPool>
#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<OCIOLutNode> 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