From f38ba33fedfdeffd2d2e742333620301a0aacc67 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Fri, 6 Jun 2014 17:20:01 -0700 Subject: [PATCH] Eliminate some pointless rerenders of the video display If the frame number hasn't changed and none of the currently visible lines have changed, there's no need to rerender the subtitles and redisplay the frame. Mostly helps with very slow opengl implementations where actually painting the video display is expensive, as the rendering is done on a background thread anyway. --- src/async_video_provider.cpp | 43 ++++++++++++++++++++++++++++++++++++ src/async_video_provider.h | 9 ++++++++ 2 files changed, 52 insertions(+) diff --git a/src/async_video_provider.cpp b/src/async_video_provider.cpp index 8a0a2dfd1..ba9cbe287 100644 --- a/src/async_video_provider.cpp +++ b/src/async_video_provider.cpp @@ -140,10 +140,53 @@ void AsyncVideoProvider::RequestFrame(int new_frame, double new_time) throw() { }); } +bool AsyncVideoProvider::NeedUpdate(std::vector const& visible_lines) { + // Always need to render after a seek + if (single_frame != NEW_SUBS_FILE || frame_number != last_rendered) + return true; + + // Obviously need to render if the number of visible lines has changed + if (visible_lines.size() != last_lines.size()) + return true; + + for (size_t i = 0; i < last_lines.size(); ++i) { + auto const& last = last_lines[i]; + auto const& cur = *visible_lines[i]; + if (last.Layer != cur.Layer) return true; + if (last.Margin != cur.Margin) return true; + if (last.Style != cur.Style) return true; + if (last.Effect != cur.Effect) return true; + if (last.Text != cur.Text) return true; + + // Changing the start/end time effects the appearance only if the + // line is animated. This is obviously not a very accurate check for + // animated lines, but false positives aren't the end of the world + if ((last.Start != cur.Start || last.End != cur.End) && + (!cur.Effect.get().empty() || cur.Text.get().find('\\') != std::string::npos)) + return true; + } + + return false; +} + void AsyncVideoProvider::ProcAsync(uint_fast32_t req_version) { // Only actually produce the frame if there's no queued changes waiting if (req_version < version || frame_number < 0) return; + std::vector visible_lines; + for (auto const& line : subs->Events) { + if (!line.Comment && !(line.Start > time || line.End <= time)) + visible_lines.push_back(&line); + } + + if (!NeedUpdate(visible_lines)) return; + + last_lines.clear(); + last_lines.reserve(visible_lines.size()); + for (auto line : visible_lines) + last_lines.push_back(*line); + last_rendered = frame_number; + try { FrameReadyEvent *evt = new FrameReadyEvent(ProcFrame(frame_number, time), time); evt->SetEventType(EVT_FRAME_READY); diff --git a/src/async_video_provider.h b/src/async_video_provider.h index 37232b978..c77a2664c 100644 --- a/src/async_video_provider.h +++ b/src/async_video_provider.h @@ -29,6 +29,7 @@ class AssFile; class SubtitlesProvider; class VideoProvider; class VideoProviderError; +struct AssDialogueBase; struct VideoFrame; namespace agi { class BackgroundRunner; @@ -58,6 +59,14 @@ class AsyncVideoProvider { /// currently loaded file is out of date. int single_frame = -1; + /// Last rendered frame number + int last_rendered = -1; + /// Last rendered subtitles on that frame + std::vector last_lines; + /// Check if we actually need to honor a frame request or if no visible + /// lines have actually changed + bool NeedUpdate(std::vector const& visible_lines); + std::shared_ptr ProcFrame(int frame, double time, bool raw = false); /// Produce a frame if req_version is still the current version