From 229e5d98c58cca0e2721505e452747bbfdcc320e Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Mon, 17 Dec 2012 08:27:11 -0800 Subject: [PATCH] Avoid making a full copy of the file for ThreadedFrameSource when possible When the set of changed lines is populated, only copy those lines rather than the entire file. On large files, this makes amend commits roughly twice as fast when video is open. --- aegisub/src/threaded_frame_source.cpp | 41 +++++++++++++++++++++++---- aegisub/src/threaded_frame_source.h | 14 ++++++++- aegisub/src/video_context.cpp | 11 ++++--- aegisub/src/video_context.h | 7 +++-- 4 files changed, 60 insertions(+), 13 deletions(-) diff --git a/aegisub/src/threaded_frame_source.cpp b/aegisub/src/threaded_frame_source.cpp index 0225161c6..a362098c1 100644 --- a/aegisub/src/threaded_frame_source.cpp +++ b/aegisub/src/threaded_frame_source.cpp @@ -123,6 +123,7 @@ void *ThreadedFrameSource::Entry() { double time; int frameNum; std::unique_ptr newSubs; + std::deque>> updates; { wxMutexLocker jobLocker(jobMutex); @@ -138,11 +139,27 @@ void *ThreadedFrameSource::Entry() { frameNum = nextFrame; nextTime = -1.; newSubs = move(nextSubs); + updates = move(changedSubs); } - if (newSubs) { + if (newSubs || updates.size()) { wxMutexLocker fileLocker(fileMutex); - subs = move(newSubs); + + if (newSubs) + subs = move(newSubs); + + if (updates.size()) { + size_t i = 0; + auto it = subs->Line.begin(); + // Replace each changed line in subs with the updated version + for (auto& update : updates) { + advance(it, update.first - i); + i = update.first; + subs->Line.insert(it, *update.second.release()); + delete &*it--; + } + } + singleFrame = -1; } @@ -194,13 +211,27 @@ ThreadedFrameSource::~ThreadedFrameSource() { Wait(); } -void ThreadedFrameSource::LoadSubtitles(AssFile *subs) throw() { - subs = new AssFile(*subs); +void ThreadedFrameSource::LoadSubtitles(const AssFile *subs) throw() { + AssFile *copy = new AssFile(*subs); wxMutexLocker locker(jobMutex); // Set nextSubs and let the worker thread move it to subs so that we don't // have to lock fileMutex on the GUI thread, as that can be locked for // extended periods of time with slow-rendering subtitles - nextSubs.reset(subs); + nextSubs.reset(copy); + changedSubs.clear(); +} + +void ThreadedFrameSource::UpdateSubtitles(const AssFile *subs, std::set changes) throw() { + std::deque>> changed; + size_t i = 0; + for (auto const& e : subs->Line) { + if (changes.count(&e)) + changed.emplace_back(i, std::unique_ptr(e.Clone())); + ++i; + } + + wxMutexLocker locker(jobMutex); + changedSubs = std::move(changed); } void ThreadedFrameSource::RequestFrame(int frame, double time) throw() { diff --git a/aegisub/src/threaded_frame_source.h b/aegisub/src/threaded_frame_source.h index a6080d25d..62b101793 100644 --- a/aegisub/src/threaded_frame_source.h +++ b/aegisub/src/threaded_frame_source.h @@ -19,7 +19,9 @@ /// @ingroup video /// +#include #include +#include #include #include @@ -28,6 +30,7 @@ #include class AegiVideoFrame; +class AssEntry; class AssFile; class SubtitlesProvider; class VideoProvider; @@ -46,6 +49,7 @@ class ThreadedFrameSource : public wxThread { int nextFrame; ///< Next queued frame, or -1 for none double nextTime; ///< Next queued time std::unique_ptr nextSubs; ///< Next queued AssFile + std::deque>> changedSubs; /// Subtitles to be loaded the next time a frame is requested std::unique_ptr subs; @@ -70,7 +74,15 @@ public: /// /// This function blocks until is it is safe for the calling thread to /// modify subs - void LoadSubtitles(AssFile *subs) throw(); + void LoadSubtitles(const AssFile *subs) throw(); + + /// @brief Update a previously loaded subtitle file + /// @param subs Subtitle file which was last passed to LoadSubtitles + /// @param changes Set of lines which have changed + /// + /// This function only supports changes to existing lines, and not + /// insertions or deletions. + void UpdateSubtitles(const AssFile *subs, std::set changes) throw(); /// @brief Queue a request for a frame /// @brief frame Frame number diff --git a/aegisub/src/video_context.cpp b/aegisub/src/video_context.cpp index 7ec26e3df..325ee4bc5 100644 --- a/aegisub/src/video_context.cpp +++ b/aegisub/src/video_context.cpp @@ -233,10 +233,13 @@ void VideoContext::Reload() { } } -void VideoContext::OnSubtitlesCommit() { +void VideoContext::OnSubtitlesCommit(int type, std::set const& changed) { if (!IsLoaded()) return; - provider->LoadSubtitles(context->ass); + if (changed.empty()) + provider->LoadSubtitles(context->ass); + else + provider->UpdateSubtitles(context->ass, changed); if (!IsPlaying()) GetFrameAsync(frame_n); } @@ -446,7 +449,7 @@ void VideoContext::LoadTimecodes(wxString filename) { ovrFPS = agi::vfr::Framerate(STD_STR(filename)); ovrTimecodeFile = filename; config::mru->Add("Timecodes", STD_STR(filename)); - OnSubtitlesCommit(); + OnSubtitlesCommit(0, std::set()); TimecodesOpen(ovrFPS); } catch (const agi::FileSystemError&) { @@ -469,7 +472,7 @@ void VideoContext::SaveTimecodes(wxString filename) { void VideoContext::CloseTimecodes() { ovrFPS = agi::vfr::Framerate(); ovrTimecodeFile.clear(); - OnSubtitlesCommit(); + OnSubtitlesCommit(0, std::set()); TimecodesOpen(videoFPS); } diff --git a/aegisub/src/video_context.h b/aegisub/src/video_context.h index a9cd637ea..37e4e4fa2 100644 --- a/aegisub/src/video_context.h +++ b/aegisub/src/video_context.h @@ -32,10 +32,10 @@ /// @ingroup video /// -#include - +#include #include #include +#include #include #include @@ -45,6 +45,7 @@ #include class AegiVideoFrame; +class AssEntry; class SubtitlesProviderErrorEvent; class ThreadedFrameSource; class VideoProvider; @@ -139,7 +140,7 @@ class VideoContext : public wxEvtHandler { void OnVideoError(VideoProviderErrorEvent const& err); void OnSubtitlesError(SubtitlesProviderErrorEvent const& err); - void OnSubtitlesCommit(); + void OnSubtitlesCommit(int type, std::set const& changed); void OnSubtitlesSave(); /// Close the video, keyframes and timecodes