Fix OCIO LUT async processor black screen and freeze

- Pass through input texture when the LUT processor is not ready yet,
  preventing black frames while the processor is being generated.
- Serialize processor generation with a single in-flight task to avoid
  concurrent OCIO lock contention that could freeze the UI.
- Invalidate cached frames after the async processor is set so the viewer
  refreshes automatically without requiring the playhead to be moved.
- Add OAK_DISABLE_HWACCEL environment variable to force software decoding.
- Add FFmpegDecoderHW regression test for H.264 4:2:2 10-bit decoding.
This commit is contained in:
2026-07-13 10:19:30 +08:00
parent 68c0477715
commit 800859b0ef
6 changed files with 125 additions and 10 deletions
+9 -1
View File
@@ -425,13 +425,17 @@ TexturePtr FFmpegDecoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
AVFramePtr ptr = PreProcessFrame(f, p);
f=std::move(ptr);
if (!f) {
// Error occurred while software scaling
qWarning() << "PreProcessFrame failed";
return nullptr;
}
// Finally, perform any GPU processing required
TexturePtr texture = ProcessFrameIntoTexture(f, p, original);
if (!texture) {
qWarning() << "ProcessFrameIntoTexture returned null";
}
return texture;
}
@@ -1476,6 +1480,10 @@ bool FFmpegDecoder::Instance::Open(const char *filename, int stream_index)
AVHWDeviceType FFmpegDecoder::Instance::ChooseHardwareDevice()
{
if (qEnvironmentVariableIsSet("OAK_DISABLE_HWACCEL")) {
return AV_HWDEVICE_TYPE_NONE;
}
#ifdef Q_OS_LINUX
// Prefer NVIDIA's NVDEC where available, then VAAPI/VDPAU.
for (AVHWDeviceType type : { AV_HWDEVICE_TYPE_CUDA, AV_HWDEVICE_TYPE_VAAPI,
+11 -5
View File
@@ -63,13 +63,19 @@ void OCIOBaseNode::Value(const NodeValueRow &value, const NodeGlobals &globals,
{
auto tex_met = value[kTextureInput];
TexturePtr t = tex_met.toTexture();
if (t && processor_) {
ColorTransformJob job;
if (t) {
if (processor_) {
ColorTransformJob job;
job.SetColorProcessor(processor_);
job.SetInputTexture(tex_met);
job.SetColorProcessor(processor_);
job.SetInputTexture(tex_met);
table->Push(NodeValue::kTexture, t->toJob(job), this);
table->Push(NodeValue::kTexture, t->toJob(job), this);
} else {
// Processor isn't ready yet (e.g. still being generated
// asynchronously), pass the input through unchanged.
table->Push(NodeValue::kTexture, QVariant::fromValue(t), this);
}
}
}
+31 -4
View File
@@ -154,10 +154,30 @@ void OCIOLutNode::GenerateProcessor()
pending_path_ = path;
pending_direction_ = direction;
pending_generation_++;
const int generation = pending_generation_;
ColorManager *manager = this->manager();
locker.unlock();
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));
@@ -169,12 +189,16 @@ void OCIOLutNode::SetProcessorResult(ColorProcessorPtr processor,
{
QMutexLocker locker(&gen_mutex_);
task_running_ = false;
if (generation != pending_generation_) {
// A newer request was issued while this one was in flight; ignore.
// A newer request was issued while this one was in flight; restart.
MaybeStartNextTask();
return;
}
if (path != pending_path_ || direction != pending_direction_) {
MaybeStartNextTask();
return;
}
@@ -182,6 +206,9 @@ void OCIOLutNode::SetProcessorResult(ColorProcessorPtr processor,
last_direction_ = direction;
last_processor_ = processor;
set_processor(processor);
// The processor has changed, ensure any cached frames are re-rendered.
InvalidateAll(kTextureInput);
}
OCIOLutNode::GenerateProcessorTask::GenerateProcessorTask(
+2
View File
@@ -80,6 +80,7 @@ private:
};
void GenerateProcessor();
void MaybeStartNextTask();
QMutex gen_mutex_;
QString last_path_;
@@ -89,6 +90,7 @@ private:
QString pending_path_;
int pending_direction_ = -1;
int pending_generation_ = 0;
bool task_running_ = false;
};
} // namespace olive