nodes: optimize node graph changes by only updating from inputs that changed
A huge optimization that ensures only the parts of a node graph that have changed get pushed to the renderer. For thread-safety, the node graph is copied elsewhere so that users can make changes asynchronously and the graph can update when its threads are ready. Up until now, if an input value changed, every node's values would be re-copied, or worse, if a connection was changed, the entire graph would be recopied. This has been negligible in testing since we've been largely testing with small graphs, but for massive projects, it's important that this be as optimized as possible.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
+45
-7
@@ -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<Node *> &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<NodeInput*> connected_inputs = connected->GetInputsIncludingArrays();
|
||||
|
||||
foreach (NodeInput* i, connected_inputs) {
|
||||
i->GetDependencies(list, traverse, exclusive_only);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QList<Node *> NodeInput::GetDependencies(bool traverse, bool exclusive_only) const
|
||||
{
|
||||
QList<Node *> list;
|
||||
|
||||
GetDependencies(list, traverse, exclusive_only);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
QList<Node *> NodeInput::GetExclusiveDependencies() const
|
||||
{
|
||||
return GetDependencies(true, true);
|
||||
}
|
||||
|
||||
QList<Node *> NodeInput::GetImmediateDependencies() const
|
||||
{
|
||||
return GetDependencies(false, false);
|
||||
}
|
||||
|
||||
QVariant NodeInput::StringToValue(const QString &string, QList<XMLNodeData::FootageConnection>& 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)
|
||||
|
||||
+10
-2
@@ -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<Node*>& list, bool traverse, bool exclusive_only) const;
|
||||
|
||||
QList<Node*> GetDependencies(bool traverse = true, bool exclusive_only = false) const;
|
||||
|
||||
QList<Node*> GetExclusiveDependencies() const;
|
||||
|
||||
QList<Node*> GetImmediateDependencies() const;
|
||||
|
||||
signals:
|
||||
void ValueChanged(const rational& start, const rational& end);
|
||||
void ValueChanged(const TimeRange& range);
|
||||
|
||||
void KeyframeEnableChanged(bool);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
+63
-130
@@ -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<NodeOutput*>(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<Node *> &source, const QList<Node *> &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<NodeOutput*>(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<NodeInputArray*>(source_input);
|
||||
NodeInputArray* dest_array = static_cast<NodeInputArray*>(dest_input);
|
||||
|
||||
for (int i=0;i<source_array->GetSize();i++) {
|
||||
DuplicateConnectionsBetweenListsInternal(source, destination, source_array->At(i), dest_array->At(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Node::DuplicateConnectionsBetweenLists(const QList<Node *> &source, const QList<Node *> &destination)
|
||||
{
|
||||
Q_ASSERT(source.size() == destination.size());
|
||||
|
||||
for (int i=0;i<source.size();i++) {
|
||||
Node* source_input_node = source.at(i);
|
||||
Node* dest_input_node = destination.at(i);
|
||||
|
||||
Q_ASSERT(source_input_node->id() == dest_input_node->id());
|
||||
|
||||
for (int j=0;j<source_input_node->params_.size();j++) {
|
||||
NodeParam* source_param = source_input_node->params_.at(j);
|
||||
|
||||
if (source_param->type() == NodeInput::kInput) {
|
||||
NodeInput* source_input = static_cast<NodeInput*>(source_param);
|
||||
NodeInput* dest_input = static_cast<NodeInput*>(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<Node*>& 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<NodeInputArray*>(input);
|
||||
|
||||
for (int i=0;i<input_array->GetSize();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<Node*>& 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<Node*>& list, bool traverse, bool exclusive_only) {
|
||||
foreach (NodeParam* p, n->parameters()) {
|
||||
if (p->type() == NodeParam::kInput) {
|
||||
NodeInput* input = static_cast<NodeInput*>(p);
|
||||
QList<Node*> Node::GetDependenciesInternal(bool traverse, bool exclusive_only) const {
|
||||
QList<NodeInput*> inputs = GetInputsIncludingArrays();
|
||||
QList<Node*> list;
|
||||
|
||||
TraverseInputInternal(list, input, traverse, exclusive_only);
|
||||
}
|
||||
foreach (NodeInput* i, inputs) {
|
||||
i->GetDependencies(list, traverse, exclusive_only);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
QList<Node *> Node::GetDependencies() const
|
||||
{
|
||||
QList<Node *> node_list;
|
||||
|
||||
GetDependenciesInternal(this, node_list, true, false);
|
||||
|
||||
return node_list;
|
||||
return GetDependenciesInternal(true, false);
|
||||
}
|
||||
|
||||
QList<Node *> Node::GetExclusiveDependencies() const
|
||||
{
|
||||
QList<Node *> node_list;
|
||||
|
||||
GetDependenciesInternal(this, node_list, true, true);
|
||||
|
||||
return node_list;
|
||||
return GetDependenciesInternal(true, true);
|
||||
}
|
||||
|
||||
QList<Node *> Node::GetImmediateDependencies() const
|
||||
{
|
||||
QList<Node *> 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<NodeInput*> 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<NodeOutput*>(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<NodeInputArray*>(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<NodeInputArray*>(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<NodeInput*>(sender()));
|
||||
InvalidateCache(range, static_cast<NodeInput*>(sender()), static_cast<NodeInput*>(sender()));
|
||||
}
|
||||
|
||||
void Node::InputConnectionChanged(NodeEdgePtr edge)
|
||||
{
|
||||
DependentEdgeChanged(edge->input());
|
||||
|
||||
InvalidateCache(RATIONAL_MIN, RATIONAL_MAX, static_cast<NodeInput*>(sender()));
|
||||
InvalidateCache(TimeRange(RATIONAL_MIN, RATIONAL_MAX), edge->input(), edge->input());
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
+12
-17
@@ -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<Node*>& source, const QList<Node *> &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<Node*>& list, NodeInput* input, bool traverse, bool exclusive_only);
|
||||
|
||||
static void GetDependenciesInternal(const Node* n, QList<Node*>& list, bool traverse, bool exclusive_only);
|
||||
QList<Node *> GetDependenciesInternal(bool traverse, bool exclusive_only) const;
|
||||
|
||||
QList<NodeParam *> 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);
|
||||
|
||||
|
||||
@@ -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<Block *> &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_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ public:
|
||||
|
||||
const QVector<Block*>& 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);
|
||||
|
||||
|
||||
@@ -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<NodeInput*>(blend->GetParameterWithID("blend_in")));
|
||||
NodeParam::ConnectEdge(last_track->output(), static_cast<NodeInput*>(blend->GetParameterWithID("base_in")));
|
||||
NodeParam::ConnectEdge(track->output(), static_cast<NodeInput*>(blend->GetInputWithID("blend_in")));
|
||||
NodeParam::ConnectEdge(last_track->output(), static_cast<NodeInput*>(blend->GetInputWithID("base_in")));
|
||||
NodeParam::ConnectEdge(blend->output(), edge->input());
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user