From 16ac2fc0e17c32d39c79e5cb893557ad9218bea8 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 2 Nov 2019 01:39:37 +1100 Subject: [PATCH] sweeping node graph changes for new rendering pipeline The new rendering pipeline strives to simplify the nodes themselves as much as possible and move much of the logic to an external rendering engine. This change removes all of the responsibilities that no longer belong to the nodes themselves and will soon be folded into the renderer. --- app/common/timerange.cpp | 38 ++++ app/common/timerange.h | 9 + app/node/block/block.cpp | 15 +- app/node/block/block.h | 4 +- app/node/block/clip/clip.cpp | 24 +-- app/node/block/clip/clip.h | 3 +- app/node/color/opacity/opacity.cpp | 2 +- app/node/distort/transform/transform.cpp | 14 +- app/node/distort/transform/transform.h | 3 +- app/node/generator/solid/solid.cpp | 28 ++- app/node/generator/solid/solid.h | 3 +- app/node/input.cpp | 162 +++++++++++++++--- app/node/input.h | 38 ++-- app/node/input/media/audio/audio.cpp | 6 +- app/node/input/media/audio/audio.h | 2 +- app/node/input/media/media.cpp | 4 +- app/node/input/media/video/video.cpp | 1 - app/node/keyframe.cpp | 13 +- app/node/keyframe.h | 13 +- app/node/node.cpp | 71 ++++---- app/node/node.h | 59 +++---- app/node/output.cpp | 40 ++--- app/node/output.h | 28 +-- app/node/output/timeline/timeline.cpp | 5 +- app/node/output/timeline/timeline.h | 2 +- app/node/output/timeline/tracklist.cpp | 8 +- app/node/output/track/track.cpp | 10 +- app/node/output/track/track.h | 2 +- app/node/output/viewer/viewer.cpp | 11 +- app/node/output/viewer/viewer.h | 3 - app/node/param.cpp | 46 +---- app/node/param.h | 29 ---- app/project/item/sequence/sequence.cpp | 2 +- app/render/backend/opengl/openglworker.cpp | 12 +- .../nodeparamviewwidgetbridge.cpp | 34 ++-- 35 files changed, 397 insertions(+), 347 deletions(-) diff --git a/app/common/timerange.cpp b/app/common/timerange.cpp index e4ec160a1..21f30578e 100644 --- a/app/common/timerange.cpp +++ b/app/common/timerange.cpp @@ -21,6 +21,11 @@ const rational &TimeRange::out() const return out_; } +const rational &TimeRange::length() const +{ + return length_; +} + void TimeRange::set_in(const rational &in) { in_ = in; @@ -40,6 +45,36 @@ void TimeRange::set_range(const rational &in, const rational &out) normalize(); } +bool TimeRange::operator<(const TimeRange &r) const +{ + return length() < r.length(); +} + +bool TimeRange::operator<=(const TimeRange &r) const +{ + return length() <= r.length(); +} + +bool TimeRange::operator>(const TimeRange &r) const +{ + return length() > r.length(); +} + +bool TimeRange::operator>=(const TimeRange &r) const +{ + return length() >= r.length(); +} + +bool TimeRange::operator==(const TimeRange &r) const +{ + return length() == r.length(); +} + +bool TimeRange::operator!=(const TimeRange &r) const +{ + return length() != r.length(); +} + void TimeRange::normalize() { // If `out` is earlier than `in`, swap them @@ -48,4 +83,7 @@ void TimeRange::normalize() in_ = out_; out_ = temp; } + + // Calculate length + length_ = out_ - in_; } diff --git a/app/common/timerange.h b/app/common/timerange.h index 1ff31992b..7ca30956f 100644 --- a/app/common/timerange.h +++ b/app/common/timerange.h @@ -10,16 +10,25 @@ public: const rational& in() const; const rational& out() const; + const rational& length() const; void set_in(const rational& in); void set_out(const rational& out); void set_range(const rational& in, const rational& out); + bool operator<(const TimeRange &r) const; + bool operator<=(const TimeRange &r) const; + bool operator>(const TimeRange &r) const; + bool operator>=(const TimeRange &r) const; + bool operator==(const TimeRange &r) const; + bool operator!=(const TimeRange &r) const; + private: void normalize(); rational in_; rational out_; + rational length_; }; #endif // TIMERANGE_H diff --git a/app/node/block/block.cpp b/app/node/block/block.cpp index 9586a9e33..023359d0c 100644 --- a/app/node/block/block.cpp +++ b/app/node/block/block.cpp @@ -64,11 +64,11 @@ void Block::set_length(const rational &length) { Q_ASSERT(length > 0); - Lock(); + LockUserInput(); length_ = length; - Unlock(); + UnlockUserInput(); Refresh(); } @@ -84,7 +84,7 @@ void Block::set_length_and_media_in(const rational &length) Block *Block::previous() { - return ValueToPtr(previous_input_->get_value(0)); + return ValueToPtr(previous_input_->get_realtime_value_of_connected_output()); } Block *Block::next() @@ -97,11 +97,8 @@ NodeInput *Block::previous_input() return previous_input_; } -QVariant Block::Value(NodeOutput *output, const rational &in, const rational &out) +QVariant Block::Value(NodeOutput *output) { - Q_UNUSED(in) - Q_UNUSED(out) - if (output == block_output_) { // Simply set the output value to a pointer to this Block return PtrToValue(this); @@ -179,7 +176,7 @@ const rational &Block::media_in() void Block::set_media_in(const rational &media_in) { - Lock(); + LockUserInput(); if (media_in_ != media_in) { media_in_ = media_in; @@ -188,7 +185,7 @@ void Block::set_media_in(const rational &media_in) SendInvalidateCache(in(), out()); } - Unlock(); + UnlockUserInput(); } const QString &Block::block_name() diff --git a/app/node/block/block.h b/app/node/block/block.h index 9facd3593..5e0a2d027 100644 --- a/app/node/block/block.h +++ b/app/node/block/block.h @@ -80,6 +80,8 @@ public: virtual bool IsBlock() override; + virtual QVariant Value(NodeOutput* output) override; + public slots: /** * @brief Refreshes internal cache of in/out points up to date @@ -105,8 +107,6 @@ signals: void Refreshed(); protected: - virtual QVariant Value(NodeOutput* output, const rational &in, const rational &out) override; - rational SequenceToMediaTime(const rational& sequence_time); rational MediaToSequenceTime(const rational& media_time); diff --git a/app/node/block/clip/clip.cpp b/app/node/block/clip/clip.cpp index 3f5ed6aa3..b8560a49e 100644 --- a/app/node/block/clip/clip.cpp +++ b/app/node/block/clip/clip.cpp @@ -61,21 +61,6 @@ NodeInput *ClipBlock::texture_input() return texture_input_; } -QVariant ClipBlock::Value(NodeOutput* param, const rational& v_in, const rational &v_out) -{ - if (param == buffer_output()) { - // If the time retrieved is within this block, get texture information - if (texture_input()->IsConnected() && v_in >= in() && v_out < out()) { - // Retrieve texture - // We convert the time given (timeline time) to media time - return texture_input_->get_value(SequenceToMediaTime(v_in), SequenceToMediaTime(v_out)); - } - return 0; - } - - return Block::Value(param, v_in, v_out); -} - void ClipBlock::InvalidateCache(const rational &start_range, const rational &end_range, NodeInput *from) { // If signal is from texture input, transform all times from media time to sequence time @@ -104,3 +89,12 @@ QList ClipBlock::RunDependencies(NodeOutput *output, const ratio return deps; } + +TimeRange ClipBlock::InputTimeAdjustment(NodeInput *input, const TimeRange &input_time) +{ + if (input == texture_input_) { + return TimeRange(SequenceToMediaTime(input_time.in()), SequenceToMediaTime(input_time.out())); + } + + return Block::InputTimeAdjustment(input, input_time); +} diff --git a/app/node/block/clip/clip.h b/app/node/block/clip/clip.h index af16904c5..072d9beda 100644 --- a/app/node/block/clip/clip.h +++ b/app/node/block/clip/clip.h @@ -46,8 +46,7 @@ public: virtual QList RunDependencies(NodeOutput *output, const rational &time) override; -protected: - virtual QVariant Value(NodeOutput* output, const rational &v_in, const rational &v_out) override; + virtual TimeRange InputTimeAdjustment(NodeInput* input, const TimeRange& input_time) override; private: NodeInput* texture_input_; diff --git a/app/node/color/opacity/opacity.cpp b/app/node/color/opacity/opacity.cpp index 5fce51451..0989defae 100644 --- a/app/node/color/opacity/opacity.cpp +++ b/app/node/color/opacity/opacity.cpp @@ -24,7 +24,7 @@ OpacityNode::OpacityNode() { opacity_input_ = new NodeInput("opacity_in"); opacity_input_->set_data_type(NodeParam::kFloat); - opacity_input_->set_value(100); + opacity_input_->set_value_at_time(0, 100); opacity_input_->set_minimum(0); opacity_input_->set_maximum(100); AddParameter(opacity_input_); diff --git a/app/node/distort/transform/transform.cpp b/app/node/distort/transform/transform.cpp index 8fb4c8e40..aa7925547 100644 --- a/app/node/distort/transform/transform.cpp +++ b/app/node/distort/transform/transform.cpp @@ -35,7 +35,7 @@ TransformDistort::TransformDistort() scale_input_ = new NodeInput("scale_in"); scale_input_->set_data_type(NodeParam::kVec2); - scale_input_->set_value(QVector2D(100.0f, 100.0f)); + scale_input_->set_value_at_time(0, QVector2D(100.0f, 100.0f)); AddParameter(scale_input_); anchor_input_ = new NodeInput("anchor_in"); @@ -79,25 +79,23 @@ void TransformDistort::Retranslate() anchor_input_->set_name(tr("Anchor Point")); } -QVariant TransformDistort::Value(NodeOutput *output, const rational &in, const rational &out) +QVariant TransformDistort::Value(NodeOutput *output) { - Q_UNUSED(out) - if (output == matrix_output_) { QMatrix4x4 mat; // Position translate - QVector2D pos = position_input_->get_value(in).value(); + QVector2D pos = position_input_->value().value(); mat.translate(pos); // Rotation - mat.rotate(rotation_input_->get_value(in).toFloat(), 0, 0, 1); + mat.rotate(rotation_input_->value().toFloat(), 0, 0, 1); // Scale - mat.scale(scale_input_->get_value(in).value()*0.01f); + mat.scale(scale_input_->value().value()*0.01f); // Anchor Point - mat.translate(-anchor_input_->get_value(in).value()); + mat.translate(-anchor_input_->value().value()); return mat; } diff --git a/app/node/distort/transform/transform.h b/app/node/distort/transform/transform.h index 4647cf39c..8a9268158 100644 --- a/app/node/distort/transform/transform.h +++ b/app/node/distort/transform/transform.h @@ -38,8 +38,7 @@ public: virtual void Retranslate() override; -protected: - virtual QVariant Value(NodeOutput *output, const rational &in, const rational &out) override; + virtual QVariant Value(NodeOutput *output) override; private: NodeInput* position_input_; diff --git a/app/node/generator/solid/solid.cpp b/app/node/generator/solid/solid.cpp index 94e92eb61..c5383edd7 100644 --- a/app/node/generator/solid/solid.cpp +++ b/app/node/generator/solid/solid.cpp @@ -56,24 +56,18 @@ NodeOutput *SolidGenerator::texture_output() return texture_output_; } -QVariant SolidGenerator::Value(NodeOutput *output, const rational &in, const rational &out) +QString SolidGenerator::Code(NodeOutput *output) { - Q_UNUSED(output) - Q_UNUSED(in) - Q_UNUSED(out) - - /* - // FIXME: Test code - if (texture_ == nullptr) { - QImage img(1920, 1080, QImage::Format_RGBA8888_Premultiplied); - img.fill(Qt::red); - - texture_ = new QOpenGLTexture(img); + if (output == texture_output()) { + // FIXME: Not color managed + return "#version 110\n" + "\n" + "uniform vec4 color_in;\n" + "\n" + "void main(void) {\n" + " gl_FragColor = color_in;\n" + "}\n"; } - texture_output_->set_value(texture_->textureId()); - // End test code - */ - - return 0; + return Node::Code(output); } diff --git a/app/node/generator/solid/solid.h b/app/node/generator/solid/solid.h index 7b70885b4..4c0cbf49a 100644 --- a/app/node/generator/solid/solid.h +++ b/app/node/generator/solid/solid.h @@ -41,8 +41,7 @@ public: NodeOutput* texture_output(); -protected: - virtual QVariant Value(NodeOutput* output, const rational &in, const rational &out) override; + virtual QString Code(NodeOutput* output) override; private: NodeInput* color_input_; diff --git a/app/node/input.cpp b/app/node/input.cpp index b195f29de..d43a986d7 100644 --- a/app/node/input.cpp +++ b/app/node/input.cpp @@ -20,6 +20,7 @@ #include "input.h" +#include "common/lerp.h" #include "node.h" #include "output.h" @@ -78,54 +79,161 @@ Node *NodeInput::get_connected_node() return nullptr; } -QVariant NodeInput::get_value(const rational& in, const rational& out) +QVariant NodeInput::get_value_at_time(const rational &time) { - QVariant v; - - if (in_ != in || out_ != out || !value_caching_) { - // Retrieve the value - if (!edges_.isEmpty()) { - // A connection - use the output of the connected Node - value_ = get_connected_output()->get_value(in, out); - } else { - // No connections - use the internal value - // FIXME: Re-implement keyframing - value_ = keyframes_.first().value(); + if (is_keyframing()) { + if (keyframes_.first().time() >= time) { + // This time precedes any keyframe, so we just return the first value + return keyframes_.first().value(); } - in_ = in; - out_ = out; + if (keyframes_.last().time() <= time) { + // This time is after any keyframes so we return the last value + return keyframes_.last().value(); + } + + // If we're here, the time must be somewhere in between the keyframes + for (int i=0;i time) { + // We must interpolate between these keyframes + + if (before.type() == NodeKeyframe::kBezier && after.type() == NodeKeyframe::kBezier) { + // FIXME: Perform a cubic bezier interpolation + } else if (before.type() == NodeKeyframe::kLinear && after.type() == NodeKeyframe::kBezier) { + // FIXME: Perform a quadratic bezier interpolation with anchors from the AFTER keyframe + } else if (before.type() == NodeKeyframe::kLinear && after.type() == NodeKeyframe::kBezier) { + // FIXME: Perform a quadratic bezier interpolation with anchors from the BEFORE keyframe + } else { + // To have arrived here, the keyframes must both be linear + qreal period_progress = (time.toDouble() - before.time().toDouble()) / (after.time().toDouble() - before.time().toDouble()); + qreal interpolated_value = lerp(before.value().toDouble(), after.value().toDouble(), period_progress); + + return interpolated_value; + } + } + } } - v = value_; - - return v; + return keyframes_.first().value(); } -void NodeInput::set_value(const QVariant &value) +void NodeInput::set_value_at_time(const rational &time, const QVariant &value) { - bool lock_mutex = (parent() != nullptr); + if (parent() != nullptr) + parent()->LockUserInput(); - if (lock_mutex) parent()->Lock(); + if (is_keyframing()) { + // Insert value into the keyframe list chronologically + + if (keyframes_.first().time() > time) { + + // Store this away for the ValueChanged signal we emit later + rational existing_first_key = keyframes_.first().time(); + + // Insert at the beginning + keyframes_.prepend(NodeKeyframe(time, value, keyframes_.first().type())); + + // Value has changed since the earliest point up until the ex-first keyframe (since the frames + // interpolating between the key we're adding and the key that existed are changing too) + emit ValueChanged(RATIONAL_MIN, existing_first_key); + + } else if (keyframes_.first().time() == time) { + + // Replace first value + keyframes_.first().set_value(value); + + // Value has changed since the earliest point up until the keyframe we just changed + emit ValueChanged(RATIONAL_MIN, time); + + } else if (keyframes_.last().time() < time) { + + // Store this away for the ValueChanged signal we emit later + rational existing_last_key = keyframes_.last().time(); + + // Append at the end + keyframes_.append(NodeKeyframe(time, value, keyframes_.last().type())); + + // Value has changed since the ex-last point up until the latest possible point (since the frames + // interpolating between the key we're adding and the key that existed are changing too) + emit ValueChanged(existing_last_key, RATIONAL_MAX); + + } else if (keyframes_.last().time() == time) { + + // Replace last value + keyframes_.last().set_value(value); + + // Value has changed from this point until the latest possible point + emit ValueChanged(time, RATIONAL_MAX); + + } else { + for (int i=0;i time) { + // Insert value in between these two keyframes + keyframes_.insert(i+1, NodeKeyframe(time, value, before.type())); + + // Values have changed since the last keyframe and the next one + emit ValueChanged(before.time(), after.time()); + break; + } + } + } - if (keyframing()) { - // FIXME: Keyframing code using time() } else { - // Not keyframing, so invalidate entire time length keyframes_.first().set_value(value); + // Values have changed for all times since the value is static emit ValueChanged(RATIONAL_MIN, RATIONAL_MAX); } - if (lock_mutex) parent()->Unlock(); + if (parent() != nullptr) + parent()->UnlockUserInput(); } -bool NodeInput::keyframing() +const QVariant &NodeInput::value() +{ + return stored_value_; +} + +void NodeInput::set_stored_value(const QVariant &value) +{ + stored_value_ = value; +} + +QVariant NodeInput::get_realtime_value_of_connected_output() +{ + if (get_connected_output() == nullptr) { + return 0; + } + + return get_connected_output()->get_realtime_value(); +} + +bool NodeInput::is_keyframing() { return keyframing_; } -void NodeInput::set_keyframing(bool k) +void NodeInput::set_is_keyframing(bool k) { keyframing_ = k; } @@ -178,7 +286,7 @@ void NodeInput::CopyValues(NodeInput *source, NodeInput *dest) dest->keyframes_ = source->keyframes_; // Copy keyframing state - dest->set_keyframing(source->keyframing()); + dest->set_is_keyframing(source->is_keyframing()); // Copy connections if (source->get_connected_output() != nullptr) { diff --git a/app/node/input.h b/app/node/input.h index cfe1607ed..5a9029ed2 100644 --- a/app/node/input.h +++ b/app/node/input.h @@ -77,35 +77,36 @@ public: Node* get_connected_node(); /** - * @brief Get the value at a given time - * - * This function will automatically retrieve the correct value for this input at the given time. - * - * If an output is connected to this input, a request is made to that output for its value at this time. If multiple - * outputs are connected (\see can_accept_multiple_inputs()), a QList (casted to a QVariant) is returned - * instead, listing all the outputs' values currently connected. - * - * If no output is connected, this will return a user-defined value, either a static value if this input is not - * keyframed, or an interpolated value between the keyframes at this time. + * @brief Get currently stored value */ - QVariant get_value(const rational &in, const rational &out = -1); + const QVariant& value(); + + /** + * @brief Calculate what the stored value should be at a certain time + */ + QVariant get_value_at_time(const rational& time); + + /** + * @brief Sets what value should be seen at a specific time + */ + void set_value_at_time(const rational& time, const QVariant& value); /** * @brief Set the value at a given time - * - * This function will only work if there are no outputs connected. */ - void set_value(const QVariant& value); + void set_stored_value(const QVariant& value); + + QVariant get_realtime_value_of_connected_output(); /** * @brief Return whether keyframing is enabled on this input or not */ - bool keyframing(); + bool is_keyframing(); /** * @brief Set whether keyframing is enabled on this input or not */ - void set_keyframing(bool k); + void set_is_keyframing(bool k); /** * @brief Return whether the Node is dependent on this input or not @@ -181,6 +182,11 @@ private: */ QVariant maximum_; + /** + * @brief Internal stored value + */ + QVariant stored_value_; + }; #endif // NODEINPUT_H diff --git a/app/node/input/media/audio/audio.cpp b/app/node/input/media/audio/audio.cpp index 42d1d16a2..f3fde6228 100644 --- a/app/node/input/media/audio/audio.cpp +++ b/app/node/input/media/audio/audio.cpp @@ -26,9 +26,9 @@ QString AudioInput::Description() return tr("Import an audio footage stream."); } -QVariant AudioInput::Value(NodeOutput *output, const rational &in, const rational &out) +QVariant AudioInput::Value(NodeOutput *) { - if (output == samples_output_) { + /*if (output == samples_output_) { // Make sure decoder is set up if (!SetupDecoder()) { return 0; @@ -41,7 +41,7 @@ QVariant AudioInput::Value(NodeOutput *output, const rational &in, const rationa samples.resize(frame_->audio_params().samples_to_bytes(frame_->sample_count())); memcpy(samples.data(), frame_->data(), static_cast(samples.size())); return samples; - } + }*/ return 0; } diff --git a/app/node/input/media/audio/audio.h b/app/node/input/media/audio/audio.h index 1faa55d9d..5ddc957a2 100644 --- a/app/node/input/media/audio/audio.h +++ b/app/node/input/media/audio/audio.h @@ -14,7 +14,7 @@ public: virtual QString Description() override; protected: - virtual QVariant Value(NodeOutput* output, const rational& in, const rational& out) override; + virtual QVariant Value(NodeOutput* output) override; private: NodeOutput* samples_output_; diff --git a/app/node/input/media/media.cpp b/app/node/input/media/media.cpp index 7e0bbaf0d..33f743787 100644 --- a/app/node/input/media/media.cpp +++ b/app/node/input/media/media.cpp @@ -37,12 +37,12 @@ void MediaInput::Release() StreamPtr MediaInput::footage() { - return footage_input_->get_value(0).value(); + return footage_input_->get_value_at_time(0).value(); } void MediaInput::SetFootage(StreamPtr f) { - footage_input_->set_value(QVariant::fromValue(f)); + footage_input_->set_value_at_time(0, QVariant::fromValue(f)); } bool MediaInput::SetupDecoder() diff --git a/app/node/input/media/video/video.cpp b/app/node/input/media/video/video.cpp index d86fa39c7..07603db1e 100644 --- a/app/node/input/media/video/video.cpp +++ b/app/node/input/media/video/video.cpp @@ -15,7 +15,6 @@ VideoInput::VideoInput() AddParameter(matrix_input_); texture_output_ = new NodeOutput("tex_out"); - texture_output_->SetValueCachingEnabled(false); AddParameter(texture_output_); } diff --git a/app/node/keyframe.cpp b/app/node/keyframe.cpp index 2ce4ef695..69421a7e4 100644 --- a/app/node/keyframe.cpp +++ b/app/node/keyframe.cpp @@ -28,7 +28,14 @@ NodeKeyframe::NodeKeyframe() : } -const rational &NodeKeyframe::time() +NodeKeyframe::NodeKeyframe(const rational &time, const QVariant &value, const NodeKeyframe::Type &type) : + time_(time), + value_(value), + type_(type) +{ +} + +const rational &NodeKeyframe::time() const { return time_; } @@ -38,7 +45,7 @@ void NodeKeyframe::set_time(const rational &time) time_ = time; } -const QVariant &NodeKeyframe::value() +const QVariant &NodeKeyframe::value() const { return value_; } @@ -48,7 +55,7 @@ void NodeKeyframe::set_value(const QVariant &value) value_ = value; } -const NodeKeyframe::Type &NodeKeyframe::type() +const NodeKeyframe::Type &NodeKeyframe::type() const { return type_; } diff --git a/app/node/keyframe.h b/app/node/keyframe.h index 59dbe6be2..1e3e8d56c 100644 --- a/app/node/keyframe.h +++ b/app/node/keyframe.h @@ -41,27 +41,34 @@ public: kBezier }; + static const Type kDefaultType = kLinear; + /** * @brief NodeKeyframe Constructor */ NodeKeyframe(); + /** + * @brief NodeKeyframe Constructor + */ + NodeKeyframe(const rational& time, const QVariant& value, const Type& type); + /** * @brief The time this keyframe is set at */ - const rational& time(); + const rational& time() const; void set_time(const rational& time); /** * @brief The value of this keyframe (i.e. the value to use at this keyframe's time) */ - const QVariant& value(); + const QVariant& value() const; void set_value(const QVariant &value); /** * @brief The method of interpolation to use with this keyframe */ - const Type& type(); + const Type& type() const; void set_type(const Type& type); private: diff --git a/app/node/node.cpp b/app/node/node.cpp index 81e121026..53b76f2ae 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -72,11 +72,9 @@ void Node::RemoveParameter(NodeParam *param) delete param; } -QVariant Node::Value(NodeOutput *output, const rational &in, const rational &out) +QVariant Node::Value(NodeOutput *output) { Q_UNUSED(output) - Q_UNUSED(in) - Q_UNUSED(out) return QVariant(); } @@ -85,11 +83,18 @@ void Node::InvalidateCache(const rational &start_range, const rational &end_rang { Q_UNUSED(from) - ClearCachedValuesInParameters(start_range, end_range); - SendInvalidateCache(start_range, end_range); } +TimeRange Node::InputTimeAdjustment(NodeInput *input, const TimeRange &input_time) +{ + Q_UNUSED(input) + + // Default behavior is no time adjustment at all + + return input_time; +} + void Node::SendInvalidateCache(const rational &start_range, const rational &end_range) { // Loop through all parameters (there should be no children that are not NodeParams) @@ -110,14 +115,24 @@ void Node::SendInvalidateCache(const rational &start_range, const rational &end_ } } -void Node::Lock() +void Node::LockUserInput() { - lock_.lock(); + user_input_lock_.lock(); } -void Node::Unlock() +void Node::UnlockUserInput() { - lock_.unlock(); + user_input_lock_.unlock(); +} + +void Node::LockProcessing() +{ + processing_lock_.lock(); +} + +void Node::UnlockProcessing() +{ + processing_lock_.unlock(); } void Node::CopyInputs(Node *source, Node *destination) @@ -159,11 +174,11 @@ rational Node::LastProcessedTime() { rational t; - Lock(); + LockUserInput(); t = last_processed_time_; - Unlock(); + UnlockUserInput(); return t; } @@ -172,39 +187,15 @@ NodeOutput *Node::LastProcessedOutput() { NodeOutput* o; - Lock(); + LockUserInput(); o = last_processed_parameter_; - Unlock(); + UnlockUserInput(); return o; } -void Node::ClearCachedValuesInParameters(const rational &start_range, const rational &end_range) -{ - Q_UNUSED(start_range) - Q_UNUSED(end_range) - - // Loop through all parameters and clear cached values - foreach (NodeParam* param, params_) { - //if (param->LastRequestedTime() >= start_range && param->LastRequestedTime() <= end_range) { - param->ClearCachedValue(); - //} - } -} - -QVariant Node::Run(NodeOutput* output, const rational& in, const rational &out) -{ - run_lock_.lock(); - - QVariant v = Value(output, in, out); - - run_lock_.unlock(); - - return v; -} - const QList& Node::parameters() { return params_; @@ -367,7 +358,7 @@ void Node::DisconnectAll() } } -void Node::Hash(QCryptographicHash *hash, NodeOutput* from, const rational &time) +/*void Node::Hash(QCryptographicHash *hash, NodeOutput* from, const rational &time) { // Add this Node's ID hash->addData(id().toUtf8()); @@ -380,7 +371,7 @@ void Node::Hash(QCryptographicHash *hash, NodeOutput* from, const rational &time // Get the value at this time NodeInput* input = static_cast(param); - QVariant v = input->get_value(time); + QVariant v = input->value(time); hash->addData(NodeParam::ValueToBytes(input->data_type(), v)); } @@ -392,7 +383,7 @@ void Node::Hash(QCryptographicHash *hash, NodeOutput* from, const rational &time // Hash the connected node dep.node()->parent()->Hash(hash, dep.node(), dep.in()); } -} +}*/ QVariant Node::PtrToValue(void *ptr) { diff --git a/app/node/node.h b/app/node/node.h index 6aed68c46..6fed53e7d 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -129,13 +129,6 @@ public: */ virtual QString Code(NodeOutput* output); - /** - * @brief Wrapper for Process() - * - * It's recommended to call this directly over Value(), yet in derivatives of Node, override Value(). - */ - QVariant Run(NodeOutput* output, const rational &in, const rational &out); - /** * @brief For nodes that have different dependencies at different times, this function can be used for that purpose * @@ -176,7 +169,7 @@ public: /** * @brief Add's unique information about this Node at the given time to a QCryptographicHash */ - virtual void Hash(QCryptographicHash* hash, NodeOutput *from, const rational& time); + //virtual void Hash(QCryptographicHash* hash, NodeOutput *from, const rational& time); /** * @brief Convert a pointer to a value that can be sent between NodeParams @@ -200,15 +193,19 @@ public: */ virtual void InvalidateCache(const rational& start_range, const rational& end_range, NodeInput* from = nullptr); - /** - * @brief Lock mutex (for thread safety) - */ - void Lock(); + virtual TimeRange InputTimeAdjustment(NodeInput* input, const TimeRange& input_time); /** - * @brief Unock mutex (for thread safety) + * @brief User input lock prevents any user changes while a graph is being rendered */ - void Unlock(); + void LockUserInput(); + void UnlockUserInput(); + + /** + * @brief Processing lock prevents more than one thread trying to process a Node at once + */ + void LockProcessing(); + void UnlockProcessing(); /** * @brief Copies inputs from from Node to another including connections @@ -235,6 +232,21 @@ public: */ virtual bool IsBlock(); + /** + * @brief The main processing function + * + * The node's main purpose is to take values from inputs to set values in outputs. For whatever subclass node you + * create, this is where the code for that goes. + * + * Note that as a video editor, the node graph has to work across time. Depending on the purpose of your node, it may + * output different values depending on the time, and even if not, it will likely be receiving different input + * depending on the time. Most of the difficult work here is handled by NodeInput::get_value() which you should pass + * the `time` parameter to. It will return its value (at that time, if it's keyframed), or pass the time to a + * corresponding output if it's connected to one. If your node doesn't directly deal with time, the default behavior + * of the NodeParam objects will handle everything related to it automatically. + */ + virtual QVariant Value(NodeOutput* output); + protected: /** * @brief Add a parameter to this node @@ -252,21 +264,6 @@ protected: */ void RemoveParameter(NodeParam* param); - /** - * @brief The main processing function - * - * The node's main purpose is to take values from inputs to set values in outputs. For whatever subclass node you - * create, this is where the code for that goes. - * - * Note that as a video editor, the node graph has to work across time. Depending on the purpose of your node, it may - * output different values depending on the time, and even if not, it will likely be receiving different input - * depending on the time. Most of the difficult work here is handled by NodeInput::get_value() which you should pass - * the `time` parameter to. It will return its value (at that time, if it's keyframed), or pass the time to a - * corresponding output if it's connected to one. If your node doesn't directly deal with time, the default behavior - * of the NodeParam objects will handle everything related to it automatically. - */ - virtual QVariant Value(NodeOutput* output, const rational &in, const rational &out); - /** * @brief Retrieve the last timecode Process() was called with */ @@ -325,12 +322,12 @@ private: /** * @brief Used for thread safety from main thread */ - QMutex lock_; + QMutex user_input_lock_; /** * @brief Used for thread safety between multiple threads */ - QMutex run_lock_; + QMutex processing_lock_; /** * @brief Internal variable for whether this Node can be deleted or not diff --git a/app/node/output.cpp b/app/node/output.cpp index 785029ced..3ef22a09e 100644 --- a/app/node/output.cpp +++ b/app/node/output.cpp @@ -23,8 +23,7 @@ #include "node/node.h" NodeOutput::NodeOutput(const QString &id) : - NodeParam(id), - linked_input_(nullptr) + NodeParam(id) { } @@ -33,40 +32,33 @@ NodeParam::Type NodeOutput::type() return kOutput; } -QVariant NodeOutput::get_value(const rational& in, const rational& out) +QVariant NodeOutput::get_realtime_value() { - mutex_.lock(); + parent()->LockProcessing(); - QVariant v; + QVariant v = parent()->Value(this); - if (in_ != in || out_ != out || !value_caching_) { - // Update the value - value_ = parent()->Run(this, in, out); - - in_ = in; - out_ = out; - } - - v = value_; - - mutex_.unlock(); + parent()->UnlockProcessing(); return v; } -void NodeOutput::push_value(const QVariant &v, const rational &in, const rational &out) +bool NodeOutput::has_cached_value(const TimeRange &time) { - value_ = v; - in_ = in; - out_ = out; + return cached_values_.contains(time); } -NodeInput *NodeOutput::linked_input() +QVariant NodeOutput::get_cached_value(const TimeRange &time) { - return linked_input_; + return cached_values_.value(time); } -void NodeOutput::set_linked_input(NodeInput *link) +void NodeOutput::cache_value(const TimeRange &time, const QVariant &value) { - linked_input_ = link; + cached_values_.insert(time, value); +} + +void NodeOutput::drop_cached_values() +{ + cached_values_.clear(); } diff --git a/app/node/output.h b/app/node/output.h index 671e5e360..7d6ffa3ee 100644 --- a/app/node/output.h +++ b/app/node/output.h @@ -21,6 +21,7 @@ #ifndef NODEOUTPUT_H #define NODEOUTPUT_H +#include "common/timerange.h" #include "param.h" /** @@ -39,30 +40,15 @@ public: */ virtual Type type() override; - /** - * @brief Get the value of this output at a given tie - * - * This function is intended to primarily be called by any connected NodeInputs. - * - * The first thing this function does is request the parent Node object to Process() at this time. The Node should - * then perform whatever actions necessary (usually taking data from inputs and creating output data) to set the - * correct value that this output should have at this time (the Node should use set_value() for this). This function - * will then return the value that was set after Process() returned. - * - * In many cases for efficiency, the Node can also ignore this request if it knows the output data will not change - * (i.e. if the time has not changed from the last Process()). - */ - virtual QVariant get_value(const rational &in, const rational &out); + QVariant get_realtime_value(); - void push_value(const QVariant& v, const rational& in, const rational &out); - - NodeInput* linked_input(); - void set_linked_input(NodeInput* link); + bool has_cached_value(const TimeRange& time); + QVariant get_cached_value(const TimeRange& time); + void cache_value(const TimeRange& time, const QVariant& value); + void drop_cached_values(); private: - QMutex mutex_; - - NodeInput* linked_input_; + QMap cached_values_; }; diff --git a/app/node/output/timeline/timeline.cpp b/app/node/output/timeline/timeline.cpp index efa492991..33793e202 100644 --- a/app/node/output/timeline/timeline.cpp +++ b/app/node/output/timeline/timeline.cpp @@ -93,11 +93,8 @@ const rational &TimelineOutput::Timebase() return timebase_; } -QVariant TimelineOutput::Value(NodeOutput *output, const rational &in, const rational &out) +QVariant TimelineOutput::Value(NodeOutput *output) { - Q_UNUSED(in) - Q_UNUSED(out) - if (output == length_output_) { return QVariant::fromValue(length_); } diff --git a/app/node/output/timeline/timeline.h b/app/node/output/timeline/timeline.h index a8f8a2268..2ea03c9ad 100644 --- a/app/node/output/timeline/timeline.h +++ b/app/node/output/timeline/timeline.h @@ -67,7 +67,7 @@ signals: void TrackRemoved(TrackOutput* track); protected: - virtual QVariant Value(NodeOutput* output, const rational &in, const rational &out) override; + virtual QVariant Value(NodeOutput* output) override; private: QVector track_inputs_; diff --git a/app/node/output/timeline/tracklist.cpp b/app/node/output/timeline/tracklist.cpp index 7efc3447b..b31be3cf2 100644 --- a/app/node/output/timeline/tracklist.cpp +++ b/app/node/output/timeline/tracklist.cpp @@ -34,7 +34,7 @@ TrackList::TrackList(TimelineOutput* parent, const enum TrackType &type, NodeInp TrackOutput *TrackList::attached_track() { - return Node::ValueToPtr(track_input_->get_value(0)); + return Node::ValueToPtr(track_input_->get_realtime_value_of_connected_output()); } void TrackList::AttachTrack(TrackOutput *track) @@ -176,7 +176,7 @@ void TrackList::TrackConnectionRemoved(NodeEdgePtr edge) return; } - DetachTrack(Node::ValueToPtr(edge->output()->get_value(0, 0))); + DetachTrack(Node::ValueToPtr(edge->output()->get_realtime_value())); } void TrackList::TrackEdgeAdded(NodeEdgePtr edge) @@ -186,7 +186,7 @@ void TrackList::TrackEdgeAdded(NodeEdgePtr edge) // If this edge pertains to the track's track input, all the tracks just added need attaching if (edge->input() == track->track_input()) { - TrackOutput* added_track = Node::ValueToPtr(edge->output()->get_value(0, 0)); + TrackOutput* added_track = Node::ValueToPtr(edge->output()->get_realtime_value()); AttachTrack(added_track); } @@ -199,7 +199,7 @@ void TrackList::TrackEdgeRemoved(NodeEdgePtr edge) // If this edge pertains to the track's track input, all the tracks just added need attaching if (edge->input() == track->track_input()) { - TrackOutput* added_track = Node::ValueToPtr(edge->output()->get_value(0, 0)); + TrackOutput* added_track = Node::ValueToPtr(edge->output()->get_realtime_value()); DetachTrack(added_track); } diff --git a/app/node/output/track/track.cpp b/app/node/output/track/track.cpp index ce35f3ecb..1ce1bbce1 100644 --- a/app/node/output/track/track.cpp +++ b/app/node/output/track/track.cpp @@ -139,7 +139,7 @@ QList TrackOutput::RunDependencies(NodeOutput* output, const rat TrackOutput *TrackOutput::next_track() { - return ValueToPtr(track_input_->get_value(0)); + return ValueToPtr(track_input_->get_realtime_value_of_connected_output()); } NodeInput *TrackOutput::track_input() @@ -191,12 +191,12 @@ void TrackOutput::InvalidateCache(const rational &start_range, const rational &e } } -QVariant TrackOutput::Value(NodeOutput *output, const rational &in, const rational &out) +QVariant TrackOutput::Value(NodeOutput *output) { if (output == track_output_) { // Set track output correctly return PtrToValue(this); - } else if (output == buffer_output()) { + /*} else if (output == buffer_output()) { ValidateCurrentBlock(in); if (current_block_ != this) { @@ -205,11 +205,11 @@ QVariant TrackOutput::Value(NodeOutput *output, const rational &in, const ration } // No texture is valid - return 0; + return 0;*/ } // Run default node processing - return Block::Value(output, in, out); + return Block::Value(output); } void TrackOutput::InsertBlockBetweenBlocks(Block *block, Block *before, Block *after) diff --git a/app/node/output/track/track.h b/app/node/output/track/track.h index f72913eb5..8b97fb16d 100644 --- a/app/node/output/track/track.h +++ b/app/node/output/track/track.h @@ -146,7 +146,7 @@ signals: void BlockRemoved(Block* block); protected: - virtual QVariant Value(NodeOutput* output, const rational& in, const rational &out) override; + virtual QVariant Value(NodeOutput* output) override; private: /** diff --git a/app/node/output/viewer/viewer.cpp b/app/node/output/viewer/viewer.cpp index 57def7aa2..5ad6c07e1 100644 --- a/app/node/output/viewer/viewer.cpp +++ b/app/node/output/viewer/viewer.cpp @@ -104,14 +104,5 @@ void ViewerOutput::set_audio_params(const AudioParams &audio) rational ViewerOutput::Length() { - return length_input_->get_value(0).value(); -} - -QVariant ViewerOutput::Value(NodeOutput *output, const rational &in, const rational &out) -{ - Q_UNUSED(output) - Q_UNUSED(in) - Q_UNUSED(out) - - return 0; + return length_input_->get_realtime_value_of_connected_output().value(); } diff --git a/app/node/output/viewer/viewer.h b/app/node/output/viewer/viewer.h index 52aacbc3f..44aaddd21 100644 --- a/app/node/output/viewer/viewer.h +++ b/app/node/output/viewer/viewer.h @@ -62,9 +62,6 @@ signals: void SizeChanged(int width, int height); -protected: - virtual QVariant Value(NodeOutput* output, const rational &in, const rational &out) override; - private: NodeInput* texture_input_; diff --git a/app/node/param.cpp b/app/node/param.cpp index 48fb68514..bc3083df9 100644 --- a/app/node/param.cpp +++ b/app/node/param.cpp @@ -32,9 +32,6 @@ #include "node/output.h" NodeParam::NodeParam(const QString &id) : - in_(-1), - out_(-1), - value_caching_(true), id_(id) { Q_ASSERT(!id_.isEmpty()); @@ -110,16 +107,14 @@ NodeEdgePtr NodeParam::ConnectEdge(NodeOutput *output, NodeInput *input) // that's difficult to diagnose. This makes that issue very clear. Q_ASSERT(output->parent() != input->parent()); - output->parent()->Lock(); - input->parent()->Lock(); + output->parent()->LockUserInput(); + input->parent()->LockUserInput(); output->edges_.append(edge); input->edges_.append(edge); - input->ClearCachedValue(); - - output->parent()->Unlock(); - input->parent()->Unlock(); + output->parent()->UnlockUserInput(); + input->parent()->UnlockUserInput(); // Emit a signal than an edge was added (only one signal needs emitting) emit input->EdgeAdded(edge); @@ -132,16 +127,14 @@ void NodeParam::DisconnectEdge(NodeEdgePtr edge) NodeOutput* output = edge->output(); NodeInput* input = edge->input(); - output->parent()->Lock(); - input->parent()->Lock(); + output->parent()->LockUserInput(); + input->parent()->LockUserInput(); output->edges_.removeAll(edge); input->edges_.removeAll(edge); - input->ClearCachedValue(); - - output->parent()->Unlock(); - input->parent()->Unlock(); + output->parent()->UnlockUserInput(); + input->parent()->UnlockUserInput(); emit input->EdgeRemoved(edge); } @@ -228,29 +221,6 @@ QByteArray NodeParam::ValueToBytes(const NodeParam::DataType &type, const QVaria return QByteArray(); } -void NodeParam::ClearCachedValue() -{ - // Since get_value() will (read: should) never receive a negative number, this will effectively invalidate any value - // currently cached - in_ = -1; - out_ = -1; -} - -const rational &NodeParam::LastRequestedIn() -{ - return in_; -} - -bool NodeParam::ValueCachingEnabled() -{ - return value_caching_; -} - -void NodeParam::SetValueCachingEnabled(bool enabled) -{ - value_caching_ = enabled; -} - template QByteArray NodeParam::ValueToBytesInternal(const QVariant &v) { diff --git a/app/node/param.h b/app/node/param.h index 40a832442..70ecf3a1e 100644 --- a/app/node/param.h +++ b/app/node/param.h @@ -229,19 +229,6 @@ public: */ static QByteArray ValueToBytes(const DataType &type, const QVariant& value); - /** - * @brief Clear the cached value - */ - void ClearCachedValue(); - - /** - * @brief Retrieve the last time this parameter had a value requested from - */ - const rational& LastRequestedIn(); - - bool ValueCachingEnabled(); - void SetValueCachingEnabled(bool enabled); - signals: /** * @brief Signal emitted when an edge is added to this parameter @@ -265,22 +252,6 @@ protected: */ QVector edges_; - /** - * @brief Currently cached value - */ - QVariant value_; - - /** - * @brief Last timecodes that a value was requested with - */ - rational in_; - rational out_; - - /** - * @brief Internal value for whether value caching is enabled - */ - bool value_caching_; - /** * @brief Internal name string */ diff --git a/app/project/item/sequence/sequence.cpp b/app/project/item/sequence/sequence.cpp index fe1e81e2d..3735192ba 100644 --- a/app/project/item/sequence/sequence.cpp +++ b/app/project/item/sequence/sequence.cpp @@ -103,7 +103,7 @@ QString Sequence::duration() return QString(); } - rational timeline_length = timeline_output_->length_output()->get_value(0, 0).value(); + rational timeline_length = timeline_output_->length_output()->get_realtime_value().value(); int64_t timestamp = olive::time_to_timestamp(timeline_length, video_params_.time_base()); diff --git a/app/render/backend/opengl/openglworker.cpp b/app/render/backend/opengl/openglworker.cpp index 1bbbb79dd..6d4dbcb20 100644 --- a/app/render/backend/opengl/openglworker.cpp +++ b/app/render/backend/opengl/openglworker.cpp @@ -66,9 +66,9 @@ void OpenGLWorker::Render(const NodeDependency &path) // Lock all Nodes to prevent UI changes during this render foreach (Node* dep, all_deps) { - dep->Lock(); + dep->LockUserInput(); } - node->Lock(); + node->LockUserInput(); // FIXME: Write traversal code @@ -77,9 +77,9 @@ void OpenGLWorker::Render(const NodeDependency &path) // Unlock all Nodes so changes can be made again foreach (Node* dep, all_deps) { - dep->Unlock(); + dep->UnlockUserInput(); } - node->Unlock(); + node->UnlockUserInput(); // Now we need the texture done so we call glFinish() functions_->glFinish(); @@ -111,3 +111,7 @@ void OpenGLWorker::FinishInit() qDebug() << "Context in" << ctx_->thread() << "successfully finished"; } + +void OpenGLWorker::RenderAsSibling(const NodeDependency &) +{ +} diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp index 8ac211f54..6d776abeb 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp @@ -61,7 +61,7 @@ void NodeParamViewWidgetBridge::CreateWidgets() { FloatSlider* slider = new FloatSlider(); - slider->SetValue(base_input->get_value(0).toDouble()); + slider->SetValue(base_input->get_value_at_time(0).toDouble()); if (base_input->has_minimum()) { slider->SetMinimum(base_input->minimum().toDouble()); @@ -78,7 +78,7 @@ void NodeParamViewWidgetBridge::CreateWidgets() } case NodeParam::kVec2: { - QVector2D vec2 = base_input->get_value(0).value(); + QVector2D vec2 = base_input->get_value_at_time(0).value(); FloatSlider* x_slider = new FloatSlider(); x_slider->SetValue(static_cast(vec2.x())); @@ -93,7 +93,7 @@ void NodeParamViewWidgetBridge::CreateWidgets() } case NodeParam::kVec3: { - QVector3D vec3 = base_input->get_value(0).value(); + QVector3D vec3 = base_input->get_value_at_time(0).value(); FloatSlider* x_slider = new FloatSlider(); x_slider->SetValue(static_cast(vec3.x())); @@ -113,7 +113,7 @@ void NodeParamViewWidgetBridge::CreateWidgets() } case NodeParam::kVec4: { - QVector4D vec4 = base_input->get_value(0).value(); + QVector4D vec4 = base_input->get_value_at_time(0).value(); FloatSlider* x_slider = new FloatSlider(); x_slider->SetValue(static_cast(vec4.x())); @@ -171,7 +171,7 @@ void NodeParamViewWidgetBridge::CreateWidgets() footage_combobox->SetRoot(pp->project()->root()); // Use multiple values - footage_combobox->SetFootage(base_input->get_value(0).value()); + footage_combobox->SetFootage(base_input->get_value_at_time(0).value()); connect(footage_combobox, SIGNAL(FootageChanged(StreamPtr)), this, SLOT(WidgetCallback())); // End test code @@ -201,14 +201,14 @@ void NodeParamViewWidgetBridge::WidgetCallback() { // Widget is a IntegerSlider IntegerSlider* int_slider = static_cast(sender()); - input->set_value(int_slider->GetValue()); + input->set_value_at_time(0, int_slider->GetValue()); break; } case NodeParam::kFloat: { // Widget is a FloatSlider FloatSlider* float_slider = static_cast(sender()); - input->set_value(float_slider->GetValue()); + input->set_value_at_time(0, float_slider->GetValue()); break; } case NodeParam::kVec2: @@ -216,7 +216,7 @@ void NodeParamViewWidgetBridge::WidgetCallback() // Widgets are two FloatSliders FloatSlider* slider = static_cast(sender()); - QVector2D val = input->get_value(0).value(); + QVector2D val = input->get_value_at_time(0).value(); if (slider == widgets_.at(0)) { // Slider is X slider @@ -226,7 +226,7 @@ void NodeParamViewWidgetBridge::WidgetCallback() val.setY(static_cast(slider->GetValue())); } - input->set_value(val); + input->set_value_at_time(0, val); break; } case NodeParam::kVec3: @@ -234,7 +234,7 @@ void NodeParamViewWidgetBridge::WidgetCallback() // Widgets are three FloatSliders FloatSlider* slider = static_cast(sender()); - QVector3D val = input->get_value(0).value(); + QVector3D val = input->get_value_at_time(0).value(); if (slider == widgets_.at(0)) { // Slider is X slider @@ -247,7 +247,7 @@ void NodeParamViewWidgetBridge::WidgetCallback() val.setZ(static_cast(slider->GetValue())); } - input->set_value(val); + input->set_value_at_time(0, val); break; } case NodeParam::kVec4: @@ -255,7 +255,7 @@ void NodeParamViewWidgetBridge::WidgetCallback() // Widgets are three FloatSliders FloatSlider* slider = static_cast(sender()); - QVector4D val = input->get_value(0).value(); + QVector4D val = input->get_value_at_time(0).value(); if (slider == widgets_.at(0)) { // Slider is X slider @@ -271,7 +271,7 @@ void NodeParamViewWidgetBridge::WidgetCallback() val.setW(static_cast(slider->GetValue())); } - input->set_value(val); + input->set_value_at_time(0, val); break; } case NodeParam::kFile: @@ -284,28 +284,28 @@ void NodeParamViewWidgetBridge::WidgetCallback() { // Sender is a QLineEdit QLineEdit* line_edit = static_cast(sender()); - input->set_value(line_edit->text()); + input->set_value_at_time(0, line_edit->text()); break; } case NodeParam::kBoolean: { // Widget is a QCheckBox QCheckBox* check_box = static_cast(sender()); - input->set_value(check_box->isChecked()); + input->set_value_at_time(0, check_box->isChecked()); break; } case NodeParam::kFont: { // Widget is a QFontComboBox QFontComboBox* font_combobox = static_cast(sender()); - input->set_value(font_combobox->currentFont()); + input->set_value_at_time(0, font_combobox->currentFont()); break; } case NodeParam::kFootage: { // Widget is a FootageComboBox FootageComboBox* footage_combobox = static_cast(sender()); - input->set_value(QVariant::fromValue(footage_combobox->SelectedFootage())); + input->set_value_at_time(0, QVariant::fromValue(footage_combobox->SelectedFootage())); break; } }