diff --git a/app/dialog/sequence/sequence.cpp b/app/dialog/sequence/sequence.cpp index 4743bf4c1..368989332 100644 --- a/app/dialog/sequence/sequence.cpp +++ b/app/dialog/sequence/sequence.cpp @@ -159,7 +159,7 @@ void SequenceDialog::accept() sequence_->SetVideoParams(video_params); sequence_->SetAudioParams(audio_params); sequence_->SetLabel(name_field_->text()); - sequence_->video_frame_cache()->SetIsAutomatic(parameter_tab_->GetSelectedPreviewAutoCache()); + sequence_->SetVideoAutoCacheEnabled(parameter_tab_->GetSelectedPreviewAutoCache()); } QDialog::accept(); @@ -193,7 +193,7 @@ SequenceDialog::SequenceParamCommand::SequenceParamCommand(Sequence* s, old_video_params_(s->GetVideoParams()), old_audio_params_(s->GetAudioParams()), old_name_(s->GetLabel()), - old_autocache_(s->video_frame_cache()->IsAutomatic()) + old_autocache_(s->IsVideoAutoCacheEnabled()) { } @@ -211,7 +211,7 @@ void SequenceDialog::SequenceParamCommand::redo() sequence_->SetAudioParams(new_audio_params_); } sequence_->SetLabel(new_name_); - sequence_->video_frame_cache()->SetIsAutomatic(new_autocache_); + sequence_->SetVideoAutoCacheEnabled(new_autocache_); } void SequenceDialog::SequenceParamCommand::undo() @@ -223,7 +223,7 @@ void SequenceDialog::SequenceParamCommand::undo() sequence_->SetAudioParams(old_audio_params_); } sequence_->SetLabel(old_name_); - sequence_->video_frame_cache()->SetIsAutomatic(old_autocache_); + sequence_->SetVideoAutoCacheEnabled(old_autocache_); } } diff --git a/app/dialog/sequence/sequencedialogparametertab.cpp b/app/dialog/sequence/sequencedialogparametertab.cpp index b00980d1c..1d0fa868e 100644 --- a/app/dialog/sequence/sequencedialogparametertab.cpp +++ b/app/dialog/sequence/sequencedialogparametertab.cpp @@ -89,7 +89,7 @@ SequenceDialogParameterTab::SequenceDialogParameterTab(Sequence* sequence, QWidg interlacing_combo_->SetInterlaceMode(vp.interlacing()); preview_resolution_field_->SetDivider(vp.divider()); preview_format_field_->SetPixelFormat(vp.format()); - preview_autocache_field_->setChecked(sequence->video_frame_cache()->IsAutomatic()); + preview_autocache_field_->setChecked(sequence->IsVideoAutoCacheEnabled()); audio_sample_rate_field_->SetSampleRate(ap.sample_rate()); audio_channels_field_->SetChannelLayout(ap.channel_layout()); diff --git a/app/node/block/clip/clip.cpp b/app/node/block/clip/clip.cpp index 43aff7934..018984bce 100644 --- a/app/node/block/clip/clip.cpp +++ b/app/node/block/clip/clip.cpp @@ -127,12 +127,6 @@ void ClipBlock::set_media_in(const rational &media_in) void ClipBlock::SetAutocache(bool e) { SetStandardValue(kAutoCacheInput, e); - - if (e) { - RequestInvalidatedFromConnected(); - } else { - qDebug() << "FIXME: signal that frames for this clip should be unqueued"; - } } rational ClipBlock::SequenceToMediaTime(const rational &sequence_time, bool ignore_reverse, bool ignore_speed) const @@ -189,7 +183,7 @@ rational ClipBlock::MediaToSequenceTime(const rational &media_time) const return sequence_time; } -void ClipBlock::RequestInvalidatedFromConnected() +void ClipBlock::RequestInvalidatedFromConnected(const TimeRange &range) { Track::Type type = GetTrackType(); @@ -198,39 +192,39 @@ void ClipBlock::RequestInvalidatedFromConnected() TimeRange max_range = InputTimeAdjustment(kBufferIn, -1, TimeRange(0, length())); if (type == Track::kVideo) { // Handle thumbnails - { - TimeRangeList invalid = connected->thumbnail_cache()->GetInvalidatedRanges(max_range); - for (const TimeRange &r : invalid) { - emit connected->thumbnail_cache()->Request(r, PlaybackCache::kPreviewsOnly); - } - } + RequestInvalidatedForCache(connected->thumbnail_cache(), max_range, range); // Handle video cache if (IsAutocaching()) { - TimeRangeList invalid = connected->video_frame_cache()->GetInvalidatedRanges(max_range); - for (const TimeRange &r : invalid) { - emit connected->video_frame_cache()->Request(r, PlaybackCache::kPreviewsOnly); - } + RequestInvalidatedForCache(connected->video_frame_cache(), max_range, range); } } else if (type == Track::kAudio) { - { - TimeRangeList invalid = connected->waveform_cache()->GetInvalidatedRanges(max_range); - for (const TimeRange &r : invalid) { - emit connected->waveform_cache()->Request(r, PlaybackCache::kPreviewsOnly); - } - } + // Handle waveforms + RequestInvalidatedForCache(connected->waveform_cache(), max_range, range); + // Handle audio cache if (IsAutocaching()) { - TimeRangeList invalid = connected->audio_playback_cache()->GetInvalidatedRanges(max_range); - for (const TimeRange &r : invalid) { - emit connected->audio_playback_cache()->Request(r, PlaybackCache::kPreviewsOnly); - } + RequestInvalidatedForCache(connected->audio_playback_cache(), max_range, range); } } } } } +void ClipBlock::RequestInvalidatedForCache(PlaybackCache *cache, const TimeRange &max_range, const TimeRange &range) +{ + if ((range.in() == RATIONAL_MIN && range.out() == RATIONAL_MAX) || !range.length().isNull()) { + // Request only this range + emit cache->Request(range.Intersected(max_range)); + } else { + // Request all ranges currently marked as invalid + TimeRangeList invalid = cache->GetInvalidatedRanges(max_range); + for (const TimeRange &r : invalid) { + emit cache->Request(r); + } + } +} + void ClipBlock::InvalidateCache(const TimeRange& range, const QString& from, int element, InvalidateCacheOptions options) { Q_UNUSED(element) @@ -238,7 +232,7 @@ void ClipBlock::InvalidateCache(const TimeRange& range, const QString& from, int // If signal is from texture input, transform all times from media time to sequence time if (from == kBufferIn) { // Render caches where necessary - RequestInvalidatedFromConnected(); + RequestInvalidatedFromConnected(range); // Adjust range from media time to sequence time TimeRange adj; @@ -311,6 +305,27 @@ void ClipBlock::InputDisconnectedEvent(const QString &input, int element, Node * } } +void ClipBlock::InputValueChangedEvent(const QString &input, int element) +{ + super::InputValueChangedEvent(input, element); + + if (input == kAutoCacheInput) { + if (IsAutocaching()) { + RequestInvalidatedFromConnected(); + } else { + Track::Type type = GetTrackType(); + + if (Node *connected = GetConnectedOutput(kBufferIn)) { + if (type == Track::kVideo) { + emit connected->video_frame_cache()->CancelAll(); + } else if (type == Track::kAudio) { + emit connected->audio_playback_cache()->CancelAll(); + } + } + } + } +} + TimeRange ClipBlock::InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const { Q_UNUSED(element) diff --git a/app/node/block/clip/clip.h b/app/node/block/clip/clip.h index 13c5a64ad..26e76e5ce 100644 --- a/app/node/block/clip/clip.h +++ b/app/node/block/clip/clip.h @@ -178,12 +178,16 @@ protected: virtual void InputDisconnectedEvent(const QString& input, int element, Node *output) override; + virtual void InputValueChangedEvent(const QString& input, int element) override; + private: rational SequenceToMediaTime(const rational& sequence_time, bool ignore_reverse = false, bool ignore_speed = false) const; rational MediaToSequenceTime(const rational& media_time) const; - void RequestInvalidatedFromConnected(); + void RequestInvalidatedFromConnected(const TimeRange &range = TimeRange()); + + void RequestInvalidatedForCache(PlaybackCache *cache, const TimeRange &max_range, const TimeRange &range); QVector block_links_; diff --git a/app/node/node.cpp b/app/node/node.cpp index 7d3bc7345..2b5220195 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -54,7 +54,7 @@ Node::Node() : AddInput(kEnabledInput, NodeValue::kBoolean, true); video_cache_ = new FrameHashCache(this); - thumbnail_cache_ = new FrameHashCache(this); + thumbnail_cache_ = new ThumbnailCache(this); audio_cache_ = new AudioPlaybackCache(this); waveform_cache_ = new AudioWaveformCache(this); } diff --git a/app/node/node.h b/app/node/node.h index 6a96f97a1..c331d9b6d 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -227,7 +227,7 @@ public: return video_cache_; } - FrameHashCache* thumbnail_cache() const + ThumbnailCache* thumbnail_cache() const { return thumbnail_cache_; } @@ -1416,7 +1416,7 @@ private: QString effect_input_; FrameHashCache *video_cache_; - FrameHashCache *thumbnail_cache_; + ThumbnailCache *thumbnail_cache_; AudioPlaybackCache *audio_cache_; AudioWaveformCache *waveform_cache_; diff --git a/app/node/output/viewer/viewer.cpp b/app/node/output/viewer/viewer.cpp index 84492de8f..efad82cc7 100644 --- a/app/node/output/viewer/viewer.cpp +++ b/app/node/output/viewer/viewer.cpp @@ -226,13 +226,13 @@ void ViewerOutput::InvalidateCache(const TimeRange& range, const QString& from, //emit connected->thumbnail_cache()->Request(range.Intersected(max_range), PlaybackCache::kPreviewsOnly); if (autocache_input_video_) { TimeRange max_range = InputTimeAdjustment(from, element, TimeRange(0, GetVideoLength())); - emit connected->video_frame_cache()->Request(range.Intersected(max_range), PlaybackCache::kPreviewsOnly); + emit connected->video_frame_cache()->Request(range.Intersected(max_range)); } } else if (from == kSamplesInput) { TimeRange max_range = InputTimeAdjustment(from, element, TimeRange(0, GetAudioLength())); - emit connected->waveform_cache()->Request(range.Intersected(max_range), PlaybackCache::kPreviewsOnly); + emit connected->waveform_cache()->Request(range.Intersected(max_range)); if (autocache_input_audio_) { - emit connected->audio_playback_cache()->Request(range.Intersected(max_range), PlaybackCache::kPreviewsOnly); + emit connected->audio_playback_cache()->Request(range.Intersected(max_range)); } } } @@ -392,7 +392,7 @@ void ViewerOutput::ConnectedToPreviewEvent() TimeRange max_range = InputTimeAdjustment(kSamplesInput, -1, TimeRange(0, GetAudioLength())); TimeRangeList invalid = connected->waveform_cache()->GetInvalidatedRanges(max_range); for (const TimeRange &r : invalid) { - emit connected->waveform_cache()->Request(r, PlaybackCache::kPreviewsOnly); + emit connected->waveform_cache()->Request(r); } } } diff --git a/app/node/output/viewer/viewer.h b/app/node/output/viewer/viewer.h index daca1d4a4..156ec6a3d 100644 --- a/app/node/output/viewer/viewer.h +++ b/app/node/output/viewer/viewer.h @@ -184,6 +184,9 @@ public: virtual void ConnectedToPreviewEvent() override; + bool IsVideoAutoCacheEnabled() const { qDebug() << "sequence ac is a stub"; return false; } + void SetVideoAutoCacheEnabled(bool e) { qDebug() << "sequence ac is a stub"; } + static const QString kVideoParamsInput; static const QString kAudioParamsInput; static const QString kSubtitleParamsInput; diff --git a/app/render/framehashcache.h b/app/render/framehashcache.h index 460d33fee..e6a726dbb 100644 --- a/app/render/framehashcache.h +++ b/app/render/framehashcache.h @@ -91,6 +91,16 @@ private slots: }; +class ThumbnailCache : public FrameHashCache +{ + Q_OBJECT +public: + ThumbnailCache(QObject* parent = nullptr) : + FrameHashCache(parent) + { + } +}; + } #endif // VIDEORENDERFRAMECACHE_H diff --git a/app/render/playbackcache.cpp b/app/render/playbackcache.cpp index 3d5db2258..80384fb63 100644 --- a/app/render/playbackcache.cpp +++ b/app/render/playbackcache.cpp @@ -39,10 +39,6 @@ void PlaybackCache::Invalidate(const TimeRange &r) InvalidateEvent(r); emit Invalidated(r); - - if (automatic_) { - emit Request(r, kCacheOnly); - } } Node *PlaybackCache::parent() const @@ -155,21 +151,11 @@ Project *PlaybackCache::GetProject() const } PlaybackCache::PlaybackCache(QObject *parent) : - QObject(parent), - automatic_(false) + QObject(parent) { uuid_ = QUuid::createUuid(); } -void PlaybackCache::SetIsAutomatic(bool e) -{ - if (automatic_ != e) { - automatic_ = e; - - emit AutomaticChanged(automatic_); - } -} - TimeRangeList PlaybackCache::GetInvalidatedRanges(TimeRange intersecting) { TimeRangeList invalidated; diff --git a/app/render/playbackcache.h b/app/render/playbackcache.h index e5c7db383..c36edd1b3 100644 --- a/app/render/playbackcache.h +++ b/app/render/playbackcache.h @@ -43,9 +43,6 @@ public: const QUuid &GetUuid() const { return uuid_; } void SetUuid(const QUuid &u) { uuid_ = u; } - bool IsAutomatic() const { return automatic_; } - void SetIsAutomatic(bool e); - TimeRangeList GetInvalidatedRanges(TimeRange intersecting); TimeRangeList GetInvalidatedRanges(const rational &length) { @@ -69,11 +66,6 @@ public: QDir GetThisCacheDirectory() const; static QDir GetThisCacheDirectory(const QString &cache_path, const QUuid &cache_id); - enum RequestType { - kCacheOnly, - kPreviewsOnly - }; - void LoadState(); void SaveState(); @@ -85,9 +77,9 @@ signals: void Validated(const olive::TimeRange& r); - void Request(const olive::TimeRange& r, olive::PlaybackCache::RequestType type); + void Request(const olive::TimeRange& r); - void AutomaticChanged(bool e); + void CancelAll(); protected: void Validate(const TimeRange& r, bool signal = true); @@ -105,8 +97,6 @@ private: QUuid uuid_; - bool automatic_; - }; } diff --git a/app/render/previewautocacher.cpp b/app/render/previewautocacher.cpp index f395fa908..1ace48877 100644 --- a/app/render/previewautocacher.cpp +++ b/app/render/previewautocacher.cpp @@ -80,28 +80,44 @@ RenderTicketPtr PreviewAutoCacher::GetSingleFrame(const rational &t, RenderTicke RenderTicketPtr PreviewAutoCacher::GetRangeOfAudio(TimeRange range, RenderTicketPriority priority) { - return RenderAudio(copied_viewer_node_->GetConnectedSampleOutput(), range, PlaybackCache::kCacheOnly, priority, nullptr); + return RenderAudio(copied_viewer_node_->GetConnectedSampleOutput(), range, priority, nullptr); } void PreviewAutoCacher::VideoInvalidatedFromCache(const TimeRange &range) { - FrameHashCache *cache = static_cast(sender()); + PlaybackCache *cache = static_cast(sender()); - VideoInvalidatedFromNode(cache->parent(), range, PlaybackCache::kCacheOnly); + VideoInvalidatedFromNode(cache, range); } -void PreviewAutoCacher::ThumbnailsInvalidatedFromCache(const TimeRange &range) +void PreviewAutoCacher::AudioInvalidatedFromCache(const TimeRange &range) { - FrameHashCache *cache = static_cast(sender()); + PlaybackCache *cache = static_cast(sender()); - VideoInvalidatedFromNode(cache->parent(), range, PlaybackCache::kPreviewsOnly); + AudioInvalidatedFromNode(cache, range); } -void PreviewAutoCacher::AudioInvalidatedFromCache(const TimeRange &range, PlaybackCache::RequestType type) +void PreviewAutoCacher::CancelForCache() { - AudioPlaybackCache *cache = static_cast(sender()); + PlaybackCache *cache = static_cast(sender()); - AudioInvalidatedFromNode(cache->parent(), range, type); + if (dynamic_cast(cache) || dynamic_cast(cache)) { + for (auto it=pending_video_jobs_.begin(); it!=pending_video_jobs_.end(); ) { + if ((*it).cache == cache) { + it = pending_video_jobs_.erase(it); + } else { + it++; + } + } + } else if (dynamic_cast(cache) || dynamic_cast(cache)) { + for (auto it=pending_audio_jobs_.begin(); it!=pending_audio_jobs_.end(); ) { + if ((*it).cache == cache) { + it = pending_audio_jobs_.erase(it); + } else { + it++; + } + } + } } void PreviewAutoCacher::AudioRendered() @@ -111,46 +127,45 @@ void PreviewAutoCacher::AudioRendered() // If the task list doesn't contain this watcher, presumably it was cleared as a result of a // viewer switch, so we'll completely ignore this watcher - if (audio_tasks_.contains(watcher)) { + if (running_audio_tasks_.removeOne(watcher)) { // Assume that a "result" is a fully completed image and a non-result is a cancelled ticket - TimeRange range = audio_tasks_.take(watcher); + TimeRange range = watcher->property("time").value(); Node *node = copy_map_.key(Node::ValueToPtr(watcher->property("node"))); if (watcher->HasResult() && node) { - AudioCacheData &d = audio_cache_data_[node]; + if (PlaybackCache *cache = Node::ValueToPtr(watcher->property("cache"))) { + AudioCacheData &d = audio_cache_data_[cache]; - JobTime watcher_job_time = watcher->property("job").value(); + JobTime watcher_job_time = watcher->property("job").value(); - TimeRangeList valid_ranges = d.job_tracker.getCurrentSubRanges(range, watcher_job_time); + TimeRangeList valid_ranges = d.job_tracker.getCurrentSubRanges(range, watcher_job_time); - AudioVisualWaveform waveform = watcher->GetTicket()->property("waveform").value(); + AudioVisualWaveform waveform = watcher->GetTicket()->property("waveform").value(); - SampleBuffer buf = watcher->Get().value(); - node->audio_playback_cache()->SetParameters(buf.audio_params()); - node->waveform_cache()->SetParameters(buf.audio_params()); + SampleBuffer buf = watcher->Get().value(); + node->audio_playback_cache()->SetParameters(buf.audio_params()); + node->waveform_cache()->SetParameters(buf.audio_params()); - PlaybackCache::RequestType type = PlaybackCache::RequestType(watcher->property("type").toInt()); + bool incomplete = watcher->GetTicket()->property("incomplete").toBool(); - if (type == PlaybackCache::kCacheOnly) { - // WritePCM is tolerant to its buffer being null, it will just write silence instead - if (AudioPlaybackCache *cache = Node::ValueToPtr(watcher->property("cache"))) { - cache->WritePCM(range, - valid_ranges, - watcher->Get().value()); + if (AudioPlaybackCache *pcm = dynamic_cast(cache)) { + // WritePCM is tolerant to its buffer being null, it will just write silence instead + pcm->WritePCM(range, + valid_ranges, + watcher->Get().value()); + } else if (AudioWaveformCache *wave = dynamic_cast(cache)) { + if (!incomplete) { + wave->WriteWaveform(range, valid_ranges, &waveform); + } } - } else { - // Detect if this audio was incomplete because it was waiting on a conform to finish - if (watcher->GetTicket()->property("incomplete").toBool()) { + + if (incomplete) { if (last_conform_task_ > watcher_job_time) { // Requeue now - node->audio_playback_cache()->Invalidate(range); + cache->Invalidate(range); } else { // Wait for conform - d.needing_conform.insert(range); - } - } else { - if (AudioWaveformCache *cache = Node::ValueToPtr(watcher->property("cache"))) { - cache->WriteWaveform(range, valid_ranges, &waveform); + d.needs_conform.insert(range); } } } @@ -169,20 +184,16 @@ void PreviewAutoCacher::VideoRendered() // If the task list doesn't contain this watcher, presumably it was cleared as a result of a // viewer switch, so we'll completely ignore this watcher - auto it = video_tasks_.find(watcher); - - if (it != video_tasks_.end()) { + if (running_video_tasks_.removeOne(watcher)) { // Assume that a "result" is a fully completed image and a non-result is a cancelled ticket if (watcher->HasResult()) { if (watcher->GetTicket()->property("cached").toBool()) { if (FrameHashCache *cache = Node::ValueToPtr(watcher->property("cache"))) { - cache->ValidateTime(it.value()); + cache->ValidateTime(watcher->property("time").value()); } } } - video_tasks_.erase(it); - // Continue rendering TryRender(); } @@ -333,7 +344,7 @@ void PreviewAutoCacher::ConnectToNodeCache(Node *node) connect(node->thumbnail_cache(), &PlaybackCache::Request, this, - &PreviewAutoCacher::ThumbnailsInvalidatedFromCache); + &PreviewAutoCacher::VideoInvalidatedFromCache); connect(node->audio_playback_cache(), &PlaybackCache::Request, @@ -345,6 +356,16 @@ void PreviewAutoCacher::ConnectToNodeCache(Node *node) this, &PreviewAutoCacher::AudioInvalidatedFromCache); + connect(node->video_frame_cache(), + &PlaybackCache::CancelAll, + this, + &PreviewAutoCacher::CancelForCache); + + connect(node->audio_playback_cache(), + &PlaybackCache::CancelAll, + this, + &PreviewAutoCacher::CancelForCache); + node->video_frame_cache()->LoadState(); node->audio_playback_cache()->LoadState(); node->thumbnail_cache()->LoadState(); @@ -360,7 +381,7 @@ void PreviewAutoCacher::DisconnectFromNodeCache(Node *node) disconnect(node->thumbnail_cache(), &PlaybackCache::Request, this, - &PreviewAutoCacher::ThumbnailsInvalidatedFromCache); + &PreviewAutoCacher::VideoInvalidatedFromCache); disconnect(node->audio_playback_cache(), &PlaybackCache::Request, @@ -372,6 +393,16 @@ void PreviewAutoCacher::DisconnectFromNodeCache(Node *node) this, &PreviewAutoCacher::AudioInvalidatedFromCache); + disconnect(node->video_frame_cache(), + &PlaybackCache::CancelAll, + this, + &PreviewAutoCacher::CancelForCache); + + disconnect(node->audio_playback_cache(), + &PlaybackCache::CancelAll, + this, + &PreviewAutoCacher::CancelForCache); + node->video_frame_cache()->SaveState(); node->audio_playback_cache()->SaveState(); node->thumbnail_cache()->SaveState(); @@ -396,41 +427,29 @@ void PreviewAutoCacher::CancelQueuedSingleFrameRender() } } -void PreviewAutoCacher::VideoInvalidatedList(Node *node, const TimeRangeList &list) -{ - foreach (const TimeRange &range, list) { - VideoInvalidatedFromNode(node, range, PlaybackCache::kCacheOnly); - } -} - -void PreviewAutoCacher::AudioInvalidatedList(Node *node, const TimeRangeList &list) -{ - foreach (const TimeRange &range, list) { - AudioInvalidatedFromNode(node, range, PlaybackCache::kCacheOnly); - } -} - void PreviewAutoCacher::StartCachingRange(const TimeRange &range, TimeRangeList *range_list, RenderJobTracker *tracker) { range_list->insert(range); tracker->insert(range, graph_changed_time_); } -void PreviewAutoCacher::StartCachingVideoRange(Node *node, const TimeRange &range, PlaybackCache::RequestType type) +void PreviewAutoCacher::StartCachingVideoRange(PlaybackCache *cache, const TimeRange &range) { - pending_video_jobs_.push_back({node, range, TimeRangeListFrameIterator({range}, viewer_node_->GetVideoParams().frame_rate_as_time_base()), type}); - video_cache_data_[node].job_tracker.insert(range, graph_changed_time_); + Node *node = cache->parent(); + pending_video_jobs_.push_back({node, cache, range, TimeRangeListFrameIterator({range}, viewer_node_->GetVideoParams().frame_rate_as_time_base())}); + video_cache_data_[cache].job_tracker.insert(range, graph_changed_time_); TryRender(); } -void PreviewAutoCacher::StartCachingAudioRange(Node *node, const TimeRange &range, PlaybackCache::RequestType type) +void PreviewAutoCacher::StartCachingAudioRange(PlaybackCache *cache, const TimeRange &range) { - pending_audio_jobs_.push_back({node, range, type}); - audio_cache_data_[node].job_tracker.insert(range, graph_changed_time_); + Node *node = cache->parent(); + pending_audio_jobs_.push_back({node, cache, range}); + audio_cache_data_[cache].job_tracker.insert(range, graph_changed_time_); TryRender(); } -void PreviewAutoCacher::VideoInvalidatedFromNode(Node *node, const TimeRange &range, PlaybackCache::RequestType type) +void PreviewAutoCacher::VideoInvalidatedFromNode(PlaybackCache *cache, const TimeRange &range) { // Stop any current render tasks because a) they might be out of date now anyway, and b) we // want to dedicate all our rendering power to realtime feedback for the user @@ -438,18 +457,18 @@ void PreviewAutoCacher::VideoInvalidatedFromNode(Node *node, const TimeRange &ra // If auto-cache is enabled and a slider is not being dragged, queue up to hash these frames if (!NodeInputDragger::IsInputBeingDragged()) { - StartCachingVideoRange(node, range, type); + StartCachingVideoRange(cache, range); } } -void PreviewAutoCacher::AudioInvalidatedFromNode(Node *node, const TimeRange &range, PlaybackCache::RequestType type) +void PreviewAutoCacher::AudioInvalidatedFromNode(PlaybackCache *cache, const TimeRange &range) { // We don't stop rendering audio because currently there's no system of requeuing audio if it's // cancelled, so some areas may end up unrendered forever // ClearAudioQueue(); // If we're auto-caching audio or require realtime waveforms, we'll have to render this - StartCachingAudioRange(node, range, type); + StartCachingAudioRange(cache, range); } void PreviewAutoCacher::SetPlayhead(const rational &playhead) @@ -465,25 +484,25 @@ void CancelTasks(const T &task_list, bool and_wait) { for (auto it=task_list.cbegin(); it!=task_list.cend(); it++) { // Signal that the ticket should not be finished - it.key()->Cancel(); + (*it)->Cancel(); } if (and_wait) { // Wait for each ticket to finish for (auto it=task_list.cbegin(); it!=task_list.cend(); it++) { - it.key()->WaitForFinished(); + (*it)->WaitForFinished(); } } } void PreviewAutoCacher::CancelVideoTasks(bool and_wait_for_them_to_finish) { - CancelTasks(video_tasks_, and_wait_for_them_to_finish); + CancelTasks(running_video_tasks_, and_wait_for_them_to_finish); } void PreviewAutoCacher::CancelAudioTasks(bool and_wait_for_them_to_finish) { - CancelTasks(audio_tasks_, and_wait_for_them_to_finish); + CancelTasks(running_audio_tasks_, and_wait_for_them_to_finish); } bool PreviewAutoCacher::IsRenderingCustomRange() const @@ -545,8 +564,8 @@ void PreviewAutoCacher::TryRender() // Check if we have jobs running in other threads that shouldn't be interrupted right now // NOTE: We don't check for downloads because, while they run in another thread, they don't // require any access to the graph and therefore don't risk race conditions. - if (!audio_tasks_.isEmpty() - || !video_tasks_.isEmpty()) { + if (!running_audio_tasks_.isEmpty() + || !running_video_tasks_.isEmpty()) { return; } @@ -558,7 +577,6 @@ void PreviewAutoCacher::TryRender() // Check if already caching this RenderTicketWatcher *watcher = RenderFrame(copied_viewer_node_->GetConnectedTextureOutput(), single_frame_render_->property("time").value(), - PlaybackCache::kCacheOnly, RenderTicketPriority(single_frame_render_->property("priority").toInt()), nullptr); video_immediate_passthroughs_[watcher].append(single_frame_render_); @@ -577,22 +595,8 @@ void PreviewAutoCacher::TryRender() if (Node *copy = copy_map_.value(d.node)) { // Queue next frames rational t; - while (video_tasks_.size() < max_tasks && d.iterator.GetNext(&t)) { - RenderTicketWatcher* render_task = video_tasks_.key(t); - - // We want this hash, if we're not already rendering, start render now - if (!render_task) { - // Don't render any hash more than once - FrameHashCache *using_cache; - - if (d.type == PlaybackCache::kCacheOnly) { - using_cache = d.node->video_frame_cache(); - } else { - using_cache = d.node->thumbnail_cache(); - } - - RenderFrame(copy, t, d.type, RenderTicketPriority::kNormal, using_cache); - } + while (running_video_tasks_.size() < max_tasks && d.iterator.GetNext(&t)) { + RenderFrame(copy, t, RenderTicketPriority::kNormal, d.cache); emit SignalCacheProxyTaskProgress(double(d.iterator.frame_index()) / double(d.iterator.size())); @@ -617,13 +621,7 @@ void PreviewAutoCacher::TryRender() // Start job if (Node *copy = copy_map_.value(d.node)) { - PlaybackCache *cache; - if (d.type == PlaybackCache::kPreviewsOnly) { - cache = d.node->waveform_cache(); - } else { - cache = d.node->audio_playback_cache(); - } - RenderAudio(copy, d.range, d.type, RenderTicketPriority::kNormal, cache); + RenderAudio(copy, d.range, RenderTicketPriority::kNormal, d.cache); } else { qCritical() << "Failed to find node copy for audio job"; } @@ -633,14 +631,14 @@ void PreviewAutoCacher::TryRender() } } -RenderTicketWatcher* PreviewAutoCacher::RenderFrame(Node *node, const rational& time, PlaybackCache::RequestType type, RenderTicketPriority priority, FrameHashCache *cache) +RenderTicketWatcher* PreviewAutoCacher::RenderFrame(Node *node, const rational& time, RenderTicketPriority priority, PlaybackCache *cache) { RenderTicketWatcher* watcher = new RenderTicketWatcher(); watcher->setProperty("job", QVariant::fromValue(last_update_time_)); watcher->setProperty("cache", Node::PtrToValue(cache)); - watcher->setProperty("type", type); + watcher->setProperty("time", QVariant::fromValue(time)); connect(watcher, &RenderTicketWatcher::Finished, this, &PreviewAutoCacher::VideoRendered); - video_tasks_.insert(watcher, time); + running_video_tasks_.append(watcher); RenderManager::RenderVideoParams rvp(node, copied_viewer_node_->GetVideoParams(), @@ -648,18 +646,18 @@ RenderTicketWatcher* PreviewAutoCacher::RenderFrame(Node *node, const rational& time, copied_color_manager_); - if (cache) { - if (type == PlaybackCache::kPreviewsOnly) { + if (FrameHashCache *frame_cache = dynamic_cast(cache)) { + if (ThumbnailCache *wave_cache = dynamic_cast(cache)) { rvp.video_params.set_divider(VideoParams::GetDividerForTargetResolution(rvp.video_params.width(), rvp.video_params.height(), 160, 120)); rvp.force_color_output = display_color_processor_; rvp.force_format = VideoParams::kFormatUnsigned8; - cache->SetTimebase(rational(1, 10)); + wave_cache->SetTimebase(rational(1, 10)); } else { - cache->SetTimebase(viewer_node_->GetVideoParams().frame_rate_as_time_base()); + frame_cache->SetTimebase(viewer_node_->GetVideoParams().frame_rate_as_time_base()); } - rvp.AddCache(cache); + rvp.AddCache(frame_cache); } rvp.priority = priority; @@ -671,21 +669,21 @@ RenderTicketWatcher* PreviewAutoCacher::RenderFrame(Node *node, const rational& return watcher; } -RenderTicketPtr PreviewAutoCacher::RenderAudio(Node *node, const TimeRange &r, PlaybackCache::RequestType type, RenderTicketPriority priority, PlaybackCache *cache) +RenderTicketPtr PreviewAutoCacher::RenderAudio(Node *node, const TimeRange &r, RenderTicketPriority priority, PlaybackCache *cache) { RenderTicketWatcher* watcher = new RenderTicketWatcher(); watcher->setProperty("job", QVariant::fromValue(last_update_time_)); watcher->setProperty("node", Node::PtrToValue(node)); - watcher->setProperty("type", type); watcher->setProperty("cache", Node::PtrToValue(cache)); + watcher->setProperty("time", QVariant::fromValue(r)); connect(watcher, &RenderTicketWatcher::Finished, this, &PreviewAutoCacher::AudioRendered); - audio_tasks_.insert(watcher, r); + running_audio_tasks_.append(watcher); RenderManager::RenderAudioParams rap(node, r, copied_viewer_node_->GetAudioParams()); - rap.generate_waveforms = (type == PlaybackCache::kPreviewsOnly); + rap.generate_waveforms = dynamic_cast(cache); rap.priority = priority; rap.clamp = false; @@ -700,15 +698,10 @@ void PreviewAutoCacher::ConformFinished() last_conform_task_.Acquire(); for (auto it=audio_cache_data_.begin(); it!=audio_cache_data_.end(); it++) { - AudioCacheData &d = it.value(); - - if (!d.needing_conform.isEmpty()) { - // This list should be empty if there was a viewer switch - foreach (const TimeRange &range, d.needing_conform) { - it.key()->audio_playback_cache()->Invalidate(range); - } - d.needing_conform.clear(); + foreach (const TimeRange &range, it.value().needs_conform) { + it.key()->Request(range); } + it.value().needs_conform.clear(); } } @@ -725,7 +718,7 @@ void PreviewAutoCacher::ForceCacheRange(const TimeRange &range) custom_autocache_range_ = range; // Re-hash these frames and start rendering - StartCachingVideoRange(viewer_node_, range, PlaybackCache::kCacheOnly); + StartCachingVideoRange(viewer_node_->video_frame_cache(), range); } void PreviewAutoCacher::SetViewerNode(ViewerOutput *viewer_node) @@ -742,17 +735,17 @@ void PreviewAutoCacher::SetViewerNode(ViewerOutput *viewer_node) delayed_requeue_timer_.stop(); // Handle video rendering tasks - if (!video_tasks_.isEmpty()) { + if (!running_video_tasks_.isEmpty()) { // Cancel any video tasks and wait for them to finish CancelVideoTasks(true); - video_tasks_.clear(); + running_video_tasks_.clear(); } // Handle audio rendering tasks - if (!audio_tasks_.isEmpty()) { + if (!running_audio_tasks_.isEmpty()) { // Cancel any audio tasks and wait for them to finish CancelAudioTasks(true); - audio_tasks_.clear(); + running_audio_tasks_.clear(); } // Clear any single frame render that might be queued diff --git a/app/render/previewautocacher.h b/app/render/previewautocacher.h index 1256d8106..18c688261 100644 --- a/app/render/previewautocacher.h +++ b/app/render/previewautocacher.h @@ -103,9 +103,9 @@ signals: private: void TryRender(); - RenderTicketWatcher *RenderFrame(Node *node, const rational &time, PlaybackCache::RequestType type, RenderTicketPriority priority, FrameHashCache *cache); + RenderTicketWatcher *RenderFrame(Node *node, const rational &time, RenderTicketPriority priority, PlaybackCache *cache); - RenderTicketPtr RenderAudio(Node *node, const TimeRange &range, PlaybackCache::RequestType type, RenderTicketPriority priority, PlaybackCache *cache); + RenderTicketPtr RenderAudio(Node *node, const TimeRange &range, RenderTicketPriority priority, PlaybackCache *cache); /** * @brief Process all changes to internal NodeGraph copy @@ -132,15 +132,12 @@ private: void CancelQueuedSingleFrameRender(); - void VideoInvalidatedList(Node *node, const TimeRangeList &list); - void AudioInvalidatedList(Node *node, const TimeRangeList &list); - void StartCachingRange(const TimeRange &range, TimeRangeList *range_list, RenderJobTracker *tracker); - void StartCachingVideoRange(Node *node, const TimeRange &range, PlaybackCache::RequestType type); - void StartCachingAudioRange(Node *node, const TimeRange &range, PlaybackCache::RequestType type); + void StartCachingVideoRange(PlaybackCache *cache, const TimeRange &range); + void StartCachingAudioRange(PlaybackCache *cache, const TimeRange &range); - void VideoInvalidatedFromNode(Node *node, const olive::TimeRange &range, PlaybackCache::RequestType type); - void AudioInvalidatedFromNode(Node *node, const olive::TimeRange &range, PlaybackCache::RequestType type); + void VideoInvalidatedFromNode(PlaybackCache *cache, const olive::TimeRange &range); + void AudioInvalidatedFromNode(PlaybackCache *cache, const olive::TimeRange &range); class QueuedJob { public: @@ -187,14 +184,14 @@ private: JobTime last_conform_task_; - QMap audio_tasks_; - QMap video_tasks_; + QVector running_video_tasks_; + QVector running_audio_tasks_; struct VideoJob { Node *node; + PlaybackCache *cache; TimeRange range; TimeRangeListFrameIterator iterator; - PlaybackCache::RequestType type; }; struct VideoCacheData { @@ -203,20 +200,20 @@ private: struct AudioJob { Node *node; + PlaybackCache *cache; TimeRange range; - PlaybackCache::RequestType type; }; struct AudioCacheData { - TimeRangeList needing_conform; RenderJobTracker job_tracker; + TimeRangeList needs_conform; }; std::list pending_video_jobs_; std::list pending_audio_jobs_; - QHash video_cache_data_; - QHash audio_cache_data_; + QHash video_cache_data_; + QHash audio_cache_data_; ColorProcessorPtr display_color_processor_; @@ -225,12 +222,13 @@ private slots: * @brief Handler for when the NodeGraph reports a video change over a certain time range */ void VideoInvalidatedFromCache(const olive::TimeRange &range); - void ThumbnailsInvalidatedFromCache(const olive::TimeRange &range); /** * @brief Handler for when the NodeGraph reports a audio change over a certain time range */ - void AudioInvalidatedFromCache(const olive::TimeRange &range, olive::PlaybackCache::RequestType type); + void AudioInvalidatedFromCache(const olive::TimeRange &range); + + void CancelForCache(); /** * @brief Handler for when the RenderManager has returned rendered audio diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 371f1e89e..c0d899fd3 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -449,11 +449,6 @@ void ViewerWidget::UpdateAutoCacher() auto_cacher_->SetPlayhead(GetTime()); } -void ViewerWidget::ClearVideoAutoCacherQueue() -{ - auto_cacher_->CancelVideoTasks(); -} - void ViewerWidget::DecrementPrequeuedAudio() { prequeuing_audio_--; @@ -656,7 +651,7 @@ void ViewerWidget::QueueNoLongerStarved() void ViewerWidget::ForceRequeueFromCurrentTime() { - ClearVideoAutoCacherQueue(); + //ClearVideoAutoCacherQueue(); int queue = DeterminePlaybackQueueSize(); playback_queue_next_frame_ = GetTimestamp() + playback_speed_; for (int i=queue_watchers_.size(); i