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.
This commit is contained in:
Thomas Goyne 2012-12-17 08:27:11 -08:00
parent fee60be5db
commit 229e5d98c5
4 changed files with 60 additions and 13 deletions

View file

@ -123,6 +123,7 @@ void *ThreadedFrameSource::Entry() {
double time; double time;
int frameNum; int frameNum;
std::unique_ptr<AssFile> newSubs; std::unique_ptr<AssFile> newSubs;
std::deque<std::pair<size_t, std::unique_ptr<AssEntry>>> updates;
{ {
wxMutexLocker jobLocker(jobMutex); wxMutexLocker jobLocker(jobMutex);
@ -138,11 +139,27 @@ void *ThreadedFrameSource::Entry() {
frameNum = nextFrame; frameNum = nextFrame;
nextTime = -1.; nextTime = -1.;
newSubs = move(nextSubs); newSubs = move(nextSubs);
updates = move(changedSubs);
} }
if (newSubs) { if (newSubs || updates.size()) {
wxMutexLocker fileLocker(fileMutex); 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; singleFrame = -1;
} }
@ -194,13 +211,27 @@ ThreadedFrameSource::~ThreadedFrameSource() {
Wait(); Wait();
} }
void ThreadedFrameSource::LoadSubtitles(AssFile *subs) throw() { void ThreadedFrameSource::LoadSubtitles(const AssFile *subs) throw() {
subs = new AssFile(*subs); AssFile *copy = new AssFile(*subs);
wxMutexLocker locker(jobMutex); wxMutexLocker locker(jobMutex);
// Set nextSubs and let the worker thread move it to subs so that we don't // 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 // have to lock fileMutex on the GUI thread, as that can be locked for
// extended periods of time with slow-rendering subtitles // extended periods of time with slow-rendering subtitles
nextSubs.reset(subs); nextSubs.reset(copy);
changedSubs.clear();
}
void ThreadedFrameSource::UpdateSubtitles(const AssFile *subs, std::set<const AssEntry*> changes) throw() {
std::deque<std::pair<size_t, std::unique_ptr<AssEntry>>> changed;
size_t i = 0;
for (auto const& e : subs->Line) {
if (changes.count(&e))
changed.emplace_back(i, std::unique_ptr<AssEntry>(e.Clone()));
++i;
}
wxMutexLocker locker(jobMutex);
changedSubs = std::move(changed);
} }
void ThreadedFrameSource::RequestFrame(int frame, double time) throw() { void ThreadedFrameSource::RequestFrame(int frame, double time) throw() {

View file

@ -19,7 +19,9 @@
/// @ingroup video /// @ingroup video
/// ///
#include <deque>
#include <memory> #include <memory>
#include <set>
#include <wx/event.h> #include <wx/event.h>
#include <wx/thread.h> #include <wx/thread.h>
@ -28,6 +30,7 @@
#include <libaegisub/scoped_ptr.h> #include <libaegisub/scoped_ptr.h>
class AegiVideoFrame; class AegiVideoFrame;
class AssEntry;
class AssFile; class AssFile;
class SubtitlesProvider; class SubtitlesProvider;
class VideoProvider; class VideoProvider;
@ -46,6 +49,7 @@ class ThreadedFrameSource : public wxThread {
int nextFrame; ///< Next queued frame, or -1 for none int nextFrame; ///< Next queued frame, or -1 for none
double nextTime; ///< Next queued time double nextTime; ///< Next queued time
std::unique_ptr<AssFile> nextSubs; ///< Next queued AssFile std::unique_ptr<AssFile> nextSubs; ///< Next queued AssFile
std::deque<std::pair<size_t, std::unique_ptr<AssEntry>>> changedSubs;
/// Subtitles to be loaded the next time a frame is requested /// Subtitles to be loaded the next time a frame is requested
std::unique_ptr<AssFile> subs; std::unique_ptr<AssFile> subs;
@ -70,7 +74,15 @@ public:
/// ///
/// This function blocks until is it is safe for the calling thread to /// This function blocks until is it is safe for the calling thread to
/// modify subs /// 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<const AssEntry *> changes) throw();
/// @brief Queue a request for a frame /// @brief Queue a request for a frame
/// @brief frame Frame number /// @brief frame Frame number

View file

@ -233,10 +233,13 @@ void VideoContext::Reload() {
} }
} }
void VideoContext::OnSubtitlesCommit() { void VideoContext::OnSubtitlesCommit(int type, std::set<const AssEntry *> const& changed) {
if (!IsLoaded()) return; if (!IsLoaded()) return;
provider->LoadSubtitles(context->ass); if (changed.empty())
provider->LoadSubtitles(context->ass);
else
provider->UpdateSubtitles(context->ass, changed);
if (!IsPlaying()) if (!IsPlaying())
GetFrameAsync(frame_n); GetFrameAsync(frame_n);
} }
@ -446,7 +449,7 @@ void VideoContext::LoadTimecodes(wxString filename) {
ovrFPS = agi::vfr::Framerate(STD_STR(filename)); ovrFPS = agi::vfr::Framerate(STD_STR(filename));
ovrTimecodeFile = filename; ovrTimecodeFile = filename;
config::mru->Add("Timecodes", STD_STR(filename)); config::mru->Add("Timecodes", STD_STR(filename));
OnSubtitlesCommit(); OnSubtitlesCommit(0, std::set<const AssEntry*>());
TimecodesOpen(ovrFPS); TimecodesOpen(ovrFPS);
} }
catch (const agi::FileSystemError&) { catch (const agi::FileSystemError&) {
@ -469,7 +472,7 @@ void VideoContext::SaveTimecodes(wxString filename) {
void VideoContext::CloseTimecodes() { void VideoContext::CloseTimecodes() {
ovrFPS = agi::vfr::Framerate(); ovrFPS = agi::vfr::Framerate();
ovrTimecodeFile.clear(); ovrTimecodeFile.clear();
OnSubtitlesCommit(); OnSubtitlesCommit(0, std::set<const AssEntry*>());
TimecodesOpen(videoFPS); TimecodesOpen(videoFPS);
} }

View file

@ -32,10 +32,10 @@
/// @ingroup video /// @ingroup video
/// ///
#include <time.h> #include <ctime>
#include <list> #include <list>
#include <memory> #include <memory>
#include <set>
#include <wx/timer.h> #include <wx/timer.h>
#include <wx/stopwatch.h> #include <wx/stopwatch.h>
@ -45,6 +45,7 @@
#include <libaegisub/vfr.h> #include <libaegisub/vfr.h>
class AegiVideoFrame; class AegiVideoFrame;
class AssEntry;
class SubtitlesProviderErrorEvent; class SubtitlesProviderErrorEvent;
class ThreadedFrameSource; class ThreadedFrameSource;
class VideoProvider; class VideoProvider;
@ -139,7 +140,7 @@ class VideoContext : public wxEvtHandler {
void OnVideoError(VideoProviderErrorEvent const& err); void OnVideoError(VideoProviderErrorEvent const& err);
void OnSubtitlesError(SubtitlesProviderErrorEvent const& err); void OnSubtitlesError(SubtitlesProviderErrorEvent const& err);
void OnSubtitlesCommit(); void OnSubtitlesCommit(int type, std::set<const AssEntry *> const& changed);
void OnSubtitlesSave(); void OnSubtitlesSave();
/// Close the video, keyframes and timecodes /// Close the video, keyframes and timecodes