diff --git a/app/node/block/block.h b/app/node/block/block.h index f3b554a5b..dd94bd32e 100644 --- a/app/node/block/block.h +++ b/app/node/block/block.h @@ -28,6 +28,10 @@ * * This is an abstract function. Since different types of Block will provide their lengths in different ways, it's * necessary to subclass and override the length() function for a Block to be usable. + * + * When overriding Node::copy(), the derivative class should also call Block::CopyParameters() on the new Block instance + * which will copy the block's name, length, and media in point. It does not copy any node-specific parameters like any + * input values or connections as per standard with Node::copy(). */ class Block : public Node { diff --git a/app/node/param.cpp b/app/node/param.cpp index f28a404fe..2adbb8467 100644 --- a/app/node/param.cpp +++ b/app/node/param.cpp @@ -130,8 +130,8 @@ void NodeParam::DisconnectEdge(NodeEdgePtr edge) output->parent()->LockUserInput(); input->parent()->LockUserInput(); - output->edges_.removeAll(edge); - input->edges_.removeAll(edge); + output->edges_.removeOne(edge); + input->edges_.removeOne(edge); output->parent()->UnlockUserInput(); input->parent()->UnlockUserInput(); diff --git a/app/render/backend/audiorenderbackend.cpp b/app/render/backend/audiorenderbackend.cpp index c72424db3..e84f9a8e0 100644 --- a/app/render/backend/audiorenderbackend.cpp +++ b/app/render/backend/audiorenderbackend.cpp @@ -26,7 +26,7 @@ void AudioRenderBackend::SetParameters(const AudioRenderingParams ¶ms) void AudioRenderBackend::InvalidateCache(const rational &start_range, const rational &end_range) { rational start_range_adj = qMax(rational(0), start_range); - rational end_range_adj = qMin(viewer_node()->Length(), end_range); + rational end_range_adj = qMin(SequenceLength(), end_range); // Add the range to the list cache_queue_.append(TimeRange(start_range_adj, end_range_adj)); @@ -41,6 +41,7 @@ void AudioRenderBackend::InvalidateCache(const rational &start_range, const rati void AudioRenderBackend::ConnectViewer(ViewerOutput *node) { connect(node, SIGNAL(AudioChangedBetween(const rational&, const rational&)), this, SLOT(InvalidateCache(const rational&, const rational&))); + connect(node, SIGNAL(AudioGraphChanged()), this, SLOT(QueueRecompile())); // FIXME: Hardcoded format SetParameters(AudioRenderingParams(node->audio_params(), SAMPLE_FMT_FLT)); @@ -49,6 +50,7 @@ void AudioRenderBackend::ConnectViewer(ViewerOutput *node) void AudioRenderBackend::DisconnectViewer(ViewerOutput *node) { disconnect(node, SIGNAL(AudioChangedBetween(const rational&, const rational&)), this, SLOT(InvalidateCache(const rational&, const rational&))); + disconnect(node, SIGNAL(AudioGraphChanged()), this, SLOT(QueueRecompile())); } bool AudioRenderBackend::GenerateCacheIDInternal(QCryptographicHash &hash) diff --git a/app/render/backend/opengl/openglbackend.cpp b/app/render/backend/opengl/openglbackend.cpp index 54ddbc503..f395a9266 100644 --- a/app/render/backend/opengl/openglbackend.cpp +++ b/app/render/backend/opengl/openglbackend.cpp @@ -80,23 +80,12 @@ bool OpenGLBackend::CompileInternal() } // Traverse node graph compiling where necessary - bool ret = TraverseCompiling(viewer_node()); - - if (ret) { - //qDebug() << "Compiled successfully!"; - compiled_ = true; - } else { - qDebug() << "Compile failed:" << GetError(); - Decompile(); - } - - return ret; + return TraverseCompiling(viewer_node()); } void OpenGLBackend::DecompileInternal() { shader_cache_.Clear(); - compiled_ = false; } bool OpenGLBackend::TraverseCompiling(Node *n) diff --git a/app/render/backend/renderbackend.cpp b/app/render/backend/renderbackend.cpp index 3b798aea4..a6d2459bf 100644 --- a/app/render/backend/renderbackend.cpp +++ b/app/render/backend/renderbackend.cpp @@ -8,7 +8,9 @@ RenderBackend::RenderBackend(QObject *parent) : compiled_(false), caching_(false), started_(false), - viewer_node_(nullptr) + viewer_node_(nullptr), + copied_viewer_node_(nullptr), + recompile_queued_(false) { } @@ -99,10 +101,34 @@ bool RenderBackend::IsInitiated() bool RenderBackend::Compile() { - if (compiled_) { + if (recompile_queued_) { + Decompile(); + recompile_queued_ = false; + } else if (compiled_) { return true; } + // Get dependencies of viewer node + QList nodes; + nodes.append(viewer_node_); + nodes.append(viewer_node_->GetDependencies()); + + // Copy all dependencies into graph + foreach (Node* n, nodes) { + Node* copy = n->copy(); + + // Copy values (but not connections yet) + Node::CopyInputs(n, copy, false); + + copied_graph_.AddNode(copy); + } + + // We know that the first node will be the viewer node since we appended that first in the copy + copied_viewer_node_ = static_cast(copied_graph_.nodes().first()); + + // Copy connections + Node::DuplicateConnectionsBetweenLists(nodes, copied_graph_.nodes()); + compiled_ = CompileInternal(); if (!compiled_) { @@ -119,6 +145,12 @@ void RenderBackend::Decompile() } DecompileInternal(); + + copied_graph_.Clear(); + + copied_viewer_node_ = nullptr; + + compiled_ = false; } void RenderBackend::RegenerateCacheID() @@ -141,6 +173,15 @@ void RenderBackend::RegenerateCacheID() CacheIDChangedEvent(cache_id_); } +rational RenderBackend::SequenceLength() +{ + if (viewer_node_ == nullptr) { + return 0; + } + + return viewer_node_->Length(); +} + void RenderBackend::SetError(const QString &error) { error_ = error; @@ -158,7 +199,7 @@ void RenderBackend::DisconnectViewer(ViewerOutput *node) void RenderBackend::CacheNext() { - if (!Init() || cache_queue_.isEmpty() || viewer_node() == nullptr || caching_) { + if (!Init() || cache_queue_.isEmpty() || !ViewerIsConnected() || caching_) { return; } @@ -191,7 +232,12 @@ bool RenderBackend::GenerateData(const TimeRange &range) ViewerOutput *RenderBackend::viewer_node() const { - return viewer_node_; + return copied_viewer_node_; +} + +bool RenderBackend::ViewerIsConnected() const +{ + return viewer_node_ != nullptr; } DecoderCache *RenderBackend::decoder_cache() @@ -245,3 +291,8 @@ void RenderBackend::ThreadRequestedSibling(NodeDependency dep) } } } + +void RenderBackend::QueueRecompile() +{ + recompile_queued_ = true; +} diff --git a/app/render/backend/renderbackend.h b/app/render/backend/renderbackend.h index 16dcbb517..3417b1498 100644 --- a/app/render/backend/renderbackend.h +++ b/app/render/backend/renderbackend.h @@ -4,6 +4,7 @@ #include #include "decodercache.h" +#include "node/graph.h" #include "node/output/viewer/viewer.h" #include "renderworker.h" @@ -45,6 +46,8 @@ protected: virtual void DecompileInternal() = 0; + rational SequenceLength(); + const QVector& threads(); /** @@ -76,6 +79,8 @@ protected: ViewerOutput* viewer_node() const; + bool ViewerIsConnected() const; + DecoderCache* decoder_cache(); const QString& cache_id() const; @@ -104,6 +109,11 @@ private: */ ViewerOutput* viewer_node_; + /** + * @brief Internal reference to the copied viewer node we made in the compilation process + */ + ViewerOutput* copied_viewer_node_; + /** * @brief Error string that can be set in SetError() to handle failures */ @@ -115,9 +125,15 @@ private: qint64 cache_time_; QString cache_id_; + NodeGraph copied_graph_; + + bool recompile_queued_; + private slots: void ThreadRequestedSibling(NodeDependency dep); + void QueueRecompile(); + }; #endif // RENDERBACKEND_H diff --git a/app/render/backend/videorenderbackend.cpp b/app/render/backend/videorenderbackend.cpp index db2c1a9f7..579a8d5c1 100644 --- a/app/render/backend/videorenderbackend.cpp +++ b/app/render/backend/videorenderbackend.cpp @@ -44,12 +44,12 @@ void VideoRenderBackend::InvalidateCache(const rational &start_range, const rati // Adjust range to min/max values rational start_range_adj = qMax(rational(0), start_range); - rational end_range_adj = qMin(viewer_node()->Length(), end_range); + rational end_range_adj = qMin(SequenceLength(), end_range); - /*qDebug() << "Cache invalidated between" + qDebug() << "Cache invalidated between" << start_range_adj.toDouble() << "and" - << end_range_adj.toDouble();*/ + << end_range_adj.toDouble(); // Snap start_range to timebase double start_range_dbl = start_range_adj.toDouble(); @@ -96,7 +96,7 @@ void VideoRenderBackend::InvalidateCache(const rational &start_range, const rati } // Remove frames after this time code if it's changed - frame_cache_.Truncate(viewer_node()->Length()); + frame_cache_.Truncate(SequenceLength()); CacheNext(); } @@ -115,6 +115,7 @@ void VideoRenderBackend::CloseInternal() void VideoRenderBackend::ConnectViewer(ViewerOutput *node) { connect(node, SIGNAL(VideoChangedBetween(const rational&, const rational&)), this, SLOT(InvalidateCache(const rational&, const rational&))); + connect(node, SIGNAL(VideoGraphChanged()), this, SLOT(QueueRecompile())); // FIXME: Hardcoded format, mode, and divider SetParameters(VideoRenderingParams(node->video_params(), olive::PIX_FMT_RGBA16F, olive::kOffline, 2)); @@ -123,6 +124,7 @@ void VideoRenderBackend::ConnectViewer(ViewerOutput *node) void VideoRenderBackend::DisconnectViewer(ViewerOutput *node) { disconnect(node, SIGNAL(VideoChangedBetween(const rational&, const rational&)), this, SLOT(InvalidateCache(const rational&, const rational&))); + disconnect(node, SIGNAL(VideoGraphChanged()), this, SLOT(QueueRecompile())); } const VideoRenderingParams &VideoRenderBackend::params() const