diff --git a/app/node/graph.cpp b/app/node/graph.cpp index 96ce5a8ba..50905efd2 100644 --- a/app/node/graph.cpp +++ b/app/node/graph.cpp @@ -91,3 +91,12 @@ bool NodeGraph::ContainsNode(Node *n) { return (n->parent() == this); } + +void NodeGraph::Release() +{ + QList all_nodes = nodes(); + + foreach (Node* n, all_nodes) { + n->Release(); + } +} diff --git a/app/node/graph.h b/app/node/graph.h index 843eb8274..2eab8ab28 100644 --- a/app/node/graph.h +++ b/app/node/graph.h @@ -80,6 +80,11 @@ public: */ bool ContainsNode(Node* n); + /** + * @brief Releases all Nodes in this NodeGraph + */ + void Release(); + signals: /** * @brief Signal emitted when a Node is added to the graph diff --git a/app/node/node.cpp b/app/node/node.cpp index 19d04abd1..f60740c57 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -315,7 +315,6 @@ void Node::Hash(QCryptographicHash *hash, NodeOutput* from, const rational &time { // Add this Node's ID hash->addData(id().toUtf8()); - qDebug() << "Hashing" << id(); // Add each value QList params = parameters(); diff --git a/app/node/output/viewer/viewer.cpp b/app/node/output/viewer/viewer.cpp index 50ad35abd..33ae2b9e3 100644 --- a/app/node/output/viewer/viewer.cpp +++ b/app/node/output/viewer/viewer.cpp @@ -80,8 +80,10 @@ void ViewerOutput::AttachViewer(ViewerPanel *viewer) void ViewerOutput::InvalidateCache(const rational &start_range, const rational &end_range, NodeInput *from) { - // Update any attached viewer - UpdateViewer(); + if (start_range == current_time_ || end_range == current_time_) { + // Update any attached viewer + UpdateViewer(); + } Node::InvalidateCache(start_range, end_range, from); } @@ -89,6 +91,7 @@ void ViewerOutput::InvalidateCache(const rational &start_range, const rational & void ViewerOutput::UpdateViewer() { texture_input_->ClearCachedValue(); + ViewerTimeChanged(current_time_); } diff --git a/app/node/param.cpp b/app/node/param.cpp index 93efe4dcc..2c267a9a8 100644 --- a/app/node/param.cpp +++ b/app/node/param.cpp @@ -238,3 +238,8 @@ void NodeParam::ClearCachedValue() // currently cached time_ = -1; } + +const rational &NodeParam::LastRequestedTime() +{ + return time_; +} diff --git a/app/node/param.h b/app/node/param.h index 34b944385..7a2efa6da 100644 --- a/app/node/param.h +++ b/app/node/param.h @@ -208,6 +208,11 @@ public: */ void ClearCachedValue(); + /** + * @brief Retrieve the last time this parameter had a value requested from + */ + const rational& LastRequestedTime(); + signals: /** * @brief Signal emitted when an edge is added to this parameter diff --git a/app/node/processor/renderer/renderer.cpp b/app/node/processor/renderer/renderer.cpp index 98f073dfd..9b0538dff 100644 --- a/app/node/processor/renderer/renderer.cpp +++ b/app/node/processor/renderer/renderer.cpp @@ -36,8 +36,7 @@ RendererProcessor::RendererProcessor() : width_(0), height_(0), divider_(1), - caching_(false), - last_requested_time_(-1) + caching_(false) { texture_input_ = new NodeInput("tex_in"); texture_input_->add_data_input(NodeInput::kTexture); @@ -79,8 +78,6 @@ void RendererProcessor::SetCacheName(const QString &s) QVariant RendererProcessor::Value(NodeOutput* output, const rational& time) { if (output == texture_output_) { - last_requested_time_ = time; - if (!texture_input_->IsConnected()) { // Nothing is connected - nothing to show or render return 0; @@ -98,7 +95,6 @@ QVariant RendererProcessor::Value(NodeOutput* output, const rational& time) // Find frame in map if (time_hash_map_.contains(time)) { - qDebug() << "Showed:" << time_hash_map_[time].toHex(); QString fn = CachePathName(time_hash_map_[time]); @@ -340,6 +336,11 @@ void RendererProcessor::ThreadCallback() time_hash_map_.insert(cache_frame_, master_thread_->hash()); + // We didn't receive a texture to download, but the viewer may still need updating + if (texture == nullptr) { + DownloadThreadFinished(cache_frame_); + } + CacheNext(); } } @@ -357,15 +358,15 @@ void RendererProcessor::ThreadRequestSibling(NodeDependency dep) void RendererProcessor::DownloadThreadFinished(const rational& time) { // Check if we just downloaded (akak finished caching) the frame we're currently on - if (texture_output_->IsConnected() && time == last_requested_time_) { + if (texture_output_->IsConnected()) { + if (texture_output_->LastRequestedTime() == time) { + texture_output_->ClearCachedValue(); + } + // Send invalidate cache signal to all nodes connected to the texture output QVector edges = texture_output()->edges(); - texture_output_->ClearCachedValue(); - foreach (NodeEdgePtr edge, edges) { - edge->input()->ClearCachedValue(); - edge->input()->parent()->InvalidateCache(time, time, edge->input()); diff --git a/app/node/processor/renderer/renderer.h b/app/node/processor/renderer/renderer.h index c60fa54fa..a50a954ca 100644 --- a/app/node/processor/renderer/renderer.h +++ b/app/node/processor/renderer/renderer.h @@ -176,8 +176,6 @@ private: QVector download_threads_; int last_download_thread_; - rational last_requested_time_; - RenderTexturePtr master_texture_; QMap time_hash_map_; diff --git a/app/node/processor/renderer/rendererdownloadthread.cpp b/app/node/processor/renderer/rendererdownloadthread.cpp index fda4685a2..577ea3571 100644 --- a/app/node/processor/renderer/rendererdownloadthread.cpp +++ b/app/node/processor/renderer/rendererdownloadthread.cpp @@ -12,7 +12,8 @@ RendererDownloadThread::RendererDownloadThread(QOpenGLContext *share_ctx, const int &height, const olive::PixelFormat &format, const olive::RenderMode &mode) : - RendererThreadBase(share_ctx, width, height, format, mode) + RendererThreadBase(share_ctx, width, height, format, mode), + cancelled_(false) { } @@ -29,6 +30,17 @@ void RendererDownloadThread::Queue(RenderTexturePtr texture, const QString& fn, texture_queue_lock_.unlock(); } +void RendererDownloadThread::Cancel() +{ + cancelled_ = true; + + texture_queue_lock_.lock(); + wait_cond_.wakeAll(); + texture_queue_lock_.unlock(); + + wait(); +} + void RendererDownloadThread::ProcessLoop() { QOpenGLFunctions* f = render_instance()->context()->functions(); @@ -53,13 +65,21 @@ void RendererDownloadThread::ProcessLoop() OIIO::ImageSpec spec(render_instance()->width(), render_instance()->height(), kRGBAChannels, format_info.oiio_desc); spec.attribute("compression", "dwaa:200"); - while (!Cancelled()) { + while (!cancelled_) { // Check queue for textures to download (use mutex to prevent collisions) texture_queue_lock_.lock(); while (texture_queue_.isEmpty()) { // Main waiting condition wait_cond_.wait(&texture_queue_lock_); + + if (cancelled_) { + break; + } + } + if (cancelled_) { + texture_queue_lock_.unlock(); + break; } working_texture = texture_queue_.takeFirst(); diff --git a/app/node/processor/renderer/rendererdownloadthread.h b/app/node/processor/renderer/rendererdownloadthread.h index f9d7e31e6..ebf27f1be 100644 --- a/app/node/processor/renderer/rendererdownloadthread.h +++ b/app/node/processor/renderer/rendererdownloadthread.h @@ -15,6 +15,9 @@ public: void Queue(RenderTexturePtr texture, const QString &fn, const rational &time); +public slots: + virtual void Cancel() override; + signals: void Downloaded(const rational& time); @@ -32,6 +35,8 @@ private: QMutex texture_queue_lock_; + QAtomicInt cancelled_; + }; using RendererDownloadThreadPtr = std::shared_ptr; diff --git a/app/node/processor/renderer/rendererprocessthread.cpp b/app/node/processor/renderer/rendererprocessthread.cpp index 3e00fbf3f..7b84af879 100644 --- a/app/node/processor/renderer/rendererprocessthread.cpp +++ b/app/node/processor/renderer/rendererprocessthread.cpp @@ -29,7 +29,8 @@ RendererProcessThread::RendererProcessThread(RendererProcessor* parent, const olive::PixelFormat &format, const olive::RenderMode &mode) : RendererThreadBase(share_ctx, width, height, format, mode), - parent_(parent) + parent_(parent), + cancelled_(false) { } @@ -70,12 +71,27 @@ RenderTexturePtr RendererProcessThread::texture() return texture_; } +void RendererProcessThread::Cancel() +{ + cancelled_ = true; + + mutex_.lock(); + wait_cond_.wakeAll(); + mutex_.unlock(); + + wait(); +} + void RendererProcessThread::ProcessLoop() { - while (!Cancelled()) { + while (!cancelled_) { // Main waiting condition wait_cond_.wait(&mutex_); + if (cancelled_) { + break; + } + // Wake up main thread caller_mutex_.lock(); wait_cond_.wakeAll(); diff --git a/app/node/processor/renderer/rendererprocessthread.h b/app/node/processor/renderer/rendererprocessthread.h index 0953ea489..84e7fc81b 100644 --- a/app/node/processor/renderer/rendererprocessthread.h +++ b/app/node/processor/renderer/rendererprocessthread.h @@ -42,6 +42,9 @@ public: RenderTexturePtr texture(); +public slots: + virtual void Cancel() override; + protected: virtual void ProcessLoop() override; @@ -61,6 +64,8 @@ private: RenderTexturePtr texture_; + QAtomicInt cancelled_; + }; using RendererProcessThreadPtr = std::shared_ptr; diff --git a/app/node/processor/renderer/rendererthreadbase.cpp b/app/node/processor/renderer/rendererthreadbase.cpp index 6d0fc2677..f6d58c464 100644 --- a/app/node/processor/renderer/rendererthreadbase.cpp +++ b/app/node/processor/renderer/rendererthreadbase.cpp @@ -24,7 +24,6 @@ RendererThreadBase::RendererThreadBase(QOpenGLContext *share_ctx, const int &width, const int &height, const olive::PixelFormat &format, const olive::RenderMode &mode) : share_ctx_(share_ctx), - cancelled_(false), width_(width), height_(height), format_(format), @@ -34,15 +33,6 @@ RendererThreadBase::RendererThreadBase(QOpenGLContext *share_ctx, const int &wid connect(share_ctx_, SIGNAL(aboutToBeDestroyed()), this, SLOT(Cancel())); } -void RendererThreadBase::Cancel() -{ - // Escape main loop - cancelled_ = true; - - // Wait until thread is finished before returning - wait(); -} - RenderInstance *RendererThreadBase::render_instance() { return render_instance_; @@ -79,11 +69,6 @@ void RendererThreadBase::run() mutex_.unlock(); } -bool RendererThreadBase::Cancelled() -{ - return cancelled_; -} - void RendererThreadBase::StartThread(QThread::Priority priority) { caller_mutex_.lock(); diff --git a/app/node/processor/renderer/rendererthreadbase.h b/app/node/processor/renderer/rendererthreadbase.h index 8fe1e15f5..12a9d35fc 100644 --- a/app/node/processor/renderer/rendererthreadbase.h +++ b/app/node/processor/renderer/rendererthreadbase.h @@ -46,13 +46,11 @@ public: virtual void run() override; public slots: - void Cancel(); + virtual void Cancel() = 0; protected: virtual void ProcessLoop() = 0; - bool Cancelled(); - QWaitCondition wait_cond_; QMutex mutex_; @@ -62,8 +60,6 @@ protected: private: QOpenGLContext* share_ctx_; - bool cancelled_; - const int& width_; const int& height_; diff --git a/app/panel/panelmanager.cpp b/app/panel/panelmanager.cpp index ef4a76e6f..b7b837327 100644 --- a/app/panel/panelmanager.cpp +++ b/app/panel/panelmanager.cpp @@ -25,7 +25,14 @@ PanelManager* olive::panel_focus_manager = nullptr; PanelManager::PanelManager(QObject *parent) : QObject(parent) { +} +void PanelManager::DeleteAllPanels() +{ + foreach (PanelWidget* panel, focus_history_) { + delete panel; + } + focus_history_.clear(); } PanelWidget *PanelManager::CurrentlyFocused() const diff --git a/app/panel/panelmanager.h b/app/panel/panelmanager.h index 48d04812c..d70528b99 100644 --- a/app/panel/panelmanager.h +++ b/app/panel/panelmanager.h @@ -47,6 +47,8 @@ class PanelManager : public QObject public: PanelManager(QObject* parent); + void DeleteAllPanels(); + /** * @brief Return the currently focused widget, or nullptr if nothing is focused */ diff --git a/app/render/renderinstance.cpp b/app/render/renderinstance.cpp index 6a4674cf7..4bb685d1c 100644 --- a/app/render/renderinstance.cpp +++ b/app/render/renderinstance.cpp @@ -49,13 +49,16 @@ bool RenderInstance::Start() return true; } + // Create context object + ctx_ = new QOpenGLContext(); + // If we're sharing resources, set this up now if (share_ctx_ != nullptr) { - ctx_.setShareContext(share_ctx_); + ctx_->setShareContext(share_ctx_); } // Create OpenGL context (automatically destroys any existing if there is one) - if (!ctx_.create()) { + if (!ctx_->create()) { qWarning() << tr("Failed to create OpenGL context in thread %1").arg(reinterpret_cast(this)); return false; } @@ -64,16 +67,16 @@ bool RenderInstance::Start() surface_.create(); // Make context current on that surface - if (!ctx_.makeCurrent(&surface_)) { + if (!ctx_->makeCurrent(&surface_)) { qWarning() << tr("Failed to makeCurrent() on offscreen surface in thread %1").arg(reinterpret_cast(this)); surface_.destroy(); return false; } - buffer_.Create(&ctx_); + buffer_.Create(ctx_); // Set viewport to the compositing dimensions - ctx_.functions()->glViewport(0, 0, width_, height_); + ctx_->functions()->glViewport(0, 0, width_, height_); // Set up default pipeline default_pipeline_ = olive::ShaderGenerator::DefaultPipeline(); @@ -83,14 +86,18 @@ bool RenderInstance::Start() void RenderInstance::Stop() { + if (IsStarted()) { + return; + } + // Destroy pipeline default_pipeline_ = nullptr; // Destroy buffer buffer_.Destroy(); - // Release OpenGL context - ctx_.doneCurrent(); + // Destroy context + delete ctx_; // Destroy offscreen surface surface_.destroy(); @@ -108,7 +115,7 @@ RenderFramebuffer *RenderInstance::buffer() QOpenGLContext *RenderInstance::context() { - return &ctx_; + return ctx_; } const int &RenderInstance::width() const diff --git a/app/render/renderinstance.h b/app/render/renderinstance.h index 6978695af..5d7dc9f4a 100644 --- a/app/render/renderinstance.h +++ b/app/render/renderinstance.h @@ -67,7 +67,7 @@ public: ShaderPtr default_pipeline() const; private: - QOpenGLContext ctx_; + QOpenGLContext* ctx_; QOpenGLContext* share_ctx_; diff --git a/app/widget/nodeview/nodeview.cpp b/app/widget/nodeview/nodeview.cpp index b391a24fe..8cd02d9a2 100644 --- a/app/widget/nodeview/nodeview.cpp +++ b/app/widget/nodeview/nodeview.cpp @@ -32,13 +32,25 @@ NodeView::NodeView(QWidget *parent) : connect(&scene_, SIGNAL(selectionChanged()), this, SLOT(SceneSelectionChangedSlot())); } +NodeView::~NodeView() +{ + // Unset the current graph + SetGraph(nullptr); +} + void NodeView::SetGraph(NodeGraph *graph) { + if (graph_ == graph) { + return; + } + if (graph_ != nullptr) { disconnect(graph_, SIGNAL(NodeAdded(Node*)), this, SLOT(AddNode(Node*))); disconnect(graph_, SIGNAL(NodeRemoved(Node*)), this, SLOT(RemoveNode(Node*))); disconnect(graph_, SIGNAL(EdgeAdded(NodeEdgePtr)), this, SLOT(AddEdge(NodeEdgePtr))); disconnect(graph_, SIGNAL(EdgeRemoved(NodeEdgePtr)), this, SLOT(RemoveEdge(NodeEdgePtr))); + + graph_->Release(); } // Clear the scene of all UI objects diff --git a/app/widget/nodeview/nodeview.h b/app/widget/nodeview/nodeview.h index 21a7212e0..9b30aef92 100644 --- a/app/widget/nodeview/nodeview.h +++ b/app/widget/nodeview/nodeview.h @@ -39,6 +39,8 @@ class NodeView : public QGraphicsView public: NodeView(QWidget* parent); + virtual ~NodeView() override; + /** * @brief Sets the graph to view */ diff --git a/app/window/mainwindow/mainwindow.cpp b/app/window/mainwindow/mainwindow.cpp index edcd173b4..0223bc206 100644 --- a/app/window/mainwindow/mainwindow.cpp +++ b/app/window/mainwindow/mainwindow.cpp @@ -79,3 +79,20 @@ void olive::MainWindow::ProjectOpen(Project* p) connect(node_panel, SIGNAL(SelectionChanged(QList)), param_panel, SLOT(SetNodes(QList))); } + +// FIXME: Test code +#include +#include "common/filefunctions.h" +// End test code + +void olive::MainWindow::closeEvent(QCloseEvent *e) +{ + olive::panel_focus_manager->DeleteAllPanels(); + + // FIXME: Test code - We have no cache management and the cache is very much testing only, so we delete it on close + // as to not clog up HDD space + QDir(GetMediaCacheLocation()).removeRecursively(); + // End test code + + QMainWindow::closeEvent(e); +} diff --git a/app/window/mainwindow/mainwindow.h b/app/window/mainwindow/mainwindow.h index 685d52308..db6f57088 100644 --- a/app/window/mainwindow/mainwindow.h +++ b/app/window/mainwindow/mainwindow.h @@ -38,6 +38,9 @@ public: public slots: void ProjectOpen(Project *p); +protected: + virtual void closeEvent(QCloseEvent* e) override; + private: