diff --git a/app/dialog/CMakeLists.txt b/app/dialog/CMakeLists.txt
index 39a7c201a..5ce244a46 100644
--- a/app/dialog/CMakeLists.txt
+++ b/app/dialog/CMakeLists.txt
@@ -22,6 +22,7 @@ add_subdirectory(keyframeproperties)
add_subdirectory(loadsave)
add_subdirectory(preferences)
add_subdirectory(projectproperties)
+add_subdirectory(rendercancel)
add_subdirectory(sequence)
add_subdirectory(speedduration)
diff --git a/app/dialog/preferences/tabs/preferencesaudiotab.cpp b/app/dialog/preferences/tabs/preferencesaudiotab.cpp
index 160feede8..a450cf2bd 100644
--- a/app/dialog/preferences/tabs/preferencesaudiotab.cpp
+++ b/app/dialog/preferences/tabs/preferencesaudiotab.cpp
@@ -105,12 +105,15 @@ void PreferencesAudioTab::Accept()
// FIXME: Qt documentation states that QAudioDeviceInfo::deviceName() is a "unique identifiers", which would make them
// ideal for saving in preferences, but in practice they don't actually appear to be unique.
// See: https://bugreports.qt.io/browse/QTBUG-16841
- Config::Current()["AudioOutput"] = selected_output_name;
- Config::Current()["AudioInput"] = selected_input_name;
+ if (Config::Current()["AudioOutput"] != selected_output_name) {
+ Config::Current()["AudioOutput"] = selected_output_name;
+ AudioManager::instance()->SetOutputDevice(selected_output);
+ }
- // Finally, set these as the current device
- AudioManager::instance()->SetOutputDevice(selected_output);
- AudioManager::instance()->SetInputDevice(selected_input);
+ if (Config::Current()["AudioInput"] != selected_input_name) {
+ Config::Current()["AudioInput"] = selected_input_name;
+ AudioManager::instance()->SetInputDevice(selected_input);
+ }
}
void PreferencesAudioTab::RefreshDevices()
diff --git a/app/dialog/rendercancel/CMakeLists.txt b/app/dialog/rendercancel/CMakeLists.txt
new file mode 100644
index 000000000..a9ca563e9
--- /dev/null
+++ b/app/dialog/rendercancel/CMakeLists.txt
@@ -0,0 +1,22 @@
+# Olive - Non-Linear Video Editor
+# Copyright (C) 2019 Olive Team
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+set(OLIVE_SOURCES
+ ${OLIVE_SOURCES}
+ dialog/rendercancel/rendercancel.h
+ dialog/rendercancel/rendercancel.cpp
+ PARENT_SCOPE
+)
diff --git a/app/dialog/rendercancel/rendercancel.cpp b/app/dialog/rendercancel/rendercancel.cpp
new file mode 100644
index 000000000..3b78e00e0
--- /dev/null
+++ b/app/dialog/rendercancel/rendercancel.cpp
@@ -0,0 +1,56 @@
+#include "rendercancel.h"
+
+RenderCancelDialog::RenderCancelDialog(QWidget *parent) :
+ LoadSaveDialog(tr("Waiting for workers to finish..."), tr("Renderer"), parent),
+ busy_workers_(0),
+ total_workers_(0)
+{
+}
+
+void RenderCancelDialog::RunIfWorkersAreBusy()
+{
+ if (busy_workers_ > 0) {
+ waiting_workers_ = busy_workers_;
+
+ exec();
+ }
+}
+
+void RenderCancelDialog::SetWorkerCount(int count)
+{
+ total_workers_ = count;
+
+ UpdateProgress();
+}
+
+void RenderCancelDialog::WorkerStarted()
+{
+ busy_workers_++;
+
+ UpdateProgress();
+}
+
+void RenderCancelDialog::WorkerDone()
+{
+ busy_workers_--;
+
+ UpdateProgress();
+}
+
+void RenderCancelDialog::showEvent(QShowEvent *event)
+{
+ UpdateProgress();
+}
+
+void RenderCancelDialog::UpdateProgress()
+{
+ if (!total_workers_ || !isVisible()) {
+ return;
+ }
+
+ SetProgress(qRound(100.0 * static_cast(waiting_workers_ - busy_workers_) / static_cast(waiting_workers_)));
+
+ if (busy_workers_ == 0) {
+ accept();
+ }
+}
diff --git a/app/dialog/rendercancel/rendercancel.h b/app/dialog/rendercancel/rendercancel.h
new file mode 100644
index 000000000..f71050fd9
--- /dev/null
+++ b/app/dialog/rendercancel/rendercancel.h
@@ -0,0 +1,35 @@
+#ifndef RENDERCANCELDIALOG_H
+#define RENDERCANCELDIALOG_H
+
+#include "dialog/loadsave/loadsave.h"
+
+class RenderCancelDialog : public LoadSaveDialog
+{
+ Q_OBJECT
+public:
+ RenderCancelDialog(QWidget* parent = nullptr);
+
+ void RunIfWorkersAreBusy();
+
+ void SetWorkerCount(int count);
+
+ void WorkerStarted();
+
+public slots:
+ void WorkerDone();
+
+protected:
+ virtual void showEvent(QShowEvent* event) override;
+
+private:
+ void UpdateProgress();
+
+ int busy_workers_;
+
+ int total_workers_;
+
+ int waiting_workers_;
+
+};
+
+#endif // RENDERCANCELDIALOG_H
diff --git a/app/panel/panelmanager.h b/app/panel/panelmanager.h
index 6e21bfcb5..dda92eae2 100644
--- a/app/panel/panelmanager.h
+++ b/app/panel/panelmanager.h
@@ -87,8 +87,6 @@ public:
template
/**
* @brief Create a panel
- * @param parent
- * @return
*/
T* CreatePanel(QWidget* parent);
@@ -114,6 +112,12 @@ public:
*/
static PanelManager* instance();
+ template
+ /**
+ * @brief Get a list of panels of a certain type
+ */
+ QList GetPanelsOfType();
+
public slots:
/**
* @brief Connect this to a QApplication's SIGNAL(focusChanged())
@@ -173,4 +177,22 @@ T* PanelManager::MostRecentlyFocused()
return nullptr;
}
+template
+QList PanelManager::GetPanelsOfType()
+{
+ QList panels;
+
+ T* cast_test;
+
+ foreach (PanelWidget* panel, focus_history_) {
+ cast_test = dynamic_cast(panel);
+
+ if (cast_test) {
+ panels.append(cast_test);
+ }
+ }
+
+ return panels;
+}
+
#endif // PANELFOCUSMANAGER_H
diff --git a/app/render/backend/opengl/openglbackend.cpp b/app/render/backend/opengl/openglbackend.cpp
index 4a529b0ec..bab0e1a28 100644
--- a/app/render/backend/opengl/openglbackend.cpp
+++ b/app/render/backend/opengl/openglbackend.cpp
@@ -39,7 +39,10 @@ bool OpenGLBackend::InitInternal()
// Create master texture (the one sent to the viewer)
master_texture_ = std::make_shared();
- master_texture_->Create(share_ctx, params().effective_width(), params().effective_height(), params().format());
+ master_texture_->Create(share_ctx,
+ params().effective_width(),
+ params().effective_height(),
+ params().format());
// Create copy buffer/pipeline
copy_buffer_.Create(share_ctx);
@@ -156,11 +159,13 @@ void OpenGLBackend::EmitCachedFrameReady(const rational &time, const QVariant &v
void OpenGLBackend::ParamsChangedEvent()
{
- // Assume if the texture is allocated, it needs to be changed. Otherwise the texture should be created through
- // InitInternal() with the correct parameters
- if (master_texture_) {
+ // If we're initiated, we need to recreate the texture. Otherwise this backend isn't active so it doesn't matter.
+ if (IsInitiated()) {
master_texture_->Destroy();
- master_texture_->Create(QOpenGLContext::currentContext(), params().effective_width(), params().effective_height(), params().format());
+ master_texture_->Create(QOpenGLContext::currentContext(),
+ params().effective_width(),
+ params().effective_height(),
+ params().format());
}
}
diff --git a/app/render/backend/renderbackend.cpp b/app/render/backend/renderbackend.cpp
index 9c140444f..f1ff1d99e 100644
--- a/app/render/backend/renderbackend.cpp
+++ b/app/render/backend/renderbackend.cpp
@@ -3,6 +3,9 @@
#include
#include
+#include "core.h"
+#include "window/mainwindow/mainwindow.h"
+
RenderBackend::RenderBackend(QObject *parent) :
QObject(parent),
compiled_(false),
@@ -12,6 +15,8 @@ RenderBackend::RenderBackend(QObject *parent) :
recompile_queued_(false),
input_update_queued_(false)
{
+ // FIXME: Don't create in CLI mode
+ cancel_dialog_ = new RenderCancelDialog(Core::instance()->main_window());
}
bool RenderBackend::Init()
@@ -30,6 +35,8 @@ bool RenderBackend::Init()
thread->start(QThread::LowPriority);
}
+ cancel_dialog_->SetWorkerCount(threads_.size());
+
started_ = InitInternal();
// Connects workers and moves them to their respective threads
@@ -50,6 +57,8 @@ void RenderBackend::Close()
started_ = false;
+ CancelQueue();
+
Decompile();
CloseInternal();
@@ -81,6 +90,8 @@ const QString &RenderBackend::GetError() const
void RenderBackend::SetViewerNode(ViewerOutput *viewer_node)
{
if (viewer_node_ != nullptr) {
+ CancelQueue();
+
DisconnectViewer(viewer_node_);
Decompile();
@@ -297,6 +308,7 @@ void RenderBackend::CacheNext()
render_job_info_.insert(cache_frame, job_time);
SetWorkerBusyState(worker, true);
+ cancel_dialog_->WorkerStarted();
QMetaObject::invokeMethod(worker,
"Render",
@@ -312,6 +324,20 @@ ViewerOutput *RenderBackend::viewer_node() const
return copied_viewer_node_;
}
+void RenderBackend::CancelQueue()
+{
+ cache_queue_.clear();
+
+ int busy = 0;
+ for (int i=0;iRunIfWorkersAreBusy();
+}
+
bool RenderBackend::ViewerIsConnected() const
{
return viewer_node_ != nullptr;
@@ -375,6 +401,9 @@ void RenderBackend::InitWorkers()
// Connect to it
ConnectWorkerToThis(processor);
+ // Connect cancel dialog to it
+ connect(processor, &RenderWorker::CompletedCache, cancel_dialog_, &RenderCancelDialog::WorkerDone, Qt::QueuedConnection);
+
// Finally, we can move it to its own thread
processor->moveToThread(thread);
diff --git a/app/render/backend/renderbackend.h b/app/render/backend/renderbackend.h
index 01cd5d5a7..db58f1cf9 100644
--- a/app/render/backend/renderbackend.h
+++ b/app/render/backend/renderbackend.h
@@ -4,6 +4,7 @@
#include
#include "common/constructors.h"
+#include "dialog/rendercancel/rendercancel.h"
#include "decodercache.h"
#include "node/graph.h"
#include "node/output/viewer/viewer.h"
@@ -15,8 +16,6 @@ class RenderBackend : public QObject
public:
RenderBackend(QObject* parent = nullptr);
- DISABLE_COPY_MOVE(RenderBackend)
-
bool Init();
void Close();
@@ -29,6 +28,8 @@ public:
ViewerOutput* viewer_node() const;
+ void CancelQueue();
+
public slots:
void InvalidateCache(const rational &start_range, const rational &end_range);
@@ -142,6 +143,8 @@ private:
QVector processor_busy_state_;
+ RenderCancelDialog* cancel_dialog_;
+
};
#endif // RENDERBACKEND_H
diff --git a/app/render/backend/videorenderbackend.cpp b/app/render/backend/videorenderbackend.cpp
index cd4ef5c1e..985857f46 100644
--- a/app/render/backend/videorenderbackend.cpp
+++ b/app/render/backend/videorenderbackend.cpp
@@ -76,10 +76,7 @@ const VideoRenderingParams &VideoRenderBackend::params() const
void VideoRenderBackend::SetParameters(const VideoRenderingParams& params)
{
- if (!AllProcessorsAreAvailable()) {
- qCritical() << "Attempted to set parameters on a backend whose workers are still busy";
- return;
- }
+ CancelQueue();
// Set new parameters
params_ = params;
diff --git a/app/window/mainwindow/mainwindow.cpp b/app/window/mainwindow/mainwindow.cpp
index 04e49f26f..81ac2cbe8 100644
--- a/app/window/mainwindow/mainwindow.cpp
+++ b/app/window/mainwindow/mainwindow.cpp
@@ -145,10 +145,14 @@ void MainWindow::ProjectOpen(Project* p)
void MainWindow::closeEvent(QCloseEvent *e)
{
+ // Close viewers first since we don't want to delete any nodes while they might be mid-render
+ QList viewers = PanelManager::instance()->GetPanelsOfType();
+ foreach (ViewerPanel* viewer, viewers) {
+ viewer->ConnectViewerNode(nullptr);
+ }
+
PanelManager::instance()->DeleteAllPanels();
-
-
QMainWindow::closeEvent(e);
}