From 9b49bea0967f3704b3a9c7ece5d52b6a9e6604fe Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 25 Mar 2021 16:57:15 +1100 Subject: [PATCH] cache: improved queue performance and stability --- app/render/previewautocacher.cpp | 76 +++++++++++++-------------- app/render/previewautocacher.h | 9 ---- app/threading/threadticket.cpp | 6 +++ app/threading/threadticket.h | 2 + app/threading/threadticketwatcher.cpp | 9 ++++ app/threading/threadticketwatcher.h | 2 + 6 files changed, 55 insertions(+), 49 deletions(-) diff --git a/app/render/previewautocacher.cpp b/app/render/previewautocacher.cpp index 7958483fd..7ccefa594 100644 --- a/app/render/previewautocacher.cpp +++ b/app/render/previewautocacher.cpp @@ -358,13 +358,6 @@ void PreviewAutoCacher::SetPlayhead(const rational &playhead) RequeueFrames(); } -void PreviewAutoCacher::ClearQueue(bool wait) -{ - ClearHashQueue(wait); - ClearVideoQueue(wait); - ClearAudioQueue(wait); -} - void PreviewAutoCacher::ClearHashQueue(bool wait) { auto copy = hash_tasks_; @@ -539,27 +532,34 @@ void PreviewAutoCacher::RequeueFrames() QVector invalidated_ranges = viewer_node_->video_frame_cache()->GetInvalidatedFrames(using_range); - ClearVideoQueue(); - foreach (const rational& t, invalidated_ranges) { const QByteArray& hash = viewer_node_->video_frame_cache()->GetHash(t); - if (t >= using_range.in() - && t < using_range.out() - && !currently_caching_hashes_.contains(hash)) { - // Don't render any hash more than once - currently_caching_hashes_.append(hash); + bool currently_caching_hash = currently_caching_hashes_.contains(hash); - RenderTicketWatcher* watcher = new RenderTicketWatcher(); - watcher->setProperty("hash", hash); - connect(watcher, &RenderTicketWatcher::Finished, this, &PreviewAutoCacher::VideoRendered); - video_tasks_.insert(watcher, hash); - watcher->SetTicket(RenderManager::instance()->RenderFrame(copied_viewer_node_, - color_manager_, - t, - RenderMode::kOffline, - viewer_node_->video_frame_cache(), - false)); + if (t >= using_range.in() + && t < using_range.out()) { + if (!currently_caching_hash) { + // Don't render any hash more than once + currently_caching_hashes_.append(hash); + + RenderTicketWatcher* watcher = new RenderTicketWatcher(); + watcher->setProperty("hash", hash); + connect(watcher, &RenderTicketWatcher::Finished, this, &PreviewAutoCacher::VideoRendered); + video_tasks_.insert(watcher, hash); + watcher->SetTicket(RenderManager::instance()->RenderFrame(copied_viewer_node_, + color_manager_, + t, + RenderMode::kOffline, + viewer_node_->video_frame_cache(), + false)); + } + } else if (currently_caching_hash) { + // Cancel this frame unless it's already started + RenderTicketWatcher* watcher = video_tasks_.key(hash); + if (watcher && watcher->HasStarted()) { + watcher->Cancel(); + } } } @@ -589,27 +589,23 @@ void PreviewAutoCacher::SetViewerNode(Sequence *viewer_node) if (viewer_node_) { // Cancel any remaining tickets and wait for them to finish - ClearQueue(true); - // Clear autocache lists - { - // We need to wait for these since they work directly on the FrameHashCache. Most of the time - // this is fine, but not if the FrameHashCache gets deleted after this function. - ClearHashQueue(true); + // We need to wait for these since they send signals directly to the FrameHashCache + // which might get deleted after this function. + ClearHashQueue(true); - // This can be cleared normally (frames will be discarded and need to be rendered again) - ClearVideoQueue(false); + // This can be cleared normally (frames will be discarded and need to be rendered again) + ClearVideoQueue(true); - // This can be cleared normally (PCM data will be discarded and need to be rendered again) - ClearAudioQueue(false); + // This can be cleared normally (PCM data will be discarded and need to be rendered again) + ClearAudioQueue(true); - // We'll need to wait for these since they work directly on the FrameHashCache. Frames will - // be in the cache for later use. - ClearVideoDownloadQueue(true); + // We'll need to wait for these since they work directly on the FrameHashCache. Frames will + // be in the cache for later use. + ClearVideoDownloadQueue(true); - // No longer caching any hashes - currently_caching_hashes_.clear(); - } + // No longer caching any hashes + currently_caching_hashes_.clear(); // Delete all of our copied nodes qDeleteAll(created_nodes_); diff --git a/app/render/previewautocacher.h b/app/render/previewautocacher.h index 90f6e9a3b..fa4e84a56 100644 --- a/app/render/previewautocacher.h +++ b/app/render/previewautocacher.h @@ -74,15 +74,6 @@ public: */ void SetPlayhead(const rational& playhead); - /** - * @brief Clears queue of running jobs - * - * Any jobs that haven't run yet are cancelled and will never run. Any jobs that are currently - * running are cancelled, but may not be finished by the time this function returns. If the - * jobs must be finished by the time this function returns, set `wait` to TRUE. - */ - void ClearQueue(bool wait = false); - void ClearHashQueue(bool wait = false); void ClearVideoQueue(bool wait = false); void ClearAudioQueue(bool wait = false); diff --git a/app/threading/threadticket.cpp b/app/threading/threadticket.cpp index 9a30fc37f..6bdf24046 100644 --- a/app/threading/threadticket.cpp +++ b/app/threading/threadticket.cpp @@ -48,6 +48,12 @@ QVariant RenderTicket::Get() return result_; } +bool RenderTicket::HasStarted() +{ + QMutexLocker locker(&lock_); + return started_; +} + bool RenderTicket::IsFinished(bool lock) { if (lock) { diff --git a/app/threading/threadticket.h b/app/threading/threadticket.h index 5ead976b6..9ba7afce7 100644 --- a/app/threading/threadticket.h +++ b/app/threading/threadticket.h @@ -52,6 +52,8 @@ public: QVariant Get(); + bool HasStarted(); + bool IsFinished(bool lock = true); bool WasCancelled(); diff --git a/app/threading/threadticketwatcher.cpp b/app/threading/threadticketwatcher.cpp index 34ba594ed..222a8c1d8 100644 --- a/app/threading/threadticketwatcher.cpp +++ b/app/threading/threadticketwatcher.cpp @@ -70,6 +70,15 @@ bool RenderTicketWatcher::IsFinished() } } +bool RenderTicketWatcher::HasStarted() +{ + if (ticket_) { + return ticket_->HasStarted(); + } else { + return false; + } +} + void RenderTicketWatcher::WaitForFinished() { if (ticket_) { diff --git a/app/threading/threadticketwatcher.h b/app/threading/threadticketwatcher.h index a6de21e1a..33458ff9b 100644 --- a/app/threading/threadticketwatcher.h +++ b/app/threading/threadticketwatcher.h @@ -44,6 +44,8 @@ public: bool IsFinished(); + bool HasStarted(); + void WaitForFinished(); QVariant Get();