From eeea16c5c36c3ac08ae64ebef40f8e9c76c0f123 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Mon, 6 Jun 2022 20:06:43 -0700 Subject: [PATCH] cache: add passthroughs to allow shallow copies of data --- app/node/block/clip/clip.cpp | 101 ++++++++++++++---- app/node/block/clip/clip.h | 27 +++-- app/render/audiowaveformcache.cpp | 11 ++ app/render/audiowaveformcache.h | 2 + app/render/framehashcache.cpp | 21 ++++ app/render/framehashcache.h | 11 +- app/render/playbackcache.cpp | 64 +++++++++-- app/render/playbackcache.h | 16 +++ app/widget/timelinewidget/tool/pointer.cpp | 3 +- .../timelinewidget/undo/timelineundosplit.cpp | 24 ++--- .../timelinewidget/undo/timelineundosplit.h | 2 + 11 files changed, 223 insertions(+), 59 deletions(-) diff --git a/app/node/block/clip/clip.cpp b/app/node/block/clip/clip.cpp index d0bbef560..54d195f49 100644 --- a/app/node/block/clip/clip.cpp +++ b/app/node/block/clip/clip.cpp @@ -183,7 +183,7 @@ rational ClipBlock::MediaToSequenceTime(const rational &media_time) const return sequence_time; } -void ClipBlock::RequestInvalidatedFromConnected(const TimeRange &range) +void ClipBlock::RequestRangeFromConnected(const TimeRange &range) { Track::Type type = GetTrackType(); @@ -192,41 +192,75 @@ void ClipBlock::RequestInvalidatedFromConnected(const TimeRange &range) TimeRange max_range = InputTimeAdjustment(kBufferIn, -1, TimeRange(0, length())); if (type == Track::kVideo) { // Handle thumbnails - RequestInvalidatedForCache(connected->thumbnail_cache(), max_range, range, true); + RequestRangeForCache(connected->thumbnail_cache(), max_range, range, true, true); // Handle video cache - RequestInvalidatedForCache(connected->video_frame_cache(), max_range, range, IsAutocaching()); + RequestRangeForCache(connected->video_frame_cache(), max_range, range, true, IsAutocaching()); } else if (type == Track::kAudio) { // Handle waveforms - RequestInvalidatedForCache(connected->waveform_cache(), max_range, range, true); + RequestRangeForCache(connected->waveform_cache(), max_range, range, true, true); // Handle audio cache - RequestInvalidatedForCache(connected->audio_playback_cache(), max_range, range, IsAutocaching()); + RequestRangeForCache(connected->audio_playback_cache(), max_range, range, true, IsAutocaching()); } } } } -void ClipBlock::RequestInvalidatedForCache(PlaybackCache *cache, const TimeRange &max_range, const TimeRange &range, bool request) +void ClipBlock::RequestInvalidatedFromConnected() { - if ((range.in() == RATIONAL_MIN && range.out() == RATIONAL_MAX) || !range.length().isNull()) { - // Request only this range - TimeRange r = range.Intersected(max_range); - cache->Invalidate(r); - if (request) { - emit cache->Request(r); - } - } else { - // Request all ranges currently marked as invalid - if (request) { - TimeRangeList invalid = cache->GetInvalidatedRanges(max_range); - for (const TimeRange &r : invalid) { - emit cache->Request(r); + Track::Type type = GetTrackType(); + + if (type == Track::kVideo || type == Track::kAudio) { + if (Node *connected = GetConnectedOutput(kBufferIn)) { + TimeRange max_range = InputTimeAdjustment(kBufferIn, -1, TimeRange(0, length())); + if (type == Track::kVideo) { + // Handle thumbnails + RequestInvalidatedForCache(connected->thumbnail_cache(), max_range); + + // Handle video cache + if (IsAutocaching()) { + RequestInvalidatedForCache(connected->video_frame_cache(), max_range); + } + } else if (type == Track::kAudio) { + // Handle waveforms + RequestInvalidatedForCache(connected->waveform_cache(), max_range); + + // Handle audio cache + if (IsAutocaching()) { + RequestInvalidatedForCache(connected->audio_playback_cache(), max_range); + } } } } } +void ClipBlock::RequestRangeForCache(PlaybackCache *cache, const TimeRange &max_range, const TimeRange &range, bool invalidate, bool request) +{ + TimeRange r = range.Intersected(max_range); + + if (invalidate) { + cache->Invalidate(r); + } + + if (request) { + emit cache->Request(r); + } +} + +void ClipBlock::RequestInvalidatedForCache(PlaybackCache *cache, const TimeRange &max_range) +{ + TimeRangeList invalid = cache->GetInvalidatedRanges(max_range); + + for (const PlaybackCache::Passthrough &p : cache->GetPassthroughs()) { + invalid.remove(p); + } + + for (const TimeRange &r : invalid) { + RequestRangeForCache(cache, max_range, r, false, true); + } +} + void ClipBlock::InvalidateCache(const TimeRange& range, const QString& from, int element, InvalidateCacheOptions options) { Q_UNUSED(element) @@ -235,7 +269,7 @@ void ClipBlock::InvalidateCache(const TimeRange& range, const QString& from, int if (from == kBufferIn) { // Render caches where necessary if (AreCachesEnabled()) { - RequestInvalidatedFromConnected(range); + RequestRangeFromConnected(range); } // Adjust range from media time to sequence time @@ -380,6 +414,33 @@ void ClipBlock::Retranslate() SetInputName(kMaintainAudioPitchInput, tr("Maintain Audio Pitch")); } +void ClipBlock::AddCachePassthroughFrom(ClipBlock *other) +{ + if (auto tc = this->video_frame_cache()) { + if (auto oc = other->video_frame_cache()) { + tc->SetPassthrough(oc); + } + } + + if (auto tc = this->audio_playback_cache()) { + if (auto oc = other->audio_playback_cache()) { + tc->SetPassthrough(oc); + } + } + + if (auto tc = this->thumbnails()) { + if (auto oc = other->thumbnails()) { + tc->SetPassthrough(oc); + } + } + + if (auto tc = this->waveform()) { + if (auto oc = other->waveform()) { + tc->SetPassthrough(oc); + } + } +} + void ClipBlock::ConnectedToPreviewEvent() { RequestInvalidatedFromConnected(); diff --git a/app/node/block/clip/clip.h b/app/node/block/clip/clip.h index f7135676a..e86496104 100644 --- a/app/node/block/clip/clip.h +++ b/app/node/block/clip/clip.h @@ -122,7 +122,7 @@ public: return block_links_; } - const FrameHashCache *connected_video_cache() const + FrameHashCache *connected_video_cache() const { if (Node *n = GetConnectedOutput(kBufferIn)) { return n->video_frame_cache(); @@ -131,7 +131,16 @@ public: } } - const FrameHashCache *thumbnails() + AudioPlaybackCache *connected_audio_cache() const + { + if (Node *n = GetConnectedOutput(kBufferIn)) { + return n->audio_playback_cache(); + } else { + return nullptr; + } + } + + FrameHashCache *thumbnails() { if (Node *n = GetConnectedOutput(kBufferIn)) { return n->thumbnail_cache(); @@ -140,7 +149,7 @@ public: } } - const AudioWaveformCache *waveform() + AudioWaveformCache *waveform() { if (Node *n = GetConnectedOutput(kBufferIn)) { return n->waveform_cache(); @@ -149,11 +158,7 @@ public: } } - void set_waveform(const AudioVisualWaveform *w) - { - qDebug() << "WAVEFORM COPY STUB"; - //audio_playback_cache()->set_visual(w); - } + void AddCachePassthroughFrom(ClipBlock *other); ViewerOutput *connected_viewer() const { @@ -194,9 +199,11 @@ private: rational MediaToSequenceTime(const rational& media_time) const; - void RequestInvalidatedFromConnected(const TimeRange &range = TimeRange()); + void RequestRangeFromConnected(const TimeRange &range); + void RequestInvalidatedFromConnected(); - void RequestInvalidatedForCache(PlaybackCache *cache, const TimeRange &max_range, const TimeRange &range, bool request); + void RequestRangeForCache(PlaybackCache *cache, const TimeRange &max_range, const TimeRange &range, bool invalidate, bool request); + void RequestInvalidatedForCache(PlaybackCache *cache, const TimeRange &max_range); QVector block_links_; diff --git a/app/render/audiowaveformcache.cpp b/app/render/audiowaveformcache.cpp index ba3a0f506..5b4898f7d 100644 --- a/app/render/audiowaveformcache.cpp +++ b/app/render/audiowaveformcache.cpp @@ -98,4 +98,15 @@ rational AudioWaveformCache::length() const return len; } +void AudioWaveformCache::SetPassthrough(PlaybackCache *cache) +{ + AudioWaveformCache *c = static_cast(cache); + waveforms_ = c->waveforms_; + for (const TimeRange &r : c->GetValidatedRanges()) { + Validate(r); + } + SetParameters(c->GetParameters()); + SetSavingEnabled(c->IsSavingEnabled()); +} + } diff --git a/app/render/audiowaveformcache.h b/app/render/audiowaveformcache.h index 0a27eb175..6dcf58f07 100644 --- a/app/render/audiowaveformcache.h +++ b/app/render/audiowaveformcache.h @@ -43,6 +43,8 @@ public: rational length() const; + virtual void SetPassthrough(PlaybackCache *cache) override; + private: class TimeRangeWithWaveform : public TimeRange { diff --git a/app/render/framehashcache.cpp b/app/render/framehashcache.cpp index adab73bfc..90f5bd858 100644 --- a/app/render/framehashcache.cpp +++ b/app/render/framehashcache.cpp @@ -64,6 +64,21 @@ void FrameHashCache::ValidateTime(const rational &time) Validate(TimeRange(time, time + timebase_)); } +QString FrameHashCache::GetValidCacheFilename(const rational &time) const +{ + if (IsFrameCached(time)) { + return CachePathName(time); + } else if (!GetPassthroughs().empty()) { + for (const Passthrough &p : GetPassthroughs()) { + if (p.Contains(time)) { + return CachePathName(GetCacheDirectory(), p.cache, time, timebase_); + } + } + } + + return QString(); +} + bool FrameHashCache::SaveCacheFrame(const int64_t &time, FramePtr frame) const { return SaveCacheFrame(GetCacheDirectory(), GetUuid(), time, frame); @@ -224,6 +239,12 @@ FramePtr FrameHashCache::LoadCacheFrame(const QString &fn) return frame; } +void FrameHashCache::SetPassthrough(PlaybackCache *cache) +{ + super::SetPassthrough(cache); + SetTimebase(static_cast(cache)->GetTimebase()); +} + void FrameHashCache::LoadStateEvent(QDataStream &stream) { uint32_t version; diff --git a/app/render/framehashcache.h b/app/render/framehashcache.h index e6a726dbb..b98460c49 100644 --- a/app/render/framehashcache.h +++ b/app/render/framehashcache.h @@ -48,14 +48,7 @@ public: return GetValidatedRanges().contains(time); } - QString GetValidCacheFilename(const rational &time) const - { - if (IsFrameCached(time)) { - return CachePathName(time); - } else { - return QString(); - } - } + QString GetValidCacheFilename(const rational &time) const; static bool SaveCacheFrame(const QString& filename, FramePtr frame); bool SaveCacheFrame(const int64_t &time, FramePtr frame) const; @@ -65,6 +58,8 @@ public: FramePtr LoadCacheFrame(const int64_t &time) const; static FramePtr LoadCacheFrame(const QString& fn); + virtual void SetPassthrough(PlaybackCache *cache) override; + protected: virtual void LoadStateEvent(QDataStream &stream) override; virtual void SaveStateEvent(QDataStream &stream) override; diff --git a/app/render/playbackcache.cpp b/app/render/playbackcache.cpp index 1dd472bed..2de3a6447 100644 --- a/app/render/playbackcache.cpp +++ b/app/render/playbackcache.cpp @@ -36,6 +36,10 @@ void PlaybackCache::Invalidate(const TimeRange &r) validated_.remove(r); + if (!passthroughs_.empty()) { + TimeRangeList::util_remove(&passthroughs_, r); + } + InvalidateEvent(r); emit Invalidated(r); @@ -72,14 +76,14 @@ void PlaybackCache::LoadState() LoadStateEvent(s); - int count; - s >> count; - switch (version) { case 1: - validated_.clear(); + { + int valid_count, pass_count; - for (int i=0; i> valid_count; + for (int i=0; i> in_num; @@ -89,8 +93,27 @@ void PlaybackCache::LoadState() validated_.insert(TimeRange(rational(in_num, in_den), rational(out_num, out_den))); } + + passthroughs_.clear(); + s >> pass_count; + for (int i=0; i> in_num; + s >> in_den; + s >> out_num; + s >> out_den; + s >> id; + + Passthrough p = TimeRange(rational(in_num, in_den), rational(out_num, out_den)); + p.cache = id; + passthroughs_.append(p); + } + break; } + } f.close(); } @@ -100,7 +123,7 @@ void PlaybackCache::SaveState() { QDir cache_dir = GetThisCacheDirectory(); QFile f(cache_dir.filePath(QStringLiteral("state"))); - if (validated_.isEmpty()) { + if (validated_.isEmpty() && passthroughs_.isEmpty()) { if (f.exists()) { f.remove(); } @@ -123,6 +146,16 @@ void PlaybackCache::SaveState() s << r.out().denominator(); } + s << passthroughs_.size(); + + for (const Passthrough &p : passthroughs_) { + s << p.in().numerator(); + s << p.in().denominator(); + s << p.out().numerator(); + s << p.out().denominator(); + s << p.cache; + } + f.close(); } } @@ -155,6 +188,21 @@ void PlaybackCache::Draw(QPainter *p, const rational &start, double scale, const } } +void PlaybackCache::SetPassthrough(PlaybackCache *cache) +{ + for (const TimeRange &r : cache->GetValidatedRanges()) { + Passthrough p = r; + p.cache = cache->GetUuid(); + passthroughs_.push_back(p); + } + + passthroughs_.append(cache->GetPassthroughs()); + + if (saving_enabled_) { + SaveState(); + } +} + void PlaybackCache::InvalidateAll() { Invalidate(TimeRange(0, RATIONAL_MAX)); @@ -211,6 +259,10 @@ TimeRangeList PlaybackCache::GetInvalidatedRanges(TimeRange intersecting) const invalidated.remove(range); } + foreach (const TimeRange &range, passthroughs_) { + invalidated.remove(range); + } + return invalidated; } diff --git a/app/render/playbackcache.h b/app/render/playbackcache.h index ac88e97e6..d96709355 100644 --- a/app/render/playbackcache.h +++ b/app/render/playbackcache.h @@ -82,8 +82,22 @@ public: bool IsSavingEnabled() const { return saving_enabled_; } void SetSavingEnabled(bool e) { saving_enabled_ = e; } + virtual void SetPassthrough(PlaybackCache *cache); + QMutex *mutex() { return &mutex_; } + class Passthrough : public TimeRange + { + public: + Passthrough(const TimeRange &r) : + TimeRange(r) + {} + + QUuid cache; + }; + + const QVector &GetPassthroughs() const { return passthroughs_; } + public slots: void InvalidateAll(); @@ -116,6 +130,8 @@ private: QMutex mutex_; + QVector passthroughs_; + }; } diff --git a/app/widget/timelinewidget/tool/pointer.cpp b/app/widget/timelinewidget/tool/pointer.cpp index 71f57b63c..615355ecc 100644 --- a/app/widget/timelinewidget/tool/pointer.cpp +++ b/app/widget/timelinewidget/tool/pointer.cpp @@ -698,8 +698,7 @@ void PointerTool::FinishDrag(TimelineViewMouseEvent *event) // Place the copy instead of the original block block = static_cast(Node::CopyNodeInGraph(block, command)); if (ClipBlock *new_clip = dynamic_cast(block)) { - qDebug() << "FIXME: Copy clip stub"; Q_UNUSED(new_clip) - //new_clip->set_waveform(static_cast(p.block)->waveform()); + new_clip->AddCachePassthroughFrom(static_cast(p.block)); } } diff --git a/app/widget/timelinewidget/undo/timelineundosplit.cpp b/app/widget/timelinewidget/undo/timelineundosplit.cpp index 3900d2b7b..da810f321 100644 --- a/app/widget/timelinewidget/undo/timelineundosplit.cpp +++ b/app/widget/timelinewidget/undo/timelineundosplit.cpp @@ -29,27 +29,20 @@ namespace olive { // // BlockSplitCommand // +void BlockSplitCommand::prepare() +{ + reconnect_tree_command_ = new MultiUndoCommand(); + new_block_ = static_cast(Node::CopyNodeInGraph(block_, reconnect_tree_command_)); +} + void BlockSplitCommand::redo() { old_length_ = block_->length(); Q_ASSERT(point_ > block_->in() && point_ < block_->out()); - if (!reconnect_tree_command_) { - reconnect_tree_command_ = new MultiUndoCommand(); - new_block_ = static_cast(Node::CopyNodeInGraph(block_, reconnect_tree_command_)); - } - reconnect_tree_command_->redo_now(); - if (ClipBlock *new_clip = dynamic_cast(new_block_)) { - ClipBlock *old_clip = static_cast(block_); - qDebug() << "FIXME: Copy waveform stub"; - Q_UNUSED(old_clip) - Q_UNUSED(new_clip) - //new_clip->set_waveform(old_clip->waveform()); - } - // Determine our new lengths rational new_length = point_ - block_->in(); rational new_part_length = block_->out() - point_; @@ -64,6 +57,11 @@ void BlockSplitCommand::redo() // Insert new block track->InsertBlockAfter(new_block(), block_); + if (ClipBlock *new_clip = dynamic_cast(new_block_)) { + ClipBlock *old_clip = static_cast(block_); + new_clip->AddCachePassthroughFrom(old_clip); + } + // If the block had an out transition, we move it to the new block moved_transition_ = NodeInput(); diff --git a/app/widget/timelinewidget/undo/timelineundosplit.h b/app/widget/timelinewidget/undo/timelineundosplit.h index 2cc12ff21..1fe9f9122 100644 --- a/app/widget/timelinewidget/undo/timelineundosplit.h +++ b/app/widget/timelinewidget/undo/timelineundosplit.h @@ -54,6 +54,8 @@ public: } protected: + virtual void prepare() override; + virtual void redo() override; virtual void undo() override;