From bdf513585f092fb976bd55b6290ffdfb289b4c22 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 20 May 2019 03:26:14 +1000 Subject: [PATCH] started memory cache server --- CMakeLists.txt | 2 + effects/effectfield.cpp | 4 +- effects/effectloaders.cpp | 2 +- nodes/node.cpp | 10 +-- nodes/node.h | 9 +- nodes/nodegraph.cpp | 47 +++++++++- nodes/nodegraph.h | 25 +++++- nodes/nodes/nodemedia.cpp | 48 +++++----- nodes/nodes/nodemedia.h | 24 +++-- rendering/memorycache.cpp | 181 ++++++++++++++++++++++++++++++++++++++ rendering/memorycache.h | 58 ++++++++++++ 11 files changed, 364 insertions(+), 46 deletions(-) create mode 100644 rendering/memorycache.cpp create mode 100644 rendering/memorycache.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 44d006642..3cd5bade8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -286,6 +286,8 @@ set(OLIVE_SOURCES rendering/exportthread.h rendering/framebufferobject.cpp rendering/framebufferobject.h + rendering/memorycache.h + rendering/memorycache.cpp rendering/pixelformats.cpp rendering/pixelformats.h rendering/qopenglshaderprogramptr.h diff --git a/effects/effectfield.cpp b/effects/effectfield.cpp index 0f225594e..d824f1cf8 100644 --- a/effects/effectfield.cpp +++ b/effects/effectfield.cpp @@ -220,7 +220,7 @@ void EffectField::PrepareDataForKeyframing(bool enabled, ComboAction *ca) // Create keyframe from perpetual data EffectKeyframe key; - key.time = GetParentRow()->ParentNode()->ParentGraph()->Time(); + key.time = GetParentRow()->ParentNode()->Time(); key.data = persistent_data_; key.type = EFFECT_KEYFRAME_LINEAR; @@ -233,7 +233,7 @@ void EffectField::PrepareDataForKeyframing(bool enabled, ComboAction *ca) // Convert keyframes to one "perpetual" keyframe // Set first keyframe to whatever the data is now - ca->append(new SetQVariant(&persistent_data_, persistent_data_, GetValueAt(GetParentRow()->ParentNode()->ParentGraph()->Time()))); + ca->append(new SetQVariant(&persistent_data_, persistent_data_, GetValueAt(GetParentRow()->ParentNode()->Time()))); // Delete all keyframes for (int i=0;i(nullptr); olive::node_library[kCornerPinEffect] = std::make_shared(nullptr); olive::node_library[kRichTextInput] = std::make_shared(nullptr); - olive::node_library[kMediaInput] = std::make_shared(nullptr); + //olive::node_library[kMediaInput] = std::make_shared(nullptr); olive::node_library[kImageOutput] = std::make_shared(nullptr); olive::node_library[kCrossDissolveTransition] = std::make_shared(nullptr); olive::node_library[kLinearFadeTransition] = std::make_shared(nullptr); diff --git a/nodes/node.cpp b/nodes/node.cpp index e28a638bf..e92ddc15c 100644 --- a/nodes/node.cpp +++ b/nodes/node.cpp @@ -38,16 +38,16 @@ int Node::ParameterCount() return parameters_.size(); } +void Node::Process(rational time) +{ + Q_UNUSED(time) +} + NodeGraph *Node::ParentGraph() { return static_cast(parent()); } -double Node::Time() -{ - return ParentGraph()->Time(); -} - const QPointF &Node::pos() { return pos_; diff --git a/nodes/node.h b/nodes/node.h index b8e00c1db..a9ed6026a 100644 --- a/nodes/node.h +++ b/nodes/node.h @@ -1,9 +1,8 @@ #ifndef NODE_H #define NODE_H -#include - #include "nodes/nodeio.h" +#include "global/rational.h" class NodeGraph; @@ -16,16 +15,20 @@ public: virtual QString name(); virtual QString id(); + virtual void Process(rational time); + void AddParameter(NodeIO* row); int IndexOfParameter(NodeIO* row); NodeIO* Parameter(int i); int ParameterCount(); NodeGraph* ParentGraph(); - double Time(); const QPointF& pos(); +protected: + virtual void GLContextChangeEvent(); + public slots: void SetPos(const QPointF& pos); diff --git a/nodes/nodegraph.cpp b/nodes/nodegraph.cpp index 8da807dea..d2bfb22fe 100644 --- a/nodes/nodegraph.cpp +++ b/nodes/nodegraph.cpp @@ -3,11 +3,20 @@ #include #include -NodeGraph::NodeGraph() +NodeGraph::NodeGraph() : + output_node_(nullptr), + ctx_(nullptr) { } +void NodeGraph::Process() +{ + if (output_node_ != nullptr) { + output_node_->Process(Time()); + } +} + void NodeGraph::childEvent(QChildEvent *event) { if (event->type() == QEvent::ChildAdded || event->type() == QEvent::ChildRemoved) { @@ -25,13 +34,45 @@ Node *NodeGraph::OutputNode() return output_node_; } -double NodeGraph::Time() +const rational& NodeGraph::Time() { return time_; } -void NodeGraph::SetTime(double d) +void NodeGraph::SetTime(const rational &d) { time_ = d; emit TimeChanged(); } + +MemoryCache *NodeGraph::memory_cache() +{ + return &memory_cache_; +} + +const int &NodeGraph::width() +{ + return width_; +} + +const int &NodeGraph::height() +{ + return height_; +} + +void NodeGraph::set_width(const int& w) +{ + width_ = w; + emit ParametersChanged(); +} + +void NodeGraph::set_height(const int& h) +{ + height_ = h; + emit ParametersChanged(); +} + +void NodeGraph::SetGLContext(QOpenGLContext *ctx) +{ + memory_cache_.SetParameters(ctx, width_, height_); +} diff --git a/nodes/nodegraph.h b/nodes/nodegraph.h index 707bba172..1b61fc699 100644 --- a/nodes/nodegraph.h +++ b/nodes/nodegraph.h @@ -5,6 +5,7 @@ #include #include "nodes/node.h" +#include "rendering/memorycache.h" class NodeGraph : public QObject { @@ -12,6 +13,12 @@ class NodeGraph : public QObject public: NodeGraph(); + const int& width(); + void set_width(const int& w); + + const int& height(); + void set_height(const int& h); + /** * @brief Process the graph * @@ -41,12 +48,18 @@ public: */ Node* OutputNode(); - double Time(); - void SetTime(double d); + const rational &Time(); + void SetTime(const rational& d); + + MemoryCache* memory_cache(); + + QOpenGLContext* GLContext(); + void SetGLContext(QOpenGLContext* ctx); signals: void NodeGraphChanged(); void TimeChanged(); + void ParametersChanged(); protected: virtual void childEvent(QChildEvent *event) override; @@ -54,7 +67,13 @@ protected: private: Node* output_node_; - double time_; + MemoryCache memory_cache_; + QOpenGLContext* ctx_; + + int width_; + int height_; + + rational time_; }; #endif // NODEGRAPH_H diff --git a/nodes/nodes/nodemedia.cpp b/nodes/nodes/nodemedia.cpp index 5304c5626..2a5988182 100644 --- a/nodes/nodes/nodemedia.cpp +++ b/nodes/nodes/nodemedia.cpp @@ -1,13 +1,16 @@ #include "nodemedia.h" -NodeMedia::NodeMedia(Clip* c) : - OldEffectNode(c) -{ - NodeIO* matrix_input = new NodeIO(this, "matrix", tr("Matrix"), true, false); - matrix_input->AddAcceptedNodeInput(olive::nodes::kMatrix); +#include "nodes/nodegraph.h" - NodeIO* texture_output = new NodeIO(this, "texture", tr("Texture"), true, false); - texture_output->SetOutputDataType(olive::nodes::kTexture); +NodeMedia::NodeMedia(NodeGraph* c) : + Node(c), + buffer_(c->memory_cache()) +{ + matrix_input_ = new NodeIO(this, "matrix", tr("Matrix"), true, false); + matrix_input_->AddAcceptedNodeInput(olive::nodes::kMatrix); + + texture_output_ = new NodeIO(this, "texture", tr("Texture"), true, false); + texture_output_->SetOutputDataType(olive::nodes::kTexture); } QString NodeMedia::name() @@ -20,27 +23,30 @@ QString NodeMedia::id() return "org.olivevideoeditor.Olive.media"; } -QString NodeMedia::category() +void NodeMedia::Process(rational time) { - return tr("Inputs"); + Q_UNUSED(time) + + buffer_.buffer()->BindBuffer(); + + // DEBUG CODE - we should see a solid red color return from this function + glClearColor(1.0, 0.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + // END DEBUG CODE + + buffer_.buffer()->ReleaseBuffer(); + + texture_output_->SetValue(buffer_.buffer()->texture()); } -QString NodeMedia::description() +NodeIO *NodeMedia::matrix_input() { - return tr("Retrieve frames from a media source."); + return matrix_input_; } -EffectType NodeMedia::type() +NodeIO *NodeMedia::texture_output() { - return EFFECT_TYPE_EFFECT; + return texture_output_; } -olive::TrackType NodeMedia::subtype() -{ - return olive::kTypeVideo; -} -OldEffectNodePtr NodeMedia::Create(Clip *c) -{ - return std::make_shared(c); -} diff --git a/nodes/nodes/nodemedia.h b/nodes/nodes/nodemedia.h index f0be0c345..3d168d31b 100644 --- a/nodes/nodes/nodemedia.h +++ b/nodes/nodes/nodemedia.h @@ -1,20 +1,28 @@ #ifndef MEDIANODE_H #define MEDIANODE_H -#include "nodes/oldeffectnode.h" +#include "nodes/node.h" +#include "rendering/memorycache.h" -class NodeMedia : public OldEffectNode +class NodeMedia : public Node { + Q_OBJECT public: - NodeMedia(Clip *c); + NodeMedia(NodeGraph *c); virtual QString name() override; virtual QString id() override; - virtual QString category() override; - virtual QString description() override; - virtual EffectType type() override; - virtual olive::TrackType subtype() override; - virtual OldEffectNodePtr Create(Clip *c) override; + + virtual void Process(rational time) override; + + NodeIO* matrix_input(); + NodeIO* texture_output(); + +private: + NodeIO* matrix_input_; + NodeIO* texture_output_; + + MemoryCache::Reference buffer_; }; #endif // MEDIANODE_H diff --git a/rendering/memorycache.cpp b/rendering/memorycache.cpp new file mode 100644 index 000000000..35d3143d8 --- /dev/null +++ b/rendering/memorycache.cpp @@ -0,0 +1,181 @@ +#include "memorycache.h" + +#include + +MemoryCache::MemoryCache() : + ctx_(nullptr), + width_(0), + height_(0) +{ + +} + +MemoryCache::~MemoryCache() +{ + buffer_array_mutex_.lock(); + + Clear(); + + buffer_array_mutex_.unlock(); +} + +void MemoryCache::SetParameters(QOpenGLContext *ctx, int width, int height) +{ + buffer_array_mutex_.lock(); + + ctx_ = ctx; + + if (ctx_ != nullptr) { + Q_ASSERT(width > 0 && height > 0); + + width_ = width; + height_ = height; + } + + Clear(); + + buffer_array_mutex_.unlock(); +} + +int MemoryCache::RequestBuffer(Reference* r) +{ + if (ctx_ == nullptr) { + qWarning() << "A memcache request was made with an invalid context"; + return -1; + } + + if (width_ == 0 || height_ == 0) { + qWarning() << "A memcache request was made with an invalid size [" << width_ << "," << height_ << "]"; + return -1; + } + + buffer_array_mutex_.lock(); + + int buffer = RequestBufferInternal(r); + + buffer_array_mutex_.unlock(); + + return buffer; +} + +int MemoryCache::RequestBufferInternal(MemoryCache::Reference *r) +{ + // Try to return a relinquished buffer + if (!relinquished_buffers_.isEmpty()) { + + int buf_index = relinquished_buffers_.takeFirst(); + + refs_.replace(buf_index, r); + + return buf_index; + } + + // If no buffer was available, we may have to create a new one + + // Check if we have enough memory (according to user-defined limits in the Config), if we don't, try to relinquish an + // old buffer and return that. + if (OutOfMemory()) { + + int least_recent_access = 0; + + // Loop through buffers for the oldest accessed buffer + for (int i=1;iRelinquish(); + refs_.replace(least_recent_access, r); + return least_recent_access; + } + + // Otherwise, just generate a new buffer + buffers_.append(FramebufferObject()); + refs_.append(r); + access_times_.append(time(nullptr)); + + return buffers_.size() - 1; +} + +void MemoryCache::RelinquishBuffer(int index) +{ + buffer_array_mutex_.lock(); + + refs_.replace(index, nullptr); + relinquished_buffers_.append(index); + + buffer_array_mutex_.unlock(); +} + +FramebufferObject* MemoryCache::Buffer(int index) +{ + access_times_.replace(index, time(nullptr)); + return &buffers_[index]; +} + +bool MemoryCache::OutOfMemory() +{ + // TODO actually check if we have any memory or not + return false; +} + +void MemoryCache::Clear() +{ + for (int i=0;iRelinquish(); + } + + buffers_.clear(); + refs_.clear(); + relinquished_buffers_.clear(); +} + +MemoryCache::Reference::Reference(MemoryCache *cache) : + cache_(cache), + buffer_(-1) +{ +} + +MemoryCache::Reference::~Reference() +{ + Relinquish(); +} + +FramebufferObject *MemoryCache::Reference::buffer() +{ + // If we don't have a buffer yet, request one + if (buffer_ == -1) { + Request(); + } + + // If we didn't receive one, return null + if (buffer_ == -1) { + return nullptr; + } + + // Otherwise, return the buffer we received + return &cache_->buffers_[buffer_]; +} + +void MemoryCache::Reference::Relinquish() +{ + if (buffer_ == -1) { + return; + } + + cache_->RelinquishBuffer(buffer_); + buffer_ = -1; +} + +void MemoryCache::Reference::Request() +{ + // Check if we already have a buffer, in which case we don't need to request a new one + if (buffer_ != -1) { + return; + } + + buffer_ = cache_->RequestBuffer(this); +} diff --git a/rendering/memorycache.h b/rendering/memorycache.h new file mode 100644 index 000000000..b5f024582 --- /dev/null +++ b/rendering/memorycache.h @@ -0,0 +1,58 @@ +#ifndef MEMORYCACHE_H +#define MEMORYCACHE_H + +#include +#include + +#include "framebufferobject.h" + +class MemoryCache +{ +public: + class Reference { + public: + Reference(MemoryCache* cache); + ~Reference(); + + FramebufferObject* buffer(); + + void Relinquish(); + private: + void Request(); + + MemoryCache* cache_; + int buffer_; + }; + + MemoryCache(); + ~MemoryCache(); + + void SetParameters(QOpenGLContext* ctx, int width, int height); + +private: + int RequestBuffer(Reference *r); + int RequestBufferInternal(Reference *r); + + void RelinquishBuffer(int index); + + FramebufferObject *Buffer(int index); + + bool OutOfMemory(); + + void Clear(); + + QList buffers_; + QList refs_; + QList access_times_; + + QOpenGLContext* ctx_; + + QList relinquished_buffers_; + + int width_; + int height_; + + QMutex buffer_array_mutex_; +}; + +#endif // MEMORYCACHE_H