perf(plugin): eliminate GPU↔CPU ping-pong in OFX render path
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().
This commit is contained in:
@@ -559,6 +559,21 @@ void olive::plugin::OliveClipInstance::setDefaultRegionOfDefinition(
|
||||
defaultRegionOfDefinitions_ = regionOfDefinition;
|
||||
}
|
||||
|
||||
void olive::plugin::OliveClipInstance::pruneImagesCache()
|
||||
{
|
||||
// Do not prune output clip images; they may have external references
|
||||
// added by getImage()/addReference() and are typically single-frame.
|
||||
if (name_ == kOfxImageEffectOutputClipName) {
|
||||
return;
|
||||
}
|
||||
while (images_.size() > kMaxInputImageCache) {
|
||||
auto it = images_.begin();
|
||||
Image *img = it.value();
|
||||
images_.erase(it);
|
||||
delete img;
|
||||
}
|
||||
}
|
||||
|
||||
void olive::plugin::OliveClipInstance::setParams(const VideoParams ¶ms)
|
||||
{
|
||||
params_ = params;
|
||||
@@ -567,7 +582,7 @@ void olive::plugin::OliveClipInstance::setParams(const VideoParams ¶ms)
|
||||
setComponents(getUnmappedComponents());
|
||||
}
|
||||
|
||||
void olive::plugin::OliveClipInstance::setInputTexture(TexturePtr texture, OfxTime time){
|
||||
void olive::plugin::OliveClipInstance::setInputTexture(TexturePtr texture, OfxTime time, bool readback_cpu){
|
||||
if (!texture) {
|
||||
return;
|
||||
}
|
||||
@@ -593,6 +608,14 @@ void olive::plugin::OliveClipInstance::setInputTexture(TexturePtr texture, OfxTi
|
||||
input_textures_.insert(time, texture);
|
||||
#endif
|
||||
|
||||
// In OpenGL render path, skip CPU readback entirely.
|
||||
// The plugin will fetch input via loadTexture() using GPU texture IDs.
|
||||
// If the plugin falls back to getImage(), it will be created on-demand
|
||||
// in getImage() with zero-initialized data.
|
||||
if (!readback_cpu) {
|
||||
return;
|
||||
}
|
||||
|
||||
AVFramePtr frame = texture->frame();
|
||||
if (!frame || !frame->data[0]) {
|
||||
frame = ReadbackTextureToFrame(texture, params_);
|
||||
@@ -620,6 +643,7 @@ void olive::plugin::OliveClipInstance::setInputTexture(TexturePtr texture, OfxTi
|
||||
regionOfDefinition, false);
|
||||
images_.insert(time, image);
|
||||
}
|
||||
pruneImagesCache();
|
||||
|
||||
uint8_t *dst = (uint8_t*)image->data();
|
||||
if (!dst) {
|
||||
@@ -679,9 +703,13 @@ copy_pixels:
|
||||
int copy_height = std::min(image->height(), src_frame->height);
|
||||
|
||||
const uint8_t *src = src_frame->data[0];
|
||||
for (int y = 0; y < copy_height; ++y) {
|
||||
std::memcpy(dst + y * dst_row_bytes, src + y * src_row_bytes,
|
||||
copy_bytes);
|
||||
if (dst_row_bytes == src_row_bytes && src_row_bytes == copy_bytes) {
|
||||
std::memcpy(dst, src, copy_bytes * copy_height);
|
||||
} else {
|
||||
for (int y = 0; y < copy_height; ++y) {
|
||||
std::memcpy(dst + y * dst_row_bytes, src + y * src_row_bytes,
|
||||
copy_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user