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 removes redundant readbacks and uploads in the OpenFX
plugin pipeline, achieving zero-copy rendering for GL-capable
plugins and reducing CPU path overhead.
PluginRenderer (OpenGL path):
- Skip ReadbackTextureToFrame + ConvertFrameIfNeeded + Upload after
plugin render. The destination texture is already valid on GPU.
- Pass readback_cpu=false to setInputTexture() so input textures are
provided via loadTexture() (GL texture IDs) instead of being
downloaded to CPU Image buffers.
- Remove duplicate ReadbackTextureToFrame block between
getClipPreferences and the second setInputTexture call.
OliveClipInstance:
- Add optional bool readback_cpu=true to setInputTexture(). When
false, only params and input_textures_ are updated; CPU readback
and memcpy into Image are skipped.
- Add pruneImagesCache() to prevent unbounded growth of images_.
Input clips are limited to 8 cached frames; output clips are
left untouched.
Micro-optimizations:
- Replace per-row memcpy loops with single block memcpy when
src/dst strides are contiguous (common case in Olive pipeline).
- Remove dead GL_PREAMBLE macro definition.
fix(viewer): resolve playback head lag, frozen frames, and pause delay
Three playback pipeline behavioral issues are fixed:
1. Prequeue blocked playhead start:
Reduce kVideoPlaybackInterval from 0.5s to 0.1s. This lowers
prequeue length from 15–30 frames to 3–6 frames, so the
playback timer starts much sooner after pressing Play.
2. Frozen display during playback:
Relax the hard frame-drop logic in RendererGeneratedFrameForQueue.
When the queue has fewer than 2 frames, keep late frames instead
of dropping them, preventing the viewer from freezing entirely
when rendering cannot keep up with playback speed.
3. Delayed frame update after pause:
Cancel in-flight render tickets in PauseInternal() before
deleting queue watchers. Previously the render thread continued
processing stale playback frames, blocking the single-frame
render requested by UpdateTextureFromNode().
Refactor FFmpeg frame processing and improve focus management in panels. Add memory sanitization for debug builds and update KDDockWidgets integration.