From 9ff9817b9544fc3b4f09a17a16314423e1f059fb Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 4 Jun 2020 16:48:23 +1000 Subject: [PATCH] cache: use render job times to synchronize cache Ensure old inaccurate jobs are never prioritized over current jobs. --- app/common/timerange.cpp | 34 ++++++++--------- app/common/timerange.h | 2 + app/render/audioplaybackcache.cpp | 13 ++++++- app/render/audioplaybackcache.h | 4 +- app/render/backend/renderbackend.cpp | 2 - app/render/framehashcache.cpp | 6 ++- app/render/framehashcache.h | 2 +- app/render/playbackcache.cpp | 55 ++++++++++++++++++++++------ app/render/playbackcache.h | 13 ++++++- app/task/cache/cache.cpp | 4 +- app/task/export/export.cpp | 2 +- app/task/render/render.cpp | 2 + app/task/render/render.h | 7 ++++ 13 files changed, 106 insertions(+), 40 deletions(-) diff --git a/app/common/timerange.cpp b/app/common/timerange.cpp index 33f92270c..9e91e721d 100644 --- a/app/common/timerange.cpp +++ b/app/common/timerange.cpp @@ -162,30 +162,28 @@ void TimeRangeList::InsertTimeRange(const TimeRange &range) void TimeRangeList::RemoveTimeRange(const TimeRange &range) { - for (int i=0;i *list, const TimeRange &remove) +{ + for (int i=0;isize();i++) { + TimeRange& compare = (*list)[i]; + + if (remove.Contains(compare)) { // This element is entirely encompassed in this range, remove it - removeAt(i); + list->removeAt(i); i--; - } else if (compare.Contains(range, false, false)) { + } else if (compare.Contains(remove, false, false)) { // The remove range is within this element, only choice is to split the element into two - TimeRange first(compare.in(), range.in()); - TimeRange last(range.out(), compare.out()); - - replace(i, first); - append(last); - } else if (compare.in() < range.in() && compare.out() > range.in()) { + list->append(TimeRange(remove.out(), compare.out())); + compare.set_out(remove.in()); + } else if (compare.in() < remove.in() && compare.out() > remove.in()) { // This element's out point overlaps the range's in, we'll trim it - TimeRange trimmed = compare; - trimmed.set_out(range.in()); - replace(i, trimmed); - } else if (compare.in() < range.out() && compare.out() > range.out()) { + compare.set_out(remove.in()); + } else if (compare.in() < remove.out() && compare.out() > remove.out()) { // This element's in point overlaps the range's out, we'll trim it - TimeRange trimmed = compare; - trimmed.set_in(range.out()); - replace(i, trimmed); + compare.set_in(remove.out()); } } } diff --git a/app/common/timerange.h b/app/common/timerange.h index 61e759abf..ed69548af 100644 --- a/app/common/timerange.h +++ b/app/common/timerange.h @@ -75,6 +75,8 @@ public: void RemoveTimeRange(const TimeRange& range); + static void RemoveTimeRange(QList* list, const TimeRange& remove); + bool ContainsTimeRange(const TimeRange& range, bool in_inclusive = true, bool out_inclusive = true) const; TimeRangeList Intersects(const TimeRange& range) const; diff --git a/app/render/audioplaybackcache.cpp b/app/render/audioplaybackcache.cpp index 3a1d69611..fd6d89b0f 100644 --- a/app/render/audioplaybackcache.cpp +++ b/app/render/audioplaybackcache.cpp @@ -63,10 +63,14 @@ void AudioPlaybackCache::SetParameters(const AudioRenderingParams ¶ms) emit Invalidated(invalidate_range); } -void AudioPlaybackCache::WritePCM(const TimeRange &range, SampleBufferPtr samples) +void AudioPlaybackCache::WritePCM(const TimeRange &range, SampleBufferPtr samples, const qint64 &job_time) { QMutexLocker locker(lock()); + if (!JobIsCurrent(range, job_time)) { + return; + } + QFile f(filename_); if (f.open(QFile::ReadWrite)) { qint64 start_offset = params_.time_to_bytes(range.in()); @@ -193,6 +197,13 @@ void AudioPlaybackCache::ShiftEvent(const rational &from, const rational &to) } } +void AudioPlaybackCache::LengthChangedEvent(const rational& old, const rational& newlen) +{ + if (newlen < old) { + QFile(filename_).resize(params_.time_to_bytes(newlen)); + } +} + const QString &AudioPlaybackCache::GetCacheFilename() const { return filename_; diff --git a/app/render/audioplaybackcache.h b/app/render/audioplaybackcache.h index e3c01d5ec..32a8f0af0 100644 --- a/app/render/audioplaybackcache.h +++ b/app/render/audioplaybackcache.h @@ -40,7 +40,7 @@ public: void SetParameters(const AudioRenderingParams& params); - void WritePCM(const TimeRange &range, SampleBufferPtr samples); + void WritePCM(const TimeRange &range, SampleBufferPtr samples, const qint64& job_time); void WriteSilence(const TimeRange &range); @@ -52,6 +52,8 @@ signals: protected: virtual void ShiftEvent(const rational& from, const rational& to) override; + virtual void LengthChangedEvent(const rational& old, const rational& newlen) override; + private: QString filename_; diff --git a/app/render/backend/renderbackend.cpp b/app/render/backend/renderbackend.cpp index 9c59fbc64..bf5153d67 100644 --- a/app/render/backend/renderbackend.cpp +++ b/app/render/backend/renderbackend.cpp @@ -351,8 +351,6 @@ void RenderBackend::WorkerFinished() if (viewer_node_) { RunNextJob(); - } else { - qDebug() << "Ignored job finish because no viewer"; } } diff --git a/app/render/framehashcache.cpp b/app/render/framehashcache.cpp index 52b86bb26..2ae7e90c3 100644 --- a/app/render/framehashcache.cpp +++ b/app/render/framehashcache.cpp @@ -41,10 +41,14 @@ QByteArray FrameHashCache::GetHash(const rational &time) return time_hash_map_.value(time); } -void FrameHashCache::SetHash(const rational &time, const QByteArray &hash) +void FrameHashCache::SetHash(const rational &time, const QByteArray &hash, const qint64& job_time) { QMutexLocker locker(lock()); + if (!JobIsCurrent(TimeRange(time, time), job_time)) { + return; + } + time_hash_map_.insert(time, hash); TimeRange validated_range(time, time + timebase_); diff --git a/app/render/framehashcache.h b/app/render/framehashcache.h index 927437623..6ef09db31 100644 --- a/app/render/framehashcache.h +++ b/app/render/framehashcache.h @@ -39,7 +39,7 @@ public: QByteArray GetHash(const rational& time); - void SetHash(const rational& time, const QByteArray& hash); + void SetHash(const rational& time, const QByteArray& hash, const qint64 &job_time); void SetTimebase(const rational& tb); diff --git a/app/render/playbackcache.cpp b/app/render/playbackcache.cpp index ec124dbaf..b223db9b0 100644 --- a/app/render/playbackcache.cpp +++ b/app/render/playbackcache.cpp @@ -20,6 +20,8 @@ #include "playbackcache.h" +#include + OLIVE_NAMESPACE_ENTER void PlaybackCache::Invalidate(const TimeRange &r) @@ -89,21 +91,13 @@ void PlaybackCache::Shift(const rational &from, const rational &to) emit Shifted(from, to); } -void PlaybackCache::Validate(const TimeRange &r) -{ - QMutexLocker locker(&lock_); - - NoLockValidate(r); - - locker.unlock(); - - emit Validated(r); -} - void PlaybackCache::NoLockInvalidate(const TimeRange &r) { invalidated_.InsertTimeRange(r); + RemoveRangeFromJobs(r); + jobs_.append({r, QDateTime::currentMSecsSinceEpoch()}); + InvalidateEvent(r); } @@ -132,6 +126,20 @@ void PlaybackCache::NoLockSetLength(const rational &r) length_ = r; } +bool PlaybackCache::JobIsCurrent(const TimeRange &r, qint64 job_time) +{ + for (int i=jobs_.size()-1; i>=0; i--) { + const JobIdentifier& job = jobs_.at(i); + + if (job.range.Contains(r, true, r.in() != r.out()) + && job_time >= job.job_time) { + return true; + } + } + + return false; +} + void PlaybackCache::LengthChangedEvent(const rational &, const rational &) { } @@ -144,4 +152,29 @@ void PlaybackCache::ShiftEvent(const rational &, const rational &) { } +void PlaybackCache::RemoveRangeFromJobs(const TimeRange &remove) +{ + // Code shamelessly copied from TimeRangeList::RemoveTimeRange + for (int i=0;i remove.in()) { + // This element's out point overlaps the range's in, we'll trim it + compare.set_out(remove.in()); + } else if (compare.in() < remove.out() && compare.out() > remove.out()) { + // This element's in point overlaps the range's out, we'll trim it + compare.set_in(remove.out()); + } + } +} + OLIVE_NAMESPACE_EXIT diff --git a/app/render/playbackcache.h b/app/render/playbackcache.h index 9229e8122..7f186137c 100644 --- a/app/render/playbackcache.h +++ b/app/render/playbackcache.h @@ -80,14 +80,14 @@ signals: void LengthChanged(const OLIVE_NAMESPACE::rational& r); protected: - void Validate(const TimeRange& r); - void NoLockInvalidate(const TimeRange& r); void NoLockValidate(const TimeRange& r); void NoLockSetLength(const rational& r); + bool JobIsCurrent(const TimeRange& r, qint64 job_time); + const rational& NoLockGetLength() const { return length_; @@ -110,10 +110,19 @@ protected: } private: + void RemoveRangeFromJobs(const TimeRange& remove); + QMutex lock_; TimeRangeList invalidated_; + struct JobIdentifier { + TimeRange range; + qint64 job_time; + }; + + QList jobs_; + rational length_; }; diff --git a/app/task/cache/cache.cpp b/app/task/cache/cache.cpp index b2f054b1c..4c10bf1cf 100644 --- a/app/task/cache/cache.cpp +++ b/app/task/cache/cache.cpp @@ -65,14 +65,14 @@ QFuture CacheTask::DownloadFrame(FramePtr frame, const QByteArray &hash) void CacheTask::FrameDownloaded(const QByteArray &hash, const std::list ×) { foreach (const rational& t, times) { - viewer()->video_frame_cache()->SetHash(t, hash); + viewer()->video_frame_cache()->SetHash(t, hash, job_time()); } } void CacheTask::AudioDownloaded(const TimeRange &range, SampleBufferPtr samples) { if (samples) { - viewer()->audio_playback_cache()->WritePCM(range, samples); + viewer()->audio_playback_cache()->WritePCM(range, samples, job_time()); } else { viewer()->audio_playback_cache()->WriteSilence(range); } diff --git a/app/task/export/export.cpp b/app/task/export/export.cpp index 9916ccdb5..97eee4830 100644 --- a/app/task/export/export.cpp +++ b/app/task/export/export.cpp @@ -177,7 +177,7 @@ void ExportTask::AudioDownloaded(const TimeRange &range, SampleBufferPtr samples adjusted_range -= params_.custom_range().in(); } - audio_data_.WritePCM(adjusted_range, samples); + audio_data_.WritePCM(adjusted_range, samples, job_time()); } OLIVE_NAMESPACE_EXIT diff --git a/app/task/render/render.cpp b/app/task/render/render.cpp index 1cd9f6dfd..bdf9f3571 100644 --- a/app/task/render/render.cpp +++ b/app/task/render/render.cpp @@ -29,6 +29,8 @@ RenderTask::RenderTask(ViewerOutput* viewer, const VideoRenderingParams &vparams video_params_(vparams), audio_params_(aparams) { + job_time_ = QDateTime::currentMSecsSinceEpoch(); + // FIXME: This makes a full copy of the node graph every time it starts, there must be a better // way. backend_.SetViewerNode(viewer_); diff --git a/app/task/render/render.h b/app/task/render/render.h index 4389791fa..e51b9681a 100644 --- a/app/task/render/render.h +++ b/app/task/render/render.h @@ -63,6 +63,11 @@ protected: void SetAnchorPoint(const rational& r); + const qint64& job_time() const + { + return job_time_; + } + private: ViewerOutput* viewer_; @@ -74,6 +79,8 @@ private: OpenGLBackend backend_; + qint64 job_time_; + }; OLIVE_NAMESPACE_EXIT