fix(renderer): macOS TBDR cross-context sync and OFX instance thread-safety

This commit fixes crashes (SIGSEGV in CImg blur_bilateral) and black-frame
corruption artifacts during playback and scrubbing on macOS Apple Silicon.

Root cause analysis:
1. macOS uses Tile-Based Deferred Rendering (TBDR). glFlush() does not
   guarantee tile memory writeback, causing glReadPixels to read incomplete
   tiles (black/corrupted frames) and cache them to disk.
2. Olive uses multiple shared OpenGL contexts (RenderProcessor contexts vs.
   thread-local PluginRenderer context). glFinish() only waits for the
   current context, not the shared context that produced the texture. CPU
   readback in PluginRenderer could read partially-rendered tiles.
3. OlivePluginInstance and OliveClipInstance are not thread-safe. Concurrent
   RenderProcessors could corrupt internal QMap/images_ and params_ via
   setInputTexture/renderAction races.

Fixes:
- OpenGLRenderer::Flush() on macOS now uses glFinish() unconditionally.
- OpenGLRenderer::DownloadFromTexture() and OpenGLRenderer::Blit() insert
  glFinish() before readback/detach to ensure tile writeback completes.
- PluginRenderer::RenderPlugin() now acquires a per-instance mutex to
  serialize concurrent OFX render calls.
- Before CPU readback in PluginRenderer, flush the renderer that originally
  produced each input texture, ensuring cross-context synchronization.
- RenderProcessor::ProcessVideoFootage() flushes after BlitColorManaged.
- Add black-frame detection in ProcessVideoCacheJob() to auto-purge TBDR-
   corrupted cache files.
- Add diagnostic qDebug() logging in viewer, decoder, renderer, and plugin
  paths to aid future debugging.
This commit is contained in:
2026-05-17 14:54:49 +08:00
parent ff0eee3a88
commit 2a84027ff9
7 changed files with 147 additions and 8 deletions
+7
View File
@@ -382,11 +382,15 @@ void ViewerDisplayWidget::OnPaint()
bg_color.blueF());
// We only draw if we have a pipeline
qDebug() << "[VIEWER] OnPaint push_mode=" << push_mode_
<< "color_service=" << (color_service() != nullptr)
<< "has_load_frame=" << !load_frame_.isNull();
if (push_mode_ != kPushNull) {
// Draw texture through color transform
VideoParams device_params = GetViewportParams();
if (push_mode_ == kPushBlank) {
qDebug() << "[VIEWER] OnPaint drawing blank";
DrawBlank(device_params);
} else if (color_service()) {
if (FramePtr frame = load_frame_.value<FramePtr>()) {
@@ -490,7 +494,10 @@ void ViewerDisplayWidget::OnPaint()
ctj.SetForceOpaque(true);
renderer()->BlitColorManaged(ctj, device_params);
qDebug() << "[VIEWER] OnPaint BlitColorManaged done";
}
} else {
qDebug() << "[VIEWER] OnPaint no color_service, skipping texture draw";
}
}