cache: reimplemented auto-caching
Implemented a smarter auto-cache that's context sensitive. Caching will automatically pause when playback begins or values are being changed and resume when inactive.
This commit is contained in:
@@ -83,6 +83,9 @@ void Config::SetDefaults()
|
||||
config_map_["DropWithoutSequenceBehavior"] = TimelineWidget::kDWSAsk;
|
||||
config_map_["Loop"] = false;
|
||||
|
||||
config_map_["AutoCache"] = true;
|
||||
config_map_["AutoCacheInterval"] = 1000;
|
||||
|
||||
config_map_["NodeCatColor0"] = QVariant::fromValue(Color(0.75f, 0.75f, 0.75f));
|
||||
config_map_["NodeCatColor1"] = QVariant::fromValue(Color(0.25f, 0.25f, 0.25f));
|
||||
config_map_["NodeCatColor2"] = QVariant::fromValue(Color(0.75f, 0.75f, 0.25f));
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
#include "undo/undostack.h"
|
||||
#include "widget/menu/menushared.h"
|
||||
#include "widget/taskview/taskviewitem.h"
|
||||
#include "widget/viewer/viewer.h"
|
||||
#include "window/mainwindow/mainwindow.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
@@ -1134,6 +1135,9 @@ void Core::CacheActiveSequence(bool in_out_only)
|
||||
arp,
|
||||
in_out_only);
|
||||
|
||||
// Stop any current auto-cache tasks
|
||||
ViewerWidget::StopAllBackgroundCacheTasks();
|
||||
|
||||
TaskDialog* dialog = new TaskDialog(task, tr("Caching Sequence"), main_window_);
|
||||
dialog->open();
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ TaskManagerPanel::TaskManagerPanel(QWidget* parent) :
|
||||
connect(TaskManager::instance(), &TaskManager::TaskAdded, view_, &TaskView::AddTask);
|
||||
connect(TaskManager::instance(), &TaskManager::TaskRemoved, view_, &TaskView::RemoveTask);
|
||||
connect(TaskManager::instance(), &TaskManager::TaskFailed, view_, &TaskView::TaskFailed);
|
||||
connect(view_, &TaskView::TaskCancelled, TaskManager::instance(), &TaskManager::CancelTask);
|
||||
|
||||
// Set strings
|
||||
Retranslate();
|
||||
|
||||
@@ -204,6 +204,10 @@ void RenderBackend::UpdateInstance(RenderWorker *instance)
|
||||
|
||||
void RenderBackend::Close()
|
||||
{
|
||||
video_pool_.threads.clear();
|
||||
audio_pool_.threads.clear();
|
||||
hash_pool_.threads.clear();
|
||||
|
||||
CancelQueue();
|
||||
|
||||
video_pool_.Destroy();
|
||||
|
||||
@@ -135,6 +135,11 @@ QList<rational> FrameHashCache::GetFrameListFromTimeRange(const TimeRangeList &r
|
||||
return GetFrameListFromTimeRange(range, timebase_);
|
||||
}
|
||||
|
||||
QList<rational> FrameHashCache::GetInvalidatedFrames() const
|
||||
{
|
||||
return GetFrameListFromTimeRange(GetInvalidatedRanges());
|
||||
}
|
||||
|
||||
void FrameHashCache::SaveCacheFrame(const QByteArray& hash,
|
||||
char* data,
|
||||
const VideoRenderingParams& vparam)
|
||||
|
||||
@@ -68,6 +68,7 @@ public:
|
||||
|
||||
static QList<rational> GetFrameListFromTimeRange(TimeRangeList range_list, const rational& timebase);
|
||||
QList<rational> GetFrameListFromTimeRange(const TimeRangeList &range) const;
|
||||
QList<rational> GetInvalidatedFrames() const;
|
||||
|
||||
protected:
|
||||
virtual void LengthChangedEvent(const rational& old, const rational& newlen) override;
|
||||
|
||||
Vendored
+3
-1
@@ -50,12 +50,14 @@ bool CacheTask::Run()
|
||||
|
||||
Render(range_to_cache, QMatrix4x4(), false);
|
||||
|
||||
download_threads_.waitForDone();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QFuture<void> CacheTask::DownloadFrame(FramePtr frame, const QByteArray &hash)
|
||||
{
|
||||
return QtConcurrent::run(FrameHashCache::SaveCacheFrame, hash, frame);
|
||||
return QtConcurrent::run(&download_threads_, FrameHashCache::SaveCacheFrame, hash, frame);
|
||||
}
|
||||
|
||||
void CacheTask::FrameDownloaded(const QByteArray &hash, const QLinkedList<rational> ×)
|
||||
|
||||
Vendored
+2
@@ -47,6 +47,8 @@ protected:
|
||||
private:
|
||||
bool in_out_only_;
|
||||
|
||||
QThreadPool download_threads_;
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -92,7 +92,7 @@ void RenderTask::Render(const TimeRangeList& range_to_cache,
|
||||
QLinkedList<HashTimePair> sorted_times;
|
||||
QLinkedList<HashTimePair>::iterator sorted_iterator;
|
||||
|
||||
// Rendering is more efficient if we cache in order
|
||||
// Rendering is more efficient if we cache in order, so here we sort
|
||||
QMap< QByteArray, QLinkedList<rational> >::const_iterator i;
|
||||
for (i=times_to_render.constBegin(); i!=times_to_render.constEnd(); i++) {
|
||||
const QByteArray& hash = i.key();
|
||||
@@ -141,9 +141,10 @@ void RenderTask::Render(const TimeRangeList& range_to_cache,
|
||||
QLinkedList<HashDownloadFuturePair>::iterator j;
|
||||
QLinkedList<RangeSampleFuturePair>::iterator k;
|
||||
|
||||
while (!render_lookup_table.isEmpty()
|
||||
|| !download_futures.isEmpty()
|
||||
|| !audio_lookup_table.isEmpty()) {
|
||||
while (!IsCancelled()
|
||||
&& (!render_lookup_table.isEmpty()
|
||||
|| !download_futures.isEmpty()
|
||||
|| !audio_lookup_table.isEmpty())) {
|
||||
|
||||
i = render_lookup_table.begin();
|
||||
|
||||
@@ -197,4 +198,9 @@ void RenderTask::AudioDownloaded(const TimeRange &, SampleBufferPtr)
|
||||
{
|
||||
}
|
||||
|
||||
void RenderTask::SetAnchorPoint(const rational &r)
|
||||
{
|
||||
anchor_point_ = r;
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -57,6 +57,8 @@ protected:
|
||||
return audio_params_;
|
||||
}
|
||||
|
||||
void SetAnchorPoint(const rational& r);
|
||||
|
||||
private:
|
||||
ViewerOutput* viewer_;
|
||||
|
||||
@@ -64,6 +66,8 @@ private:
|
||||
|
||||
AudioRenderingParams audio_params_;
|
||||
|
||||
rational anchor_point_;
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -72,6 +72,17 @@ Task *TaskManager::GetFirstTask() const
|
||||
return tasks_.begin().value();
|
||||
}
|
||||
|
||||
void TaskManager::CancelTaskAndWait(Task* t)
|
||||
{
|
||||
t->Cancel();
|
||||
|
||||
QFutureWatcher<bool>* w = tasks_.key(t);
|
||||
|
||||
if (w) {
|
||||
w->waitForFinished();
|
||||
}
|
||||
}
|
||||
|
||||
void TaskManager::AddTask(Task* t)
|
||||
{
|
||||
// Create a watcher for signalling
|
||||
@@ -89,6 +100,17 @@ void TaskManager::AddTask(Task* t)
|
||||
emit TaskListChanged();
|
||||
}
|
||||
|
||||
void TaskManager::CancelTask(Task *t)
|
||||
{
|
||||
if (failed_tasks_.contains(t)) {
|
||||
failed_tasks_.removeOne(t);
|
||||
emit TaskRemoved(t);
|
||||
t->deleteLater();
|
||||
} else {
|
||||
t->Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void TaskManager::TaskFinished()
|
||||
{
|
||||
QFutureWatcher<bool>* watcher = static_cast<QFutureWatcher<bool>*>(sender());
|
||||
|
||||
@@ -63,6 +63,8 @@ public:
|
||||
|
||||
Task* GetFirstTask() const;
|
||||
|
||||
void CancelTaskAndWait(Task* t);
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* @brief Add a new Task
|
||||
@@ -81,6 +83,8 @@ public slots:
|
||||
*/
|
||||
void AddTask(Task *t);
|
||||
|
||||
void CancelTask(Task* t);
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief Signal emitted when a Task is added by AddTask()
|
||||
|
||||
@@ -47,6 +47,7 @@ void TaskView::AddTask(Task *t)
|
||||
{
|
||||
// Create TaskViewItem (UI representation of a Task) and connect it
|
||||
TaskViewItem* item = new TaskViewItem(t);
|
||||
connect(item, &TaskViewItem::TaskCancelled, this, &TaskView::TaskCancelled);
|
||||
items_.insert(t, item);
|
||||
layout_->insertWidget(layout_->count()-1, item);
|
||||
}
|
||||
|
||||
@@ -42,6 +42,9 @@ class TaskView : public QScrollArea
|
||||
public:
|
||||
TaskView(QWidget* parent);
|
||||
|
||||
signals:
|
||||
void TaskCancelled(Task* t);
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* @brief Creates a TaskViewItem, connects it to a Task, and adds it to this widget
|
||||
|
||||
@@ -61,12 +61,13 @@ TaskViewItem::TaskViewItem(Task* task, QWidget *parent) :
|
||||
|
||||
// Connect to the task
|
||||
connect(task_, &Task::ProgressChanged, this, &TaskViewItem::UpdateProgress);
|
||||
connect(cancel_btn_, &QPushButton::clicked, task_, &Task::Cancel, Qt::DirectConnection);
|
||||
connect(cancel_btn_, &QPushButton::clicked, this, [this] { emit TaskCancelled(task_); });
|
||||
}
|
||||
|
||||
void TaskViewItem::Failed()
|
||||
{
|
||||
task_status_lbl_->setText(task_->GetError());
|
||||
task_status_lbl_->setStyleSheet("color: red");
|
||||
task_status_lbl_->setText(tr("Error: %1").arg(task_->GetError()));
|
||||
}
|
||||
|
||||
void TaskViewItem::UpdateProgress(double d)
|
||||
|
||||
@@ -47,6 +47,9 @@ public:
|
||||
|
||||
void Failed();
|
||||
|
||||
signals:
|
||||
void TaskCancelled(Task* t);
|
||||
|
||||
private:
|
||||
QLabel* task_name_lbl_;
|
||||
QProgressBar* progress_bar_;
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "project/item/sequence/sequence.h"
|
||||
#include "project/project.h"
|
||||
#include "render/pixelformat.h"
|
||||
#include "task/taskmanager.h"
|
||||
#include "widget/menu/menu.h"
|
||||
#include "window/mainwindow/mainwindow.h"
|
||||
|
||||
@@ -43,6 +44,8 @@ OLIVE_NAMESPACE_ENTER
|
||||
|
||||
const int kMaxPreQueueSize = 16;
|
||||
|
||||
CacheTask* ViewerWidget::cache_background_task_ = nullptr;
|
||||
|
||||
ViewerWidget::ViewerWidget(QWidget *parent) :
|
||||
TimeBasedWidget(false, true, parent),
|
||||
playback_speed_(0),
|
||||
@@ -102,11 +105,19 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
|
||||
// FIXME: Magic number
|
||||
SetScale(48.0);
|
||||
|
||||
// Start background renderers
|
||||
// Start background renderer
|
||||
renderer_ = new OpenGLBackend(this);
|
||||
|
||||
// Setup cache wait timer (waits a few seconds of inactivity before caching)
|
||||
cache_wait_timer_.setInterval(1000);
|
||||
cache_wait_timer_.setSingleShot(true);
|
||||
connect(&cache_wait_timer_, &QTimer::timeout, this, &ViewerWidget::StartBackgroundCaching);
|
||||
connect(TaskManager::instance(), &TaskManager::TaskRemoved, this, &ViewerWidget::BackgroundCacheFinished);
|
||||
|
||||
// Ensures that seeking on the waveform view updates the time as expected
|
||||
connect(waveform_view_, &AudioWaveformView::TimeChanged, this, &ViewerWidget::SetTimeAndSignal);
|
||||
|
||||
// Ensures renderer is updated if the global pixel format is changed
|
||||
connect(PixelFormat::instance(), &PixelFormat::FormatChanged, this, &ViewerWidget::UpdateRendererParameters);
|
||||
|
||||
SetAutoMaxScrollBar(true);
|
||||
@@ -196,6 +207,8 @@ void ViewerWidget::ConnectNodeInternal(ViewerOutput *n)
|
||||
|
||||
// Set texture to new texture (or null if no viewer node is available)
|
||||
ForceUpdate();
|
||||
|
||||
StartBackgroundCaching();
|
||||
}
|
||||
|
||||
void ViewerWidget::DisconnectNodeInternal(ViewerOutput *n)
|
||||
@@ -223,6 +236,10 @@ void ViewerWidget::DisconnectNodeInternal(ViewerOutput *n)
|
||||
|
||||
waveform_view_->SetViewer(nullptr);
|
||||
waveform_view_->ConnectTimelinePoints(nullptr);
|
||||
|
||||
StopAllBackgroundCacheTasks();
|
||||
cache_background_task_ = nullptr;
|
||||
cache_wait_timer_.stop();
|
||||
}
|
||||
|
||||
void ViewerWidget::ConnectedNodeChanged(ViewerOutput *n)
|
||||
@@ -343,6 +360,16 @@ void ViewerWidget::SetGizmos(Node *node)
|
||||
display_widget_->SetGizmos(node);
|
||||
}
|
||||
|
||||
void ViewerWidget::StopAllBackgroundCacheTasks()
|
||||
{
|
||||
TaskManager::instance()->CancelTaskAndWait(cache_background_task_);
|
||||
}
|
||||
|
||||
void ViewerWidget::SetBackgroundCacheTask(CacheTask *t)
|
||||
{
|
||||
cache_background_task_ = t;
|
||||
}
|
||||
|
||||
FramePtr DecodeCachedImage(const QString &fn, const rational& time)
|
||||
{
|
||||
FramePtr frame = nullptr;
|
||||
@@ -453,6 +480,8 @@ void ViewerWidget::PlayInternal(int speed, bool in_to_out_only)
|
||||
} else {
|
||||
FillPlaybackQueue();
|
||||
}
|
||||
|
||||
StopBackgroundCache();
|
||||
}
|
||||
|
||||
void ViewerWidget::PushScrubbedAudio()
|
||||
@@ -614,6 +643,29 @@ void ViewerWidget::FinishPlayPreprocess()
|
||||
}
|
||||
}
|
||||
|
||||
VideoRenderingParams ViewerWidget::GenerateVideoParams() const
|
||||
{
|
||||
return VideoRenderingParams(GetConnectedNode()->video_params(),
|
||||
GetCurrentPixelFormat(),
|
||||
RenderMode::kOffline,
|
||||
divider_);
|
||||
}
|
||||
|
||||
AudioRenderingParams ViewerWidget::GenerateAudioParams() const
|
||||
{
|
||||
return AudioRenderingParams(GetConnectedNode()->audio_params(),
|
||||
SampleFormat::kInternalFormat);
|
||||
}
|
||||
|
||||
void ViewerWidget::StopBackgroundCache()
|
||||
{
|
||||
cache_wait_timer_.stop();
|
||||
if (cache_background_task_) {
|
||||
cache_background_task_->Cancel();
|
||||
cache_background_task_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerWidget::UpdateStack()
|
||||
{
|
||||
if (GetConnectedNode()
|
||||
@@ -753,15 +805,38 @@ void ViewerWidget::HashGenerated()
|
||||
watcher->deleteLater();
|
||||
}
|
||||
|
||||
void ViewerWidget::StartBackgroundCaching()
|
||||
{
|
||||
if (Config::Current()["AutoCache"].toBool()) {
|
||||
if (cache_background_task_) {
|
||||
|
||||
// Something else is caching right now, we don't want to do multiple at once so we'll check
|
||||
// again in our next interval
|
||||
cache_wait_timer_.start();
|
||||
|
||||
} else {
|
||||
cache_background_task_ = new CacheTask(GetConnectedNode(),
|
||||
GenerateVideoParams(),
|
||||
GenerateAudioParams(),
|
||||
false);
|
||||
|
||||
TaskManager::instance()->AddTask(cache_background_task_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerWidget::BackgroundCacheFinished(Task* t)
|
||||
{
|
||||
if (cache_background_task_ == t) {
|
||||
cache_background_task_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerWidget::UpdateRendererParameters()
|
||||
{
|
||||
renderer_->SetVideoParams(VideoRenderingParams(GetConnectedNode()->video_params(),
|
||||
GetCurrentPixelFormat(),
|
||||
RenderMode::kOffline,
|
||||
divider_));
|
||||
renderer_->SetVideoParams(GenerateVideoParams());
|
||||
|
||||
renderer_->SetAudioParams(AudioRenderingParams(GetConnectedNode()->audio_params(),
|
||||
SampleFormat::kInternalFormat));
|
||||
renderer_->SetAudioParams(GenerateAudioParams());
|
||||
|
||||
display_widget_->SetVideoParams(GetConnectedNode()->video_params());
|
||||
}
|
||||
@@ -923,6 +998,7 @@ void ViewerWidget::Pause()
|
||||
}
|
||||
|
||||
prequeuing_ = false;
|
||||
StartBackgroundCaching();
|
||||
}
|
||||
|
||||
void ViewerWidget::ShuttleLeft()
|
||||
@@ -1093,6 +1169,11 @@ void ViewerWidget::ViewerInvalidatedRange(const TimeRange &range)
|
||||
ForceUpdate();
|
||||
}
|
||||
|
||||
// Restart the cache wait timer
|
||||
StopBackgroundCache();
|
||||
cache_wait_timer_.start();
|
||||
|
||||
/*
|
||||
QList<rational> invalidated_frames = GetConnectedNode()->video_frame_cache()->GetFrameListFromTimeRange({range});
|
||||
foreach (const rational& r, invalidated_frames) {
|
||||
QFutureWatcher<QByteArray>* watcher = new QFutureWatcher<QByteArray>();
|
||||
@@ -1100,6 +1181,7 @@ void ViewerWidget::ViewerInvalidatedRange(const TimeRange &range)
|
||||
hash_watchers_.insert(watcher, r);
|
||||
watcher->setFuture(renderer_->Hash(r, true));
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "node/output/viewer/viewer.h"
|
||||
#include "panel/scope/scope.h"
|
||||
#include "render/backend/opengl/openglbackend.h"
|
||||
#include "task/cache/cache.h"
|
||||
#include "viewerdisplay.h"
|
||||
#include "viewerplaybacktimer.h"
|
||||
#include "viewerqueue.h"
|
||||
@@ -97,6 +98,9 @@ public:
|
||||
|
||||
void SetGizmos(Node* node);
|
||||
|
||||
static void StopAllBackgroundCacheTasks();
|
||||
static void SetBackgroundCacheTask(CacheTask* t);
|
||||
|
||||
public slots:
|
||||
void Play(bool in_to_out_only);
|
||||
|
||||
@@ -192,6 +196,11 @@ private:
|
||||
|
||||
void FinishPlayPreprocess();
|
||||
|
||||
VideoRenderingParams GenerateVideoParams() const;
|
||||
AudioRenderingParams GenerateAudioParams() const;
|
||||
|
||||
void StopBackgroundCache();
|
||||
|
||||
QStackedWidget* stack_;
|
||||
|
||||
ViewerSizer* sizer_;
|
||||
@@ -235,6 +244,10 @@ private:
|
||||
|
||||
QHash<QFutureWatcher<QByteArray>*, rational> hash_watchers_;
|
||||
|
||||
QTimer cache_wait_timer_;
|
||||
|
||||
static CacheTask* cache_background_task_;
|
||||
|
||||
private slots:
|
||||
void PlaybackTimerUpdate();
|
||||
|
||||
@@ -272,6 +285,10 @@ private slots:
|
||||
|
||||
void HashGenerated();
|
||||
|
||||
void StartBackgroundCaching();
|
||||
|
||||
void BackgroundCacheFinished(Task *t);
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -62,6 +62,7 @@ void MainStatusBar::UpdateStatus()
|
||||
if (manager_->GetTaskCount() == 0) {
|
||||
clearMessage();
|
||||
bar_->setVisible(false);
|
||||
bar_->setValue(0);
|
||||
} else {
|
||||
Task* t = manager_->GetFirstTask();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user