diff --git a/app/dialog/export/export.cpp b/app/dialog/export/export.cpp index e079b9296..81b7f845b 100644 --- a/app/dialog/export/export.cpp +++ b/app/dialog/export/export.cpp @@ -229,7 +229,7 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) : // Update renderer // FIXME: This is going to be VERY slow since it will need to hash every single frame. It would be better to have a // the renderer save the map as some sort of file that this can load. - preview_viewer_->video_renderer()->InvalidateCache(TimeRange(0, viewer_node_->Length())); + preview_viewer_->video_renderer()->InvalidateCache(TimeRange(0, viewer_node_->Length()), nullptr); progress_timer_.setInterval(1000); connect(&progress_timer_, &QTimer::timeout, this, &ExportDialog::UpdateTimeLabels); diff --git a/app/node/block/block.cpp b/app/node/block/block.cpp index ad314a946..ccb3f0140 100644 --- a/app/node/block/block.cpp +++ b/app/node/block/block.cpp @@ -40,7 +40,7 @@ Block::Block() : length_input_->SetConnectable(false); length_input_->set_is_keyframable(false); AddInput(length_input_); - connect(length_input_, SIGNAL(ValueChanged(const rational&, const rational&)), this, SLOT(LengthInputChanged())); + connect(length_input_, &NodeInput::ValueChanged, this, &Block::LengthInputChanged); media_in_input_ = new NodeInput("media_in_in", NodeParam::kRational); media_in_input_->SetConnectable(false); @@ -343,14 +343,14 @@ NodeInput *Block::speed_input() const return speed_input_; } -void Block::InvalidateCache(const rational &start_range, const rational &end_range, NodeInput *from) +void Block::InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput *source) { // We ignore length changes since they don't have an effect on our frames if (from == length_input_) { return; } - Node::InvalidateCache(start_range, end_range, from); + Node::InvalidateCache(range, from, source); } OLIVE_NAMESPACE_EXIT diff --git a/app/node/block/block.h b/app/node/block/block.h index a0feb41a9..26df9d13a 100644 --- a/app/node/block/block.h +++ b/app/node/block/block.h @@ -97,7 +97,7 @@ public: NodeInput* media_in_input() const; NodeInput* speed_input() const; - virtual void InvalidateCache(const rational& start_range, const rational& end_range, NodeInput* from = nullptr) override; + virtual void InvalidateCache(const TimeRange& range, NodeInput* from, NodeInput* source) override; public slots: diff --git a/app/node/block/clip/clip.cpp b/app/node/block/clip/clip.cpp index ebd286171..8db5efdd5 100644 --- a/app/node/block/clip/clip.cpp +++ b/app/node/block/clip/clip.cpp @@ -59,12 +59,12 @@ NodeInput *ClipBlock::texture_input() const return texture_input_; } -void ClipBlock::InvalidateCache(const rational &start_range, const rational &end_range, NodeInput *from) +void ClipBlock::InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput *source) { // If signal is from texture input, transform all times from media time to sequence time if (from == texture_input_) { - rational start = MediaToSequenceTime(start_range); - rational end = MediaToSequenceTime(end_range); + rational start = MediaToSequenceTime(range.in()); + rational end = MediaToSequenceTime(range.out()); // Ensure range actually covers this clip's area if (!(end < in() || start > out())) { @@ -73,12 +73,12 @@ void ClipBlock::InvalidateCache(const rational &start_range, const rational &end start = qMax(start, in()); end = qMin(end, out()); - Node::InvalidateCache(start, end, from); + Node::InvalidateCache(TimeRange(start, end), from, source); } } else { // Otherwise, pass signal along normally - Node::InvalidateCache(start_range, end_range, from); + Node::InvalidateCache(range, from, source); } } diff --git a/app/node/block/clip/clip.h b/app/node/block/clip/clip.h index 6dc996b08..56f81c927 100644 --- a/app/node/block/clip/clip.h +++ b/app/node/block/clip/clip.h @@ -44,7 +44,7 @@ public: NodeInput* texture_input() const; - virtual void InvalidateCache(const rational &start_range, const rational &end_range, NodeInput *from = nullptr) override; + virtual void InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput* source) override; virtual TimeRange InputTimeAdjustment(NodeInput* input, const TimeRange& input_time) const override; diff --git a/app/node/input.cpp b/app/node/input.cpp index cdea0d1ed..e025f5ed7 100644 --- a/app/node/input.cpp +++ b/app/node/input.cpp @@ -60,7 +60,7 @@ NodeInput::NodeInput(const QString &id, const NodeParam::DataType &type) : Init(type); } -bool NodeInput::IsArray() +bool NodeInput::IsArray() const { return false; } @@ -273,7 +273,6 @@ void NodeInput::SaveConnections(QXmlStreamWriter *writer) const writer->writeEndElement(); // connections } - const NodeParam::DataType &NodeInput::data_type() const { return data_type_; @@ -363,6 +362,45 @@ QVariant NodeInput::StringToValue(const DataType& data_type, const QString &stri } } +void NodeInput::GetDependencies(QList &list, bool traverse, bool exclusive_only) const +{ + if (IsConnected() + && (get_connected_output()->edges().size() == 1 || !exclusive_only)) { + Node* connected = get_connected_node(); + + if (!list.contains(connected)) { + list.append(connected); + + if (traverse) { + QList connected_inputs = connected->GetInputsIncludingArrays(); + + foreach (NodeInput* i, connected_inputs) { + i->GetDependencies(list, traverse, exclusive_only); + } + } + } + } +} + +QList NodeInput::GetDependencies(bool traverse, bool exclusive_only) const +{ + QList list; + + GetDependencies(list, traverse, exclusive_only); + + return list; +} + +QList NodeInput::GetExclusiveDependencies() const +{ + return GetDependencies(true, true); +} + +QList NodeInput::GetImmediateDependencies() const +{ + return GetDependencies(false, false); +} + QVariant NodeInput::StringToValue(const QString &string, QList& footage_connections) { if (data_type_ == NodeParam::kFootage) { @@ -757,7 +795,7 @@ void NodeInput::KeyframeBezierInChanged() start = keyframe_tracks_.at(key->track()).at(keyframe_index - 1)->time(); } - emit ValueChanged(start, end); + emit ValueChanged(TimeRange(start, end)); } void NodeInput::KeyframeBezierOutChanged() @@ -772,7 +810,7 @@ void NodeInput::KeyframeBezierOutChanged() end = keyframe_tracks_.at(key->track()).at(keyframe_index + 1)->time(); } - emit ValueChanged(start, end); + emit ValueChanged(TimeRange(start, end)); } int NodeInput::FindIndexOfKeyframeFromRawPtr(NodeKeyframe *raw_ptr) const @@ -855,7 +893,7 @@ TimeRange NodeInput::get_range_around_index(int index, int track) const void NodeInput::emit_time_range(const TimeRange &range) { - emit ValueChanged(range.in(), range.out()); + emit ValueChanged(range); } void NodeInput::emit_range_affected_by_keyframe(NodeKeyframe *key) @@ -915,7 +953,7 @@ void NodeInput::set_standard_value(const QVariant &value, int track) if (is_using_standard_value(track)) { // If this standard value is being used, we need to send a value changed signal - emit ValueChanged(RATIONAL_MIN, RATIONAL_MAX); + emit ValueChanged(TimeRange(RATIONAL_MIN, RATIONAL_MAX)); } } @@ -964,7 +1002,7 @@ void NodeInput::CopyValues(NodeInput *source, NodeInput *dest, bool include_conn } } - emit dest->ValueChanged(RATIONAL_MIN, RATIONAL_MAX); + emit dest->ValueChanged(TimeRange(RATIONAL_MIN, RATIONAL_MAX)); } void NodeInput::set_property(const QString &key, const QVariant &value) diff --git a/app/node/input.h b/app/node/input.h index 6d1910623..c672907ee 100644 --- a/app/node/input.h +++ b/app/node/input.h @@ -49,7 +49,7 @@ public: NodeInput(const QString &id, const DataType& type, const QVariant& default_value); NodeInput(const QString &id, const DataType& type); - virtual bool IsArray(); + virtual bool IsArray() const; /** * @brief Returns kInput @@ -270,8 +270,16 @@ public: static QVariant StringToValue(const DataType &data_type, const QString &string); + void GetDependencies(QList& list, bool traverse, bool exclusive_only) const; + + QList GetDependencies(bool traverse = true, bool exclusive_only = false) const; + + QList GetExclusiveDependencies() const; + + QList GetImmediateDependencies() const; + signals: - void ValueChanged(const rational& start, const rational& end); + void ValueChanged(const TimeRange& range); void KeyframeEnableChanged(bool); diff --git a/app/node/input/media/media.cpp b/app/node/input/media/media.cpp index a69207ab6..ff371da53 100644 --- a/app/node/input/media/media.cpp +++ b/app/node/input/media/media.cpp @@ -31,7 +31,7 @@ MediaInput::MediaInput() : footage_input_ = new NodeInput("footage_in", NodeInput::kFootage); footage_input_->SetConnectable(false); footage_input_->set_is_keyframable(false); - connect(footage_input_, SIGNAL(ValueChanged(const rational&, const rational&)), this, SLOT(FootageChanged())); + connect(footage_input_, &NodeInput::ValueChanged, this, &MediaInput::FootageChanged); AddInput(footage_input_); } @@ -96,7 +96,7 @@ void MediaInput::FootageChanged() void MediaInput::FootageParametersChanged() { - InvalidateCache(0, RATIONAL_MAX, footage_input_); + InvalidateCache(TimeRange(0, RATIONAL_MAX), footage_input_, footage_input_); } OLIVE_NAMESPACE_EXIT diff --git a/app/node/inputarray.cpp b/app/node/inputarray.cpp index 4cea15ac2..42789b7d6 100644 --- a/app/node/inputarray.cpp +++ b/app/node/inputarray.cpp @@ -33,7 +33,7 @@ NodeInputArray::NodeInputArray(const QString &id, const DataType &type, const QV { } -bool NodeInputArray::IsArray() +bool NodeInputArray::IsArray() const { return true; } @@ -79,8 +79,8 @@ void NodeInputArray::SetSize(int size) sub_params_.replace(i, new_param); connect(new_param, &NodeInput::ValueChanged, this, &NodeInput::ValueChanged); - connect(new_param, &NodeInput::EdgeAdded, this, &NodeInput::EdgeAdded); - connect(new_param, &NodeInput::EdgeRemoved, this, &NodeInput::EdgeRemoved); + connect(new_param, &NodeInput::EdgeAdded, this, &NodeInputArray::SubParamEdgeAdded); + connect(new_param, &NodeInput::EdgeRemoved, this, &NodeInputArray::SubParamEdgeRemoved); } } diff --git a/app/node/inputarray.h b/app/node/inputarray.h index d43300e22..8a935f73e 100644 --- a/app/node/inputarray.h +++ b/app/node/inputarray.h @@ -31,7 +31,7 @@ class NodeInputArray : public NodeInput public: NodeInputArray(const QString &id, const DataType& type, const QVariant& default_value = 0); - virtual bool IsArray() override; + virtual bool IsArray() const override; int GetSize() const; @@ -54,6 +54,10 @@ public: signals: void SizeChanged(int size); + void SubParamEdgeAdded(NodeEdgePtr edge); + + void SubParamEdgeRemoved(NodeEdgePtr edge); + protected: virtual void LoadInternal(QXmlStreamReader* reader, XMLNodeData& xml_node_data, const QAtomicInt *cancelled) override; diff --git a/app/node/node.cpp b/app/node/node.cpp index ae681ecf1..ffde76468 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -75,7 +75,13 @@ void Node::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, const QAto continue; } - NodeParam* param = GetParameterWithID(param_id); + NodeParam* param; + + if (reader->name() == QStringLiteral("input")) { + param = GetInputWithID(param_id); + } else { + param = GetOutputWithID(param_id); + } if (!param) { qDebug() << "No parameter in" << id() << "with parameter" << param_id; @@ -153,21 +159,21 @@ NodeValueTable Node::Value(NodeValueDatabase &value) const return value.Merge(); } -void Node::InvalidateCache(const rational &start_range, const rational &end_range, NodeInput *from) +void Node::InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput *source) { Q_UNUSED(from) - SendInvalidateCache(start_range, end_range); + SendInvalidateCache(range, source); } -void Node::InvalidateVisible(NodeInput *from) +void Node::InvalidateVisible(NodeInput *from, NodeInput* source) { Q_UNUSED(from) foreach (NodeParam* param, params_) { if (param->type() == NodeParam::kOutput) { foreach (NodeEdgePtr edge, param->edges()) { - edge->input()->parentNode()->InvalidateVisible(edge->input()); + edge->input()->parentNode()->InvalidateVisible(edge->input(), source); } } } @@ -185,7 +191,7 @@ TimeRange Node::OutputTimeAdjustment(NodeInput *, const TimeRange &input_time) c return input_time; } -void Node::SendInvalidateCache(const rational &start_range, const rational &end_range) +void Node::SendInvalidateCache(const TimeRange &range, NodeInput *source) { // Loop through all parameters (there should be no children that are not NodeParams) foreach (NodeParam* param, params_) { @@ -199,25 +205,7 @@ void Node::SendInvalidateCache(const rational &start_range, const rational &end_ Node* connected_node = connected_input->parentNode(); // Send clear cache signal to the Node - connected_node->InvalidateCache(start_range, end_range, connected_input); - } - } - } -} - -void Node::DependentEdgeChanged(NodeInput *from) -{ - Q_UNUSED(from) - - foreach (NodeParam* p, params_) { - if (p->type() == NodeParam::kOutput && p->IsConnected()) { - NodeOutput* out = static_cast(p); - - foreach (NodeEdgePtr edge, out->edges()) { - NodeInput* connected_input = edge->input(); - Node* connected_node = connected_input->parentNode(); - - connected_node->DependentEdgeChanged(connected_input); + connected_node->InvalidateCache(range, connected_input, source); } } } @@ -300,57 +288,6 @@ void Node::CopyInputs(Node *source, Node *destination, bool include_connections) } } -void DuplicateConnectionsBetweenListsInternal(const QList &source, const QList &destination, NodeInput* source_input, NodeInput* dest_input) -{ - if (source_input->IsConnected()) { - // Get this input's connected outputs - NodeOutput* source_output = source_input->get_connected_output(); - Node* source_output_node = source_output->parentNode(); - - // Find equivalent in destination list - Node* dest_output_node = destination.at(source.indexOf(source_output_node)); - - Q_ASSERT(dest_output_node->id() == source_output_node->id()); - - NodeOutput* dest_output = static_cast(dest_output_node->GetParameterWithID(source_output->id())); - - NodeParam::ConnectEdge(dest_output, dest_input); - } - - // If inputs are arrays, duplicate their connections too - if (source_input->IsArray()) { - NodeInputArray* source_array = static_cast(source_input); - NodeInputArray* dest_array = static_cast(dest_input); - - for (int i=0;iGetSize();i++) { - DuplicateConnectionsBetweenListsInternal(source, destination, source_array->At(i), dest_array->At(i)); - } - } -} - -void Node::DuplicateConnectionsBetweenLists(const QList &source, const QList &destination) -{ - Q_ASSERT(source.size() == destination.size()); - - for (int i=0;iid() == dest_input_node->id()); - - for (int j=0;jparams_.size();j++) { - NodeParam* source_param = source_input_node->params_.at(j); - - if (source_param->type() == NodeInput::kInput) { - NodeInput* source_input = static_cast(source_param); - NodeInput* dest_input = static_cast(dest_input_node->params_.at(j)); - - DuplicateConnectionsBetweenListsInternal(source, destination, source_input, dest_input); - } - } - } -} - bool Node::CanBeDeleted() const { return can_be_deleted_; @@ -381,29 +318,6 @@ int Node::IndexOfParameter(NodeParam *param) const return params_.indexOf(param); } -void Node::TraverseInputInternal(QList& list, NodeInput* input, bool traverse, bool exclusive_only) { - if (input->IsConnected() - && (input->get_connected_output()->edges().size() == 1 || !exclusive_only)) { - Node* connected = input->get_connected_node(); - - if (!list.contains(connected)) { - list.append(connected); - - if (traverse) { - GetDependenciesInternal(connected, list, traverse, exclusive_only); - } - } - } - - if (input->IsArray()) { - NodeInputArray* input_array = static_cast(input); - - for (int i=0;iGetSize();i++) { - TraverseInputInternal(list, input_array->At(i), traverse, exclusive_only); - } - } -} - /** * @brief Recursively collects dependencies of Node `n` and appends them to QList `list` * @@ -412,41 +326,30 @@ void Node::TraverseInputInternal(QList& list, NodeInput* input, bool trav * TRUE to recursively traverse each node for a complete dependency graph. FALSE to return only the immediate * dependencies. */ -void Node::GetDependenciesInternal(const Node* n, QList& list, bool traverse, bool exclusive_only) { - foreach (NodeParam* p, n->parameters()) { - if (p->type() == NodeParam::kInput) { - NodeInput* input = static_cast(p); +QList Node::GetDependenciesInternal(bool traverse, bool exclusive_only) const { + QList inputs = GetInputsIncludingArrays(); + QList list; - TraverseInputInternal(list, input, traverse, exclusive_only); - } + foreach (NodeInput* i, inputs) { + i->GetDependencies(list, traverse, exclusive_only); } + + return list; } QList Node::GetDependencies() const { - QList node_list; - - GetDependenciesInternal(this, node_list, true, false); - - return node_list; + return GetDependenciesInternal(true, false); } QList Node::GetExclusiveDependencies() const { - QList node_list; - - GetDependenciesInternal(this, node_list, true, true); - - return node_list; + return GetDependenciesInternal(true, true); } QList Node::GetImmediateDependencies() const { - QList node_list; - - GetDependenciesInternal(this, node_list, false, false); - - return node_list; + return GetDependenciesInternal(false, false); } Node::Capabilities Node::GetCapabilities(const NodeValueDatabase &) const @@ -479,7 +382,7 @@ NodeInput *Node::ShaderIterativeInput() const return nullptr; } -NodeInput* Node::ProcessesSamplesFrom(const NodeValueDatabase &value) const +NodeInput* Node::ProcessesSamplesFrom(const NodeValueDatabase &) const { return nullptr; } @@ -488,11 +391,25 @@ void Node::ProcessSamples(const NodeValueDatabase &, const AudioRenderingParams& { } -NodeParam *Node::GetParameterWithID(const QString &id) const +NodeInput *Node::GetInputWithID(const QString &id) const { - foreach (NodeParam* param, params_) { - if (param->id() == id) { - return param; + QList inputs = GetInputsIncludingArrays(); + + foreach (NodeInput* i, inputs) { + if (i->id() == id) { + return i; + } + } + + return nullptr; +} + +NodeOutput *Node::GetOutputWithID(const QString &id) const +{ + foreach (NodeParam* p, params_) { + if (p->type() == NodeParam::kOutput + && p->id() == id) { + return static_cast(p); } } @@ -665,25 +582,41 @@ void Node::ConnectInput(NodeInput *input) connect(input, &NodeInput::ValueChanged, this, &Node::InputChanged); connect(input, &NodeInput::EdgeAdded, this, &Node::InputConnectionChanged); connect(input, &NodeInput::EdgeRemoved, this, &Node::InputConnectionChanged); + + if (input->IsArray()) { + NodeInputArray* array = static_cast(input); + + connect(array, &NodeInputArray::SubParamEdgeAdded, this, &Node::InputConnectionChanged); + connect(array, &NodeInputArray::SubParamEdgeRemoved, this, &Node::InputConnectionChanged); + connect(array, &NodeInputArray::SubParamEdgeAdded, this, &Node::EdgeAdded); + connect(array, &NodeInputArray::SubParamEdgeRemoved, this, &Node::EdgeRemoved); + } } void Node::DisconnectInput(NodeInput *input) { + if (input->IsArray()) { + NodeInputArray* array = static_cast(input); + + disconnect(array, &NodeInputArray::SubParamEdgeAdded, this, &Node::InputConnectionChanged); + disconnect(array, &NodeInputArray::SubParamEdgeRemoved, this, &Node::InputConnectionChanged); + disconnect(array, &NodeInputArray::SubParamEdgeAdded, this, &Node::EdgeAdded); + disconnect(array, &NodeInputArray::SubParamEdgeRemoved, this, &Node::EdgeRemoved); + } + disconnect(input, &NodeInput::ValueChanged, this, &Node::InputChanged); disconnect(input, &NodeInput::EdgeAdded, this, &Node::InputConnectionChanged); disconnect(input, &NodeInput::EdgeRemoved, this, &Node::InputConnectionChanged); } -void Node::InputChanged(rational start, rational end) +void Node::InputChanged(const TimeRange& range) { - InvalidateCache(start, end, static_cast(sender())); + InvalidateCache(range, static_cast(sender()), static_cast(sender())); } void Node::InputConnectionChanged(NodeEdgePtr edge) { - DependentEdgeChanged(edge->input()); - - InvalidateCache(RATIONAL_MIN, RATIONAL_MAX, static_cast(sender())); + InvalidateCache(TimeRange(RATIONAL_MIN, RATIONAL_MAX), edge->input(), edge->input()); } OLIVE_NAMESPACE_EXIT diff --git a/app/node/node.h b/app/node/node.h index 0ec7e06eb..16b33c8c1 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -29,7 +29,6 @@ #include "codec/samplebuffer.h" #include "common/rational.h" #include "common/xmlutils.h" -#include "node/dependency.h" #include "node/input.h" #include "node/inputarray.h" #include "node/output.h" @@ -196,9 +195,14 @@ public: virtual void ProcessSamples(const NodeValueDatabase &values, const AudioRenderingParams& params, const SampleBufferPtr input, SampleBufferPtr output, int index) const; /** - * @brief Returns the parameter with the specified ID (or nullptr if it doesn't exist) + * @brief Returns the input with the specified ID (or nullptr if it doesn't exist) */ - NodeParam* GetParameterWithID(const QString& id) const; + NodeInput* GetInputWithID(const QString& id) const; + + /** + * @brief Returns the output with the specified ID (or nullptr if it doesn't exist) + */ + NodeOutput* GetOutputWithID(const QString& id) const; /** * @brief Returns whether this Node outputs data to the Node `n` in any way @@ -261,12 +265,12 @@ public: * the DAG. Even if the time needs to be transformed somehow (e.g. converting media time to sequence time), you can * call this function with transformed time and relay the signal that way. */ - virtual void InvalidateCache(const rational& start_range, const rational& end_range, NodeInput* from = nullptr); + virtual void InvalidateCache(const TimeRange& range, NodeInput* from, NodeInput* source); /** * @brief Signal through node graph to only invalidate frames that are currently visible on a ViewerWidget */ - virtual void InvalidateVisible(NodeInput *from); + virtual void InvalidateVisible(NodeInput *from, NodeInput* source); /** * @brief Adjusts time that should be sent to nodes connected to certain inputs. @@ -288,11 +292,6 @@ public: */ static void CopyInputs(Node* source, Node* destination, bool include_connections = true); - /** - * @brief For a list of copies nodes, this function will duplicate all the connections in the source list to the destination list - */ - static void DuplicateConnectionsBetweenLists(const QList& source, const QList &destination); - /** * @brief Return whether this Node can be deleted or not */ @@ -358,9 +357,7 @@ protected: void ClearCachedValuesInParameters(const rational& start_range, const rational& end_range); - void SendInvalidateCache(const rational& start_range, const rational& end_range); - - virtual void DependentEdgeChanged(NodeInput* from); + void SendInvalidateCache(const TimeRange &range, NodeInput *source); virtual void LoadInternal(QXmlStreamReader* reader, XMLNodeData& xml_node_data); @@ -403,9 +400,7 @@ private: void DisconnectInput(NodeInput* input); - static void TraverseInputInternal(QList& list, NodeInput* input, bool traverse, bool exclusive_only); - - static void GetDependenciesInternal(const Node* n, QList& list, bool traverse, bool exclusive_only); + QList GetDependenciesInternal(bool traverse, bool exclusive_only) const; QList params_; @@ -425,7 +420,7 @@ private: QPointF position_; private slots: - void InputChanged(rational start, rational end); + void InputChanged(const TimeRange &range); void InputConnectionChanged(NodeEdgePtr edge); diff --git a/app/node/output/track/track.cpp b/app/node/output/track/track.cpp index 3e8df213d..aeb6be27c 100644 --- a/app/node/output/track/track.cpp +++ b/app/node/output/track/track.cpp @@ -38,8 +38,8 @@ TrackOutput::TrackOutput() : block_input_ = new NodeInputArray("block_in", NodeParam::kAny); block_input_->set_is_keyframable(false); AddInput(block_input_); - connect(block_input_, &NodeInputArray::EdgeAdded, this, &TrackOutput::BlockConnected); - connect(block_input_, &NodeInputArray::EdgeRemoved, this, &TrackOutput::BlockDisconnected); + connect(block_input_, &NodeInputArray::SubParamEdgeAdded, this, &TrackOutput::BlockConnected); + connect(block_input_, &NodeInputArray::SubParamEdgeRemoved, this, &TrackOutput::BlockDisconnected); connect(block_input_, &NodeInputArray::SizeChanged, this, &TrackOutput::BlockListSizeChanged); muted_input_ = new NodeInput("muted_in", NodeParam::kBoolean); @@ -236,10 +236,10 @@ const QVector &TrackOutput::Blocks() const return block_cache_; } -void TrackOutput::InvalidateCache(const rational &start_range, const rational &end_range, NodeInput *from) +void TrackOutput::InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput *source) { if (block_invalidate_cache_stack_ == 0) { - Node::InvalidateCache(qMax(start_range, rational(0)), qMin(end_range, track_length()), from); + Node::InvalidateCache(TimeRange(qMax(range.in(), rational(0)), qMin(range.out(), track_length())), from, source); } } @@ -276,7 +276,7 @@ void TrackOutput::InsertBlockAtIndex(Block *block, int index) UnblockInvalidateCache(); - InvalidateCache(block->in(), track_length()); + InvalidateCache(TimeRange(block->in(), track_length()), block_input_, block_input_); } void TrackOutput::AppendBlock(Block *block) @@ -291,7 +291,7 @@ void TrackOutput::AppendBlock(Block *block) UnblockInvalidateCache(); // Invalidate area that block was added to - InvalidateCache(block->in(), track_length()); + InvalidateCache(TimeRange(block->in(), track_length()), block_input_, block_input_); } void TrackOutput::BlockInvalidateCache() @@ -316,7 +316,7 @@ void TrackOutput::RippleRemoveBlock(Block *block) UnblockInvalidateCache(); - InvalidateCache(remove_in, track_length()); + InvalidateCache(TimeRange(remove_in, track_length()), block_input_, block_input_); } void TrackOutput::ReplaceBlock(Block *old, Block *replace) @@ -334,9 +334,9 @@ void TrackOutput::ReplaceBlock(Block *old, Block *replace) UnblockInvalidateCache(); if (old->length() == replace->length()) { - InvalidateCache(replace->in(), replace->out()); + InvalidateCache(TimeRange(replace->in(), replace->out()), block_input_, block_input_); } else { - InvalidateCache(replace->in(), RATIONAL_MAX); + InvalidateCache(TimeRange(replace->in(), RATIONAL_MAX), block_input_, block_input_); } } @@ -407,6 +407,11 @@ bool TrackOutput::IsLocked() const return locked_; } +NodeInputArray *TrackOutput::block_input() const +{ + return block_input_; +} + void TrackOutput::SetTrackName(const QString &name) { track_name_ = name; @@ -415,7 +420,7 @@ void TrackOutput::SetTrackName(const QString &name) void TrackOutput::SetMuted(bool e) { muted_input_->set_standard_value(e); - InvalidateCache(0, track_length()); + InvalidateCache(TimeRange(0, track_length()), block_input_, block_input_); } void TrackOutput::SetLocked(bool e) @@ -462,8 +467,9 @@ void TrackOutput::UpdateInOutFrom(int index) track_length_ = new_track_length; emit TrackLengthChanged(); - InvalidateCache(qMin(old_track_length, new_track_length), - qMax(old_track_length, new_track_length)); + InvalidateCache(TimeRange(qMin(old_track_length, new_track_length), qMax(old_track_length, new_track_length)), + block_input_, + block_input_); } } diff --git a/app/node/output/track/track.h b/app/node/output/track/track.h index 9ff9df009..11eec0f7b 100644 --- a/app/node/output/track/track.h +++ b/app/node/output/track/track.h @@ -84,7 +84,7 @@ public: const QVector& Blocks() const; - virtual void InvalidateCache(const rational& start_range, const rational& end_range, NodeInput* from = nullptr) override; + virtual void InvalidateCache(const TimeRange& range, NodeInput* from, NodeInput *source) override; /** * @brief Adds Block `block` at the very beginning of the Sequence before all other clips @@ -150,6 +150,8 @@ public: bool IsLocked() const; + NodeInputArray* block_input() const; + public slots: void SetTrackName(const QString& name); diff --git a/app/node/output/track/tracklist.cpp b/app/node/output/track/tracklist.cpp index 05f9a6e6f..b3246f257 100644 --- a/app/node/output/track/tracklist.cpp +++ b/app/node/output/track/tracklist.cpp @@ -31,8 +31,8 @@ TrackList::TrackList(ViewerOutput *parent, const Timeline::TrackType &type, Node track_input_(track_input), type_(type) { - connect(track_input, &NodeInputArray::EdgeAdded, this, &TrackList::TrackConnected); - connect(track_input, &NodeInputArray::EdgeRemoved, this, &TrackList::TrackDisconnected); + connect(track_input, &NodeInputArray::SubParamEdgeAdded, this, &TrackList::TrackConnected); + connect(track_input, &NodeInputArray::SubParamEdgeRemoved, this, &TrackList::TrackDisconnected); connect(track_input, &NodeInputArray::SizeChanged, this, &TrackList::TrackListSizeChanged); } @@ -120,8 +120,8 @@ TrackOutput* TrackList::AddTrack() Node* blend = NodeFactory::CreateFromID(QStringLiteral("org.olivevideoeditor.Olive.alphaoverblend")); GetParentGraph()->AddNode(blend); - NodeParam::ConnectEdge(track->output(), static_cast(blend->GetParameterWithID("blend_in"))); - NodeParam::ConnectEdge(last_track->output(), static_cast(blend->GetParameterWithID("base_in"))); + NodeParam::ConnectEdge(track->output(), static_cast(blend->GetInputWithID("blend_in"))); + NodeParam::ConnectEdge(last_track->output(), static_cast(blend->GetInputWithID("base_in"))); NodeParam::ConnectEdge(blend->output(), edge->input()); break; } diff --git a/app/node/output/viewer/viewer.cpp b/app/node/output/viewer/viewer.cpp index d8d472371..665f287f6 100644 --- a/app/node/output/viewer/viewer.cpp +++ b/app/node/output/viewer/viewer.cpp @@ -92,26 +92,24 @@ NodeInput *ViewerOutput::samples_input() const return samples_input_; } -void ViewerOutput::InvalidateCache(const rational &start_range, const rational &end_range, NodeInput *from) +void ViewerOutput::InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput *source) { - Node::InvalidateCache(start_range, end_range, from); - if (from == texture_input()) { - emit VideoChangedBetween(TimeRange(start_range, end_range)); + emit VideoChangedBetween(range, source); } else if (from == samples_input()) { - emit AudioChangedBetween(TimeRange(start_range, end_range)); + emit AudioChangedBetween(range, source); } - SendInvalidateCache(start_range, end_range); + Node::InvalidateCache(range, from, source); } -void ViewerOutput::InvalidateVisible(NodeInput* from) +void ViewerOutput::InvalidateVisible(NodeInput* from, NodeInput *source) { if (from == texture_input()) { - emit VisibleInvalidated(); + emit VisibleInvalidated(source); } - Node::InvalidateVisible(from); + Node::InvalidateVisible(from, source); } const VideoParams &ViewerOutput::video_params() const @@ -164,17 +162,6 @@ const QUuid &ViewerOutput::uuid() const return uuid_; } -void ViewerOutput::DependentEdgeChanged(NodeInput *from) -{ - if (from == texture_input_) { - emit VideoGraphChanged(); - } else if (from == samples_input_) { - emit AudioGraphChanged(); - } - - Node::DependentEdgeChanged(from); -} - void ViewerOutput::UpdateTrackCache() { track_cache_.clear(); diff --git a/app/node/output/viewer/viewer.h b/app/node/output/viewer/viewer.h index 95afbce70..57101b67a 100644 --- a/app/node/output/viewer/viewer.h +++ b/app/node/output/viewer/viewer.h @@ -55,8 +55,8 @@ public: NodeInput* texture_input() const; NodeInput* samples_input() const; - virtual void InvalidateCache(const rational &start_range, const rational &end_range, NodeInput *from = nullptr) override; - virtual void InvalidateVisible(NodeInput *from) override; + virtual void InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput* source) override; + virtual void InvalidateVisible(NodeInput *from, NodeInput* source) override; const VideoParams& video_params() const; const AudioParams& audio_params() const; @@ -79,21 +79,14 @@ public: const QString& media_name() const; void set_media_name(const QString& name); -protected: - virtual void DependentEdgeChanged(NodeInput* from) override; - signals: void TimebaseChanged(const rational&); - void VideoChangedBetween(const TimeRange& range); + void VideoChangedBetween(const TimeRange& range, NodeInput* source); - void AudioChangedBetween(const TimeRange& range); + void AudioChangedBetween(const TimeRange& range, NodeInput* source); - void VisibleInvalidated(); - - void VideoGraphChanged(); - - void AudioGraphChanged(); + void VisibleInvalidated(NodeInput* source); void LengthChanged(const rational& length); diff --git a/app/render/backend/audio/audiobackend.cpp b/app/render/backend/audio/audiobackend.cpp index 07c023187..6b6f81ab2 100644 --- a/app/render/backend/audio/audiobackend.cpp +++ b/app/render/backend/audio/audiobackend.cpp @@ -39,7 +39,7 @@ bool AudioBackend::InitInternal() // Initiate one thread per CPU core for (int i=0;iSetParameters(params()); processors_.append(processor); } @@ -51,18 +51,6 @@ void AudioBackend::CloseInternal() { } -bool AudioBackend::CompileInternal() -{ - // This backend doesn't compile anything yet - return AudioRenderBackend::CompileInternal(); -} - -void AudioBackend::DecompileInternal() -{ - // This backend doesn't compile anything yet - AudioRenderBackend::DecompileInternal(); -} - void AudioBackend::ConnectWorkerToThis(RenderWorker *worker) { AudioRenderBackend::ConnectWorkerToThis(worker); diff --git a/app/render/backend/audio/audiobackend.h b/app/render/backend/audio/audiobackend.h index 81b423c68..1eaa2941d 100644 --- a/app/render/backend/audio/audiobackend.h +++ b/app/render/backend/audio/audiobackend.h @@ -40,10 +40,6 @@ protected: virtual void CloseInternal() override; - virtual bool CompileInternal() override; - - virtual void DecompileInternal() override; - virtual void ConnectWorkerToThis(RenderWorker* worker) override; private slots: diff --git a/app/render/backend/audiorenderbackend.cpp b/app/render/backend/audiorenderbackend.cpp index d5226945a..b4df5f4ba 100644 --- a/app/render/backend/audiorenderbackend.cpp +++ b/app/render/backend/audiorenderbackend.cpp @@ -58,31 +58,15 @@ void AudioRenderBackend::SetParameters(const AudioRenderingParams ¶ms) void AudioRenderBackend::ConnectViewer(ViewerOutput *node) { connect(node, &ViewerOutput::AudioChangedBetween, this, &AudioRenderBackend::InvalidateCache); - connect(node, &ViewerOutput::AudioGraphChanged, this, &AudioRenderBackend::QueueRecompile); connect(node, &ViewerOutput::LengthChanged, this, &AudioRenderBackend::TruncateCache); } void AudioRenderBackend::DisconnectViewer(ViewerOutput *node) { disconnect(node, &ViewerOutput::AudioChangedBetween, this, &AudioRenderBackend::InvalidateCache); - disconnect(node, &ViewerOutput::AudioGraphChanged, this, &AudioRenderBackend::QueueRecompile); disconnect(node, &ViewerOutput::LengthChanged, this, &AudioRenderBackend::TruncateCache); } -bool AudioRenderBackend::CompileInternal() -{ - for (int i=0;ihas_conformed_version(params)) { // Index JUST finished, requeue this time - InvalidateCache(range); + InvalidateCache(range, nullptr); } else { @@ -215,7 +199,7 @@ void AudioRenderBackend::ConformUpdated(Stream *stream, AudioRenderingParams par // Send invalidate cache signal ic_from_conform_ = true; - InvalidateCache(copy.affected_range); + InvalidateCache(copy.affected_range, nullptr); ic_from_conform_ = false; } diff --git a/app/render/backend/audiorenderbackend.h b/app/render/backend/audiorenderbackend.h index f2420079d..77a774915 100644 --- a/app/render/backend/audiorenderbackend.h +++ b/app/render/backend/audiorenderbackend.h @@ -54,10 +54,6 @@ protected: virtual void DisconnectViewer(ViewerOutput* node) override; - virtual bool CompileInternal() override; - - virtual void DecompileInternal() override; - /** * @brief Internal function for generating the cache ID */ @@ -73,8 +69,6 @@ protected: virtual void InvalidateCacheInternal(const rational &start_range, const rational &end_range) override; - QHash copy_map_; - private: struct ConformWaitInfo { StreamPtr stream; diff --git a/app/render/backend/exporter.cpp b/app/render/backend/exporter.cpp index 5b92a663f..aa8af3165 100644 --- a/app/render/backend/exporter.cpp +++ b/app/render/backend/exporter.cpp @@ -291,14 +291,14 @@ void Exporter::EncoderOpenedSuccessfully() video_backend_->SetOperatingMode(VideoRenderWorker::kHashOnly); connect(video_backend_, &VideoRenderBackend::QueueComplete, this, &Exporter::VideoHashesComplete); - video_backend_->InvalidateCache(export_range_); + video_backend_->InvalidateCache(export_range_, nullptr); } if (!audio_done_) { // We set the audio backend to render the full sequence to the disk connect(audio_backend_, &AudioRenderBackend::AudioComplete, this, &Exporter::AudioRendered); - audio_backend_->InvalidateCache(export_range_); + audio_backend_->InvalidateCache(export_range_, nullptr); } } @@ -344,7 +344,7 @@ void Exporter::VideoHashesComplete() } foreach (const TimeRange& range, ranges) { - video_backend_->InvalidateCache(range); + video_backend_->InvalidateCache(range, nullptr); } } diff --git a/app/render/backend/opengl/openglbackend.cpp b/app/render/backend/opengl/openglbackend.cpp index 2db0cefa6..e1144dc5d 100644 --- a/app/render/backend/opengl/openglbackend.cpp +++ b/app/render/backend/opengl/openglbackend.cpp @@ -89,15 +89,6 @@ void OpenGLBackend::CloseInternal() VideoRenderBackend::CloseInternal(); } -bool OpenGLBackend::CompileInternal() -{ - return true; -} - -void OpenGLBackend::DecompileInternal() -{ -} - void OpenGLBackend::ParamsChangedEvent() { // If we're initiated, we need to recreate the texture. Otherwise this backend isn't active so it doesn't matter. diff --git a/app/render/backend/opengl/openglbackend.h b/app/render/backend/opengl/openglbackend.h index 59fc34465..62945ffa1 100644 --- a/app/render/backend/opengl/openglbackend.h +++ b/app/render/backend/opengl/openglbackend.h @@ -43,10 +43,6 @@ protected: virtual void CloseInternal() override; - virtual bool CompileInternal() override; - - virtual void DecompileInternal() override; - virtual void ParamsChangedEvent() override; private: diff --git a/app/render/backend/renderbackend.cpp b/app/render/backend/renderbackend.cpp index 0cfb0eae4..310a85431 100644 --- a/app/render/backend/renderbackend.cpp +++ b/app/render/backend/renderbackend.cpp @@ -31,12 +31,9 @@ OLIVE_NAMESPACE_ENTER RenderBackend::RenderBackend(QObject *parent) : QObject(parent), - compiled_(false), started_(false), viewer_node_(nullptr), - copied_viewer_node_(nullptr), - recompile_queued_(false), - input_update_queued_(false) + copied_viewer_node_(nullptr) { // FIXME: Don't create in CLI mode cancel_dialog_ = new RenderCancelDialog(Core::instance()->main_window()); @@ -84,7 +81,7 @@ void RenderBackend::Close() CancelQueue(); - Decompile(); + SetViewerNode(nullptr); CloseInternal(); @@ -114,23 +111,30 @@ const QString &RenderBackend::GetError() const void RenderBackend::SetViewerNode(ViewerOutput *viewer_node) { - if (viewer_node_ != nullptr) { + if (viewer_node_) { CancelQueue(); DisconnectViewer(viewer_node_); - Decompile(); + copied_graph_.Clear(); + copied_viewer_node_ = nullptr; + node_copy_map_.clear(); } viewer_node_ = viewer_node; - if (viewer_node_ != nullptr) { + if (viewer_node_) { ConnectViewer(viewer_node_); RegenerateCacheID(); - } - InvalidateCache(TimeRange(0, RATIONAL_MAX)); + copied_viewer_node_ = static_cast(viewer_node_->copy()); + copied_graph_.AddNode(copied_viewer_node_); + node_copy_map_.insert(viewer_node_, copied_viewer_node_); + + InvalidateCache(TimeRange(0, RATIONAL_MAX), + static_cast(viewer_node_->GetInputWithID(GetDependentInput()->id()))); + } } bool RenderBackend::IsInitiated() @@ -138,58 +142,6 @@ bool RenderBackend::IsInitiated() return started_; } -bool RenderBackend::Compile() -{ - if (compiled_) { - return true; - } - - // Get dependencies of viewer node - source_node_list_.append(viewer_node_); - source_node_list_.append(viewer_node_->GetDependencies()); - - // Copy all dependencies into graph - foreach (Node* n, source_node_list_) { - Node* copy = n->copy(); - - Node::CopyInputs(n, copy, false); - - copied_graph_.AddNode(copy); - } - - // We just copied the inputs, so if an input update is queued, it's unnecessary - input_update_queued_ = false; - - // 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(source_node_list_, copied_graph_.nodes()); - - compiled_ = CompileInternal(); - - if (!compiled_) { - Decompile(); - } - - return compiled_; -} - -void RenderBackend::Decompile() -{ - if (!compiled_) { - return; - } - - DecompileInternal(); - - copied_graph_.Clear(); - copied_viewer_node_ = nullptr; - source_node_list_.clear(); - - compiled_ = false; -} - void RenderBackend::RegenerateCacheID() { QCryptographicHash hash(QCryptographicHash::Sha1); @@ -261,34 +213,19 @@ void RenderBackend::CacheNext() return; } - if (!Init() - || !ViewerIsConnected() - || !CanRender()) { + if (!ViewerIsConnected() + || !CanRender() + || !Init()) { return; } - if ((input_update_queued_ || recompile_queued_) && !AllProcessorsAreAvailable()) { - return; - } - - if (recompile_queued_) { - Decompile(); - recompile_queued_ = false; - } - - if (!compiled_ && !Compile()) { - return; - } - - if (input_update_queued_) { - for (int i=0;iget_connected_node(); @@ -356,12 +293,8 @@ void RenderBackend::CancelQueue() cancel_dialog_->RunIfWorkersAreBusy(); } -void RenderBackend::InvalidateCache(const TimeRange &range) +void RenderBackend::InvalidateCache(const TimeRange &range, NodeInput *from) { - if (!CanRender()) { - return; - } - // Adjust range to min/max values rational start_range_adj = qMax(rational(0), range.in()); rational end_range_adj = qMin(GetSequenceLength(), range.out()); @@ -371,15 +304,18 @@ void RenderBackend::InvalidateCache(const TimeRange &range) << "and" << end_range_adj.toDouble(); - // Queue value update - QueueValueUpdate(); + if (from) { + // Queue value update + qDebug() << " from" << from->parentNode()->id() << "::" << from->id(); + QueueValueUpdate(from); + } InvalidateCacheInternal(start_range_adj, end_range_adj); } bool RenderBackend::ViewerIsConnected() const { - return viewer_node_ != nullptr; + return viewer_node_; } const QString &RenderBackend::cache_id() const @@ -387,9 +323,23 @@ const QString &RenderBackend::cache_id() const return cache_id_; } -void RenderBackend::QueueValueUpdate() +void RenderBackend::QueueValueUpdate(NodeInput* from) { - input_update_queued_ = true; + if (!input_update_queued_.isEmpty()) { + // Remove any inputs that are dependents of this input since they may have been removed since + // it was queued + QList deps = from->GetDependencies(); + + for (int i=0;iparentNode())) { + // We don't need to queue this value since this input supersedes it + input_update_queued_.removeAt(i); + i--; + } + } + } + + input_update_queued_.append(from); } bool RenderBackend::WorkerIsBusy(RenderWorker *worker) const @@ -402,6 +352,87 @@ void RenderBackend::SetWorkerBusyState(RenderWorker *worker, bool busy) processor_busy_state_.replace(processors_.indexOf(worker), busy); } +void RenderBackend::CopyNodeInputValue(NodeInput *input) +{ + // Find our copy of this parameter + Node* our_copy_node = node_copy_map_.value(input->parentNode()); + NodeInput* our_copy = our_copy_node->GetInputWithID(input->id()); + + // Copy the standard/keyframe values between these two inputs + NodeInput::CopyValues(input, + our_copy, + false); + + // Handle connections + if (input->IsConnected() || our_copy->IsConnected()) { + // If one of the inputs is connected, it's likely this change came from connecting or + // disconnecting whatever was connected to it + + { + // We start by removing all old dependencies from the map + QList old_deps = our_copy->GetExclusiveDependencies(); + + foreach (Node* i, old_deps) { + Node* n = node_copy_map_.take(node_copy_map_.key(i)); + copied_graph_.TakeNode(n); + delete n; + } + } + + // Then we copy all node dependencies and connections (if there are any) + CopyNodeMakeConnection(input, our_copy); + } + + // Call on sub-elements too + if (input->IsArray()) { + foreach (NodeInput* i, static_cast(input)->sub_params()) { + CopyNodeInputValue(i); + } + } +} + +Node* RenderBackend::CopyNodeConnections(Node* src_node) +{ + // Check if this node is already in the map + Node* dst_node = node_copy_map_.value(src_node); + + // If not, create it now + if (!dst_node) { + dst_node = src_node->copy(); + copied_graph_.AddNode(dst_node); + node_copy_map_.insert(src_node, dst_node); + } + + // Make sure its values are copied + Node::CopyInputs(src_node, dst_node, false); + + // Copy all connections + QList src_node_inputs = src_node->GetInputsIncludingArrays(); + QList dst_node_inputs = dst_node->GetInputsIncludingArrays(); + + for (int i=0;iid() << "from" << src_input->parentNode()->id(); + + if (src_input->IsConnected()) { + Node* dst_node = CopyNodeConnections(src_input->get_connected_node()); + + NodeOutput* corresponding_output = dst_node->GetOutputWithID(src_input->get_connected_output()->id()); + + NodeParam::ConnectEdge(corresponding_output, + dst_input); + } +} + bool RenderBackend::AllProcessorsAreAvailable() const { foreach (bool busy, processor_busy_state_) { @@ -455,11 +486,6 @@ void RenderBackend::InitWorkers() processor_busy_state_.fill(false); } -void RenderBackend::QueueRecompile() -{ - recompile_queued_ = true; -} - void RenderBackend::FootageUnavailable(StreamPtr stream, Decoder::RetrieveState state, const TimeRange &range, const rational &stream_time) { if (state == Decoder::kFailedToOpen){ @@ -484,7 +510,7 @@ void RenderBackend::FootageUnavailable(StreamPtr stream, Decoder::RetrieveState || (stream->type() == Stream::kAudio && std::static_pointer_cast(stream)->index_done())) { // Index JUST finished, requeue this time - InvalidateCache(range); + InvalidateCache(range, nullptr); } else { @@ -529,7 +555,7 @@ void RenderBackend::IndexUpdated(Stream* stream) } if (footage_ready) { - InvalidateCache(info.affected_range); + InvalidateCache(info.affected_range, nullptr); footage_wait_info_.removeAt(i); i--; } diff --git a/app/render/backend/renderbackend.h b/app/render/backend/renderbackend.h index 7b1b120fb..1e8db284a 100644 --- a/app/render/backend/renderbackend.h +++ b/app/render/backend/renderbackend.h @@ -52,11 +52,7 @@ public: void CancelQueue(); public slots: - void InvalidateCache(const TimeRange &range); - - bool Compile(); - - void Decompile(); + void InvalidateCache(const TimeRange &range, NodeInput *from); signals: void QueueComplete(); @@ -68,10 +64,6 @@ protected: virtual void CloseInternal(); - virtual bool CompileInternal() = 0; - - virtual void DecompileInternal() = 0; - virtual bool CanRender(); virtual TimeRange PopNextFrameFromQueue(); @@ -111,7 +103,7 @@ protected: const QString& cache_id() const; - void QueueValueUpdate(); + void QueueValueUpdate(NodeInput *from); bool AllProcessorsAreAvailable() const; bool WorkerIsBusy(RenderWorker* worker) const; @@ -121,18 +113,17 @@ protected: QVector processors_; - bool compiled_; - QHash render_job_info_; - QList source_node_list_; + QHash node_copy_map_; NodeGraph copied_graph_; -protected slots: - void QueueRecompile(); - private: + void CopyNodeInputValue(NodeInput* input); + Node *CopyNodeConnections(Node *src_node); + void CopyNodeMakeConnection(NodeInput *src_input, NodeInput *dst_input); + /** * @brief Internal list of RenderProcessThreads */ @@ -160,8 +151,7 @@ private: QString cache_id_; - bool recompile_queued_; - bool input_update_queued_; + QList input_update_queued_; QVector processor_busy_state_; diff --git a/app/render/backend/videorenderbackend.cpp b/app/render/backend/videorenderbackend.cpp index 7e13d1af7..7452b8ab7 100644 --- a/app/render/backend/videorenderbackend.cpp +++ b/app/render/backend/videorenderbackend.cpp @@ -49,14 +49,12 @@ VideoRenderBackend::VideoRenderBackend(QObject *parent) : void VideoRenderBackend::ConnectViewer(ViewerOutput *node) { connect(node, &ViewerOutput::VideoChangedBetween, this, &VideoRenderBackend::InvalidateCache); - connect(node, &ViewerOutput::VideoGraphChanged, this, &VideoRenderBackend::QueueRecompile); connect(node, &ViewerOutput::LengthChanged, this, &VideoRenderBackend::TruncateFrameCacheLength); } void VideoRenderBackend::DisconnectViewer(ViewerOutput *node) { disconnect(node, &ViewerOutput::VideoChangedBetween, this, &VideoRenderBackend::InvalidateCache); - disconnect(node, &ViewerOutput::VideoGraphChanged, this, &VideoRenderBackend::QueueRecompile); disconnect(node, &ViewerOutput::LengthChanged, this, &VideoRenderBackend::TruncateFrameCacheLength); frame_cache_.Clear(); diff --git a/app/render/backend/videorenderworker.cpp b/app/render/backend/videorenderworker.cpp index ec1a4ad47..5dabdc5cb 100644 --- a/app/render/backend/videorenderworker.cpp +++ b/app/render/backend/videorenderworker.cpp @@ -261,7 +261,10 @@ void VideoRenderWorker::SetFrameGenerationParams(int width, int height, const QM bool VideoRenderWorker::InitInternal() { - ResizeDownloadBuffer(); + if (video_params_.is_valid()) { + ResizeDownloadBuffer(); + } + return true; } diff --git a/app/widget/keyframeview/keyframeviewbase.cpp b/app/widget/keyframeview/keyframeviewbase.cpp index 300fbf4b3..2baac4e4c 100644 --- a/app/widget/keyframeview/keyframeviewbase.cpp +++ b/app/widget/keyframeview/keyframeviewbase.cpp @@ -174,7 +174,7 @@ void KeyframeViewBase::mouseMoveEvent(QMouseEvent *event) // frame gets rendered in this time input_parent->blockSignals(false); - input_parent->parentNode()->InvalidateVisible(input_parent); + input_parent->parentNode()->InvalidateVisible(input_parent, input_parent); } } } @@ -377,7 +377,7 @@ void KeyframeViewBase::ProcessBezierDrag(QPointF mouse_diff_scaled, bool include input_parent->blockSignals(false); - input_parent->parentNode()->InvalidateVisible(input_parent); + input_parent->parentNode()->InvalidateVisible(input_parent, input_parent); } } diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp index bc90a50e0..aaf1fefba 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp @@ -255,7 +255,7 @@ void NodeParamViewWidgetBridge::ProcessSlider(SliderBase *slider, const QVariant input_->blockSignals(false); - input_->parentNode()->InvalidateVisible(input_); + input_->parentNode()->InvalidateVisible(input_, input_); } else { if (dragging_) { @@ -513,9 +513,9 @@ rational NodeParamViewWidgetBridge::GetCurrentTimeAsNodeTime() const return GetAdjustedTime(GetTimeTarget(), input_->parentNode(), time_, NodeParam::kInput); } -void NodeParamViewWidgetBridge::InputValueChanged(const rational &start, const rational &end) +void NodeParamViewWidgetBridge::InputValueChanged(const TimeRange &range) { - if (!dragging_ && start <= time_ && end >= time_) { + if (!dragging_ && range.in() <= time_ && range.out() >= time_) { // We'll need to update the widgets because the values have changed on our current time UpdateWidgetValues(); } diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.h b/app/widget/nodeparamview/nodeparamviewwidgetbridge.h index 51b4c6ebc..d1543c73e 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.h +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.h @@ -68,7 +68,7 @@ private: private slots: void WidgetCallback(); - void InputValueChanged(const rational& start, const rational& end); + void InputValueChanged(const TimeRange& range); void PropertyChanged(const QString& key, const QVariant& value); diff --git a/app/widget/timelinewidget/undo/undo.cpp b/app/widget/timelinewidget/undo/undo.cpp index 8dd71617a..8ba10b15e 100644 --- a/app/widget/timelinewidget/undo/undo.cpp +++ b/app/widget/timelinewidget/undo/undo.cpp @@ -274,7 +274,9 @@ void TrackRippleRemoveAreaCommand::redo_internal() track_->UnblockInvalidateCache(); - track_->InvalidateCache(in_, insert_ ? out_ : RATIONAL_MAX); + track_->InvalidateCache(TimeRange(in_, insert_ ? out_ : RATIONAL_MAX), + track_->block_input(), + track_->block_input()); } void TrackRippleRemoveAreaCommand::undo_internal() @@ -328,7 +330,7 @@ void TrackRippleRemoveAreaCommand::undo_internal() track_->UnblockInvalidateCache(); - track_->InvalidateCache(in_, insert_ ? out_ : RATIONAL_MAX); + track_->InvalidateCache(TimeRange(in_, insert_ ? out_ : RATIONAL_MAX), track_->block_input(), track_->block_input()); } TrackPlaceBlockCommand::TrackPlaceBlockCommand(TrackList *timeline, int track, Block *block, rational in, QUndoCommand *parent) : diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index f25e1084b..4107ceab1 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -152,8 +152,8 @@ void ViewerWidget::ConnectNodeInternal(ViewerOutput *n) connect(n, &ViewerOutput::LengthChanged, this, &ViewerWidget::LengthChangedSlot); connect(n, &ViewerOutput::VideoParamsChanged, this, &ViewerWidget::UpdateRendererParameters); connect(n, &ViewerOutput::VisibleInvalidated, this, &ViewerWidget::InvalidateVisible); - connect(n, &ViewerOutput::VideoGraphChanged, this, &ViewerWidget::UpdateStack); - connect(n, &ViewerOutput::AudioGraphChanged, this, &ViewerWidget::UpdateStack); + connect(n, &ViewerOutput::VideoChangedBetween, this, &ViewerWidget::UpdateStack); + connect(n, &ViewerOutput::AudioChangedBetween, this, &ViewerWidget::UpdateStack); SizeChangedSlot(n->video_params().width(), n->video_params().height()); LengthChangedSlot(n->Length()); @@ -194,8 +194,8 @@ void ViewerWidget::DisconnectNodeInternal(ViewerOutput *n) disconnect(n, &ViewerOutput::LengthChanged, this, &ViewerWidget::LengthChangedSlot); disconnect(n, &ViewerOutput::VideoParamsChanged, this, &ViewerWidget::UpdateRendererParameters); disconnect(n, &ViewerOutput::VisibleInvalidated, this, &ViewerWidget::InvalidateVisible); - disconnect(n, &ViewerOutput::VideoGraphChanged, this, &ViewerWidget::UpdateStack); - disconnect(n, &ViewerOutput::AudioGraphChanged, this, &ViewerWidget::UpdateStack); + disconnect(n, &ViewerOutput::VideoChangedBetween, this, &ViewerWidget::UpdateStack); + disconnect(n, &ViewerOutput::AudioChangedBetween, this, &ViewerWidget::UpdateStack); // Effectively disables the viewer and clears the state SizeChangedSlot(0, 0); @@ -434,10 +434,14 @@ void ViewerWidget::SetColorTransform(const ColorTransform &transform, ViewerDisp void ViewerWidget::UpdateStack() { - if (!GetConnectedNode() || GetConnectedNode()->texture_input()->IsConnected()) { - stack_->setCurrentWidget(sizer_); - } else { + if (GetConnectedNode() + && !GetConnectedNode()->texture_input()->IsConnected() + && GetConnectedNode()->samples_input()->IsConnected()) { + // If we have a node AND video is disconnected AND audio is connected, show waveform view stack_->setCurrentWidget(waveform_view_); + } else { + // Otherwise show regular display + stack_->setCurrentWidget(sizer_); } } @@ -527,7 +531,7 @@ void ViewerWidget::UpdateRendererParameters() if (video_renderer_->params() != vparam) { video_renderer_->SetParameters(vparam); - video_renderer_->InvalidateCache(TimeRange(0, GetConnectedNode()->Length())); + video_renderer_->InvalidateCache(TimeRange(0, GetConnectedNode()->Length()), nullptr); } AudioRenderingParams aparam(GetConnectedNode()->audio_params(), @@ -535,7 +539,7 @@ void ViewerWidget::UpdateRendererParameters() if (audio_renderer_->params() != aparam) { audio_renderer_->SetParameters(aparam); - audio_renderer_->InvalidateCache(TimeRange(0, GetConnectedNode()->Length())); + audio_renderer_->InvalidateCache(TimeRange(0, GetConnectedNode()->Length()), nullptr); } } @@ -869,9 +873,9 @@ void ViewerWidget::SetZoomFromMenu(QAction *action) sizer_->SetZoom(action->data().toInt()); } -void ViewerWidget::InvalidateVisible() +void ViewerWidget::InvalidateVisible(NodeInput* source) { - video_renderer_->InvalidateCache(TimeRange(GetTime(), GetTime())); + video_renderer_->InvalidateCache(TimeRange(GetTime(), GetTime()), source); } OLIVE_NAMESPACE_EXIT diff --git a/app/widget/viewer/viewer.h b/app/widget/viewer/viewer.h index ec638238a..8cf6bb24c 100644 --- a/app/widget/viewer/viewer.h +++ b/app/widget/viewer/viewer.h @@ -233,7 +233,7 @@ private slots: void SetZoomFromMenu(QAction* action); - void InvalidateVisible(); + void InvalidateVisible(NodeInput *source); void UpdateStack();