From 5227e10f39a7749125a95d36485515d0492cd4f6 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 6 Dec 2019 15:37:31 +1100 Subject: [PATCH] render one frame per thread for increased parallelism If the nodes are now stateless, there's nothing stopping the renderer from rendering multiple frames at once. Earlier since the nodes held some of their input/output data (and that data could change per frame), it was not possible to render multiple frames at once without conflicts. Now that the node state is held in render threads, they can do whatever they want at any time. --- app/node/input.cpp | 4 +- app/node/input.h | 2 +- app/node/node.cpp | 2 +- app/node/param.cpp | 14 ++- app/node/param.h | 2 +- app/render/backend/audio/audiobackend.cpp | 4 +- app/render/backend/audio/audioworker.cpp | 5 +- app/render/backend/audio/audioworker.h | 2 +- app/render/backend/audiorenderbackend.cpp | 2 + app/render/backend/audiorenderworker.cpp | 4 +- app/render/backend/audiorenderworker.h | 2 +- app/render/backend/decodercache.h | 2 +- app/render/backend/opengl/CMakeLists.txt | 1 - app/render/backend/opengl/openglbackend.cpp | 23 ++-- .../backend/opengl/openglshadercache.cpp | 28 ----- app/render/backend/opengl/openglshadercache.h | 27 +---- app/render/backend/opengl/openglworker.cpp | 6 +- app/render/backend/opengl/openglworker.h | 2 - app/render/backend/renderbackend.cpp | 103 +++++++++++------- app/render/backend/renderbackend.h | 13 +-- app/render/backend/rendercache.h | 46 +++++++- app/render/backend/renderworker.cpp | 20 ++-- app/render/backend/renderworker.h | 6 +- app/render/backend/videorenderbackend.cpp | 13 +-- app/render/backend/videorenderbackend.h | 6 - app/render/backend/videorenderframecache.cpp | 19 +++- app/render/backend/videorenderframecache.h | 6 +- app/render/backend/videorenderworker.cpp | 13 ++- app/render/backend/videorenderworker.h | 6 +- 29 files changed, 201 insertions(+), 182 deletions(-) delete mode 100644 app/render/backend/opengl/openglshadercache.cpp diff --git a/app/node/input.cpp b/app/node/input.cpp index b86dea364..eb9c79ee1 100644 --- a/app/node/input.cpp +++ b/app/node/input.cpp @@ -283,7 +283,7 @@ void NodeInput::set_maximum(const QVariant &max) has_maximum_ = true; } -void NodeInput::CopyValues(NodeInput *source, NodeInput *dest, bool include_connections) +void NodeInput::CopyValues(NodeInput *source, NodeInput *dest, bool include_connections, bool lock_connections) { Q_ASSERT(source->id() == dest->id()); @@ -295,7 +295,7 @@ void NodeInput::CopyValues(NodeInput *source, NodeInput *dest, bool include_conn // Copy connections if (include_connections && source->get_connected_output() != nullptr) { - ConnectEdge(source->get_connected_output(), dest); + ConnectEdge(source->get_connected_output(), dest, lock_connections); } // If these inputs are an array, copy the subparams too diff --git a/app/node/input.h b/app/node/input.h index a9db08aa6..4885833a4 100644 --- a/app/node/input.h +++ b/app/node/input.h @@ -121,7 +121,7 @@ public: /** * @brief Copy all values including keyframe information and connections from another NodeInput */ - static void CopyValues(NodeInput* source, NodeInput* dest, bool include_connections = true); + static void CopyValues(NodeInput* source, NodeInput* dest, bool include_connections = true, bool lock_connections = true); signals: void ValueChanged(const rational& start, const rational& end); diff --git a/app/node/node.cpp b/app/node/node.cpp index 527e5f2e1..cec2712fa 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -182,7 +182,7 @@ void Node::CopyInputs(Node *source, Node *destination, bool include_connections) if (src->dependent()) { NodeInput* dst = static_cast(dst_param.at(i)); - NodeInput::CopyValues(src, dst, include_connections); + NodeInput::CopyValues(src, dst, include_connections, false); } } } diff --git a/app/node/param.cpp b/app/node/param.cpp index 359d29bf9..09a9d07d0 100644 --- a/app/node/param.cpp +++ b/app/node/param.cpp @@ -103,7 +103,7 @@ void NodeParam::DisconnectAll() } } -NodeEdgePtr NodeParam::ConnectEdge(NodeOutput *output, NodeInput *input) +NodeEdgePtr NodeParam::ConnectEdge(NodeOutput *output, NodeInput *input, bool lock) { // If the input can only accept one input (the default) and has one already, disconnect it DisconnectForNewOutput(input); @@ -127,14 +127,18 @@ NodeEdgePtr NodeParam::ConnectEdge(NodeOutput *output, NodeInput *input) // that's difficult to diagnose. This makes that issue very clear. Q_ASSERT(output->parentNode() != input->parentNode()); - output->parentNode()->LockUserInput(); - input->parentNode()->LockUserInput(); + if (lock) { + output->parentNode()->LockUserInput(); + input->parentNode()->LockUserInput(); + } output->edges_.append(edge); input->edges_.append(edge); - output->parentNode()->UnlockUserInput(); - input->parentNode()->UnlockUserInput(); + if (lock) { + output->parentNode()->UnlockUserInput(); + input->parentNode()->UnlockUserInput(); + } // Emit a signal than an edge was added (only one signal needs emitting) emit input->EdgeAdded(edge); diff --git a/app/node/param.h b/app/node/param.h index a0ca4e665..f7ac48dd4 100644 --- a/app/node/param.h +++ b/app/node/param.h @@ -292,7 +292,7 @@ public: * * This function emits EdgeAdded(). */ - static NodeEdgePtr ConnectEdge(NodeOutput *output, NodeInput *input); + static NodeEdgePtr ConnectEdge(NodeOutput *output, NodeInput *input, bool lock = true); /** * @brief Disconnect an edge diff --git a/app/render/backend/audio/audiobackend.cpp b/app/render/backend/audio/audiobackend.cpp index 82647ab66..f1c5ae55d 100644 --- a/app/render/backend/audio/audiobackend.cpp +++ b/app/render/backend/audio/audiobackend.cpp @@ -37,7 +37,7 @@ bool AudioBackend::InitInternal() // Initiate one thread per CPU core for (int i=0;iSetParameters(params()); processors_.append(processor); } @@ -67,8 +67,6 @@ void AudioBackend::ConnectWorkerToThis(RenderWorker *worker) void AudioBackend::ThreadCompletedCache(NodeDependency dep, NodeValueTable data) { - caching_ = false; - QByteArray cached_samples = data.Get(NodeParam::kSamples).toByteArray(); int offset = params().time_to_bytes(dep.in()); diff --git a/app/render/backend/audio/audioworker.cpp b/app/render/backend/audio/audioworker.cpp index f48f43f50..c012976af 100644 --- a/app/render/backend/audio/audioworker.cpp +++ b/app/render/backend/audio/audioworker.cpp @@ -1,12 +1,13 @@ #include "audioworker.h" -AudioWorker::AudioWorker(DecoderCache *decoder_cache, QObject *parent) : - AudioRenderWorker(decoder_cache, parent) +AudioWorker::AudioWorker(QObject *parent) : + AudioRenderWorker(parent) { } void AudioWorker::FrameToValue(StreamPtr stream, FramePtr frame, NodeValueTable *table) { Q_UNUSED(stream) + table->Push(NodeParam::kSamples, frame->ToByteArray()); } diff --git a/app/render/backend/audio/audioworker.h b/app/render/backend/audio/audioworker.h index 686888dee..3fdcb02ce 100644 --- a/app/render/backend/audio/audioworker.h +++ b/app/render/backend/audio/audioworker.h @@ -6,7 +6,7 @@ class AudioWorker : public AudioRenderWorker { public: - AudioWorker(DecoderCache* decoder_cache, QObject* parent = nullptr); + AudioWorker(QObject* parent = nullptr); protected: virtual void FrameToValue(StreamPtr stream, FramePtr frame, NodeValueTable* table) override; diff --git a/app/render/backend/audiorenderbackend.cpp b/app/render/backend/audiorenderbackend.cpp index 1b49e9b37..ba0b0dd07 100644 --- a/app/render/backend/audiorenderbackend.cpp +++ b/app/render/backend/audiorenderbackend.cpp @@ -29,6 +29,8 @@ void AudioRenderBackend::SetParameters(const AudioRenderingParams ¶ms) void AudioRenderBackend::InvalidateCache(const rational &start_range, const rational &end_range) { + RenderBackend::InvalidateCache(start_range, end_range); + rational start_range_adj = qMax(rational(0), start_range); rational end_range_adj = qMin(SequenceLength(), end_range); diff --git a/app/render/backend/audiorenderworker.cpp b/app/render/backend/audiorenderworker.cpp index 61196027b..438b62fd7 100644 --- a/app/render/backend/audiorenderworker.cpp +++ b/app/render/backend/audiorenderworker.cpp @@ -2,8 +2,8 @@ #include "audio/audiomanager.h" -AudioRenderWorker::AudioRenderWorker(DecoderCache *decoder_cache, QObject *parent) : - RenderWorker(decoder_cache, parent) +AudioRenderWorker::AudioRenderWorker(QObject *parent) : + RenderWorker(parent) { } diff --git a/app/render/backend/audiorenderworker.h b/app/render/backend/audiorenderworker.h index 9aea9c154..4a2fae507 100644 --- a/app/render/backend/audiorenderworker.h +++ b/app/render/backend/audiorenderworker.h @@ -7,7 +7,7 @@ class AudioRenderWorker : public RenderWorker { Q_OBJECT public: - AudioRenderWorker(DecoderCache* decoder_cache, QObject* parent = nullptr); + AudioRenderWorker(QObject* parent = nullptr); void SetParameters(const AudioRenderingParams& audio_params); diff --git a/app/render/backend/decodercache.h b/app/render/backend/decodercache.h index 6cda4c30f..c2a2adee0 100644 --- a/app/render/backend/decodercache.h +++ b/app/render/backend/decodercache.h @@ -5,6 +5,6 @@ #include "project/item/footage/stream.h" #include "rendercache.h" -using DecoderCache = RenderCache; +using DecoderCache = ThreadSafeRenderCache; #endif // DECODERCACHE_H diff --git a/app/render/backend/opengl/CMakeLists.txt b/app/render/backend/opengl/CMakeLists.txt index 631879dcf..0b292eadd 100644 --- a/app/render/backend/opengl/CMakeLists.txt +++ b/app/render/backend/opengl/CMakeLists.txt @@ -27,7 +27,6 @@ set(OLIVE_SOURCES render/backend/opengl/openglshader.h render/backend/opengl/openglshader.cpp render/backend/opengl/openglshadercache.h - render/backend/opengl/openglshadercache.cpp render/backend/opengl/opengltexture.h render/backend/opengl/opengltexture.cpp render/backend/opengl/openglworker.h diff --git a/app/render/backend/opengl/openglbackend.cpp b/app/render/backend/opengl/openglbackend.cpp index 8c2c9607f..dae35f97e 100644 --- a/app/render/backend/opengl/openglbackend.cpp +++ b/app/render/backend/opengl/openglbackend.cpp @@ -31,7 +31,7 @@ bool OpenGLBackend::InitInternal() // Initiate one thread per CPU core for (int i=0;iSetParameters(params()); processors_.append(processor); } @@ -76,14 +76,14 @@ bool OpenGLBackend::CompileInternal() foreach (Node* n, nodes) { // Check if we have a shader or not - if (!shader_cache_.HasShader(n)) { + if (!shader_cache_.Has(n->id())) { // Since we don't have a shader, compile one now QString node_code = n->Code(); // If the node has no code, it mustn't be GPU accelerated if (node_code.isEmpty()) { // We enter a null shader so we don't try to compile this again - shader_cache_.AddShader(n, nullptr); + shader_cache_.Add(n->id(), nullptr); } else { // Since we have shader code, compile it now OpenGLShaderPtr program; @@ -113,7 +113,7 @@ bool OpenGLBackend::CompileInternal() return false; } - shader_cache_.AddShader(n, program); + shader_cache_.Add(n->id(), program); //qDebug() << "Compiled" << connected_output->parent()->id() << "->" << connected_output->id(); } @@ -166,26 +166,33 @@ void OpenGLBackend::ThreadCompletedFrame(NodeDependency path, QByteArray hash, N emit CachedFrameReady(path.in(), value); } - CompletedFrame(); + // Queue up a new frame for this worker + CacheNext(); } void OpenGLBackend::ThreadCompletedDownload(NodeDependency dep, QByteArray hash) { frame_cache()->SetHash(dep.in(), hash); - emit CachedTimeReady(dep.in()); + // Emit for each frame that has this hash (some may have been added in ThreadSkippedFrame) + QList times_with_this_hash = frame_cache()->TimesWithHash(hash); + foreach (const rational& t, times_with_this_hash) { + emit CachedTimeReady(t); + } } void OpenGLBackend::ThreadSkippedFrame(NodeDependency dep, QByteArray hash) { frame_cache()->SetHash(dep.in(), hash); - CompletedFrame(); + // Queue up a new frame for this worker + CacheNext(); } void OpenGLBackend::ThreadHashAlreadyExists(NodeDependency dep, QByteArray hash) { ThreadCompletedDownload(dep, hash); - CompletedFrame(); + // Queue up a new frame for this worker + CacheNext(); } diff --git a/app/render/backend/opengl/openglshadercache.cpp b/app/render/backend/opengl/openglshadercache.cpp deleted file mode 100644 index e2f3d5cf9..000000000 --- a/app/render/backend/opengl/openglshadercache.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "openglshadercache.h" - -#include "node/node.h" - -void OpenGLShaderCache::Clear() -{ - compiled_nodes_.clear(); -} - -void OpenGLShaderCache::AddShader(Node *output, OpenGLShaderPtr shader) -{ - compiled_nodes_.insert(GenerateShaderID(output), shader); -} - -OpenGLShaderPtr OpenGLShaderCache::GetShader(Node *output) -{ - return compiled_nodes_.value(GenerateShaderID(output)); -} - -QString OpenGLShaderCache::GenerateShaderID(Node *output) -{ - return output->id(); -} - -bool OpenGLShaderCache::HasShader(Node *output) -{ - return compiled_nodes_.contains(GenerateShaderID(output)); -} diff --git a/app/render/backend/opengl/openglshadercache.h b/app/render/backend/opengl/openglshadercache.h index 8626f31b2..3ccf6e8dc 100644 --- a/app/render/backend/opengl/openglshadercache.h +++ b/app/render/backend/opengl/openglshadercache.h @@ -1,32 +1,11 @@ #ifndef OPENGLSHADERCACHE_H #define OPENGLSHADERCACHE_H -#include +#include -#include "node/output.h" #include "openglshader.h" +#include "render/backend/rendercache.h" -/** - * @brief Thread-safe cache of OpenGL shaders - */ -class OpenGLShaderCache -{ -public: - OpenGLShaderCache() = default; - - void Clear(); - - void AddShader(Node* output, OpenGLShaderPtr shader); - - OpenGLShaderPtr GetShader(Node* output); - - bool HasShader(Node* output); - -private: - QString GenerateShaderID(Node *output); - - QMap compiled_nodes_; - -}; +using OpenGLShaderCache = RenderCache; #endif // OPENGLSHADERCACHE_H diff --git a/app/render/backend/opengl/openglworker.cpp b/app/render/backend/opengl/openglworker.cpp index dda783874..82ff47595 100644 --- a/app/render/backend/opengl/openglworker.cpp +++ b/app/render/backend/opengl/openglworker.cpp @@ -7,8 +7,8 @@ #include "render/colormanager.h" #include "render/pixelservice.h" -OpenGLWorker::OpenGLWorker(QOpenGLContext *share_ctx, OpenGLShaderCache *shader_cache, DecoderCache *decoder_cache, ColorProcessorCache *color_cache, VideoRenderFrameCache *frame_cache, QObject *parent) : - VideoRenderWorker(decoder_cache, color_cache, frame_cache, parent), +OpenGLWorker::OpenGLWorker(QOpenGLContext *share_ctx, OpenGLShaderCache *shader_cache, VideoRenderFrameCache *frame_cache, QObject *parent) : + VideoRenderWorker(frame_cache, parent), share_ctx_(share_ctx), ctx_(nullptr), functions_(nullptr), @@ -143,7 +143,7 @@ void OpenGLWorker::ParametersChangedEvent() void OpenGLWorker::RunNodeAccelerated(Node *node, const NodeValueDatabase *input_params, NodeValueTable *output_params) { - OpenGLShaderPtr shader = shader_cache_->GetShader(node); + OpenGLShaderPtr shader = shader_cache_->Get(node->id()); if (!shader) { return; diff --git a/app/render/backend/opengl/openglworker.h b/app/render/backend/opengl/openglworker.h index e0dfe013f..f0bba8c76 100644 --- a/app/render/backend/opengl/openglworker.h +++ b/app/render/backend/opengl/openglworker.h @@ -13,8 +13,6 @@ class OpenGLWorker : public VideoRenderWorker { public: OpenGLWorker(QOpenGLContext* share_ctx, OpenGLShaderCache* shader_cache, - DecoderCache* decoder_cache, - ColorProcessorCache *color_cache, VideoRenderFrameCache* frame_cache, QObject* parent = nullptr); diff --git a/app/render/backend/renderbackend.cpp b/app/render/backend/renderbackend.cpp index 4ef03b849..ad4b56ac4 100644 --- a/app/render/backend/renderbackend.cpp +++ b/app/render/backend/renderbackend.cpp @@ -6,12 +6,12 @@ RenderBackend::RenderBackend(QObject *parent) : QObject(parent), compiled_(false), - caching_(false), started_(false), viewer_node_(nullptr), copied_viewer_node_(nullptr), value_update_queued_(false), - recompile_queued_(false) + recompile_queued_(false), + input_update_queued_(false) { } @@ -53,17 +53,22 @@ void RenderBackend::Close() CloseInternal(); - decoder_cache_.Clear(); + for (int i=0;iquit(); - thread->wait(); // FIXME: Maximum time in case a thread is stuck? + threads_.at(i)->quit(); } + + for (int i=0;iwait(); // FIXME: Maximum time in case a thread is stuck? + delete threads_.at(i); + delete processors_.at(i); + } + threads_.clear(); - - foreach (RenderWorker* processor, processors_) { - delete processor; - } processors_.clear(); } @@ -100,12 +105,17 @@ bool RenderBackend::IsInitiated() return started_; } +void RenderBackend::InvalidateCache(const rational &start_range, const rational &end_range) +{ + Q_UNUSED(start_range) + Q_UNUSED(end_range) + + input_update_queued_ = true; +} + bool RenderBackend::Compile() { - if (recompile_queued_) { - Decompile(); - recompile_queued_ = false; - } else if (compiled_) { + if (compiled_) { return true; } @@ -199,39 +209,48 @@ void RenderBackend::DisconnectViewer(ViewerOutput *node) void RenderBackend::CacheNext() { - if (!Init() || cache_queue_.isEmpty() || !ViewerIsConnected() || caching_) { + if (!Init() + || cache_queue_.isEmpty() + || !ViewerIsConnected()) { return; } - UpdateNodeInputs(); - - TimeRange cache_frame = cache_queue_.takeFirst(); - - //qDebug() << "Caching" << cache_frame.in(); - - caching_ = GenerateData(cache_frame); -} - -bool RenderBackend::GenerateData(const TimeRange &range) -{ - if (!Compile()) { - qDebug() << "Graph remains uncompiled, nothing to be done"; - return false; + if ((input_update_queued_ || recompile_queued_) && !AllProcessorsAreAvailable()) { + qDebug() << "Blocking render while we wait for all workers to finish..."; + return; } - NodeDependency dep = NodeDependency(GetDependentInput()->get_connected_node(), range.in(), range.out()); + if (input_update_queued_) { + UpdateNodeInputs(); + input_update_queued_ = false; + } + + if (recompile_queued_) { + Decompile(); + recompile_queued_ = false; + } + + if (!compiled_ && !Compile()) { + qDebug() << "Graph remains uncompiled, nothing to be done"; + return; + } foreach (RenderWorker* worker, processors_) { - if (worker->IsAvailable() || worker == processors_.last()) { + if (cache_queue_.isEmpty()) { + break; + } + + if (worker->IsAvailable()) { + TimeRange cache_frame = cache_queue_.takeFirst(); + + NodeDependency dep = NodeDependency(GetDependentInput()->get_connected_node(), cache_frame.in(), cache_frame.out()); + QMetaObject::invokeMethod(worker, "Render", Qt::QueuedConnection, Q_ARG(NodeDependency, dep)); - return true; } } - - return false; } ViewerOutput *RenderBackend::viewer_node() const @@ -244,11 +263,6 @@ bool RenderBackend::ViewerIsConnected() const return viewer_node_ != nullptr; } -DecoderCache *RenderBackend::decoder_cache() -{ - return &decoder_cache_; -} - const QString &RenderBackend::cache_id() const { return cache_id_; @@ -274,6 +288,17 @@ void RenderBackend::UpdateNodeInputs() } } +bool RenderBackend::AllProcessorsAreAvailable() const +{ + foreach (RenderWorker* worker, processors_) { + if (!worker->IsAvailable()) { + return false; + } + } + + return true; +} + const QVector &RenderBackend::threads() { return threads_; diff --git a/app/render/backend/renderbackend.h b/app/render/backend/renderbackend.h index 50eb5aa80..f159149db 100644 --- a/app/render/backend/renderbackend.h +++ b/app/render/backend/renderbackend.h @@ -30,7 +30,7 @@ public: bool IsInitiated(); public slots: - virtual void InvalidateCache(const rational &start_range, const rational &end_range) = 0; + virtual void InvalidateCache(const rational &start_range, const rational &end_range); bool Compile(); @@ -70,8 +70,6 @@ protected: */ void CacheNext(); - bool GenerateData(const TimeRange& range); - void InitWorkers(); virtual NodeInput* GetDependentInput() = 0; @@ -82,8 +80,6 @@ protected: bool ViewerIsConnected() const; - DecoderCache* decoder_cache(); - const QString& cache_id() const; void QueueValueUpdate(const TimeRange& range); @@ -96,9 +92,9 @@ protected: bool compiled_; - bool caching_; - private: + bool AllProcessorsAreAvailable() const; + /** * @brief Internal list of RenderProcessThreads */ @@ -124,8 +120,6 @@ private: */ QString error_; - DecoderCache decoder_cache_; - QString cache_name_; qint64 cache_time_; QString cache_id_; @@ -137,6 +131,7 @@ private: TimeRange value_update_range_; bool recompile_queued_; + bool input_update_queued_; private slots: void ThreadRequestedSibling(NodeDependency dep); diff --git a/app/render/backend/rendercache.h b/app/render/backend/rendercache.h index e38f7bf56..1a015168b 100644 --- a/app/render/backend/rendercache.h +++ b/app/render/backend/rendercache.h @@ -2,6 +2,7 @@ #define RENDERCACHE_H #include +#include template class RenderCache @@ -11,12 +12,53 @@ public: void Clear(){values_.clear();} - void Add(K stream, V shader){values_.insert(stream, shader);} + void Add(K key, V val){values_.insert(key, val);} - V Get(K stream){return values_.value(stream);} + V Get(K key) const {return values_.value(key);} + + bool Has(K key) const {return values_.contains(key);} private: QMap values_; }; +template +class ThreadSafeRenderCache +{ +public: + ThreadSafeRenderCache() = default; + + void Clear() { + lock_.lock(); + values_.clear(); + lock_.unlock(); + } + + void Add(K key, V val) { + lock_.lock(); + values_.insert(key, val); + lock_.unlock(); + } + + V Get(K key) { + lock_.lock(); + V val = values_.value(key); + lock_.unlock(); + return val; + } + + bool Has(K key) { + lock_.lock(); + bool has = values_.contains(key); + lock_.unlock(); + return has; + } + +private: + QMap values_; + + QMutex lock_; + +}; + #endif // RENDERCACHE_H diff --git a/app/render/backend/renderworker.cpp b/app/render/backend/renderworker.cpp index 43da6b949..913b2defb 100644 --- a/app/render/backend/renderworker.cpp +++ b/app/render/backend/renderworker.cpp @@ -4,17 +4,16 @@ #include "node/block/block.h" -RenderWorker::RenderWorker(DecoderCache *decoder_cache, QObject *parent) : +RenderWorker::RenderWorker(QObject *parent) : QObject(parent), working_(0), - started_(false), - decoder_cache_(decoder_cache) + started_(false) { } bool RenderWorker::IsAvailable() { - return (working_ == 0); + return !working_; } bool RenderWorker::Init() @@ -34,6 +33,8 @@ void RenderWorker::Close() { CloseInternal(); + decoder_cache_.Clear(); + started_ = false; } @@ -70,11 +71,6 @@ NodeValueTable RenderWorker::RenderAsSibling(NodeDependency dep) return value; } -DecoderCache *RenderWorker::decoder_cache() -{ - return decoder_cache_; -} - NodeValueTable RenderWorker::RenderInternal(const NodeDependency &path) { return RenderAsSibling(path); @@ -101,13 +97,13 @@ StreamPtr RenderWorker::ResolveStreamFromInput(NodeInput *input) DecoderPtr RenderWorker::ResolveDecoderFromInput(StreamPtr stream) { // Access a map of Node inputs and decoder instances and retrieve a frame! - DecoderPtr decoder = decoder_cache()->Get(stream.get()); + DecoderPtr decoder = decoder_cache_.Get(stream.get()); if (decoder == nullptr && stream != nullptr) { // Create a new Decoder here - DecoderPtr decoder = Decoder::CreateFromID(stream->footage()->decoder()); + decoder = Decoder::CreateFromID(stream->footage()->decoder()); decoder->set_stream(stream); - decoder_cache()->Add(stream.get(), decoder); + decoder_cache_.Add(stream.get(), decoder); } return decoder; diff --git a/app/render/backend/renderworker.h b/app/render/backend/renderworker.h index 1fcafe3c9..0664012d4 100644 --- a/app/render/backend/renderworker.h +++ b/app/render/backend/renderworker.h @@ -12,7 +12,7 @@ class RenderWorker : public QObject { Q_OBJECT public: - RenderWorker(DecoderCache* decoder_cache, QObject* parent = nullptr); + RenderWorker(QObject* parent = nullptr); DISABLE_COPY_MOVE(RenderWorker) @@ -56,14 +56,12 @@ protected: virtual NodeValueTable RenderBlock(TrackOutput *track, const TimeRange& range) = 0; - DecoderCache* decoder_cache(); - QAtomicInt working_; private: bool started_; - DecoderCache* decoder_cache_; + DecoderCache decoder_cache_; }; diff --git a/app/render/backend/videorenderbackend.cpp b/app/render/backend/videorenderbackend.cpp index 5379140b5..3e54f4752 100644 --- a/app/render/backend/videorenderbackend.cpp +++ b/app/render/backend/videorenderbackend.cpp @@ -43,6 +43,8 @@ void VideoRenderBackend::InvalidateCache(const rational &start_range, const rati return; } + RenderBackend::InvalidateCache(start_range, end_range); + // Adjust range to min/max values rational start_range_adj = qMax(rational(0), start_range); rational end_range_adj = qMin(SequenceLength(), end_range); @@ -185,11 +187,6 @@ VideoRenderFrameCache *VideoRenderBackend::frame_cache() return &frame_cache_; } -ColorProcessorCache *VideoRenderBackend::color_cache() -{ - return &color_cache_; -} - const char *VideoRenderBackend::GetCachedFrame(const rational &time) { last_time_requested_ = time; @@ -237,9 +234,3 @@ NodeInput *VideoRenderBackend::GetDependentInput() { return viewer_node()->texture_input(); } - -void VideoRenderBackend::CompletedFrame() -{ - caching_ = false; - CacheNext(); -} diff --git a/app/render/backend/videorenderbackend.h b/app/render/backend/videorenderbackend.h index 261f736e7..975ab856c 100644 --- a/app/render/backend/videorenderbackend.h +++ b/app/render/backend/videorenderbackend.h @@ -82,8 +82,6 @@ protected: VideoRenderFrameCache* frame_cache(); - ColorProcessorCache* color_cache(); - const VideoRenderingParams& params() const; /** @@ -95,8 +93,6 @@ protected: virtual void ConnectWorkerToThis(RenderWorker* processor) override; - void CompletedFrame(); - signals: void CachedFrameReady(const rational& time, QVariant value); void CachedTimeReady(const rational& time); @@ -108,8 +104,6 @@ private: VideoRenderFrameCache frame_cache_; - ColorProcessorCache color_cache_; - rational last_time_requested_; private slots: diff --git a/app/render/backend/videorenderframecache.cpp b/app/render/backend/videorenderframecache.cpp index a6dc985e8..9de93623b 100644 --- a/app/render/backend/videorenderframecache.cpp +++ b/app/render/backend/videorenderframecache.cpp @@ -46,7 +46,7 @@ void VideoRenderFrameCache::SetCacheID(const QString &id) cache_id_ = id; } -QByteArray VideoRenderFrameCache::TimeToHash(const rational &time) +QByteArray VideoRenderFrameCache::TimeToHash(const rational &time) const { return time_hash_map_.value(time); } @@ -80,6 +80,21 @@ void VideoRenderFrameCache::Truncate(const rational &time) } } +QList VideoRenderFrameCache::TimesWithHash(const QByteArray &hash) +{ + QList list; + + QMap::const_iterator iterator; + + for (iterator=time_hash_map_.begin();iterator!=time_hash_map_.end();iterator++) { + if (iterator.value() == hash) { + list.append(iterator.key()); + } + } + + return list; +} + void VideoRenderFrameCache::RemoveHashFromCurrentlyCaching(const QByteArray &hash) { currently_caching_lock_.lock(); @@ -87,7 +102,7 @@ void VideoRenderFrameCache::RemoveHashFromCurrentlyCaching(const QByteArray &has currently_caching_lock_.unlock(); } -QString VideoRenderFrameCache::CachePathName(const QByteArray &hash) +QString VideoRenderFrameCache::CachePathName(const QByteArray &hash) const { QDir this_cache_dir = QDir(GetMediaCacheLocation()).filePath(cache_id_); this_cache_dir.mkpath("."); diff --git a/app/render/backend/videorenderframecache.h b/app/render/backend/videorenderframecache.h index de89ec123..74a40f286 100644 --- a/app/render/backend/videorenderframecache.h +++ b/app/render/backend/videorenderframecache.h @@ -28,17 +28,19 @@ public: /** * @brief Return the path of the cached image at this time */ - QString CachePathName(const QByteArray &hash); + QString CachePathName(const QByteArray &hash) const; void SetCacheID(const QString& id); - QByteArray TimeToHash(const rational& time); + QByteArray TimeToHash(const rational& time) const; void SetHash(const rational& time, const QByteArray& hash); void RemoveHash(const rational& time, const QByteArray &hash); void Truncate(const rational& time); + QList TimesWithHash(const QByteArray& hash); + private: void RemoveHashFromCurrentlyCaching(const QByteArray& hash); diff --git a/app/render/backend/videorenderworker.cpp b/app/render/backend/videorenderworker.cpp index b6de16da0..acfd9035c 100644 --- a/app/render/backend/videorenderworker.cpp +++ b/app/render/backend/videorenderworker.cpp @@ -4,10 +4,9 @@ #include "node/node.h" #include "render/pixelservice.h" -VideoRenderWorker::VideoRenderWorker(DecoderCache *decoder_cache, ColorProcessorCache *color_cache, VideoRenderFrameCache *frame_cache, QObject *parent) : - RenderWorker(decoder_cache, parent), - frame_cache_(frame_cache), - color_cache_(color_cache) +VideoRenderWorker::VideoRenderWorker(VideoRenderFrameCache *frame_cache, QObject *parent) : + RenderWorker(parent), + frame_cache_(frame_cache) { } @@ -19,6 +18,8 @@ const VideoRenderingParams &VideoRenderWorker::video_params() NodeValueTable VideoRenderWorker::RenderInternal(const NodeDependency& path) { + qDebug() << "Rendering" << path.in().toDouble() << "on" << this; + // Get hash of node graph // We use SHA-1 for speed (benchmarks show it's the fastest hash available to us) QCryptographicHash hasher(QCryptographicHash::Sha1); @@ -182,7 +183,7 @@ NodeValueTable VideoRenderWorker::RenderBlock(TrackOutput *track, const TimeRang return table; } -ColorProcessorCache *VideoRenderWorker::color_cache() const +ColorProcessorCache *VideoRenderWorker::color_cache() { - return color_cache_; + return &color_cache_; } diff --git a/app/render/backend/videorenderworker.h b/app/render/backend/videorenderworker.h index 320abee66..20f5d9b0f 100644 --- a/app/render/backend/videorenderworker.h +++ b/app/render/backend/videorenderworker.h @@ -12,7 +12,7 @@ class VideoRenderWorker : public RenderWorker { Q_OBJECT public: - VideoRenderWorker(DecoderCache* decoder_cache, ColorProcessorCache* color_cache, VideoRenderFrameCache* frame_cache, QObject* parent = nullptr); + VideoRenderWorker(VideoRenderFrameCache* frame_cache, QObject* parent = nullptr); void SetParameters(const VideoRenderingParams& video_params); @@ -45,7 +45,7 @@ protected: virtual NodeValueTable RenderBlock(TrackOutput *track, const TimeRange& range) override; - ColorProcessorCache* color_cache() const; + ColorProcessorCache* color_cache(); private: void ProcessNode(); @@ -56,7 +56,7 @@ private: VideoRenderFrameCache* frame_cache_; - ColorProcessorCache* color_cache_; + ColorProcessorCache color_cache_; QByteArray download_buffer_;