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;
int frameNum;
std::unique_ptr<AssFile> newSubs;
std::deque<std::pair<size_t, std::unique_ptr<AssEntry>>> 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<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() {

View File

@ -19,7 +19,9 @@
/// @ingroup video
///
#include <deque>
#include <memory>
#include <set>
#include <wx/event.h>
#include <wx/thread.h>
@ -28,6 +30,7 @@
#include <libaegisub/scoped_ptr.h>
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<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
std::unique_ptr<AssFile> 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<const AssEntry *> changes) throw();
/// @brief Queue a request for a frame
/// @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;
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<const AssEntry*>());
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<const AssEntry*>());
TimecodesOpen(videoFPS);
}

View File

@ -32,10 +32,10 @@
/// @ingroup video
///
#include <time.h>
#include <ctime>
#include <list>
#include <memory>
#include <set>
#include <wx/timer.h>
#include <wx/stopwatch.h>
@ -45,6 +45,7 @@
#include <libaegisub/vfr.h>
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 AssEntry *> const& changed);
void OnSubtitlesSave();
/// Close the video, keyframes and timecodes