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/main.cpp b/app/main.cpp index 859ed0b76..1b4b6e3a7 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -50,9 +50,6 @@ int main(int argc, char *argv[]) { format.setProfile(QSurfaceFormat::CoreProfile); QSurfaceFormat::setDefaultFormat(format); - // Try to share OpenGL contexts - QApplication::setAttribute(Qt::AA_ShareOpenGLContexts); - // Create application instance QApplication a(argc, argv); diff --git a/app/node/CMakeLists.txt b/app/node/CMakeLists.txt index 89780c8dc..965982241 100644 --- a/app/node/CMakeLists.txt +++ b/app/node/CMakeLists.txt @@ -16,7 +16,7 @@ add_subdirectory(audio) add_subdirectory(block) -add_subdirectory(distort) +add_subdirectory(generator) add_subdirectory(input) add_subdirectory(math) add_subdirectory(output) 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/factory.cpp b/app/node/factory.cpp index 2e2f79652..09791c6a5 100644 --- a/app/node/factory.cpp +++ b/app/node/factory.cpp @@ -25,7 +25,7 @@ #include "block/clip/clip.h" #include "block/gap/gap.h" #include "block/transition/externaltransition.h" -#include "distort/transform/transform.h" +#include "generator/matrix/matrix.h" #include "input/media/video/video.h" #include "input/media/audio/audio.h" #include "input/time/timeinput.h" @@ -141,8 +141,8 @@ Node *NodeFactory::CreateInternal(const NodeFactory::InternalID &id) return new ClipBlock(); case kGapBlock: return new GapBlock(); - case kTransformDistort: - return new TransformDistort(); + case kMatrixGenerator: + return new MatrixGenerator(); case kVideoInput: return new VideoInput(); case kAudioInput: diff --git a/app/node/factory.h b/app/node/factory.h index 9cd683781..c6329de7e 100644 --- a/app/node/factory.h +++ b/app/node/factory.h @@ -36,7 +36,7 @@ public: kClipBlock, kGapBlock, kAudioInput, - kTransformDistort, + kMatrixGenerator, kVideoInput, kTrackOutput, kAudioVolume, diff --git a/app/node/distort/CMakeLists.txt b/app/node/generator/CMakeLists.txt similarity index 96% rename from app/node/distort/CMakeLists.txt rename to app/node/generator/CMakeLists.txt index f43e36675..9e8d25261 100644 --- a/app/node/distort/CMakeLists.txt +++ b/app/node/generator/CMakeLists.txt @@ -14,7 +14,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -add_subdirectory(transform) +add_subdirectory(matrix) set(OLIVE_SOURCES ${OLIVE_SOURCES} diff --git a/app/node/distort/transform/CMakeLists.txt b/app/node/generator/matrix/CMakeLists.txt similarity index 90% rename from app/node/distort/transform/CMakeLists.txt rename to app/node/generator/matrix/CMakeLists.txt index bfa0cfc5a..b986f2aca 100644 --- a/app/node/distort/transform/CMakeLists.txt +++ b/app/node/generator/matrix/CMakeLists.txt @@ -16,7 +16,7 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} - node/distort/transform/transform.h - node/distort/transform/transform.cpp + node/generator/matrix/matrix.h + node/generator/matrix/matrix.cpp PARENT_SCOPE ) diff --git a/app/node/distort/transform/transform.cpp b/app/node/generator/matrix/matrix.cpp similarity index 81% rename from app/node/distort/transform/transform.cpp rename to app/node/generator/matrix/matrix.cpp index 649aa065e..c967869ed 100644 --- a/app/node/distort/transform/transform.cpp +++ b/app/node/generator/matrix/matrix.cpp @@ -18,14 +18,14 @@ ***/ -#include "transform.h" +#include "matrix.h" #include #include OLIVE_NAMESPACE_ENTER -TransformDistort::TransformDistort() +MatrixGenerator::MatrixGenerator() { position_input_ = new NodeInput("pos_in", NodeParam::kVec2); AddInput(position_input_); @@ -42,39 +42,39 @@ TransformDistort::TransformDistort() uniform_scale_input_ = new NodeInput("uniform_scale_in", NodeParam::kBoolean, true); uniform_scale_input_->set_is_keyframable(false); uniform_scale_input_->SetConnectable(false); - connect(uniform_scale_input_, &NodeInput::ValueChanged, this, &TransformDistort::UniformScaleChanged); + connect(uniform_scale_input_, &NodeInput::ValueChanged, this, &MatrixGenerator::UniformScaleChanged); AddInput(uniform_scale_input_); anchor_input_ = new NodeInput("anchor_in", NodeParam::kVec2); AddInput(anchor_input_); } -Node *TransformDistort::copy() const +Node *MatrixGenerator::copy() const { - return new TransformDistort(); + return new MatrixGenerator(); } -QString TransformDistort::Name() const +QString MatrixGenerator::Name() const { - return tr("Transform"); + return tr("Orthographic Matrix"); } -QString TransformDistort::id() const +QString MatrixGenerator::id() const { return QStringLiteral("org.olivevideoeditor.Olive.transform"); } -QString TransformDistort::Category() const +QString MatrixGenerator::Category() const { - return tr("Distort"); + return tr("Generator"); } -QString TransformDistort::Description() const +QString MatrixGenerator::Description() const { - return tr("Apply transformations to position, rotation, and scale."); + return tr("Generate an orthographic matrix using position, rotation, and scale."); } -void TransformDistort::Retranslate() +void MatrixGenerator::Retranslate() { position_input_->set_name(tr("Position")); rotation_input_->set_name(tr("Rotation")); @@ -83,7 +83,7 @@ void TransformDistort::Retranslate() anchor_input_->set_name(tr("Anchor Point")); } -NodeValueTable TransformDistort::Value(NodeValueDatabase &value) const +NodeValueTable MatrixGenerator::Value(NodeValueDatabase &value) const { QMatrix4x4 mat; @@ -110,7 +110,7 @@ NodeValueTable TransformDistort::Value(NodeValueDatabase &value) const return output; } -void TransformDistort::UniformScaleChanged() +void MatrixGenerator::UniformScaleChanged() { scale_input_->set_property("disabley", uniform_scale_input_->get_standard_value().toBool()); } diff --git a/app/node/distort/transform/transform.h b/app/node/generator/matrix/matrix.h similarity index 92% rename from app/node/distort/transform/transform.h rename to app/node/generator/matrix/matrix.h index 6e3acd6a4..53c5c5356 100644 --- a/app/node/distort/transform/transform.h +++ b/app/node/generator/matrix/matrix.h @@ -18,18 +18,18 @@ ***/ -#ifndef TRANSFORMDISTORT_H -#define TRANSFORMDISTORT_H +#ifndef MATRIXGENERATOR_H +#define MATRIXGENERATOR_H #include "node/node.h" OLIVE_NAMESPACE_ENTER -class TransformDistort : public Node +class MatrixGenerator : public Node { Q_OBJECT public: - TransformDistort(); + MatrixGenerator(); virtual Node* copy() 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/audio/audio.cpp b/app/node/input/media/audio/audio.cpp index c51b021d5..0dadd4b60 100644 --- a/app/node/input/media/audio/audio.cpp +++ b/app/node/input/media/audio/audio.cpp @@ -22,10 +22,6 @@ OLIVE_NAMESPACE_ENTER -AudioInput::AudioInput() -{ -} - Node *AudioInput::copy() const { return new AudioInput(); diff --git a/app/node/input/media/audio/audio.h b/app/node/input/media/audio/audio.h index 2bc6db0ef..b28500c53 100644 --- a/app/node/input/media/audio/audio.h +++ b/app/node/input/media/audio/audio.h @@ -28,7 +28,7 @@ OLIVE_NAMESPACE_ENTER class AudioInput : public MediaInput { public: - AudioInput(); + AudioInput() = default; virtual Node* copy() const override; @@ -36,7 +36,6 @@ public: virtual QString id() const override; virtual QString Description() const override; -private: }; OLIVE_NAMESPACE_EXIT diff --git a/app/node/input/media/media.cpp b/app/node/input/media/media.cpp index a69207ab6..b7bad0efc 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_); } @@ -67,7 +67,7 @@ NodeValueTable MediaInput::Value(NodeValueDatabase &value) const } // Push buffer to the top of the stack - NodeValue buffer = value[footage_input_].GetWithMeta(NodeParam::kSamples); + NodeValue buffer = value[footage_input_].GetWithMeta(NodeParam::kBuffer); if (buffer.type() != NodeParam::kNone) { table.Push(buffer); } @@ -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/input/media/video/video.cpp b/app/node/input/media/video/video.cpp index 2820cb292..e6caab973 100644 --- a/app/node/input/media/video/video.cpp +++ b/app/node/input/media/video/video.cpp @@ -31,12 +31,6 @@ OLIVE_NAMESPACE_ENTER -VideoInput::VideoInput() -{ - matrix_input_ = new NodeInput("matrix_in", NodeInput::kMatrix); - AddInput(matrix_input_); -} - Node *VideoInput::copy() const { return new VideoInput(); @@ -57,31 +51,4 @@ QString VideoInput::Description() const return tr("Import a video footage stream."); } -NodeInput *VideoInput::matrix_input() const -{ - return matrix_input_; -} - -Node::Capabilities VideoInput::GetCapabilities(const NodeValueDatabase &) const -{ - return kShader; -} - -QString VideoInput::ShaderVertexCode(const NodeValueDatabase&) const -{ - return ReadFileAsString(":/shaders/videoinput.vert"); -} - -QString VideoInput::ShaderFragmentCode(const NodeValueDatabase&) const -{ - return ReadFileAsString(":/shaders/videoinput.frag"); -} - -void VideoInput::Retranslate() -{ - MediaInput::Retranslate(); - - matrix_input_->set_name(tr("Transform")); -} - OLIVE_NAMESPACE_EXIT diff --git a/app/node/input/media/video/video.h b/app/node/input/media/video/video.h index fdeb109fe..23a5698c8 100644 --- a/app/node/input/media/video/video.h +++ b/app/node/input/media/video/video.h @@ -31,7 +31,7 @@ OLIVE_NAMESPACE_ENTER class VideoInput : public MediaInput { public: - VideoInput(); + VideoInput() = default; virtual Node* copy() const override; @@ -39,19 +39,6 @@ public: virtual QString id() const override; virtual QString Description() const override; - NodeInput* matrix_input() const; - - virtual Capabilities GetCapabilities(const NodeValueDatabase&) const override; - virtual QString ShaderVertexCode(const NodeValueDatabase&) const override; - virtual QString ShaderFragmentCode(const NodeValueDatabase&) const override; - - virtual void Retranslate() override; - -protected: - -private: - NodeInput* matrix_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/math/math/math.cpp b/app/node/math/math/math.cpp index c90764eed..93e802481 100644 --- a/app/node/math/math/math.cpp +++ b/app/node/math/math/math.cpp @@ -131,9 +131,9 @@ QString MathNode::ShaderFragmentCode(const NodeValueDatabase &input) const // Override the operation for this operation since we multiply texture COORDS by the matrix rather than NodeParam* tex_in = (type_a == NodeParam::kTexture) ? param_a_in_ : param_b_in_; - NodeParam* mat_in = (type_a == NodeParam::kTexture) ? param_b_in_ : param_a_in_; - operation = QStringLiteral("texture(%1, (vec4(ove_texcoord, 0.0, 1.0) * %2).xy)").arg(tex_in->id(), mat_in->id()); + // No-op frag shader (can we return QString() instead?) + operation = QStringLiteral("texture(%1, ove_texcoord)").arg(tex_in->id()); } else { switch (GetOperation()) { @@ -176,6 +176,25 @@ QString MathNode::ShaderFragmentCode(const NodeValueDatabase &input) const operation); } +QString MathNode::ShaderVertexCode(const NodeValueDatabase &input) const +{ + PairingCalculator calc(input[param_a_in_], input[param_b_in_]); + + if (calc.GetMostLikelyPairing() == kPairTextureMatrix && GetOperation() == kOpMultiply) { + + NodeParam::DataType type_a = calc.GetMostLikelyValueA().type(); + + // Override the operation for this operation since we multiply texture COORDS by the matrix rather than + NodeParam* tex_in = (type_a == NodeParam::kTexture) ? param_a_in_ : param_b_in_; + NodeParam* mat_in = (type_a == NodeParam::kTexture) ? param_b_in_ : param_a_in_; + + return ReadFileAsString(":/shaders/matrix.vert").arg(mat_in->id(), tex_in->id()); + + } + + return QString(); +} + NodeValue MathNode::InputValueFromTable(NodeInput *input, NodeValueDatabase &db, bool take) const { if (input == param_a_in_ || input == param_b_in_) { @@ -382,6 +401,11 @@ MathNode::Operation MathNode::GetOperation() const return static_cast(method_in_->get_standard_value().toInt()); } +void MathNode::SetOperation(MathNode::Operation o) +{ + method_in_->set_standard_value(o); +} + QString MathNode::GetShaderUniformType(const NodeParam::DataType &type) { switch (type) { @@ -405,6 +429,81 @@ QString MathNode::GetShaderVariableCall(const QString &input_id, const NodeParam return input_id; } +QVector4D MathNode::RetrieveVector(const NodeValue &val) +{ + // QVariant doesn't know that QVector*D can convert themselves so we do it here + switch (val.type()) { + case NodeParam::kVec2: + return val.data().value(); + case NodeParam::kVec3: + return val.data().value(); + case NodeParam::kVec4: + default: + return val.data().value(); + } +} + +void MathNode::PushVector(NodeValueTable *output, NodeParam::DataType type, const QVector4D &vec) +{ + switch (type) { + case NodeParam::kVec2: + output->Push(type, QVector2D(vec)); + break; + case NodeParam::kVec3: + output->Push(type, QVector3D(vec)); + break; + case NodeParam::kVec4: + output->Push(type, vec); + break; + default: + break; + } +} + +float MathNode::RetrieveNumber(const NodeValue &val) +{ + if (val.type() == NodeParam::kRational) { + return val.data().value().toDouble(); + } else { + return val.data().toFloat(); + } +} + +MathNode::PairingCalculator::PairingCalculator(const NodeValueTable &table_a, const NodeValueTable &table_b) +{ + QVector pair_likelihood_a = GetPairLikelihood(table_a); + QVector pair_likelihood_b = GetPairLikelihood(table_b); + + int weight_a = qMax(0, table_b.Count() - table_a.Count()); + int weight_b = qMax(0, table_a.Count() - table_b.Count()); + + QVector likelihoods(kPairCount); + + for (int i=0;i -1) { + if (most_likely_pairing_ == kPairNone + || likelihoods.at(i) > likelihoods.at(most_likely_pairing_)) { + most_likely_pairing_ = static_cast(i); + } + } + } + + if (most_likely_pairing_ != kPairNone) { + most_likely_value_a_ = table_a.At(pair_likelihood_a.at(most_likely_pairing_)); + most_likely_value_b_ = table_b.At(pair_likelihood_b.at(most_likely_pairing_)); + } +} + QVector MathNode::PairingCalculator::GetPairLikelihood(const NodeValueTable &table) { // FIXME: When we introduce a manual override, placing it here would be the least problematic @@ -448,88 +547,6 @@ QVector MathNode::PairingCalculator::GetPairLikelihood(const NodeValueTable return likelihood; } -MathNode::Pairing MathNode::PairingCalculator::GetMostLikelyPairingInternal(const QVector &a, - const QVector &b, - const int& weight_a, - const int& weight_b) -{ - QVector likelihoods(kPairCount); - - for (int i=0;i -1) { - if (pairing == kPairNone - || likelihoods.at(i) > likelihoods.at(pairing)) { - pairing = static_cast(i); - } - } - } - - return pairing; -} - -QVector4D MathNode::RetrieveVector(const NodeValue &val) -{ - // QVariant doesn't know that QVector*D can convert themselves so we do it here - switch (val.type()) { - case NodeParam::kVec2: - return val.data().value(); - case NodeParam::kVec3: - return val.data().value(); - case NodeParam::kVec4: - default: - return val.data().value(); - } -} - -void MathNode::PushVector(NodeValueTable *output, NodeParam::DataType type, const QVector4D &vec) -{ - switch (type) { - case NodeParam::kVec2: - output->Push(type, QVector2D(vec)); - break; - case NodeParam::kVec3: - output->Push(type, QVector3D(vec)); - break; - case NodeParam::kVec4: - output->Push(type, vec); - break; - default: - break; - } -} - -float MathNode::RetrieveNumber(const NodeValue &val) -{ - if (val.type() == NodeParam::kRational) { - return val.data().value().toDouble(); - } else { - return val.data().toFloat(); - } -} - -MathNode::PairingCalculator::PairingCalculator(const NodeValueTable &table_a, const NodeValueTable &table_b) : - table_a_(table_a), - table_b_(table_b) -{ - pair_likelihood_a_ = GetPairLikelihood(table_a_); - pair_likelihood_b_ = GetPairLikelihood(table_b_); - - most_likely_pairing_ = GetMostLikelyPairingInternal(pair_likelihood_a_, - pair_likelihood_b_, - qMax(0, table_b_.Count() - table_a_.Count()), - qMax(0, table_a_.Count() - table_b_.Count())); -} - bool MathNode::PairingCalculator::FoundMostLikelyPairing() const { return (most_likely_pairing_ > kPairNone && most_likely_pairing_ < kPairCount); @@ -542,17 +559,12 @@ MathNode::Pairing MathNode::PairingCalculator::GetMostLikelyPairing() const const NodeValue &MathNode::PairingCalculator::GetMostLikelyValueA() const { - return GetMostLikelyValue(table_a_, pair_likelihood_a_); + return most_likely_value_a_; } const NodeValue &MathNode::PairingCalculator::GetMostLikelyValueB() const { - return GetMostLikelyValue(table_b_, pair_likelihood_b_); -} - -const NodeValue& MathNode::PairingCalculator::GetMostLikelyValue(const NodeValueTable &table, const QVector &likelihood) const -{ - return table.At(likelihood.at(most_likely_pairing_)); + return most_likely_value_b_; } template diff --git a/app/node/math/math/math.h b/app/node/math/math/math.h index 306dfc487..1909c4bd5 100644 --- a/app/node/math/math/math.h +++ b/app/node/math/math/math.h @@ -42,6 +42,7 @@ public: virtual Capabilities GetCapabilities(const NodeValueDatabase&) const override; virtual QString ShaderID(const NodeValueDatabase&) const override; virtual QString ShaderFragmentCode(const NodeValueDatabase&) const override; + virtual QString ShaderVertexCode(const NodeValueDatabase&input) const override; virtual NodeValue InputValueFromTable(NodeInput* input, NodeValueDatabase &db, bool take) const override; @@ -53,7 +54,6 @@ public: NodeInput* param_a_in() const; NodeInput* param_b_in() const; -private: enum Operation { kOpAdd, kOpSubtract, @@ -63,7 +63,9 @@ private: }; Operation GetOperation() const; + void SetOperation(Operation o); +private: enum Pairing { kPairNone = -1, @@ -96,21 +98,13 @@ private: const NodeValue& GetMostLikelyValueB() const; private: - static Pairing GetMostLikelyPairingInternal(const QVector &a, const QVector &b, const int &weight_a, const int &weight_b); - static QVector GetPairLikelihood(const NodeValueTable& table); - const NodeValue &GetMostLikelyValue(const NodeValueTable& table, const QVector& likelihood) const; - Pairing most_likely_pairing_; - const NodeValueTable& table_a_; + NodeValue most_likely_value_a_; - const NodeValueTable& table_b_; - - QVector pair_likelihood_a_; - - QVector pair_likelihood_b_; + NodeValue most_likely_value_b_; }; 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..3adc7e6b7 100644 --- a/app/node/output/track/track.cpp +++ b/app/node/output/track/track.cpp @@ -38,9 +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::SizeChanged, this, &TrackOutput::BlockListSizeChanged); + connect(block_input_, &NodeInputArray::SubParamEdgeAdded, this, &TrackOutput::BlockConnected); + connect(block_input_, &NodeInputArray::SubParamEdgeRemoved, this, &TrackOutput::BlockDisconnected); muted_input_ = new NodeInput("muted_in", NodeParam::kBoolean); muted_input_->set_is_keyframable(false); @@ -128,6 +127,8 @@ const int &TrackOutput::Index() void TrackOutput::SetIndex(const int &index) { index_ = index; + + emit IndexChanged(index); } Block *TrackOutput::BlockContainingTime(const rational &time) const @@ -231,15 +232,15 @@ QList TrackOutput::BlocksAtTimeRange(const TimeRange &range) const return list; } -const QVector &TrackOutput::Blocks() const +const QList &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); } } @@ -263,35 +264,42 @@ void TrackOutput::InsertBlockAfter(Block *block, Block *before) void TrackOutput::PrependBlock(Block *block) { - InsertBlockAtIndex(block, 0); + BlockInvalidateCache(); + + block_input_->Prepend(); + NodeParam::ConnectEdge(block->output(), block_input_->First()); + + UnblockInvalidateCache(); + + // Everything has shifted at this point + InvalidateCache(TimeRange(0, track_length()), block_input_, block_input_); } void TrackOutput::InsertBlockAtIndex(Block *block, int index) { BlockInvalidateCache(); - block_input_->InsertAt(index); + int insert_index = GetInputIndexFromCacheIndex(index); + block_input_->InsertAt(insert_index); NodeParam::ConnectEdge(block->output(), - block_input_->At(index)); + block_input_->At(insert_index)); UnblockInvalidateCache(); - InvalidateCache(block->in(), track_length()); + InvalidateCache(TimeRange(block->in(), track_length()), block_input_, block_input_); } void TrackOutput::AppendBlock(Block *block) { BlockInvalidateCache(); - int last_index = block_input_->GetSize(); block_input_->Append(); - NodeParam::ConnectEdge(block->output(), - block_input_->At(last_index)); + NodeParam::ConnectEdge(block->output(), block_input_->Last()); 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() @@ -310,20 +318,18 @@ void TrackOutput::RippleRemoveBlock(Block *block) rational remove_in = block->in(); - int index_of_block_to_remove = block_cache_.indexOf(block); - - block_input_->RemoveAt(index_of_block_to_remove); + block_input_->RemoveAt(GetInputIndexFromCacheIndex(block)); UnblockInvalidateCache(); - InvalidateCache(remove_in, track_length()); + InvalidateCache(TimeRange(remove_in, track_length()), block_input_, block_input_); } void TrackOutput::ReplaceBlock(Block *old, Block *replace) { BlockInvalidateCache(); - int index_of_old_block = block_cache_.indexOf(old); + int index_of_old_block = GetInputIndexFromCacheIndex(old); NodeParam::DisconnectEdge(old->output(), block_input_->At(index_of_old_block)); @@ -334,9 +340,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 +413,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 +426,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) @@ -425,107 +436,107 @@ void TrackOutput::SetLocked(bool e) void TrackOutput::UpdateInOutFrom(int index) { - Q_ASSERT(index >= 0); - Q_ASSERT(index < block_cache_.size()); - - rational new_track_length; - // Find block just before this one to find the last out point - for (int i=index-1;i>=0;i--) { + rational last_out = (index == 0) ? 0 : block_cache_.at(index - 1)->out(); + + // Iterate through all blocks updating their in/outs + for (int i=index; iout(); - break; - } - } + b->set_in(last_out); - for (int i=index;ilength(); - if (b) { - // Set in - b->set_in(new_track_length); + b->set_out(last_out); - // Set out - new_track_length += b->length(); - b->set_out(new_track_length); - - emit b->Refreshed(); - } + emit b->Refreshed(); } // Update track length - if (new_track_length != track_length_) { + if (last_out != track_length_) { rational old_track_length = track_length_; - track_length_ = new_track_length; + track_length_ = last_out; emit TrackLengthChanged(); - InvalidateCache(qMin(old_track_length, new_track_length), - qMax(old_track_length, new_track_length)); + InvalidateCache(TimeRange(qMin(old_track_length, last_out), qMax(old_track_length, last_out)), + block_input_, + block_input_); } } -void TrackOutput::UpdatePreviousAndNextOfIndex(int index) +int TrackOutput::GetInputIndexFromCacheIndex(int cache_index) { - Block* ref = block_cache_.at(index); + return GetInputIndexFromCacheIndex(block_cache_.at(cache_index)); +} - Block* previous = nullptr; - Block* next = nullptr; - - // Find previous - for (int i=index-1;i>=0;i--) { - previous = block_cache_.at(i); - - if (previous) { - break; +int TrackOutput::GetInputIndexFromCacheIndex(Block *block) +{ + for (int i=0; iGetSize(); i++) { + if (block_input_->At(i)->get_connected_node() == block) { + return i; } } - // Find next - for (int i=index+1;iset_previous(previous); - ref->set_next(next); - - if (previous) - previous->set_next(ref); - - if (next) - next->set_previous(ref); - } else { - // Link previous and next together - if (previous) - previous->set_next(next); - - if (next) - next->set_previous(previous); - } + return -1; } void TrackOutput::BlockConnected(NodeEdgePtr edge) { - int block_index = block_input_->IndexOfSubParameter(edge->input()); - - Q_ASSERT(block_index >= 0); - + // Determine what node was just connected Node* connected_node = edge->output()->parentNode(); - Block* connected_block = connected_node->IsBlock() ? static_cast(connected_node) : nullptr; - block_cache_.replace(block_index, connected_block); - UpdatePreviousAndNextOfIndex(block_index); - UpdateInOutFrom(block_index); - if (connected_block) { - connect(connected_block, SIGNAL(LengthChanged(const rational&)), this, SLOT(BlockLengthChanged())); + // If this node is a block, we can do something with it + if (connected_node->IsBlock()) { + Block* connected_block = static_cast(connected_node); + + // See where this input falls in our internal "block cache" + Block* next = nullptr; + for (int i=block_input_->IndexOfSubParameter(edge->input())+1; iGetSize(); i++) { + Node* that_node = block_input_->At(i)->get_connected_node(); + + // If we find a block, this is the block that will follow the one just connected + if (that_node && that_node->IsBlock()) { + next = static_cast(that_node); + break; + } + } + + int real_block_index; + + // Either insert or append depending on if we found a "next" block + if (next) { + // Insert block before this next block + real_block_index = block_cache_.indexOf(next); + block_cache_.insert(real_block_index, connected_block); + + // Update values with next + next->set_previous(connected_block); + connected_block->set_next(next); + } else { + // No "next", this block must come at the end + real_block_index = block_cache_.size(); + block_cache_.append(connected_block); + + // Update next value + connected_block->set_next(nullptr); + } + + // For all blocks after the block we inserted (including it), update the "previous" and "next" + // fields as well as the in/out values + if (real_block_index == 0) { + connected_block->set_previous(nullptr); + } else { + Block* prev = block_cache_.at(real_block_index - 1); + + connected_block->set_previous(prev); + prev->set_next(connected_block); + } + + UpdateInOutFrom(real_block_index); + + // Make connections to this block + connect(connected_block, &Block::LengthChanged, this, &TrackOutput::BlockLengthChanged); emit BlockAdded(connected_block); } @@ -533,46 +544,41 @@ void TrackOutput::BlockConnected(NodeEdgePtr edge) void TrackOutput::BlockDisconnected(NodeEdgePtr edge) { - int block_index = block_input_->IndexOfSubParameter(edge->input()); - - Q_ASSERT(block_index >= 0); - - block_cache_.replace(block_index, nullptr); - UpdatePreviousAndNextOfIndex(block_index); - UpdateInOutFrom(block_index); - + // See what kind of node was just connected Node* connected_node = edge->output()->parentNode(); - Block* connected_block = connected_node->IsBlock() ? static_cast(connected_node) : nullptr; - if (connected_block) { - disconnect(connected_block, SIGNAL(LengthChanged(const rational&)), this, SLOT(BlockLengthChanged())); - // Update previous and next references + // If this was a block, we would have put it in our block cache in BlockConnected() + if (connected_node->IsBlock()) { + Block* connected_block = static_cast(connected_node); + + // Determine what index this block was in our cache and remove it + int index_of_block = block_cache_.indexOf(connected_block); + block_cache_.removeAt(index_of_block); + + // If there were blocks following this one, update their ins/outs + UpdateInOutFrom(index_of_block); + + // Join the previous and next blocks together + if (connected_block->previous()) { + connected_block->previous()->set_next(connected_block->next()); + } + + if (connected_block->next()) { + connected_block->next()->set_previous(connected_block->previous()); + } + + disconnect(connected_block, &Block::LengthChanged, this, &TrackOutput::BlockLengthChanged); + emit BlockRemoved(connected_block); } } -void TrackOutput::BlockListSizeChanged(int size) -{ - int old_size = block_cache_.size(); - - block_cache_.resize(size); - - // Fill new slots with nullptr - for (int i=old_size;i(sender()); - int index = block_cache_.indexOf(b); - - Q_ASSERT(index >= 0); - - UpdateInOutFrom(index); + UpdateInOutFrom(block_cache_.indexOf(b)); } void TrackOutput::MutedInputValueChanged() diff --git a/app/node/output/track/track.h b/app/node/output/track/track.h index 9ff9df009..89f58903a 100644 --- a/app/node/output/track/track.h +++ b/app/node/output/track/track.h @@ -82,9 +82,9 @@ public: Block* BlockAtTime(const rational& time) const; QList BlocksAtTimeRange(const TimeRange& range) const; - const QVector& Blocks() const; + const QList &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); @@ -183,14 +185,20 @@ signals: */ void MutedChanged(bool e); + /** + * @brief Signal emitted when the index has changed + */ + void IndexChanged(int i); + protected: private: void UpdateInOutFrom(int index); - void UpdatePreviousAndNextOfIndex(int index); + int GetInputIndexFromCacheIndex(int cache_index); + int GetInputIndexFromCacheIndex(Block* block); - QVector block_cache_; + QList block_cache_; NodeInputArray* block_input_; @@ -215,8 +223,6 @@ private slots: void BlockDisconnected(NodeEdgePtr edge); - void BlockListSizeChanged(int size); - void BlockLengthChanged(); void MutedInputValueChanged(); diff --git a/app/node/output/track/tracklist.cpp b/app/node/output/track/tracklist.cpp index 05f9a6e6f..743c1b50b 100644 --- a/app/node/output/track/tracklist.cpp +++ b/app/node/output/track/tracklist.cpp @@ -31,9 +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::SizeChanged, this, &TrackList::TrackListSizeChanged); + connect(track_input, &NodeInputArray::SubParamEdgeAdded, this, &TrackList::TrackConnected); + connect(track_input, &NodeInputArray::SubParamEdgeRemoved, this, &TrackList::TrackDisconnected); } const Timeline::TrackType &TrackList::type() const @@ -51,38 +50,22 @@ void TrackList::TrackRemovedBlock(Block *block) emit BlockRemoved(block); } -void TrackList::TrackListSizeChanged(int size) -{ - int old_size = track_cache_.size(); - - track_cache_.resize(size); - - // Fill new slots with nullptr - for (int i=old_size;i &TrackList::Tracks() const +const QVector &TrackList::GetTracks() const { return track_cache_; } -TrackOutput *TrackList::TrackAt(int index) const +TrackOutput *TrackList::GetTrackAt(int index) const { - if (index < 0 || index >= track_cache_.size()) { - return nullptr; - } - return track_cache_.at(index); } -const rational &TrackList::TrackLength() const +const rational &TrackList::GetTotalLength() const { return total_length_; } -int TrackList::TrackCount() const +int TrackList::GetTrackCount() const { return track_cache_.size(); } @@ -98,7 +81,7 @@ TrackOutput* TrackList::AddTrack() NodeParam::ConnectEdge(track->output(), track_input_->At(track_input_->GetSize() - 1)); - // FIXME: Test code only + // Auto-merge with previous track if (track_input_->GetSize() > 1) { TrackOutput* last_track = nullptr; @@ -120,8 +103,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; } @@ -142,7 +125,6 @@ TrackOutput* TrackList::AddTrack() } } } - // End test code return track; } @@ -173,14 +155,38 @@ void TrackList::TrackConnected(NodeEdgePtr edge) if (connected_node->IsTrack()) { TrackOutput* connected_track = static_cast(connected_node); - track_cache_.replace(track_index, connected_track); + { + // Find "real" index + TrackOutput* next = nullptr; + for (int i=track_index+1; iGetSize(); i++) { + Node* that_track = track_input_->At(i)->get_connected_node(); + + if (that_track && that_track->IsTrack()) { + next = static_cast(that_track); + break; + } + } + + int track_index; + + if (next) { + // Insert track before "next" + track_index = track_cache_.indexOf(next); + track_cache_.insert(track_index, connected_track); + } else { + // No "next", this track must come at the end + track_index = track_cache_.size(); + track_cache_.append(connected_track); + } + + connected_track->SetIndex(track_index); + } connect(connected_track, &TrackOutput::BlockAdded, this, &TrackList::TrackAddedBlock); connect(connected_track, &TrackOutput::BlockRemoved, this, &TrackList::TrackRemovedBlock); connect(connected_track, &TrackOutput::TrackLengthChanged, this, &TrackList::UpdateTotalLength); connect(connected_track, &TrackOutput::TrackHeightChanged, this, &TrackList::TrackHeightChangedSlot); - connected_track->SetIndex(track_index); connected_track->set_track_type(type_); emit TrackListChanged(); @@ -201,10 +207,17 @@ void TrackList::TrackDisconnected(NodeEdgePtr edge) Q_ASSERT(track_index >= 0); Node* connected_node = edge->output()->parentNode(); - TrackOutput* track = connected_node->IsTrack() ? static_cast(connected_node) : nullptr; - if (track) { - track_cache_.replace(track_index, nullptr); + if (connected_node->IsTrack()) { + TrackOutput* track = static_cast(connected_node); + + int index_of_track = track_cache_.indexOf(track); + track_cache_.removeAt(index_of_track); + + // Update indices for all subsequent tracks + for (int i=index_of_track; iSetIndex(i); + } // Traverse through Tracks uncaching and disconnecting them emit TrackRemoved(track); diff --git a/app/node/output/track/tracklist.h b/app/node/output/track/tracklist.h index 283394909..44c380cc9 100644 --- a/app/node/output/track/tracklist.h +++ b/app/node/output/track/tracklist.h @@ -38,17 +38,17 @@ public: const Timeline::TrackType& type() const; - const QVector& Tracks() const; + const QVector& GetTracks() const; - TrackOutput* TrackAt(int index) const; + TrackOutput* GetTrackAt(int index) const; TrackOutput *AddTrack(); void RemoveTrack(); - const rational& TrackLength() const; + const rational& GetTotalLength() const; - int TrackCount() const; + int GetTrackCount() const; NodeGraph* GetParentGraph() const; @@ -100,11 +100,6 @@ private slots: */ void TrackRemovedBlock(Block* block); - /** - * @brief Slot for when the count of tracks in the track input changes - */ - void TrackListSizeChanged(int size); - /** * @brief Slot for when any of the track's length changes so we can update the length of the tracklist */ diff --git a/app/node/output/viewer/viewer.cpp b/app/node/output/viewer/viewer.cpp index d8d472371..e06c5443d 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,26 +162,13 @@ 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(); foreach (TrackList* list, track_lists_) { - foreach (TrackOutput* track, list->Tracks()) { - if (track) { - track_cache_.append(track); - } + foreach (TrackOutput* track, list->GetTracks()) { + track_cache_.append(track); } } } @@ -206,7 +191,7 @@ void ViewerOutput::UpdateLength(const rational &length) rational new_length = 0; foreach (TrackList* list, track_lists_) { - new_length = qMax(new_length, list->TrackLength()); + new_length = qMax(new_length, list->GetTotalLength()); } if (new_length != timeline_length_) { 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/panel/curve/curve.cpp b/app/panel/curve/curve.cpp index d67972a7e..233b637f5 100644 --- a/app/panel/curve/curve.cpp +++ b/app/panel/curve/curve.cpp @@ -32,9 +32,16 @@ CurvePanel::CurvePanel(QWidget *parent) : Retranslate(); } +NodeInput *CurvePanel::GetInput() const +{ + return static_cast(GetTimeBasedWidget())->GetInput(); +} + void CurvePanel::SetInput(NodeInput *input) { static_cast(GetTimeBasedWidget())->SetInput(input); + + Retranslate(); } void CurvePanel::SetTimeTarget(Node *target) @@ -59,6 +66,13 @@ void CurvePanel::Retranslate() TimeBasedPanel::Retranslate(); SetTitle(tr("Curve Editor")); + + NodeInput* connected_input = static_cast(GetTimeBasedWidget())->GetInput(); + if (connected_input) { + SetSubtitle(connected_input->name()); + } else { + SetSubtitle(QString()); + } } OLIVE_NAMESPACE_EXIT diff --git a/app/panel/curve/curve.h b/app/panel/curve/curve.h index 36a3ba564..761b00a0f 100644 --- a/app/panel/curve/curve.h +++ b/app/panel/curve/curve.h @@ -32,6 +32,8 @@ class CurvePanel : public TimeBasedPanel public: CurvePanel(QWidget* parent); + NodeInput* GetInput() const; + public slots: void SetInput(NodeInput* input); diff --git a/app/panel/panelmanager.cpp b/app/panel/panelmanager.cpp index a76d65093..f85e6824f 100644 --- a/app/panel/panelmanager.cpp +++ b/app/panel/panelmanager.cpp @@ -144,7 +144,10 @@ void PanelManager::FocusChanged(QWidget *old, QWidget *now) void PanelManager::SetPanelsLocked(bool locked) { foreach (PanelWidget* panel, focus_history_) { - panel->SetMovementLocked(locked); + // Only affect panels actually in our layout + if (!panel->isFloating()) { + panel->SetMovementLocked(locked); + } } locked_ = locked; diff --git a/app/panel/panelmanager.h b/app/panel/panelmanager.h index 7acba73e3..9e773ec92 100644 --- a/app/panel/panelmanager.h +++ b/app/panel/panelmanager.h @@ -177,6 +177,10 @@ T *PanelManager::CreatePanel(QWidget *parent) panel->SetMovementLocked(locked_); + // Sane default for panel geometry + panel->resize(parent->size() / 3); + panel->move(panel->mapFromGlobal(parent->mapToGlobal(parent->pos()))); + // Connect destroy signal so we can remove it from focus history connect(panel, &PanelWidget::destroyed, this, &PanelManager::PanelDestroyed); diff --git a/app/panel/param/param.cpp b/app/panel/param/param.cpp index e714f8d05..bdbb8e8e0 100644 --- a/app/panel/param/param.cpp +++ b/app/panel/param/param.cpp @@ -20,15 +20,19 @@ #include "param.h" +#include "window/mainwindow/mainwindow.h" + OLIVE_NAMESPACE_ENTER ParamPanel::ParamPanel(QWidget* parent) : TimeBasedPanel(QStringLiteral("ParamPanel"), parent) { NodeParamView* view = new NodeParamView(); - connect(view, &NodeParamView::SelectedInputChanged, this, &ParamPanel::SelectedInputChanged); + connect(view, &NodeParamView::InputDoubleClicked, this, &ParamPanel::CreateCurvePanel); connect(view, &NodeParamView::TimeTargetChanged, this, &ParamPanel::TimeTargetChanged); connect(view, &NodeParamView::RequestSelectNode, this, &ParamPanel::RequestSelectNode); + connect(view, &NodeParamView::OpenedNode, this, &ParamPanel::OpeningNode); + connect(view, &NodeParamView::ClosedNode, this, &ParamPanel::ClosingNode); SetTimeBasedWidget(view); Retranslate(); @@ -41,6 +45,20 @@ void ParamPanel::SetNodes(QList nodes) Retranslate(); } +void ParamPanel::SetTimestamp(const int64_t ×tamp) +{ + TimeBasedPanel::SetTimestamp(timestamp); + + // Ensure all CurvePanels are updated with this time too + QHash::const_iterator i; + + for (i=open_curve_panels_.begin(); i!=open_curve_panels_.end(); i++) { + if (i.value() && i.value() != sender()) { + i.value()->SetTimestamp(timestamp); + } + } +} + void ParamPanel::Retranslate() { SetTitle(tr("Parameter Editor")); @@ -56,4 +74,67 @@ void ParamPanel::Retranslate() } } +void ParamPanel::CreateCurvePanel(NodeInput *input) +{ + if (!input->is_keyframable()) { + return; + } + + CurvePanel* panel = open_curve_panels_.value(input); + + if (panel) { + panel->raise(); + return; + } + + NodeParamView* view = static_cast(GetTimeBasedWidget()); + + panel = Core::instance()->main_window()->AppendCurvePanel(); + + panel->SetInput(input); + panel->SetTimebase(view->timebase()); + panel->SetTimestamp(view->GetTimestamp()); + + connect(view, &NodeParamView::TimebaseChanged, panel, &CurvePanel::SetTimebase); + connect(view, &NodeParamView::TimeChanged, panel, &CurvePanel::SetTimestamp); + connect(panel, &CurvePanel::TimeChanged, view, &NodeParamView::SetTimestamp); + connect(panel, &CurvePanel::TimeChanged, view, &NodeParamView::TimeChanged); + connect(panel, &CurvePanel::CloseRequested, this, &ParamPanel::ClosingCurvePanel); + + open_curve_panels_.insert(input, panel); +} + +void ParamPanel::OpeningNode(Node *n) +{ + QList inputs = n->GetInputsIncludingArrays(); + + foreach (NodeInput* i, inputs) { + if (open_curve_panels_.contains(i)) { + // We had a CurvePanel open for this input that was closed in ClosingNode(), re-open it + CreateCurvePanel(i); + } + } +} + +void ParamPanel::ClosingNode(Node *n) +{ + QList inputs = n->GetInputsIncludingArrays(); + + foreach (NodeInput* i, inputs) { + CurvePanel* panel = open_curve_panels_.value(i); + + // Close the panel (this also destroys it), but keep a reference in the hash + if (panel) { + panel->close(); + open_curve_panels_.insert(i, nullptr); + } + } +} + +void ParamPanel::ClosingCurvePanel() +{ + CurvePanel* panel = static_cast(sender()); + open_curve_panels_.remove(panel->GetInput()); +} + OLIVE_NAMESPACE_EXIT diff --git a/app/panel/param/param.h b/app/panel/param/param.h index 7d78aab64..0ad5523c4 100644 --- a/app/panel/param/param.h +++ b/app/panel/param/param.h @@ -21,6 +21,7 @@ #ifndef PARAM_H #define PARAM_H +#include "panel/curve/curve.h" #include "panel/timebased/timebased.h" #include "widget/nodeparamview/nodeparamview.h" @@ -35,9 +36,9 @@ public: public slots: void SetNodes(QList nodes); -signals: - void SelectedInputChanged(NodeInput* input); + virtual void SetTimestamp(const int64_t& timestamp) override; +signals: void TimeTargetChanged(Node* node); void RequestSelectNode(const QList& target); @@ -45,6 +46,18 @@ signals: protected: virtual void Retranslate() override; +private slots: + void CreateCurvePanel(NodeInput* input); + + void OpeningNode(Node* n); + + void ClosingNode(Node* n); + + void ClosingCurvePanel(); + +private: + QHash open_curve_panels_; + }; OLIVE_NAMESPACE_EXIT diff --git a/app/panel/scope/scope.cpp b/app/panel/scope/scope.cpp index 222fcec64..5daa7e9e0 100644 --- a/app/panel/scope/scope.cpp +++ b/app/panel/scope/scope.cpp @@ -84,19 +84,10 @@ QString ScopePanel::TypeToName(ScopePanel::Type t) return QString(); } -void ScopePanel::SetDisplayReferredTexture(OpenGLTexture *texture) -{ - Q_UNUSED(texture) -} - void ScopePanel::SetReferenceBuffer(Frame *frame) { histogram_->SetBuffer(frame); -} - -void ScopePanel::SetReferenceTexture(OpenGLTexture *texture) -{ - waveform_view_->SetTexture(texture); + waveform_view_->SetBuffer(frame); } void ScopePanel::SetColorManager(ColorManager *manager) diff --git a/app/panel/scope/scope.h b/app/panel/scope/scope.h index f44e37aa6..3e771af69 100644 --- a/app/panel/scope/scope.h +++ b/app/panel/scope/scope.h @@ -50,12 +50,8 @@ public: static QString TypeToName(Type t); public slots: - void SetDisplayReferredTexture(OpenGLTexture* texture); - void SetReferenceBuffer(Frame* frame); - void SetReferenceTexture(OpenGLTexture* texture); - void SetColorManager(ColorManager* manager); protected: diff --git a/app/panel/timebased/timebased.cpp b/app/panel/timebased/timebased.cpp index 4da1e95c0..37a0959fa 100644 --- a/app/panel/timebased/timebased.cpp +++ b/app/panel/timebased/timebased.cpp @@ -68,7 +68,7 @@ void TimeBasedPanel::SetTimebase(const rational &timebase) widget_->SetTimebase(timebase); } -void TimeBasedPanel::SetTime(const int64_t ×tamp) +void TimeBasedPanel::SetTimestamp(const int64_t ×tamp) { widget_->SetTimestamp(timestamp); } diff --git a/app/panel/timebased/timebased.h b/app/panel/timebased/timebased.h index 9c68567b3..90fb83723 100644 --- a/app/panel/timebased/timebased.h +++ b/app/panel/timebased/timebased.h @@ -89,7 +89,7 @@ public: public slots: void SetTimebase(const rational& timebase); - void SetTime(const int64_t& timestamp); + virtual void SetTimestamp(const int64_t& timestamp); signals: void TimeChanged(const int64_t& time); diff --git a/app/panel/viewer/viewerbase.cpp b/app/panel/viewer/viewerbase.cpp index da1726540..3cab17f98 100644 --- a/app/panel/viewer/viewerbase.cpp +++ b/app/panel/viewer/viewerbase.cpp @@ -25,8 +25,7 @@ OLIVE_NAMESPACE_ENTER ViewerPanelBase::ViewerPanelBase(const QString& object_name, QWidget *parent) : - TimeBasedPanel(object_name, parent), - scope_panel_count_(0) + TimeBasedPanel(object_name, parent) { } @@ -98,36 +97,13 @@ void ViewerPanelBase::CreateScopePanel(ScopePanel::Type type) p->SetType(type); - // We treat our scope panels as kind of children, and destroy them if we're ever destroyed - connect(this, &ViewerPanelBase::destroyed, p, &ScopePanel::deleteLater); - - // If the scope closes, reduce the count (we do this because if no scopes are open, we can optimize the viewer slightly) - connect(p, &ScopePanel::CloseRequested, this, &ViewerPanelBase::ScopePanelClosed); - // Connect viewer widget texture drawing to scope panel - connect(vw, &ViewerWidget::DrewManagedTexture, p, &ScopePanel::SetDisplayReferredTexture); connect(vw, &ViewerWidget::LoadedBuffer, p, &ScopePanel::SetReferenceBuffer); - connect(vw, &ViewerWidget::LoadedTexture, p, &ScopePanel::SetReferenceTexture); connect(vw, &ViewerWidget::ColorManagerChanged, p, &ScopePanel::SetColorManager); p->SetColorManager(vw->color_manager()); - if (!scope_panel_count_) { - vw->SetEmitDrewManagedTextureEnabled(true); - } - - scope_panel_count_++; - vw->ForceUpdate(); } -void ViewerPanelBase::ScopePanelClosed() -{ - scope_panel_count_--; - - if (!scope_panel_count_) { - static_cast(GetTimeBasedWidget())->SetEmitDrewManagedTextureEnabled(false); - } -} - OLIVE_NAMESPACE_EXIT diff --git a/app/panel/viewer/viewerbase.h b/app/panel/viewer/viewerbase.h index 7d551141d..a0acd2d29 100644 --- a/app/panel/viewer/viewerbase.h +++ b/app/panel/viewer/viewerbase.h @@ -59,12 +59,6 @@ public: protected: void CreateScopePanel(ScopePanel::Type type); -private: - int scope_panel_count_; - -private slots: - void ScopePanelClosed(); - }; OLIVE_NAMESPACE_EXIT 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/audiorenderworker.cpp b/app/render/backend/audiorenderworker.cpp index b60417c37..c8d53c8b1 100644 --- a/app/render/backend/audiorenderworker.cpp +++ b/app/render/backend/audiorenderworker.cpp @@ -100,7 +100,7 @@ NodeValueTable AudioRenderWorker::RenderBlock(const TrackOutput *track, const Ti { // Save waveform to file - Block* src_block = static_cast(copy_map_->value(b)); + Block* src_block = static_cast(copy_map_->key(b)); QDir local_appdata_dir(Config::Current()["DiskCachePath"].toString()); QDir waveform_loc = local_appdata_dir.filePath(QStringLiteral("waveform")); waveform_loc.mkpath("."); 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/opengl/openglproxy.cpp b/app/render/backend/opengl/openglproxy.cpp index 663096514..194ac6dc4 100644 --- a/app/render/backend/opengl/openglproxy.cpp +++ b/app/render/backend/opengl/openglproxy.cpp @@ -132,7 +132,7 @@ void OpenGLProxy::FrameToValue(FramePtr frame, StreamPtr stream, NodeValueTable* VideoRenderingParams footage_params(frame->width(), frame->height(), frame->format()); - footage_tex_ref = texture_cache_.Get(ctx_, footage_params, frame->data(), frame->linesize_pixels()); + footage_tex_ref = texture_cache_.Get(ctx_, footage_params, frame); if (ocio_method == ColorManager::kOCIOFast) { if (!color_processor->IsEnabled()) { @@ -212,8 +212,8 @@ void OpenGLProxy::RunNodeAccelerated(const Node *node, const TimeRange &range, N if (!shader) { // Since we have shader code, compile it now - QString frag_code = node->ShaderFragmentCode(input_params); QString vert_code = node->ShaderVertexCode(input_params); + QString frag_code = node->ShaderFragmentCode(input_params); if (frag_code.isEmpty()) { frag_code = OpenGLShader::CodeDefaultFragment(); @@ -258,7 +258,7 @@ void OpenGLProxy::RunNodeAccelerated(const Node *node, const TimeRange &range, N NodeInput* input = static_cast(param); // Get value from database at this input - NodeValue meta_value = node->InputValueFromTable(input, input_params, true); + NodeValue meta_value = node->InputValueFromTable(input, input_params, false); const QVariant& value = meta_value.data(); NodeParam::DataType data_type; diff --git a/app/render/backend/opengl/openglrenderfunctions.cpp b/app/render/backend/opengl/openglrenderfunctions.cpp index 2e6458b10..2a3b538a9 100644 --- a/app/render/backend/opengl/openglrenderfunctions.cpp +++ b/app/render/backend/opengl/openglrenderfunctions.cpp @@ -37,23 +37,23 @@ const GLfloat blit_vertices[] = { }; const GLfloat blit_texcoords[] = { - 0.0, 0.0, - 1.0, 0.0, - 1.0, 1.0, + 0.0f, 0.0f, + 1.0f, 0.0f, + 1.0f, 1.0f, - 0.0, 0.0, - 0.0, 1.0, - 1.0, 1.0 + 0.0f, 0.0f, + 0.0f, 1.0f, + 1.0f, 1.0f }; const GLfloat flipped_blit_texcoords[] = { - 0.0, 1.0, - 1.0, 1.0, - 1.0, 0.0, + 0.0f, 1.0f, + 1.0f, 1.0f, + 1.0f, 0.0f, - 0.0, 1.0, - 0.0, 0.0, - 1.0, 0.0 + 0.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 0.0f }; /** @@ -65,7 +65,8 @@ const GLfloat flipped_blit_texcoords[] = { * * Currently active QOpenGLFunctions object (use context()->functions() if unsure). */ -void OpenGLRenderFunctions::PrepareToDraw(QOpenGLFunctions* f) { +void OpenGLRenderFunctions::PrepareToDraw(QOpenGLFunctions* f) +{ f->glGenerateMipmap(GL_TEXTURE_2D); f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); @@ -141,7 +142,6 @@ void OpenGLRenderFunctions::Blit(OpenGLShaderPtr pipeline, bool flipped, QMatrix void OpenGLRenderFunctions::Blit(OpenGLShader *pipeline, bool flipped, QMatrix4x4 matrix) { - // FIXME: is currentContext() reliable here? QOpenGLFunctions* func = QOpenGLContext::currentContext()->functions(); PrepareToDraw(func); @@ -153,13 +153,13 @@ void OpenGLRenderFunctions::Blit(OpenGLShader *pipeline, bool flipped, QMatrix4x QOpenGLBuffer m_vbo; m_vbo.create(); m_vbo.bind(); - m_vbo.allocate(blit_vertices, 18 * static_cast(sizeof(GLfloat))); + m_vbo.allocate(blit_vertices, 18 * sizeof(GLfloat)); m_vbo.release(); QOpenGLBuffer m_vbo2; m_vbo2.create(); m_vbo2.bind(); - m_vbo2.allocate(flipped ? flipped_blit_texcoords : blit_texcoords, 12 * static_cast(sizeof(GLfloat))); + m_vbo2.allocate(flipped ? flipped_blit_texcoords : blit_texcoords, 12 * sizeof(GLfloat)); m_vbo2.release(); pipeline->bind(); @@ -167,13 +167,13 @@ void OpenGLRenderFunctions::Blit(OpenGLShader *pipeline, bool flipped, QMatrix4x pipeline->setUniformValue("ove_mvpmat", matrix); pipeline->setUniformValue("ove_maintex", 0); - GLuint vertex_location = static_cast(pipeline->attributeLocation("a_position")); + int vertex_location = pipeline->attributeLocation("a_position"); m_vbo.bind(); func->glEnableVertexAttribArray(vertex_location); func->glVertexAttribPointer(vertex_location, 3, GL_FLOAT, GL_FALSE, 0, nullptr); m_vbo.release(); - GLuint tex_location = static_cast(pipeline->attributeLocation("a_texcoord")); + int tex_location = pipeline->attributeLocation("a_texcoord"); m_vbo2.bind(); func->glEnableVertexAttribArray(tex_location); func->glVertexAttribPointer(tex_location, 2, GL_FLOAT, GL_FALSE, 0, nullptr); diff --git a/app/render/backend/opengl/openglshader.cpp b/app/render/backend/opengl/openglshader.cpp index b5aa8dc8b..21ff57752 100644 --- a/app/render/backend/opengl/openglshader.cpp +++ b/app/render/backend/opengl/openglshader.cpp @@ -61,8 +61,8 @@ OpenGLShaderPtr OpenGLShader::CreateOCIO(QOpenGLContext* ctx, shaderDesc.setLut3DEdgeLen(OCIO_LUT3D_EDGE_SIZE); // Compute LUT - GLfloat* ocio_lut_data = new GLfloat[OCIO_NUM_3D_ENTRIES]; - processor->getGpuLut3D(ocio_lut_data, shaderDesc); + std::vector ocio_lut_data(OCIO_NUM_3D_ENTRIES); + processor->getGpuLut3D(&ocio_lut_data[0], shaderDesc); // Create LUT texture xf->glGenTextures(1, &lut_texture); @@ -81,10 +81,7 @@ OpenGLShaderPtr OpenGLShader::CreateOCIO(QOpenGLContext* ctx, // Allocate storage for texture xf->glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB16F, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, - 0, GL_RGB, GL_FLOAT, ocio_lut_data); - - // Delete local copy - delete [] ocio_lut_data; + 0, GL_RGB, GL_FLOAT, &ocio_lut_data[0]); // Create OCIO shader code QString shader_text; diff --git a/app/render/backend/opengl/opengltexture.cpp b/app/render/backend/opengl/opengltexture.cpp index c0c1621ea..d5013c10f 100644 --- a/app/render/backend/opengl/opengltexture.cpp +++ b/app/render/backend/opengl/opengltexture.cpp @@ -74,6 +74,11 @@ void OpenGLTexture::Create(QOpenGLContext *ctx, int width, int height, const Pix } void OpenGLTexture::Create(QOpenGLContext *ctx, FramePtr frame) +{ + Create(ctx, frame.get()); +} + +void OpenGLTexture::Create(QOpenGLContext *ctx, Frame *frame) { Create(ctx, frame->width(), frame->height(), frame->format(), frame->data(), frame->linesize_pixels()); } @@ -120,6 +125,16 @@ const GLuint &OpenGLTexture::texture() const return texture_; } +void OpenGLTexture::Upload(FramePtr frame) +{ + Upload(frame.get()); +} + +void OpenGLTexture::Upload(Frame *frame) +{ + Upload(frame->data(), frame->linesize_pixels()); +} + void OpenGLTexture::Upload(const void *data, int linesize) { if (!IsCreated()) { diff --git a/app/render/backend/opengl/opengltexture.h b/app/render/backend/opengl/opengltexture.h index 941e2aae8..70fa653f7 100644 --- a/app/render/backend/opengl/opengltexture.h +++ b/app/render/backend/opengl/opengltexture.h @@ -44,6 +44,7 @@ public: void Create(QOpenGLContext* ctx, int width, int height, const PixelFormat::Format &format, const void *data, int linesize); void Create(QOpenGLContext* ctx, int width, int height, const PixelFormat::Format &format); void Create(QOpenGLContext* ctx, FramePtr frame); + void Create(QOpenGLContext* ctx, Frame* frame); bool IsCreated() const; @@ -59,6 +60,8 @@ public: const GLuint& texture() const; + void Upload(FramePtr frame); + void Upload(Frame* frame); void Upload(const void *data, int linesize); public slots: diff --git a/app/render/backend/opengl/opengltexturecache.cpp b/app/render/backend/opengl/opengltexturecache.cpp index 95e0638ee..a54994467 100644 --- a/app/render/backend/opengl/opengltexturecache.cpp +++ b/app/render/backend/opengl/opengltexturecache.cpp @@ -29,6 +29,16 @@ OpenGLTextureCache::~OpenGLTextureCache() } } +OpenGLTextureCache::ReferencePtr OpenGLTextureCache::Get(QOpenGLContext *ctx, const VideoRenderingParams ¶ms, FramePtr frame) +{ + return Get(ctx, params, frame.get()); +} + +OpenGLTextureCache::ReferencePtr OpenGLTextureCache::Get(QOpenGLContext *ctx, const VideoRenderingParams ¶ms, Frame *frame) +{ + return Get(ctx, params, frame->data(), frame->linesize_pixels()); +} + OpenGLTextureCache::ReferencePtr OpenGLTextureCache::Get(QOpenGLContext* ctx, const VideoRenderingParams ¶ms, const void *data, int linesize) { OpenGLTexturePtr texture = nullptr; diff --git a/app/render/backend/opengl/opengltexturecache.h b/app/render/backend/opengl/opengltexturecache.h index 7be07bfbb..2baa87a67 100644 --- a/app/render/backend/opengl/opengltexturecache.h +++ b/app/render/backend/opengl/opengltexturecache.h @@ -57,6 +57,8 @@ public: DISABLE_COPY_MOVE(OpenGLTextureCache) + ReferencePtr Get(QOpenGLContext *ctx, const VideoRenderingParams& params, FramePtr frame); + ReferencePtr Get(QOpenGLContext *ctx, const VideoRenderingParams& params, Frame* frame); ReferencePtr Get(QOpenGLContext *ctx, const VideoRenderingParams& params, const void *data, int linesize); ReferencePtr Get(QOpenGLContext *ctx, const VideoRenderingParams& params); diff --git a/app/render/backend/renderbackend.cpp b/app/render/backend/renderbackend.cpp index 0cfb0eae4..38d07ad35 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/shaders/videoinput.vert b/app/shaders/matrix.vert similarity index 79% rename from app/shaders/videoinput.vert rename to app/shaders/matrix.vert index 59efb9515..dcfc4741f 100644 --- a/app/shaders/videoinput.vert +++ b/app/shaders/matrix.vert @@ -1,8 +1,8 @@ #version 150 -uniform mat4 matrix_in; +uniform mat4 %1; -uniform vec2 footage_in_resolution; +uniform vec2 %2_resolution; uniform vec2 ove_resolution; in vec4 a_position; @@ -27,10 +27,10 @@ void main() { transform *= scale_mat4(vec3(1.0 / ove_resolution, 1.0)); // Multiply by received matrix - transform *= matrix_in; + transform *= %1; // Scale back out to footage size - transform *= scale_mat4(vec3(footage_in_resolution, 1.0)); + transform *= scale_mat4(vec3(%2_resolution, 1.0)); gl_Position = transform * a_position; ove_texcoord = a_texcoord; diff --git a/app/shaders/shaders.qrc b/app/shaders/shaders.qrc index ab4e87535..7e6f4876c 100644 --- a/app/shaders/shaders.qrc +++ b/app/shaders/shaders.qrc @@ -17,7 +17,6 @@ solid.xml stroke.frag stroke.xml - videoinput.frag - videoinput.vert + matrix.vert diff --git a/app/shaders/videoinput.frag b/app/shaders/videoinput.frag deleted file mode 100644 index 01eb57b2e..000000000 --- a/app/shaders/videoinput.frag +++ /dev/null @@ -1,11 +0,0 @@ -#version 150 - -uniform sampler2D footage_in; - -in vec2 ove_texcoord; - -out vec4 fragColor; - -void main(void) { - fragColor = texture(footage_in, ove_texcoord); -} diff --git a/app/widget/curvewidget/curvewidget.cpp b/app/widget/curvewidget/curvewidget.cpp index a16e0a726..71c742619 100644 --- a/app/widget/curvewidget/curvewidget.cpp +++ b/app/widget/curvewidget/curvewidget.cpp @@ -108,6 +108,11 @@ CurveWidget::~CurveWidget() view_->Clear(); } +NodeInput *CurveWidget::GetInput() const +{ + return input_; +} + void CurveWidget::SetInput(NodeInput *input) { if (bridge_) { diff --git a/app/widget/curvewidget/curvewidget.h b/app/widget/curvewidget/curvewidget.h index 551825028..b0276bb7f 100644 --- a/app/widget/curvewidget/curvewidget.h +++ b/app/widget/curvewidget/curvewidget.h @@ -41,6 +41,7 @@ public: virtual ~CurveWidget() override; + NodeInput* GetInput() const; void SetInput(NodeInput* input); const double& GetVerticalScale(); 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/nodeparamview.cpp b/app/widget/nodeparamview/nodeparamview.cpp index 6b57f2c14..b9d1e7366 100644 --- a/app/widget/nodeparamview/nodeparamview.cpp +++ b/app/widget/nodeparamview/nodeparamview.cpp @@ -126,6 +126,8 @@ void NodeParamView::SetNodes(QList nodes) // If we already have item widgets, delete them all now foreach (NodeParamViewItem* item, items_) { + emit ClosedNode(item->GetNode()); + delete item; } items_.clear(); @@ -149,12 +151,14 @@ void NodeParamView::SetNodes(QList nodes) connect(item, &NodeParamViewItem::KeyframeAdded, keyframe_view_, &KeyframeView::AddKeyframe); connect(item, &NodeParamViewItem::KeyframeRemoved, keyframe_view_, &KeyframeView::RemoveKeyframe); connect(item, &NodeParamViewItem::RequestSetTime, this, &NodeParamView::ItemRequestedTimeChanged); - connect(item, &NodeParamViewItem::InputClicked, this, &NodeParamView::SelectedInputChanged); + connect(item, &NodeParamViewItem::InputDoubleClicked, this, &NodeParamView::InputDoubleClicked); connect(item, &NodeParamViewItem::RequestSelectNode, this, &NodeParamView::RequestSelectNode); items_.append(item); QTimer::singleShot(1, item, &NodeParamViewItem::SignalAllKeyframes); + + emit OpenedNode(node); } ViewerOutput* viewer = nodes_.first()->FindOutputNode(); diff --git a/app/widget/nodeparamview/nodeparamview.h b/app/widget/nodeparamview/nodeparamview.h index 5188469fe..097fcd4ae 100644 --- a/app/widget/nodeparamview/nodeparamview.h +++ b/app/widget/nodeparamview/nodeparamview.h @@ -41,12 +41,16 @@ public: const QList& nodes(); signals: - void SelectedInputChanged(NodeInput* input); + void InputDoubleClicked(NodeInput* input); void TimeTargetChanged(Node* target); void RequestSelectNode(const QList& target); + void OpenedNode(Node* n); + + void ClosedNode(Node* n); + protected: virtual void resizeEvent(QResizeEvent *event) override; diff --git a/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp b/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp index 9c48bc888..65ed2504d 100644 --- a/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp +++ b/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp @@ -42,6 +42,8 @@ NodeParamViewConnectedLabel::NodeParamViewConnectedLabel(NodeInput *input, QWidg connect(connected_to_lbl_, &ClickableLabel::MouseClicked, this, &NodeParamViewConnectedLabel::ConnectionClicked); layout->addWidget(connected_to_lbl_); + layout->addStretch(); + // Set up "link" font QFont link_font = connected_to_lbl_->font(); link_font.setUnderline(true); diff --git a/app/widget/nodeparamview/nodeparamviewitem.cpp b/app/widget/nodeparamview/nodeparamviewitem.cpp index b464e3135..6d7a2f0ee 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.cpp +++ b/app/widget/nodeparamview/nodeparamviewitem.cpp @@ -46,7 +46,6 @@ NodeParamViewItem::NodeParamViewItem(Node *node, QWidget *parent) : QHBoxLayout* title_bar_layout = new QHBoxLayout(title_bar_); title_bar_collapse_btn_ = new CollapseButton(); - connect(title_bar_collapse_btn_, &QPushButton::toggled, this, &NodeParamViewItemBody::setVisible); title_bar_layout->addWidget(title_bar_collapse_btn_); title_bar_lbl_ = new QLabel(title_bar_); @@ -66,11 +65,12 @@ NodeParamViewItem::NodeParamViewItem(Node *node, QWidget *parent) : } body_ = new NodeParamViewItemBody(inputs); - connect(body_, &NodeParamViewItemBody::InputClicked, this, &NodeParamViewItem::InputClicked); + connect(body_, &NodeParamViewItemBody::InputDoubleClicked, this, &NodeParamViewItem::InputDoubleClicked); connect(body_, &NodeParamViewItemBody::RequestSelectNode, this, &NodeParamViewItem::RequestSelectNode); connect(body_, &NodeParamViewItemBody::RequestSetTime, this, &NodeParamViewItem::RequestSetTime); connect(body_, &NodeParamViewItemBody::KeyframeAdded, this, &NodeParamViewItem::KeyframeAdded); connect(body_, &NodeParamViewItemBody::KeyframeRemoved, this, &NodeParamViewItem::KeyframeRemoved); + connect(title_bar_collapse_btn_, &QPushButton::toggled, body_, &NodeParamViewItemBody::setVisible); main_layout->addWidget(body_); Retranslate(); @@ -88,6 +88,11 @@ void NodeParamViewItem::SetTime(const rational &time) body_->SetTime(time_); } +Node *NodeParamViewItem::GetNode() const +{ + return node_; +} + void NodeParamViewItem::SignalAllKeyframes() { body_->SignalAllKeyframes(); @@ -142,7 +147,7 @@ NodeParamViewItemBody::NodeParamViewItemBody(const QVector &inputs, // Add descriptor label ui_objects.main_label = new ClickableLabel(); - connect(ui_objects.main_label, &ClickableLabel::MouseClicked, this, &NodeParamViewItemBody::LabelClicked); + connect(ui_objects.main_label, &ClickableLabel::MouseDoubleClicked, this, &NodeParamViewItemBody::LabelDoubleClicked); if (input->IsArray()) { QHBoxLayout* array_label_layout = new QHBoxLayout(); @@ -164,7 +169,7 @@ NodeParamViewItemBody::NodeParamViewItemBody(const QVector &inputs, connect(sub_body, &NodeParamViewItemBody::KeyframeAdded, this, &NodeParamViewItemBody::KeyframeAdded); connect(sub_body, &NodeParamViewItemBody::KeyframeRemoved, this, &NodeParamViewItemBody::KeyframeRemoved); connect(sub_body, &NodeParamViewItemBody::RequestSetTime, this, &NodeParamViewItemBody::RequestSetTime); - connect(sub_body, &NodeParamViewItemBody::InputClicked, this, &NodeParamViewItemBody::InputClicked); + connect(sub_body, &NodeParamViewItemBody::InputDoubleClicked, this, &NodeParamViewItemBody::InputDoubleClicked); connect(sub_body, &NodeParamViewItemBody::RequestSelectNode, this, &NodeParamViewItemBody::RequestSelectNode); } else { content_layout->addWidget(ui_objects.main_label, row_count, 0); @@ -330,12 +335,13 @@ void NodeParamViewItemBody::InputAddedKeyframe(NodeKeyframePtr key) InputAddedKeyframeInternal(input, key); } -void NodeParamViewItemBody::LabelClicked() +void NodeParamViewItemBody::LabelDoubleClicked() { QMap::const_iterator iterator; + for (iterator=input_ui_map_.begin(); iterator!=input_ui_map_.end(); iterator++) { - if (iterator.value().connected_label == sender()) { - emit InputClicked(iterator.key()); + if (iterator.value().main_label == sender()) { + emit InputDoubleClicked(iterator.key()); return; } } diff --git a/app/widget/nodeparamview/nodeparamviewitem.h b/app/widget/nodeparamview/nodeparamviewitem.h index 5963391ba..5209d68ed 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.h +++ b/app/widget/nodeparamview/nodeparamviewitem.h @@ -64,7 +64,7 @@ signals: void RequestSetTime(const rational& time); - void InputClicked(NodeInput* input); + void InputDoubleClicked(NodeInput* input); void RequestSelectNode(const QList& node); @@ -93,7 +93,7 @@ private slots: void InputAddedKeyframe(NodeKeyframePtr key); - void LabelClicked(); + void LabelDoubleClicked(); void ConnectionClicked(); @@ -109,6 +109,8 @@ public: void SetTime(const rational& time); + Node* GetNode() const; + public slots: void SignalAllKeyframes(); @@ -119,7 +121,7 @@ signals: void RequestSetTime(const rational& time); - void InputClicked(NodeInput* input); + void InputDoubleClicked(NodeInput* input); void RequestSelectNode(const QList& node); 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/scope/waveform/waveform.cpp b/app/widget/scope/waveform/waveform.cpp index 8a30dac66..64c71004d 100644 --- a/app/widget/scope/waveform/waveform.cpp +++ b/app/widget/scope/waveform/waveform.cpp @@ -28,27 +28,35 @@ OLIVE_NAMESPACE_ENTER WaveformScope::WaveformScope(QWidget* parent) : ManagedDisplayWidget(parent), - texture_(nullptr) + buffer_(nullptr) { EnableDefaultContextMenu(); } -void WaveformScope::SetTexture(OpenGLTexture *texture) +void WaveformScope::SetBuffer(Frame *frame) { - texture_ = texture; + buffer_ = frame; - update(); + UploadTextureFromBuffer(); } void WaveformScope::initializeGL() { ManagedDisplayWidget::initializeGL(); + makeCurrent(); pipeline_ = OpenGLShader::Create(); pipeline_->create(); pipeline_->addShaderFromSourceCode(QOpenGLShader::Vertex, OpenGLShader::CodeDefaultVertex()); pipeline_->addShaderFromSourceCode(QOpenGLShader::Fragment, Node::ReadFileAsString(":/shaders/rgbwaveform.frag")); pipeline_->link(); + doneCurrent(); + + connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &WaveformScope::CleanUp, Qt::DirectConnection); + + if (buffer_) { + UploadTextureFromBuffer(); + } } void WaveformScope::paintGL() @@ -56,12 +64,12 @@ void WaveformScope::paintGL() context()->functions()->glClearColor(0, 0, 0, 0); context()->functions()->glClear(GL_COLOR_BUFFER_BIT); - if (!pipeline_ || !texture_) { + if (!pipeline_ || !texture_.IsCreated()) { return; } pipeline_->bind(); - pipeline_->setUniformValue("ove_resolution", texture_->width(), texture_->height()); + pipeline_->setUniformValue("ove_resolution", texture_.width(), texture_.height()); pipeline_->setUniformValue("ove_viewport", width(), height()); // The general size of a pixel @@ -69,17 +77,42 @@ void WaveformScope::paintGL() pipeline_->release(); - texture_->Bind(); + texture_.Bind(); OpenGLRenderFunctions::Blit(pipeline_); - texture_->Release(); + texture_.Release(); +} + +void WaveformScope::UploadTextureFromBuffer() +{ + makeCurrent(); + + if (!texture_.IsCreated() + || texture_.width() != buffer_->width() + || texture_.height() != buffer_->height() + || texture_.format() != buffer_->format()) { + texture_.Destroy(); + texture_.Create(context(), buffer_); + } else { + texture_.Upload(buffer_); + } + + doneCurrent(); + + update(); } void WaveformScope::CleanUp() { + qDebug() << "Cleaned up..."; + + makeCurrent(); + pipeline_ = nullptr; - texture_ = nullptr; + texture_.Destroy(); + + doneCurrent(); } OLIVE_NAMESPACE_EXIT diff --git a/app/widget/scope/waveform/waveform.h b/app/widget/scope/waveform/waveform.h index f9f930c5c..efb7a08b4 100644 --- a/app/widget/scope/waveform/waveform.h +++ b/app/widget/scope/waveform/waveform.h @@ -36,7 +36,7 @@ public: WaveformScope(QWidget* parent = nullptr); public slots: - void SetTexture(OpenGLTexture* texture); + void SetBuffer(Frame* frame); protected: virtual void initializeGL() override; @@ -44,9 +44,13 @@ protected: virtual void paintGL() override; private: + void UploadTextureFromBuffer(); + OpenGLShaderPtr pipeline_; - OpenGLTexture* texture_; + OpenGLTexture texture_; + + Frame* buffer_; private slots: void CleanUp(); diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index 0ce7604d8..41a1da410 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -241,7 +241,7 @@ void TimelineWidget::ConnectNodeInternal(ViewerOutput *n) view->ConnectTrackList(track_list); // Defer to the track to make all the block UI items necessary - foreach (TrackOutput* track, n->track_list(track_type)->Tracks()) { + foreach (TrackOutput* track, n->track_list(track_type)->GetTracks()) { AddTrack(track, track_type); } } @@ -881,7 +881,7 @@ void TimelineWidget::InsertGapsAt(const rational &earliest_point, const rational TrackOutput *TimelineWidget::GetTrackFromReference(const TrackReference &ref) { - return GetConnectedNode()->track_list(ref.type())->TrackAt(ref.index()); + return GetConnectedNode()->track_list(ref.type())->GetTrackAt(ref.index()); } int TimelineWidget::GetTrackY(const TrackReference &ref) @@ -1017,9 +1017,7 @@ void TimelineWidget::AddBlock(Block *block, TrackReference track) void TimelineWidget::RemoveBlock(Block *block) { - delete block_items_[block]; - - block_items_.remove(block); + delete block_items_.take(block); } void TimelineWidget::AddTrack(TrackOutput *track, Timeline::TrackType type) @@ -1027,15 +1025,32 @@ void TimelineWidget::AddTrack(TrackOutput *track, Timeline::TrackType type) foreach (Block* b, track->Blocks()) { AddBlock(b, TrackReference(type, track->Index())); } + + connect(track, &TrackOutput::IndexChanged, this, &TimelineWidget::TrackIndexChanged); } void TimelineWidget::RemoveTrack(TrackOutput *track) { + disconnect(track, &TrackOutput::IndexChanged, this, &TimelineWidget::TrackIndexChanged); + foreach (Block* b, track->Blocks()) { RemoveBlock(b); } } +void TimelineWidget::TrackIndexChanged() +{ + TrackOutput* track = static_cast(sender()); + TrackReference ref(track->track_type(), track->Index()); + + foreach (Block* b, track->Blocks()) { + TimelineViewBlockItem* item = block_items_.value(b); + + item->SetYCoords(GetTrackY(ref), GetTrackHeight(ref)); + item->SetTrack(ref); + } +} + void TimelineWidget::ViewSelectionChanged() { if (rubberband_.isVisible()) { diff --git a/app/widget/timelinewidget/timelinewidget.h b/app/widget/timelinewidget/timelinewidget.h index a00027602..6f7b6a41c 100644 --- a/app/widget/timelinewidget/timelinewidget.h +++ b/app/widget/timelinewidget/timelinewidget.h @@ -486,6 +486,7 @@ private slots: void AddTrack(TrackOutput* track, Timeline::TrackType type); void RemoveTrack(TrackOutput* track); + void TrackIndexChanged(); void ViewSelectionChanged(); diff --git a/app/widget/timelinewidget/tool/import.cpp b/app/widget/timelinewidget/tool/import.cpp index 73264b243..fc8662f49 100644 --- a/app/widget/timelinewidget/tool/import.cpp +++ b/app/widget/timelinewidget/tool/import.cpp @@ -30,9 +30,10 @@ #include "core.h" #include "dialog/sequence/sequence.h" #include "node/audio/volume/volume.h" -#include "node/distort/transform/transform.h" +#include "node/generator/matrix/matrix.h" #include "node/input/media/audio/audio.h" #include "node/input/media/video/video.h" +#include "node/math/math/math.h" #include "project/item/sequence/sequence.h" #include "widget/nodeview/nodeviewundo.h" #include "window/mainwindow/mainwindow.h" @@ -397,15 +398,17 @@ void TimelineWidget::ImportTool::DropGhosts(bool insert) VideoInput* video_input = new VideoInput(); video_input->SetFootage(footage_stream); new NodeAddCommand(dst_graph, video_input, command); - new NodeEdgeAddCommand(video_input->output(), clip->texture_input(), command); - TransformDistort* transform = new TransformDistort(); - new NodeAddCommand(dst_graph, transform, command); - new NodeEdgeAddCommand(transform->output(), video_input->matrix_input(), command); + MatrixGenerator* matrix = new MatrixGenerator(); + new NodeAddCommand(dst_graph, matrix, command); - //OpacityNode* opacity = new OpacityNode(); - //NodeParam::ConnectEdge(opacity->texture_output(), clip->texture_input()); - //NodeParam::ConnectEdge(media->texture_output(), opacity->texture_input()); + MathNode* multiply = new MathNode(); + multiply->SetOperation(MathNode::kOpMultiply); + new NodeAddCommand(dst_graph, multiply, command); + + new NodeEdgeAddCommand(video_input->output(), multiply->param_a_in(), command); + new NodeEdgeAddCommand(matrix->output(), multiply->param_b_in(), command); + new NodeEdgeAddCommand(multiply->output(), clip->texture_input(), command); break; } case Stream::kAudio: diff --git a/app/widget/timelinewidget/trackview/trackview.cpp b/app/widget/timelinewidget/trackview/trackview.cpp index 1d0721ea1..06d34f136 100644 --- a/app/widget/timelinewidget/trackview/trackview.cpp +++ b/app/widget/timelinewidget/trackview/trackview.cpp @@ -81,7 +81,7 @@ void TrackView::ConnectTrackList(TrackList *list) list_ = list; if (list_ != nullptr) { - foreach (TrackOutput* track, list_->Tracks()) { + foreach (TrackOutput* track, list_->GetTracks()) { TrackViewItem* item = new TrackViewItem(track); items_.append(item); splitter_->Insert(track->Index(), track->GetTrackHeight(), item); @@ -120,7 +120,7 @@ void TrackView::ScrollbarRangeChanged(int, int max) void TrackView::TrackHeightChanged(int index, int height) { - list_->TrackAt(index)->SetTrackHeight(height); + list_->GetTrackAt(index)->SetTrackHeight(height); } void TrackView::InsertTrack(TrackOutput *track) diff --git a/app/widget/timelinewidget/trackview/trackviewsplitter.cpp b/app/widget/timelinewidget/trackview/trackviewsplitter.cpp index b8895876e..108d70039 100644 --- a/app/widget/timelinewidget/trackview/trackviewsplitter.cpp +++ b/app/widget/timelinewidget/trackview/trackviewsplitter.cpp @@ -141,7 +141,7 @@ void TrackViewSplitter::Remove(int index) QList sz = sizes(); if (alignment_ == Qt::AlignBottom) { - index = count() - index; + index = count() - 1 - index; } sz.removeAt(index); diff --git a/app/widget/timelinewidget/undo/undo.cpp b/app/widget/timelinewidget/undo/undo.cpp index 8dd71617a..6833b6c67 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) : @@ -345,13 +347,13 @@ void TrackPlaceBlockCommand::redo_internal() added_track_count_ = 0; // Get track (or make it if necessary) - while (track_index_ >= timeline_->Tracks().size()) { + while (track_index_ >= timeline_->GetTracks().size()) { timeline_->AddTrack(); added_track_count_++; } - track_ = timeline_->TrackAt(track_index_); + track_ = timeline_->GetTrackAt(track_index_); append_ = (in_ >= track_->track_length()); @@ -608,36 +610,34 @@ void TrackCleanGapsCommand::redo_internal() GapBlock* on_gap = nullptr; QList consecutive_gaps; - TrackOutput* track = track_list_->TrackAt(track_index_); + TrackOutput* track = track_list_->GetTrackAt(track_index_); foreach (Block* b, track->Blocks()) { - if (b) { - if (b->type() == Block::kGap) { - if (on_gap) { - consecutive_gaps.append(static_cast(b)); - } else { - on_gap = static_cast(b); - } - } else if (on_gap) { - merged_gaps_.append({on_gap, on_gap->length(), consecutive_gaps}); - - // Remove each gap and add to the length of the merged - // We can block the IC signal because merging gaps won't actually change anything - track->BlockInvalidateCache(); - rational new_gap_length = on_gap->length(); - foreach (GapBlock* gap, consecutive_gaps) { - track->RippleRemoveBlock(gap); - static_cast(track->parent())->TakeNode(gap, &memory_manager_); - - new_gap_length += gap->length(); - } - on_gap->set_length_and_media_out(new_gap_length); - track->UnblockInvalidateCache(); - - // Reset state - on_gap = nullptr; - consecutive_gaps.clear(); + if (b->type() == Block::kGap) { + if (on_gap) { + consecutive_gaps.append(static_cast(b)); + } else { + on_gap = static_cast(b); } + } else if (on_gap) { + merged_gaps_.append({on_gap, on_gap->length(), consecutive_gaps}); + + // Remove each gap and add to the length of the merged + // We can block the IC signal because merging gaps won't actually change anything + track->BlockInvalidateCache(); + rational new_gap_length = on_gap->length(); + foreach (GapBlock* gap, consecutive_gaps) { + track->RippleRemoveBlock(gap); + static_cast(track->parent())->TakeNode(gap, &memory_manager_); + + new_gap_length += gap->length(); + } + on_gap->set_length_and_media_out(new_gap_length); + track->UnblockInvalidateCache(); + + // Reset state + on_gap = nullptr; + consecutive_gaps.clear(); } } @@ -655,7 +655,7 @@ void TrackCleanGapsCommand::redo_internal() void TrackCleanGapsCommand::undo_internal() { - TrackOutput* track = track_list_->TrackAt(track_index_); + TrackOutput* track = track_list_->GetTrackAt(track_index_); // Restored removed end gaps foreach (GapBlock* gap, removed_end_gaps_) { diff --git a/app/widget/timelinewidget/view/CMakeLists.txt b/app/widget/timelinewidget/view/CMakeLists.txt index f1aa3a9a1..5bbce0175 100644 --- a/app/widget/timelinewidget/view/CMakeLists.txt +++ b/app/widget/timelinewidget/view/CMakeLists.txt @@ -28,8 +28,6 @@ set(OLIVE_SOURCES widget/timelinewidget/view/timelineviewbase.cpp widget/timelinewidget/view/timelineviewblockitem.h widget/timelinewidget/view/timelineviewblockitem.cpp - widget/timelinewidget/view/timelineviewenditem.h - widget/timelinewidget/view/timelineviewenditem.cpp widget/timelinewidget/view/timelineviewghostitem.h widget/timelinewidget/view/timelineviewghostitem.cpp PARENT_SCOPE diff --git a/app/widget/timelinewidget/view/timelineview.cpp b/app/widget/timelinewidget/view/timelineview.cpp index 8c57c2469..04dc87864 100644 --- a/app/widget/timelinewidget/view/timelineview.cpp +++ b/app/widget/timelinewidget/view/timelineview.cpp @@ -189,11 +189,7 @@ void TimelineView::drawBackground(QPainter *painter, const QRectF &rect) int line_y = 0; - foreach (TrackOutput* track, connected_track_list_->Tracks()) { - if (!track) { - continue; - } - + foreach (TrackOutput* track, connected_track_list_->GetTracks()) { line_y += track->GetTrackHeight(); // One px gap between tracks @@ -230,6 +226,17 @@ void TimelineView::ToolChangedEvent(Tool::Item tool) } } +void TimelineView::SceneRectUpdateEvent(QRectF &rect) +{ + if (alignment() & Qt::AlignTop) { + rect.setTop(0); + rect.setBottom(GetHeightOfAllTracks() + height() / 2); + } else if (alignment() & Qt::AlignBottom) { + rect.setBottom(0); + rect.setTop(GetHeightOfAllTracks() - height() / 2); + } +} + Timeline::TrackType TimelineView::ConnectedTrackType() { if (connected_track_list_) { @@ -279,8 +286,25 @@ TimelineViewMouseEvent TimelineView::CreateMouseEvent(const QPoint& pos, Qt::Key return timeline_event; } -int TimelineView::GetTrackY(int track_index) +int TimelineView::GetHeightOfAllTracks() const { + if (connected_track_list_) { + if (alignment() & Qt::AlignTop) { + return GetTrackY(connected_track_list_->GetTrackCount()); + } else { + return GetTrackY(connected_track_list_->GetTrackCount() - 1); + } + } else { + return 0; + } +} + +int TimelineView::GetTrackY(int track_index) const +{ + if (!connected_track_list_ || !connected_track_list_->GetTrackCount()) { + return 0; + } + int y = 0; if (alignment() & Qt::AlignBottom) { @@ -301,16 +325,16 @@ int TimelineView::GetTrackY(int track_index) return y; } -int TimelineView::GetTrackHeight(int track_index) +int TimelineView::GetTrackHeight(int track_index) const { - if (!connected_track_list_ || track_index >= connected_track_list_->TrackCount()) { + if (!connected_track_list_ || track_index >= connected_track_list_->GetTrackCount()) { return TrackOutput::GetDefaultTrackHeight(); } - return connected_track_list_->TrackAt(track_index)->GetTrackHeight(); + return connected_track_list_->GetTrackAt(track_index)->GetTrackHeight(); } -QPoint TimelineView::GetScrollCoordinates() +QPoint TimelineView::GetScrollCoordinates() const { return QPoint(horizontalScrollBar()->value(), verticalScrollBar()->value()); } diff --git a/app/widget/timelinewidget/view/timelineview.h b/app/widget/timelinewidget/view/timelineview.h index bb1e7e06e..ecf66c2b7 100644 --- a/app/widget/timelinewidget/view/timelineview.h +++ b/app/widget/timelinewidget/view/timelineview.h @@ -53,10 +53,10 @@ public: void DeselectAll(); - int GetTrackY(int track_index); - int GetTrackHeight(int track_index); + int GetTrackY(int track_index) const; + int GetTrackHeight(int track_index) const; - QPoint GetScrollCoordinates(); + QPoint GetScrollCoordinates() const; void SetScrollCoordinates(const QPoint& pt); void ConnectTrackList(TrackList* list); @@ -91,6 +91,8 @@ protected: virtual void ToolChangedEvent(Tool::Item tool) override; + virtual void SceneRectUpdateEvent(QRectF& rect) override; + private: Timeline::TrackType ConnectedTrackType(); Stream::Type TrackTypeToStreamType(Timeline::TrackType track_type); @@ -100,6 +102,8 @@ private: TimelineViewMouseEvent CreateMouseEvent(const QPoint &pos, Qt::KeyboardModifiers modifiers); + int GetHeightOfAllTracks() const; + int SceneToTrack(double y); void UserSetTime(const int64_t& time); diff --git a/app/widget/timelinewidget/view/timelineviewbase.cpp b/app/widget/timelinewidget/view/timelineviewbase.cpp index f10108831..31f53b586 100644 --- a/app/widget/timelinewidget/view/timelineviewbase.cpp +++ b/app/widget/timelinewidget/view/timelineviewbase.cpp @@ -44,10 +44,6 @@ TimelineViewBase::TimelineViewBase(QWidget *parent) : { setScene(&scene_); - // Create end item - end_item_ = new TimelineViewEndItem(); - scene_.addItem(end_item_); - // Set default scale SetScale(1.0); @@ -223,54 +219,28 @@ qreal TimelineViewBase::GetPlayheadX() void TimelineViewBase::SetEndTime(const rational &length) { - end_item_->SetEndTime(length); + end_time_ = length; + + UpdateSceneRect(); } void TimelineViewBase::UpdateSceneRect() { QRectF bounding_rect = scene_.itemsBoundingRect(); - if (limit_y_axis_) { - // Make a gap of half the viewport height - if (alignment() & Qt::AlignBottom) { - bounding_rect.setTop(bounding_rect.top() - height()/2); - } else { - bounding_rect.setBottom(bounding_rect.bottom() + height()/2); - } - - // Ensure the scene height is always AT LEAST the height of the view - // The scrollbar appears to have a 1px margin on the top and bottom, hence the -2 - int minimum_height = height() - horizontalScrollBar()->height() - 2; - - if (alignment() & Qt::AlignBottom) { - // Ensure the scene left and bottom are always 0 - bounding_rect.setBottomLeft(QPointF(0, 0)); - - if (bounding_rect.top() > minimum_height) { - bounding_rect.setTop(-minimum_height); - } - } else { - // Ensure the scene left and top are always 0 - bounding_rect.setTopLeft(QPointF(0, 0)); - - if (bounding_rect.height() < minimum_height) { - bounding_rect.setHeight(minimum_height); - } - } - } else { - // We'll still limit the X to 0 since that behavior is desired by all TimelineViewBase derivatives - bounding_rect.setLeft(0); - } + // There's no need for a timeline to ever go below 0 on the X scale + bounding_rect.setLeft(0); // Ensure the scene is always the full length of the timeline with a gap at the end to work with - end_item_->SetEndPadding(width()/2); + bounding_rect.setRight(TimeToScene(end_time_) + width() / 2); + + // Any further rect processing from derivatives can be done here + SceneRectUpdateEvent(bounding_rect); // If the scene is already this rect, do nothing - if (scene_.sceneRect() == bounding_rect) { - return; + if (scene_.sceneRect() != bounding_rect) { + scene_.setSceneRect(bounding_rect); } - - scene_.setSceneRect(bounding_rect); } void TimelineViewBase::PageScrollToPlayhead() @@ -299,9 +269,10 @@ void TimelineViewBase::ScaleChangedEvent(const double &scale) { TimelineScaledObject::ScaleChangedEvent(scale); - end_item_->SetScale(scale); + // Update scene rect + UpdateSceneRect(); - // Force redraw for playhead + // Force redraw for playhead if the above function didn't do it viewport()->update(); } diff --git a/app/widget/timelinewidget/view/timelineviewbase.h b/app/widget/timelinewidget/view/timelineviewbase.h index ccd1d0b68..c64718bb6 100644 --- a/app/widget/timelinewidget/view/timelineviewbase.h +++ b/app/widget/timelinewidget/view/timelineviewbase.h @@ -25,7 +25,6 @@ #include "core.h" #include "timelineplayhead.h" -#include "timelineviewenditem.h" #include "widget/timelinewidget/timelinescaledobject.h" OLIVE_NAMESPACE_ENTER @@ -57,6 +56,8 @@ protected: virtual void ScaleChangedEvent(const double& scale) override; + virtual void SceneRectUpdateEvent(QRectF&){} + bool HandleZoomFromScroll(QWheelEvent* event); bool WheelEventIsAZoomEvent(QWheelEvent* event); @@ -95,14 +96,14 @@ private: bool dragging_hand_; DragMode pre_hand_drag_mode_; - TimelineViewEndItem* end_item_; - QGraphicsScene scene_; bool limit_y_axis_; DragMode default_drag_mode_; + rational end_time_; + private slots: /** * @brief Slot called whenever the view resizes or the scene contents change to enforce minimum scene sizes diff --git a/app/widget/timelinewidget/view/timelineviewblockitem.cpp b/app/widget/timelinewidget/view/timelineviewblockitem.cpp index 1f022450a..7e182657f 100644 --- a/app/widget/timelinewidget/view/timelineviewblockitem.cpp +++ b/app/widget/timelinewidget/view/timelineviewblockitem.cpp @@ -134,7 +134,11 @@ void TimelineViewBlockItem::paint(QPainter *painter, const QStyleOptionGraphicsI } else { painter->setPen(Qt::lightGray); } - painter->drawText(rect(), static_cast(Qt::AlignLeft | Qt::AlignTop), block_->block_name()); + + int text_top = TrackOutput::GetTrackHeightMinimum() / 2 - painter->fontMetrics().height() / 2; + QRectF text_rect = rect(); + text_rect.adjust(0, text_top, 0, 0); + painter->drawText(text_rect, Qt::AlignLeft | Qt::AlignTop, block_->block_name()); // Linked clips are underlined if (block_->HasLinks()) { diff --git a/app/widget/timelinewidget/view/timelineviewenditem.cpp b/app/widget/timelinewidget/view/timelineviewenditem.cpp deleted file mode 100644 index 767a2a008..000000000 --- a/app/widget/timelinewidget/view/timelineviewenditem.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#include "timelineviewenditem.h" - -OLIVE_NAMESPACE_ENTER - -TimelineViewEndItem::TimelineViewEndItem(QGraphicsItem *parent) : - TimelineViewRect(parent), - end_padding_(0) -{ -} - -void TimelineViewEndItem::SetEndTime(const rational &time) -{ - end_time_ = time; - - UpdateRect(); -} - -void TimelineViewEndItem::SetEndPadding(int padding) -{ - end_padding_ = padding; - - UpdateRect(); -} - -void TimelineViewEndItem::UpdateRect() -{ - // Doesn't need to be more than one pixel - setRect(0, 0, 1, 1); - - setPos(TimeToScene(end_time_) + end_padding_, 0); -} - -void TimelineViewEndItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) -{ - // Item is invisible, this is a no-op - Q_UNUSED(painter) - Q_UNUSED(option) - Q_UNUSED(widget) -} - -OLIVE_NAMESPACE_EXIT diff --git a/app/widget/timelinewidget/view/timelineviewenditem.h b/app/widget/timelinewidget/view/timelineviewenditem.h deleted file mode 100644 index e3f758af4..000000000 --- a/app/widget/timelinewidget/view/timelineviewenditem.h +++ /dev/null @@ -1,53 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#ifndef TIMELINEVIEWENDITEM_H -#define TIMELINEVIEWENDITEM_H - -#include "timelineviewrect.h" - -OLIVE_NAMESPACE_ENTER - -/** - * @brief An item placed at the end point of the Timeline to ensure the correct scene size - */ -class TimelineViewEndItem : public TimelineViewRect -{ -public: - TimelineViewEndItem(QGraphicsItem* parent = nullptr); - - void SetEndTime(const rational& time); - - void SetEndPadding(int padding); - - virtual void UpdateRect() override; - -protected: - virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; - -private: - rational end_time_; - - int end_padding_; -}; - -OLIVE_NAMESPACE_EXIT - -#endif // TIMELINEVIEWENDITEM_H diff --git a/app/widget/viewer/CMakeLists.txt b/app/widget/viewer/CMakeLists.txt index 5b457a802..e96b9e639 100644 --- a/app/widget/viewer/CMakeLists.txt +++ b/app/widget/viewer/CMakeLists.txt @@ -24,8 +24,8 @@ set(OLIVE_SOURCES widget/viewer/pixelsamplerwidget.cpp widget/viewer/viewer.h widget/viewer/viewer.cpp - widget/viewer/viewerglwidget.h - widget/viewer/viewerglwidget.cpp + widget/viewer/viewerdisplay.h + widget/viewer/viewerdisplay.cpp widget/viewer/viewersafemargininfo.h widget/viewer/viewersizer.h widget/viewer/viewersizer.cpp diff --git a/app/widget/viewer/footageviewer.cpp b/app/widget/viewer/footageviewer.cpp index 08f1c4b71..3ee2569b0 100644 --- a/app/widget/viewer/footageviewer.cpp +++ b/app/widget/viewer/footageviewer.cpp @@ -35,7 +35,7 @@ FootageViewerWidget::FootageViewerWidget(QWidget *parent) : audio_node_ = new AudioInput(); viewer_node_ = new ViewerOutput(); - connect(main_gl_widget(), &ViewerGLWidget::DragStarted, this, &FootageViewerWidget::StartFootageDrag); + connect(main_gl_widget(), &ViewerDisplayWidget::DragStarted, this, &FootageViewerWidget::StartFootageDrag); controls_->SetAudioVideoDragButtonsVisible(true); connect(controls_, &PlaybackControls::VideoPressed, this, &FootageViewerWidget::StartVideoDrag); diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index e56ef59ac..d4ebb84f0 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -62,15 +62,13 @@ ViewerWidget::ViewerWidget(QWidget *parent) : sizer_ = new ViewerSizer(); stack_->addWidget(sizer_); - ViewerGLWidget* main_widget = new ViewerGLWidget(); - connect(main_widget, &ViewerGLWidget::customContextMenuRequested, this, &ViewerWidget::ShowContextMenu); - connect(main_widget, &ViewerGLWidget::CursorColor, this, &ViewerWidget::CursorColor); - connect(main_widget, &ViewerGLWidget::LoadedBuffer, this, &ViewerWidget::LoadedBuffer); - connect(main_widget, &ViewerGLWidget::LoadedTexture, this, &ViewerWidget::LoadedTexture); - connect(main_widget, &ViewerGLWidget::DrewManagedTexture, this, &ViewerWidget::DrewManagedTexture); - connect(main_widget, &ViewerGLWidget::ColorProcessorChanged, this, &ViewerWidget::ColorProcessorChanged); - connect(main_widget, &ViewerGLWidget::ColorManagerChanged, this, &ViewerWidget::ColorManagerChanged); - connect(sizer_, &ViewerSizer::RequestMatrix, main_widget, &ViewerGLWidget::SetMatrix); + ViewerDisplayWidget* main_widget = new ViewerDisplayWidget(); + connect(main_widget, &ViewerDisplayWidget::customContextMenuRequested, this, &ViewerWidget::ShowContextMenu); + connect(main_widget, &ViewerDisplayWidget::CursorColor, this, &ViewerWidget::CursorColor); + connect(main_widget, &ViewerDisplayWidget::LoadedBuffer, this, &ViewerWidget::LoadedBuffer); + connect(main_widget, &ViewerDisplayWidget::ColorProcessorChanged, this, &ViewerWidget::ColorProcessorChanged); + connect(main_widget, &ViewerDisplayWidget::ColorManagerChanged, this, &ViewerWidget::ColorManagerChanged); + connect(sizer_, &ViewerSizer::RequestMatrix, main_widget, &ViewerDisplayWidget::SetMatrix); sizer_->SetWidget(main_widget); gl_widgets_.append(main_widget); @@ -152,8 +150,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()); @@ -168,7 +166,7 @@ void ViewerWidget::ConnectNodeInternal(ViewerOutput *n) using_manager = nullptr; } - foreach (ViewerGLWidget* glw, gl_widgets_) { + foreach (ViewerDisplayWidget* glw, gl_widgets_) { glw->ConnectColorManager(using_manager); } @@ -194,13 +192,13 @@ 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); - foreach (ViewerGLWidget* glw, gl_widgets_) { + foreach (ViewerDisplayWidget* glw, gl_widgets_) { glw->DisconnectColorManager(); } @@ -234,12 +232,12 @@ void ViewerWidget::resizeEvent(QResizeEvent *event) UpdateMinimumScale(); } -const QList &ViewerWidget::gl_widgets() const +const QList &ViewerWidget::gl_widgets() const { return gl_widgets_; } -ViewerGLWidget *ViewerWidget::main_gl_widget() const +ViewerDisplayWidget *ViewerWidget::main_gl_widget() const { return gl_widgets_.first(); } @@ -280,7 +278,7 @@ void ViewerWidget::SetOverrideSize(int width, int height) void ViewerWidget::SetMatrix(const QMatrix4x4 &mat) { - foreach (ViewerGLWidget* glw, gl_widgets_) { + foreach (ViewerDisplayWidget* glw, gl_widgets_) { glw->SetMatrix(mat); } } @@ -309,7 +307,7 @@ void ViewerWidget::SetFullScreen(QScreen *screen) vw->gl_widget()->ConnectColorManager(main_gl_widget()->color_manager()); main_gl_widget()->ConnectSibling(vw->gl_widget()); connect(vw, &ViewerWindow::destroyed, this, &ViewerWidget::WindowAboutToClose); - connect(vw->gl_widget(), &ViewerGLWidget::customContextMenuRequested, this, &ViewerWidget::ShowContextMenu); + connect(vw->gl_widget(), &ViewerDisplayWidget::customContextMenuRequested, this, &ViewerWidget::ShowContextMenu); if (GetConnectedNode()) { vw->SetResolution(GetConnectedNode()->video_params().width(), GetConnectedNode()->video_params().height()); @@ -373,7 +371,7 @@ void ViewerWidget::PlayInternal(int speed, bool in_to_out_only) controls_->ShowPauseButton(); if (stack_->currentWidget() == sizer_) { - connect(main_gl_widget(), &ViewerGLWidget::frameSwapped, this, &ViewerWidget::PlaybackTimerUpdate); + connect(main_gl_widget(), &ViewerDisplayWidget::frameSwapped, this, &ViewerWidget::PlaybackTimerUpdate); } else { connect(AudioManager::instance(), &AudioManager::OutputNotified, this, &ViewerWidget::PlaybackTimerUpdate); } @@ -427,17 +425,21 @@ void ViewerWidget::UpdateMinimumScale() } } -void ViewerWidget::SetColorTransform(const ColorTransform &transform, ViewerGLWidget *sender) +void ViewerWidget::SetColorTransform(const ColorTransform &transform, ViewerDisplayWidget *sender) { sender->SetColorTransform(transform); } 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 +529,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 +537,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); } } @@ -543,7 +545,7 @@ void ViewerWidget::ShowContextMenu(const QPoint &pos) { Menu menu(static_cast(sender())); - context_menu_widget_ = static_cast(sender()); + context_menu_widget_ = static_cast(sender()); // Color options if (context_menu_widget_->color_manager() && color_menu_enabled_) { @@ -683,7 +685,7 @@ void ViewerWidget::Pause() controls_->ShowPlayButton(); if (stack_->currentWidget() == sizer_) { - disconnect(main_gl_widget(), &ViewerGLWidget::frameSwapped, this, &ViewerWidget::PlaybackTimerUpdate); + disconnect(main_gl_widget(), &ViewerDisplayWidget::frameSwapped, this, &ViewerWidget::PlaybackTimerUpdate); } else { disconnect(AudioManager::instance(), &AudioManager::OutputNotified, this, &ViewerWidget::PlaybackTimerUpdate); } @@ -736,16 +738,11 @@ void ViewerWidget::SetColorTransform(const ColorTransform &transform) void ViewerWidget::SetSignalCursorColorEnabled(bool e) { - foreach (ViewerGLWidget* glw, gl_widgets_) { + foreach (ViewerDisplayWidget* glw, gl_widgets_) { glw->SetSignalCursorColorEnabled(e); } } -void ViewerWidget::SetEmitDrewManagedTextureEnabled(bool e) -{ - main_gl_widget()->SetEmitDrewManagedTextureEnabled(e); -} - void ViewerWidget::TimebaseChangedEvent(const rational &timebase) { TimeBasedWidget::TimebaseChangedEvent(timebase); @@ -869,9 +866,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 c1fbf6481..57b6e2970 100644 --- a/app/widget/viewer/viewer.h +++ b/app/widget/viewer/viewer.h @@ -35,7 +35,7 @@ #include "render/backend/opengl/openglbackend.h" #include "render/backend/opengl/opengltexture.h" #include "render/backend/audio/audiobackend.h" -#include "viewerglwidget.h" +#include "viewerdisplay.h" #include "viewersizer.h" #include "viewerwindow.h" #include "widget/playbackcontrols/playbackcontrols.h" @@ -107,11 +107,6 @@ public slots: */ void SetSignalCursorColorEnabled(bool e); - /** - * @brief Wrapper for ViewerGLWidget::SetEmitDrewManagedTextureEnabled() - */ - void SetEmitDrewManagedTextureEnabled(bool e); - signals: /** * @brief Wrapper for ViewerGLWidget::CursorColor() @@ -123,16 +118,6 @@ signals: */ void LoadedBuffer(Frame* load_buffer); - /** - * @brief Wrapper for ViewerGLWidget::LoadedTexture() - */ - void LoadedTexture(OpenGLTexture* texture); - - /** - * @brief Wrapper for ViewerGLWidget::DrewManagedTexture() - */ - void DrewManagedTexture(OpenGLTexture* texture); - /** * @brief Request a scope panel * @@ -167,8 +152,8 @@ protected: PlaybackControls* controls_; - const QList &gl_widgets() const; - ViewerGLWidget* main_gl_widget() const; + const QList &gl_widgets() const; + ViewerDisplayWidget* main_gl_widget() const; private: void UpdateTimeInternal(int64_t i); @@ -183,7 +168,7 @@ private: void UpdateMinimumScale(); - void SetColorTransform(const ColorTransform& transform, ViewerGLWidget* sender); + void SetColorTransform(const ColorTransform& transform, ViewerDisplayWidget* sender); QStackedWidget* stack_; @@ -212,9 +197,9 @@ private: QList windows_; - QList gl_widgets_; + QList gl_widgets_; - ViewerGLWidget* context_menu_widget_; + ViewerDisplayWidget* context_menu_widget_; private slots: void PlaybackTimerUpdate(); @@ -233,7 +218,7 @@ private slots: void SetZoomFromMenu(QAction* action); - void InvalidateVisible(); + void InvalidateVisible(NodeInput *source); void UpdateStack(); diff --git a/app/widget/viewer/viewerglwidget.cpp b/app/widget/viewer/viewerdisplay.cpp similarity index 70% rename from app/widget/viewer/viewerglwidget.cpp rename to app/widget/viewer/viewerdisplay.cpp index a4cbe935f..779f65237 100644 --- a/app/widget/viewer/viewerglwidget.cpp +++ b/app/widget/viewer/viewerdisplay.cpp @@ -18,7 +18,7 @@ ***/ -#include "viewerglwidget.h" +#include "viewerdisplay.h" #include #include @@ -37,30 +37,28 @@ OLIVE_NAMESPACE_ENTER #ifdef Q_OS_LINUX -bool ViewerGLWidget::nouveau_check_done_ = false; +bool ViewerDisplayWidget::nouveau_check_done_ = false; #endif -ViewerGLWidget::ViewerGLWidget(QWidget *parent) : +ViewerDisplayWidget::ViewerDisplayWidget(QWidget *parent) : ManagedDisplayWidget(parent), - managed_copy_pipeline_(nullptr), has_image_(false), - signal_cursor_color_(false), - enable_display_referred_signal_(false) + signal_cursor_color_(false) { } -ViewerGLWidget::~ViewerGLWidget() +ViewerDisplayWidget::~ViewerDisplayWidget() { ContextCleanup(); } -void ViewerGLWidget::SetMatrix(const QMatrix4x4 &mat) +void ViewerDisplayWidget::SetMatrix(const QMatrix4x4 &mat) { matrix_ = mat; update(); } -void ViewerGLWidget::SetImage(const QString &fn) +void ViewerDisplayWidget::SetImage(const QString &fn) { has_image_ = false; @@ -91,14 +89,12 @@ void ViewerGLWidget::SetImage(const QString &fn) input->read_image(input->spec().format, load_buffer_.data(), OIIO::AutoStride, load_buffer_.linesize_bytes()); input->close(); - emit LoadedBuffer(&load_buffer_); - - texture_.Upload(load_buffer_.data(), load_buffer_.linesize_pixels()); - - emit LoadedTexture(&texture_); + texture_.Upload(&load_buffer_); doneCurrent(); + emit LoadedBuffer(&load_buffer_); + has_image_ = true; #if OIIO_VERSION < 10903 @@ -119,13 +115,13 @@ void ViewerGLWidget::SetImage(const QString &fn) } } -void ViewerGLWidget::SetSignalCursorColorEnabled(bool e) +void ViewerDisplayWidget::SetSignalCursorColorEnabled(bool e) { signal_cursor_color_ = e; setMouseTracking(e); } -void ViewerGLWidget::SetImageFromLoadBuffer(Frame *in_buffer) +void ViewerDisplayWidget::SetImageFromLoadBuffer(Frame *in_buffer) { has_image_ = in_buffer; @@ -138,7 +134,7 @@ void ViewerGLWidget::SetImageFromLoadBuffer(Frame *in_buffer) || texture_.format() != in_buffer->format()) { texture_.Create(context(), in_buffer->width(), in_buffer->height(), in_buffer->format(), in_buffer->data(), load_buffer_.linesize_pixels()); } else { - texture_.Upload(in_buffer->data(), load_buffer_.linesize_pixels()); + texture_.Upload(in_buffer); } doneCurrent(); @@ -147,44 +143,32 @@ void ViewerGLWidget::SetImageFromLoadBuffer(Frame *in_buffer) update(); } -void ViewerGLWidget::SetEmitDrewManagedTextureEnabled(bool e) +void ViewerDisplayWidget::ConnectSibling(ViewerDisplayWidget *sibling) { - enable_display_referred_signal_ = e; - - if (!enable_display_referred_signal_) { - // Destroy the texture now - managed_texture_.Destroy(); - managed_copy_pipeline_ = nullptr; - framebuffer_.Destroy(); - } -} - -void ViewerGLWidget::ConnectSibling(ViewerGLWidget *sibling) -{ - connect(this, &ViewerGLWidget::LoadedBuffer, sibling, &ViewerGLWidget::SetImageFromLoadBuffer, Qt::QueuedConnection); + connect(this, &ViewerDisplayWidget::LoadedBuffer, sibling, &ViewerDisplayWidget::SetImageFromLoadBuffer, Qt::QueuedConnection); sibling->SetImageFromLoadBuffer(&load_buffer_); } -const ViewerSafeMarginInfo &ViewerGLWidget::GetSafeMargin() const +const ViewerSafeMarginInfo &ViewerDisplayWidget::GetSafeMargin() const { return safe_margin_; } -void ViewerGLWidget::SetSafeMargins(const ViewerSafeMarginInfo &safe_margin) +void ViewerDisplayWidget::SetSafeMargins(const ViewerSafeMarginInfo &safe_margin) { safe_margin_ = safe_margin; update(); } -void ViewerGLWidget::mousePressEvent(QMouseEvent *event) +void ViewerDisplayWidget::mousePressEvent(QMouseEvent *event) { QOpenGLWidget::mousePressEvent(event); emit DragStarted(); } -void ViewerGLWidget::mouseMoveEvent(QMouseEvent *event) +void ViewerDisplayWidget::mouseMoveEvent(QMouseEvent *event) { QOpenGLWidget::mouseMoveEvent(event); @@ -209,11 +193,11 @@ void ViewerGLWidget::mouseMoveEvent(QMouseEvent *event) } } -void ViewerGLWidget::initializeGL() +void ViewerDisplayWidget::initializeGL() { ManagedDisplayWidget::initializeGL(); - connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &ViewerGLWidget::ContextCleanup, Qt::DirectConnection); + connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &ViewerDisplayWidget::ContextCleanup, Qt::DirectConnection); #ifdef Q_OS_LINUX if (!nouveau_check_done_) { @@ -231,7 +215,7 @@ void ViewerGLWidget::initializeGL() #endif } -void ViewerGLWidget::paintGL() +void ViewerDisplayWidget::paintGL() { // Get functions attached to this context (they will already be initialized) QOpenGLFunctions* f = context()->functions(); @@ -243,33 +227,6 @@ void ViewerGLWidget::paintGL() // We only draw if we have a pipeline if (has_image_ && color_service() && texture_.IsCreated()) { - // If we're distributing our display-referred final buffer, we'll have to make a copy of it - if (enable_display_referred_signal_) { - - if (!managed_texture_.IsCreated() - || managed_texture_.width() != texture_.width() - || managed_texture_.height() != texture_.height() - || managed_texture_.format() != texture_.format()) { - managed_texture_.Destroy(); - - managed_texture_.Create(context(), texture_.width(), texture_.height(), texture_.format()); - } - - if (!managed_copy_pipeline_) { - managed_copy_pipeline_ = OpenGLShader::CreateDefault(); - } - - if (!framebuffer_.IsCreated()) { - framebuffer_.Create(context()); - } - - framebuffer_.Attach(&managed_texture_); - framebuffer_.Bind(); - - context()->functions()->glViewport(0, 0, managed_texture_.width(), managed_texture_.height()); - - } - // Bind retrieved texture f->glBindTexture(GL_TEXTURE_2D, texture_.texture()); @@ -279,24 +236,6 @@ void ViewerGLWidget::paintGL() // Release retrieved texture f->glBindTexture(GL_TEXTURE_2D, 0); - if (enable_display_referred_signal_) { - - framebuffer_.Release(); - framebuffer_.Detach(); - - emit DrewManagedTexture(&managed_texture_); - - // Bind retrieved texture - managed_texture_.Bind(); - - context()->functions()->glViewport(0, 0, width(), height()); - - OpenGLRenderFunctions::Blit(managed_copy_pipeline_); - - // Bind retrieved texture - managed_texture_.Release(); - - } } // Draw action/title safe areas @@ -333,7 +272,7 @@ void ViewerGLWidget::paintGL() } #ifdef Q_OS_LINUX -void ViewerGLWidget::ShowNouveauWarning() +void ViewerDisplayWidget::ShowNouveauWarning() { QMessageBox::warning(this, tr("Driver Warning"), @@ -344,14 +283,11 @@ void ViewerGLWidget::ShowNouveauWarning() } #endif -void ViewerGLWidget::ContextCleanup() +void ViewerDisplayWidget::ContextCleanup() { makeCurrent(); - managed_copy_pipeline_ = nullptr; texture_.Destroy(); - managed_texture_.Destroy(); - framebuffer_.Destroy(); doneCurrent(); } diff --git a/app/widget/viewer/viewerglwidget.h b/app/widget/viewer/viewerdisplay.h similarity index 80% rename from app/widget/viewer/viewerglwidget.h rename to app/widget/viewer/viewerdisplay.h index 496456405..43e42eed2 100644 --- a/app/widget/viewer/viewerglwidget.h +++ b/app/widget/viewer/viewerdisplay.h @@ -49,7 +49,7 @@ OLIVE_NAMESPACE_ENTER * the same texture object, use SetTexture() since it will nearly always be faster to just set it than to check *and* * set it. */ -class ViewerGLWidget : public ManagedDisplayWidget +class ViewerDisplayWidget : public ManagedDisplayWidget { Q_OBJECT public: @@ -60,9 +60,9 @@ public: * * QWidget parent. */ - ViewerGLWidget(QWidget* parent = nullptr); + ViewerDisplayWidget(QWidget* parent = nullptr); - virtual ~ViewerGLWidget() override; + virtual ~ViewerDisplayWidget() override; /** * @brief Set an image to load and display on screen @@ -71,7 +71,7 @@ public: const QMatrix4x4& GetMatrix(); - void ConnectSibling(ViewerGLWidget* sibling); + void ConnectSibling(ViewerDisplayWidget* sibling); const ViewerSafeMarginInfo& GetSafeMargin() const; void SetSafeMargins(const ViewerSafeMarginInfo& safe_margin); @@ -101,15 +101,6 @@ public slots: */ void SetImageFromLoadBuffer(Frame* in_buffer); - /** - * @brief Enables or disables DrewManagedTexture() - * - * To emit a display referred texture, it needs to be copied after the color transform is complete. This naturally - * adds extra GPU cycles that are wasted if there's nothing receiving the signal. Therefore, the signal is disabled - * by default. - */ - void SetEmitDrewManagedTextureEnabled(bool e); - signals: /** * @brief Signal emitted when the user starts dragging from the viewer @@ -130,18 +121,6 @@ signals: */ void LoadedBuffer(Frame* load_buffer); - /** - * @brief Signal emitted when a buffer is loaded into a texture - * - * This texture will be the direct output of the renderer in reference space in GPU VRAM. - */ - void LoadedTexture(OpenGLTexture* texture); - - /** - * @brief Emitted when the a texture has been transformed to display - */ - void DrewManagedTexture(OpenGLTexture* texture); - protected: /** * @brief Override the mouse press event simply to emit the DragStarted() signal @@ -173,23 +152,6 @@ private: */ OpenGLTexture texture_; - /** - * @brief Internal framebuffer used to draw to managed_texture_ - */ - OpenGLFramebuffer framebuffer_; - - /** - * @brief Internal referenceto the OpenGL texture that's been managed - * - * Kept so that scopes can use the display-referred buffer without having to transform again. - */ - OpenGLTexture managed_texture_; - - /** - * @brief Pipeline used to draw to managed_texture_ - */ - OpenGLShaderPtr managed_copy_pipeline_; - /** * @brief Drawing matrix (defaults to identity) */ @@ -210,8 +172,6 @@ private: ViewerSafeMarginInfo safe_margin_; - bool enable_display_referred_signal_; - private slots: /** * @brief Slot to connect just before the OpenGL context is destroyed to clean up resources diff --git a/app/widget/viewer/viewerwindow.cpp b/app/widget/viewer/viewerwindow.cpp index f05961e4a..3f92f997d 100644 --- a/app/widget/viewer/viewerwindow.cpp +++ b/app/widget/viewer/viewerwindow.cpp @@ -32,11 +32,11 @@ ViewerWindow::ViewerWindow(QWidget *parent) : layout->setMargin(0); layout->setSpacing(0); - gl_widget_ = new ViewerGLWidget(); + gl_widget_ = new ViewerDisplayWidget(); layout->addWidget(gl_widget_); } -ViewerGLWidget *ViewerWindow::gl_widget() const +ViewerDisplayWidget *ViewerWindow::gl_widget() const { return gl_widget_; } diff --git a/app/widget/viewer/viewerwindow.h b/app/widget/viewer/viewerwindow.h index 19c1676a4..8baa30c9c 100644 --- a/app/widget/viewer/viewerwindow.h +++ b/app/widget/viewer/viewerwindow.h @@ -23,7 +23,7 @@ #include -#include "viewerglwidget.h" +#include "viewerdisplay.h" OLIVE_NAMESPACE_ENTER @@ -32,7 +32,7 @@ class ViewerWindow : public QWidget public: ViewerWindow(QWidget* parent = nullptr); - ViewerGLWidget* gl_widget() const; + ViewerDisplayWidget* gl_widget() const; /** * @brief Used to adjust resulting picture to be the right aspect ratio @@ -45,7 +45,7 @@ protected: virtual void closeEvent(QCloseEvent* e) override; private: - ViewerGLWidget* gl_widget_; + ViewerDisplayWidget* gl_widget_; }; diff --git a/app/window/mainwindow/mainwindow.cpp b/app/window/mainwindow/mainwindow.cpp index 5b19b855c..bd375f994 100644 --- a/app/window/mainwindow/mainwindow.cpp +++ b/app/window/mainwindow/mainwindow.cpp @@ -77,27 +77,17 @@ MainWindow::MainWindow(QWidget *parent) : AppendProjectPanel(); tool_panel_ = PanelManager::instance()->CreatePanel(this); task_man_panel_ = PanelManager::instance()->CreatePanel(this); - curve_panel_ = PanelManager::instance()->CreatePanel(this); AppendTimelinePanel(); audio_monitor_panel_ = PanelManager::instance()->CreatePanel(this); // Make connections to sequence viewer connect(node_panel_, &NodePanel::SelectionChanged, param_panel_, &ParamPanel::SetNodes); - connect(param_panel_, &ParamPanel::SelectedInputChanged, curve_panel_, &CurvePanel::SetInput); - connect(param_panel_, &ParamPanel::TimebaseChanged, curve_panel_, &CurvePanel::SetTimebase); - connect(param_panel_, &ParamPanel::TimeTargetChanged, curve_panel_, &CurvePanel::SetTimeTarget); connect(param_panel_, &ParamPanel::RequestSelectNode, node_panel_, &NodePanel::Select); - connect(sequence_viewer_panel_, &SequenceViewerPanel::TimeChanged, param_panel_, &ParamPanel::SetTime); - connect(sequence_viewer_panel_, &SequenceViewerPanel::TimeChanged, curve_panel_, &CurvePanel::SetTime); - connect(param_panel_, &ParamPanel::TimeChanged, sequence_viewer_panel_, &SequenceViewerPanel::SetTime); - connect(param_panel_, &ParamPanel::TimeChanged, curve_panel_, &CurvePanel::SetTime); - connect(curve_panel_, &CurvePanel::TimeChanged, sequence_viewer_panel_, &SequenceViewerPanel::SetTime); - connect(curve_panel_, &CurvePanel::TimeChanged, param_panel_, &ParamPanel::SetTime); - + connect(sequence_viewer_panel_, &SequenceViewerPanel::TimeChanged, param_panel_, &ParamPanel::SetTimestamp); + connect(param_panel_, &ParamPanel::TimeChanged, sequence_viewer_panel_, &SequenceViewerPanel::SetTimestamp); connect(PanelManager::instance(), &PanelManager::FocusedPanelChanged, this, &MainWindow::FocusedPanelChanged); sequence_viewer_panel_->ConnectTimeBasedPanel(param_panel_); - sequence_viewer_panel_->ConnectTimeBasedPanel(curve_panel_); footage_viewer_panel_->ConnectPixelSamplerPanel(pixel_sampler_panel_); sequence_viewer_panel_->ConnectPixelSamplerPanel(pixel_sampler_panel_); @@ -243,24 +233,35 @@ void MainWindow::FolderOpen(Project* p, Item *i, bool floating) // If the panel is closed, just destroy it panel->SetSignalInsteadOfClose(true); - connect(panel, &ProjectPanel::CloseRequested, this, &MainWindow::FolderCloseRequested); + panel->setProperty("parent_list", reinterpret_cast(&folder_panels_)); + connect(panel, &ProjectPanel::CloseRequested, this, &MainWindow::FloatingPanelCloseRequested); folder_panels_.append(panel); } ScopePanel *MainWindow::AppendScopePanel() { - ScopePanel* panel = PanelManager::instance()->CreatePanel(this); + return AppendFloatingPanelInternal(scope_panels_); +} - SetUniquePanelID(panel, scope_panels_); +CurvePanel *MainWindow::AppendCurvePanel() +{ + CurvePanel* p = AppendFloatingPanelInternal(curve_panels_); - panel->setFloating(true); - panel->show(); + sequence_viewer_panel_->ConnectTimeBasedPanel(p); - panel->SetSignalInsteadOfClose(true); - connect(panel, &ScopePanel::CloseRequested, this, &MainWindow::ScopeCloseRequested); + return p; - return panel; + /*connect(panel, &TimelinePanel::TimeChanged, curve_panel_, &CurvePanel::SetTime); + connect(curve_panel_, &CurvePanel::TimeChanged, panel, &TimelinePanel::SetTime); + connect(param_panel_, &ParamPanel::SelectedInputChanged, curve_panel_, &CurvePanel::SetInput); + connect(param_panel_, &ParamPanel::TimebaseChanged, curve_panel_, &CurvePanel::SetTimebase); + connect(param_panel_, &ParamPanel::TimeTargetChanged, curve_panel_, &CurvePanel::SetTimeTarget); + connect(param_panel_, &ParamPanel::TimeChanged, curve_panel_, &CurvePanel::SetTime); + connect(curve_panel_, &CurvePanel::TimeChanged, sequence_viewer_panel_, &SequenceViewerPanel::SetTime); + connect(curve_panel_, &CurvePanel::TimeChanged, param_panel_, &ParamPanel::SetTime); + sequence_viewer_panel_->ConnectTimeBasedPanel(curve_panel_); + connect(sequence_viewer_panel_, &SequenceViewerPanel::TimeChanged, curve_panel_, &CurvePanel::SetTime);*/ } void MainWindow::SetFullscreen(bool fullscreen) @@ -423,19 +424,14 @@ void MainWindow::ProjectCloseRequested() Core::instance()->CloseProject(Core::instance()->GetSharedPtrFromProject(p), true); } -void MainWindow::FolderCloseRequested() +void MainWindow::FloatingPanelCloseRequested() { - ProjectPanel* panel = static_cast(sender()); + PanelWidget* panel = static_cast(sender()); - folder_panels_.removeOne(panel); - panel->deleteLater(); -} + quintptr list_ptr = panel->property("parent_list").value(); + QList* list = reinterpret_cast< QList* >(list_ptr); -void MainWindow::ScopeCloseRequested() -{ - ScopePanel* panel = static_cast(sender()); - - scope_panels_.removeOne(panel); + list->removeOne(panel); panel->deleteLater(); } @@ -491,13 +487,11 @@ TimelinePanel* MainWindow::AppendTimelinePanel() TimelinePanel* panel = AppendPanelInternal(timeline_panels_); connect(panel, &PanelWidget::CloseRequested, this, &MainWindow::TimelineCloseRequested); - connect(panel, &TimelinePanel::TimeChanged, param_panel_, &ParamPanel::SetTime); - connect(panel, &TimelinePanel::TimeChanged, sequence_viewer_panel_, &SequenceViewerPanel::SetTime); - connect(panel, &TimelinePanel::TimeChanged, curve_panel_, &CurvePanel::SetTime); + connect(panel, &TimelinePanel::TimeChanged, param_panel_, &ParamPanel::SetTimestamp); + connect(panel, &TimelinePanel::TimeChanged, sequence_viewer_panel_, &SequenceViewerPanel::SetTimestamp); connect(panel, &TimelinePanel::SelectionChanged, node_panel_, &NodePanel::SelectWithDependencies); - connect(param_panel_, &ParamPanel::TimeChanged, panel, &TimelinePanel::SetTime); - connect(curve_panel_, &CurvePanel::TimeChanged, panel, &TimelinePanel::SetTime); - connect(sequence_viewer_panel_, &SequenceViewerPanel::TimeChanged, panel, &TimelinePanel::SetTime); + connect(param_panel_, &ParamPanel::TimeChanged, panel, &TimelinePanel::SetTimestamp); + connect(sequence_viewer_panel_, &SequenceViewerPanel::TimeChanged, panel, &TimelinePanel::SetTimestamp); connect(sequence_viewer_panel_->video_renderer(), &VideoRenderBackend::CachedTimeReady, panel->ruler(), &TimeRuler::CacheTimeReady); connect(sequence_viewer_panel_->video_renderer(), &VideoRenderBackend::RangeInvalidated, panel->ruler(), &TimeRuler::CacheInvalidatedRange); @@ -598,10 +592,6 @@ void MainWindow::SetDefaultLayout() timeline_panels_.first()->show(); addDockWidget(Qt::BottomDockWidgetArea, timeline_panels_.first()); - curve_panel_->hide(); - curve_panel_->setFloating(true); - addDockWidget(Qt::BottomDockWidgetArea, curve_panel_); - task_man_panel_->hide(); task_man_panel_->setFloating(true); addDockWidget(Qt::BottomDockWidgetArea, task_man_panel_); @@ -652,4 +642,22 @@ void MainWindow::SetUniquePanelID(T *panel, const QList &list) panel->setObjectName(panel->objectName().append(QString::number(list.size()))); } +template +T *MainWindow::AppendFloatingPanelInternal(QList &list) +{ + T* panel = PanelManager::instance()->CreatePanel(this); + + SetUniquePanelID(panel, list); + + panel->setFloating(true); + panel->show(); + + panel->SetSignalInsteadOfClose(true); + connect(panel, &PanelWidget::CloseRequested, this, &MainWindow::FloatingPanelCloseRequested); + + panel->setProperty("parent_list", reinterpret_cast(&list)); + + return panel; +} + OLIVE_NAMESPACE_EXIT diff --git a/app/window/mainwindow/mainwindow.h b/app/window/mainwindow/mainwindow.h index 643e72133..362b88ca4 100644 --- a/app/window/mainwindow/mainwindow.h +++ b/app/window/mainwindow/mainwindow.h @@ -68,6 +68,8 @@ public: ScopePanel* AppendScopePanel(); + CurvePanel* AppendCurvePanel(); + #ifdef Q_OS_WINDOWS void SetTaskbarButtonState(TBPFLAG flags); @@ -100,6 +102,9 @@ private: template T* AppendPanelInternal(QList& list); + template + T* AppendFloatingPanelInternal(QList& list); + template void SetUniquePanelID(T* panel, const QList& list); @@ -122,7 +127,7 @@ private: QList timeline_panels_; AudioMonitorPanel* audio_monitor_panel_; TaskManagerPanel* task_man_panel_; - CurvePanel* curve_panel_; + QList curve_panels_; PixelSamplerPanel* pixel_sampler_panel_; QList scope_panels_; @@ -141,9 +146,7 @@ private slots: void ProjectCloseRequested(); - void FolderCloseRequested(); - - void ScopeCloseRequested(); + void FloatingPanelCloseRequested(); void LoadLayoutInternal(QXmlStreamReader* reader, XMLNodeData *xml_data);