diff --git a/app/node/block/block.cpp b/app/node/block/block.cpp index 4c31954f5..5d57b46c5 100644 --- a/app/node/block/block.cpp +++ b/app/node/block/block.cpp @@ -27,6 +27,7 @@ Block::Block() : { previous_input_ = new NodeInput("prev_block"); previous_input_->add_data_input(NodeParam::kBlock); + previous_input_->set_dependent(false); AddParameter(previous_input_); block_output_ = new NodeOutput("block_out"); @@ -63,8 +64,12 @@ const rational& Block::length() void Block::set_length(const rational &length) { + Lock(); + length_ = length; + Unlock(); + RefreshFollowing(); } @@ -181,16 +186,6 @@ void Block::set_media_in(const rational &media_in) } } -QList Block::RunDependencies(NodeOutput* param, const rational &time) -{ - // Base Blocks have no direct dependencies - - Q_UNUSED(param) - Q_UNUSED(time) - - return QList(); -} - rational Block::SequenceToMediaTime(const rational &sequence_time) { // These constants are not considered "values" per se, so we don't modify them diff --git a/app/node/block/block.h b/app/node/block/block.h index b1d693b9a..ee08ce4b2 100644 --- a/app/node/block/block.h +++ b/app/node/block/block.h @@ -67,11 +67,6 @@ public: const rational& media_in(); void set_media_in(const rational& media_in); - /** - * @brief Override removes previous input as that is not a direct dependency - */ - virtual QList RunDependencies(NodeOutput* output, const rational &time) override; - public slots: /** * @brief Refreshes internal cache of in/out points up to date diff --git a/app/node/input.cpp b/app/node/input.cpp index 94f519dbb..c7bd6775b 100644 --- a/app/node/input.cpp +++ b/app/node/input.cpp @@ -20,11 +20,13 @@ #include "input.h" +#include "node.h" #include "output.h" NodeInput::NodeInput(const QString& id) : NodeParam(id), - keyframing_(false) + keyframing_(false), + dependent_(true) { // Have at least one keyframe/value active at any time keyframes_.append(NodeKeyframe()); @@ -74,8 +76,6 @@ QVariant NodeInput::get_value(const rational& time) { QVariant v; - lock_.lock(); - if (time_ != time) { // Retrieve the value if (!edges_.isEmpty()) { @@ -92,13 +92,15 @@ QVariant NodeInput::get_value(const rational& time) v = value_; - lock_.unlock(); - return v; } void NodeInput::set_value(const QVariant &value) { + bool lock_mutex = (parent() != nullptr); + + if (lock_mutex) parent()->Lock(); + if (keyframing()) { // FIXME: Keyframing code using time() } else { @@ -107,6 +109,8 @@ void NodeInput::set_value(const QVariant &value) emit ValueChanged(RATIONAL_MIN, RATIONAL_MAX); } + + if (lock_mutex) parent()->Unlock(); } bool NodeInput::keyframing() @@ -119,7 +123,31 @@ void NodeInput::set_keyframing(bool k) keyframing_ = k; } +bool NodeInput::dependent() +{ + return dependent_; +} + +void NodeInput::set_dependent(bool d) +{ + dependent_ = d; +} + const QList &NodeInput::inputs() { return inputs_; } + +void NodeInput::CopyValues(NodeInput *source, NodeInput *dest) +{ + // Copy values + dest->keyframes_ = source->keyframes_; + + // Copy keyframing state + dest->set_keyframing(source->keyframing()); + + // Copy connections + if (source->get_connected_output() != nullptr) { + ConnectEdge(source->get_connected_output(), dest); + } +} diff --git a/app/node/input.h b/app/node/input.h index 03936532b..6c56d6eca 100644 --- a/app/node/input.h +++ b/app/node/input.h @@ -103,11 +103,28 @@ public: */ void set_keyframing(bool k); + /** + * @brief Return whether the Node is dependent on this input or not + * + * \see set_dependent() + */ + bool dependent(); + + /** + * @brief Set whether the Node is dependent on this input + */ + void set_dependent(bool d); + /** * @brief A list of input data types accepted by this parameter */ const QList& inputs(); + /** + * @brief Copy all values including keyframe information and connections from another NodeInput + */ + static void CopyValues(NodeInput* source, NodeInput* dest); + signals: void ValueChanged(const rational& start, const rational& end); @@ -132,6 +149,11 @@ private: */ bool keyframing_; + /** + * @brief Internal dependent setting + */ + bool dependent_; + }; #endif // NODEINPUT_H diff --git a/app/node/input/media/media.cpp b/app/node/input/media/media.cpp index 309e1c15d..ffec28176 100644 --- a/app/node/input/media/media.cpp +++ b/app/node/input/media/media.cpp @@ -103,7 +103,6 @@ void MediaInput::Hash(QCryptographicHash *hash, NodeOutput *from, const rational // Use frame value from Decoder if (from == texture_output_ && SetupDecoder()) { - qDebug() << "[MediaInput] Hashing pts" << decoder_->GetTimestampFromTime(time); hash->addData(QString::number(decoder_->GetTimestampFromTime(time)).toUtf8()); // FIXME: Add OCIO data // FIXME: Add alpha association value diff --git a/app/node/node.cpp b/app/node/node.cpp index 641567f81..19d04abd1 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -98,6 +98,36 @@ void Node::InvalidateCache(const rational &start_range, const rational &end_rang } } +void Node::Lock() +{ + lock_.lock(); +} + +void Node::Unlock() +{ + lock_.unlock(); +} + +void Node::CopyInputs(Node *source, Node *destination) +{ + Q_ASSERT(source->id() == destination->id()); + + QList src_param = source->parameters(); + QList dst_param = destination->parameters(); + + for (int i=0;itype() == NodeParam::kInput) { + NodeInput* src = static_cast(src_param.at(i)); + + if (src->dependent()) { + NodeInput* dst = static_cast(dst_param.at(i)); + + NodeInput::CopyValues(src, dst); + } + } + } +} + void Node::IgnoreCacheInvalidationFrom(NodeInput *input) { ignore_invalid_cache_inputs_.append(input); @@ -131,13 +161,7 @@ NodeOutput *Node::LastProcessedOutput() QVariant Node::Run(NodeOutput* output, const rational& time) { - lock_.lock(); - - QVariant v = Value(output, time); - - lock_.unlock(); - - return v; + return Value(output, time); } NodeParam *Node::ParamAt(int index) @@ -178,7 +202,9 @@ void GetDependenciesInternal(Node* n, QList& list, bool traverse) { foreach (NodeEdgePtr edge, param_edges) { Node* connected_node = edge->output()->parent(); - list.append(connected_node); + if (!list.contains(connected_node)) { + list.append(connected_node); + } if (traverse) { GetDependenciesInternal(connected_node, list, traverse); @@ -250,13 +276,17 @@ QList Node::RunDependencies(NodeOutput *output, const rational & foreach (NodeParam* p, params) { if (p->type() == NodeParam::kInput) { - NodeOutput* potential_dep = static_cast(p)->get_connected_output(); + NodeInput* input = static_cast(p); - if (potential_dep != nullptr) { - run_deps.append(NodeDependency(potential_dep, time)); + // Check if Node is dependent on this input or not + if (input->dependent()) { + NodeOutput* potential_dep = input->get_connected_output(); + + if (potential_dep != nullptr) { + run_deps.append(NodeDependency(potential_dep, time)); + } } } - } return run_deps; @@ -285,11 +315,14 @@ void Node::Hash(QCryptographicHash *hash, NodeOutput* from, const rational &time { // Add this Node's ID hash->addData(id().toUtf8()); + qDebug() << "Hashing" << id(); // Add each value QList params = parameters(); foreach (NodeParam* param, params) { - if (param->type() == NodeParam::kInput && !param->IsConnected()) { + if (param->type() == NodeParam::kInput + && !param->IsConnected() + && static_cast(param)->dependent()) { // Get the value at this time QVariant v = static_cast(param)->get_value(time); hash->addData(v.toByteArray()); // FIXME: Does this work on all value types? diff --git a/app/node/node.h b/app/node/node.h index b6475c31e..7a5f59430 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -130,9 +130,9 @@ public: QList GetImmediateDependencies(); /** - * @brief Thread-safe wrapper for Process() + * @brief Wrapper for Process() * - * It's recommended to call this directly over Process(), yet in derivatives of Node, override Process(). + * It's recommended to call this directly over Value(), yet in derivatives of Node, override Value(). */ QVariant Run(NodeOutput* output, const rational& time); @@ -175,6 +175,23 @@ public: */ virtual void InvalidateCache(const rational& start_range, const rational& end_range, NodeInput* from = nullptr); + /** + * @brief Lock mutex (for thread safety) + */ + void Lock(); + + /** + * @brief Unock mutex (for thread safety) + */ + void Unlock(); + + /** + * @brief Copies inputs from from Node to another including connections + * + * Nodes must be of the same types (i.e. have the same ID) + */ + static void CopyInputs(Node* source, Node* destination); + protected: /** * @brief Add a parameter to this node @@ -265,7 +282,7 @@ private: NodeOutput* last_processed_parameter_; /** - * @brief Used for thread safety in Run() + * @brief Used for thread safety */ QMutex lock_; diff --git a/app/node/output.cpp b/app/node/output.cpp index 17ffef804..c59ede324 100644 --- a/app/node/output.cpp +++ b/app/node/output.cpp @@ -51,8 +51,6 @@ QVariant NodeOutput::get_value(const rational& time) { QVariant v; - lock_.lock(); - if (time_ != time) { // Update the value value_ = parent()->Run(this, time); @@ -62,8 +60,6 @@ QVariant NodeOutput::get_value(const rational& time) v = value_; - lock_.unlock(); - return v; } diff --git a/app/node/output/track/track.cpp b/app/node/output/track/track.cpp index cb69dc23a..2de89e3a8 100644 --- a/app/node/output/track/track.cpp +++ b/app/node/output/track/track.cpp @@ -31,6 +31,7 @@ TrackOutput::TrackOutput() : { track_input_ = new NodeInput("track_in"); track_input_->add_data_input(NodeParam::kTrack); + track_input_->set_dependent(false); AddParameter(track_input_); track_output_ = new NodeOutput("track_out"); @@ -359,6 +360,10 @@ void TrackOutput::RemoveBlock(Block *block) void TrackOutput::RippleRemoveBlock(Block *block) { + BlockInvalidateCache(); + + rational remove_in = block->in(); + Block* previous = block->previous(); Block* next = block->next(); @@ -374,6 +379,10 @@ void TrackOutput::RippleRemoveBlock(Block *block) Block::ConnectBlocks(previous, next); } + UnblockInvalidateCache(); + + InvalidateCache(remove_in, in()); + // FIXME: Should there be removing the Blocks from the graph? } @@ -391,8 +400,12 @@ Block* TrackOutput::SplitBlock(Block *block, rational time) Block* copy = block->copy(); copy->set_length(original_length - block->length()); + copy->set_media_in(block->media_in() + block->length()); + InsertBlockAfter(copy, block); + Node::CopyInputs(block, copy); + UnblockInvalidateCache(); return copy; @@ -449,6 +462,8 @@ void TrackOutput::RippleRemoveArea(rational in, rational out, Block *insert) } } + BlockInvalidateCache(); + // If we picked up a block to splice if (splice != nullptr) { @@ -495,6 +510,10 @@ void TrackOutput::RippleRemoveArea(rational in, rational out, Block *insert) InsertBlockBetweenBlocks(insert, trim_out_to_in, trim_in_to_out); } } + + UnblockInvalidateCache(); + + InvalidateCache(in, out); } void TrackOutput::ReplaceBlock(Block *old, Block *replace) diff --git a/app/node/param.cpp b/app/node/param.cpp index 68d775ae9..93efe4dcc 100644 --- a/app/node/param.cpp +++ b/app/node/param.cpp @@ -149,11 +149,17 @@ NodeEdgePtr NodeParam::ConnectEdge(NodeOutput *output, NodeInput *input) NodeEdgePtr edge = std::make_shared(output, input); + output->parent()->Lock(); + input->parent()->Lock(); + output->edges_.append(edge); input->edges_.append(edge); input->ClearCachedValue(); + output->parent()->Unlock(); + input->parent()->Unlock(); + // Emit a signal than an edge was added (only one signal needs emitting) emit input->EdgeAdded(edge); @@ -165,11 +171,17 @@ void NodeParam::DisconnectEdge(NodeEdgePtr edge) NodeOutput* output = edge->output(); NodeInput* input = edge->input(); + output->parent()->Lock(); + input->parent()->Lock(); + output->edges_.removeAll(edge); input->edges_.removeAll(edge); input->ClearCachedValue(); + output->parent()->Unlock(); + input->parent()->Unlock(); + emit input->EdgeRemoved(edge); } diff --git a/app/node/param.h b/app/node/param.h index 65b2044db..34b944385 100644 --- a/app/node/param.h +++ b/app/node/param.h @@ -231,11 +231,6 @@ protected: */ QVector edges_; - /** - * @brief Used for thread safety - */ - QMutex lock_; - /** * @brief Currently cached value */ diff --git a/app/node/processor/renderer/renderer.cpp b/app/node/processor/renderer/renderer.cpp index 577947207..98f073dfd 100644 --- a/app/node/processor/renderer/renderer.cpp +++ b/app/node/processor/renderer/renderer.cpp @@ -98,6 +98,8 @@ QVariant RendererProcessor::Value(NodeOutput* output, const rational& time) // Find frame in map if (time_hash_map_.contains(time)) { + qDebug() << "Showed:" << time_hash_map_[time].toHex(); + QString fn = CachePathName(time_hash_map_[time]); if (QFileInfo::exists(fn)) { @@ -362,6 +364,8 @@ void RendererProcessor::DownloadThreadFinished(const rational& time) texture_output_->ClearCachedValue(); foreach (NodeEdgePtr edge, edges) { + edge->input()->ClearCachedValue(); + edge->input()->parent()->InvalidateCache(time, time, edge->input()); diff --git a/app/node/processor/renderer/rendererprocessthread.cpp b/app/node/processor/renderer/rendererprocessthread.cpp index ed323adfc..3e00fbf3f 100644 --- a/app/node/processor/renderer/rendererprocessthread.cpp +++ b/app/node/processor/renderer/rendererprocessthread.cpp @@ -85,11 +85,20 @@ void RendererProcessThread::ProcessLoop() NodeOutput* output_to_process = path_.node(); Node* node_to_process = output_to_process->parent(); + node_to_process->Lock(); + + QList all_deps = node_to_process->GetDependencies(); + foreach (Node* dep, all_deps) { + dep->Lock(); + } + // Check hash QCryptographicHash hasher(QCryptographicHash::Sha1); node_to_process->Hash(&hasher, output_to_process, path_.time()); hash_ = hasher.result(); + texture_ = nullptr; + if (!parent_->HasHash(hash_)) { QList deps = node_to_process->RunDependencies(output_to_process, path_.time()); @@ -106,6 +115,12 @@ void RendererProcessThread::ProcessLoop() render_instance()->context()->functions()->glFinish(); } + foreach (Node* dep, all_deps) { + dep->Unlock(); + } + + node_to_process->Unlock(); + emit FinishedPath(); } } diff --git a/app/node/processor/renderer/rendererthreadbase.cpp b/app/node/processor/renderer/rendererthreadbase.cpp index 34589638e..6d0fc2677 100644 --- a/app/node/processor/renderer/rendererthreadbase.cpp +++ b/app/node/processor/renderer/rendererthreadbase.cpp @@ -31,6 +31,7 @@ RendererThreadBase::RendererThreadBase(QOpenGLContext *share_ctx, const int &wid mode_(mode), render_instance_(nullptr) { + connect(share_ctx_, SIGNAL(aboutToBeDestroyed()), this, SLOT(Cancel())); } void RendererThreadBase::Cancel() diff --git a/app/node/processor/renderer/rendererthreadbase.h b/app/node/processor/renderer/rendererthreadbase.h index fc1b8f302..8fe1e15f5 100644 --- a/app/node/processor/renderer/rendererthreadbase.h +++ b/app/node/processor/renderer/rendererthreadbase.h @@ -39,14 +39,15 @@ public: const olive::PixelFormat& format, const olive::RenderMode& mode); - void Cancel(); - RenderInstance* render_instance(); void StartThread(Priority priority = InheritPriority); virtual void run() override; +public slots: + void Cancel(); + protected: virtual void ProcessLoop() = 0; diff --git a/app/render/renderframebuffer.cpp b/app/render/renderframebuffer.cpp index 5fc21178c..148be05a7 100644 --- a/app/render/renderframebuffer.cpp +++ b/app/render/renderframebuffer.cpp @@ -48,6 +48,8 @@ void RenderFramebuffer::Create(QOpenGLContext *ctx) context_ = ctx; + connect(context_, SIGNAL(aboutToBeDestroyed()), this, SLOT(Destroy())); + // Create framebuffer object context_->functions()->glGenFramebuffers(1, &buffer_); } @@ -55,6 +57,8 @@ void RenderFramebuffer::Create(QOpenGLContext *ctx) void RenderFramebuffer::Destroy() { if (context_ != nullptr) { + disconnect(context_, SIGNAL(aboutToBeDestroyed()), this, SLOT(Destroy())); + context_->functions()->glDeleteFramebuffers(1, &buffer_); buffer_ = 0; diff --git a/app/render/renderframebuffer.h b/app/render/renderframebuffer.h index 10f3fac98..c1353ad62 100644 --- a/app/render/renderframebuffer.h +++ b/app/render/renderframebuffer.h @@ -27,6 +27,7 @@ class RenderFramebuffer : public QObject { + Q_OBJECT public: RenderFramebuffer(); ~RenderFramebuffer(); @@ -37,8 +38,6 @@ public: void Create(QOpenGLContext *ctx); - void Destroy(); - bool IsCreated() const; void Bind(); @@ -53,6 +52,9 @@ public: const GLuint& buffer() const; +public slots: + void Destroy(); + private: void AttachInternal(GLuint tex); diff --git a/app/render/rendertexture.cpp b/app/render/rendertexture.cpp index 16ac9680d..a2f823277 100644 --- a/app/render/rendertexture.cpp +++ b/app/render/rendertexture.cpp @@ -25,10 +25,6 @@ #include "render/pixelservice.h" -// FIXME: Test code -QMutex m; -// End test code - RenderTexture::RenderTexture() : context_(nullptr), texture_(0) @@ -64,6 +60,8 @@ void RenderTexture::Create(QOpenGLContext *ctx, int width, int height, const oli height_ = height; format_ = format; + connect(context_, SIGNAL(aboutToBeDestroyed()), this, SLOT(Destroy())); + // Create main texture CreateInternal(&texture_, data); @@ -76,6 +74,8 @@ void RenderTexture::Create(QOpenGLContext *ctx, int width, int height, const oli void RenderTexture::Destroy() { if (context_ != nullptr) { + disconnect(context_, SIGNAL(aboutToBeDestroyed()), this, SLOT(Destroy())); + context_->functions()->glDeleteTextures(1, &texture_); texture_ = 0; diff --git a/app/render/rendertexture.h b/app/render/rendertexture.h index 360f451d0..4d405b3c0 100644 --- a/app/render/rendertexture.h +++ b/app/render/rendertexture.h @@ -26,13 +26,9 @@ #include "pixelformat.h" -// FIXME: Test code -#include -extern QMutex m; -// End test code - class RenderTexture : public QObject { + Q_OBJECT public: enum Type { kSingleBuffer, @@ -51,8 +47,6 @@ public: bool IsCreated() const; - void Destroy(); - void Bind(); void Release(); @@ -73,6 +67,9 @@ public: uchar *Download() const; +public slots: + void Destroy(); + private: void CreateInternal(GLuint *tex, void *data = nullptr);