From f719ac027aeccf8061175874079ef9a48e3c5897 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 6 Dec 2019 13:28:13 +1100 Subject: [PATCH 1/7] improved Footage class semantics Set const functions to const and use DISABLE_COPY_MOVE instead of manually deleting copy and move constructors. --- app/project/item/footage/footage.cpp | 16 ++++++------- app/project/item/footage/footage.h | 35 +++++++--------------------- 2 files changed, 17 insertions(+), 34 deletions(-) diff --git a/app/project/item/footage/footage.cpp b/app/project/item/footage/footage.cpp index b82bc16ce..ec33d94dc 100644 --- a/app/project/item/footage/footage.cpp +++ b/app/project/item/footage/footage.cpp @@ -35,7 +35,7 @@ Footage::~Footage() ClearStreams(); } -const Footage::Status& Footage::status() +const Footage::Status& Footage::status() const { return status_; } @@ -56,7 +56,7 @@ void Footage::Clear() set_status(kUnprobed); } -const QString &Footage::filename() +const QString &Footage::filename() const { return filename_; } @@ -66,7 +66,7 @@ void Footage::set_filename(const QString &s) filename_ = s; } -const QDateTime &Footage::timestamp() +const QDateTime &Footage::timestamp() const { return timestamp_; } @@ -85,17 +85,17 @@ void Footage::add_stream(StreamPtr s) streams_.last()->set_footage(this); } -StreamPtr Footage::stream(int index) +StreamPtr Footage::stream(int index) const { return streams_.at(index); } -const QList &Footage::streams() +const QList &Footage::streams() const { return streams_; } -int Footage::stream_count() +int Footage::stream_count() const { return streams_.size(); } @@ -105,7 +105,7 @@ Item::Type Footage::type() const return kFootage; } -const QString &Footage::decoder() +const QString &Footage::decoder() const { return decoder_; } @@ -139,7 +139,7 @@ QIcon Footage::icon() return olive::icon::Image; } - /* fall through */ + /* fall-through */ case kInvalid: return olive::icon::Error; } diff --git a/app/project/item/footage/footage.h b/app/project/item/footage/footage.h index 53775a467..2059e0544 100644 --- a/app/project/item/footage/footage.h +++ b/app/project/item/footage/footage.h @@ -24,6 +24,7 @@ #include #include +#include "common/constructors.h" #include "common/rational.h" #include "project/item/item.h" #include "project/item/footage/audiostream.h" @@ -59,25 +60,7 @@ public: */ virtual ~Footage() override; - /** - * @brief Deleted copy constructor - */ - Footage(const Footage& other) = delete; - - /** - * @brief Deleted move constructor - */ - Footage(Footage&& other) = delete; - - /** - * @brief Deleted copy assignment - */ - Footage& operator=(const Footage& other) = delete; - - /** - * @brief Deleted move assignment - */ - Footage& operator=(Footage&& other) = delete; + DISABLE_COPY_MOVE(Footage) /** * @brief Check the ready state of this Footage object @@ -86,7 +69,7 @@ public: * * If the Footage has been successfully probed, this will return TRUE. */ - const Status& status(); + const Status& status() const; /** * @brief Set ready state @@ -114,7 +97,7 @@ public: /** * @brief Return the current filename of this Footage object */ - const QString& filename(); + const QString& filename() const; /** * @brief Set the filename @@ -134,7 +117,7 @@ public: * The file's last modified timestamp is stored for potential organization in the ProjectExplorer. It can be * retrieved here. */ - const QDateTime& timestamp(); + const QDateTime& timestamp() const; /** * @brief Set the last modified time/date @@ -172,17 +155,17 @@ public: * * The stream at the index provided */ - StreamPtr stream(int index); + StreamPtr stream(int index) const; /** * @brief Returns a list of the streams in this Footage */ - const QList& streams(); + const QList& streams() const; /** * @brief Retrieve total number of streams in this Footage file */ - int stream_count(); + int stream_count() const; /** * @brief Item::Type() override @@ -198,7 +181,7 @@ public: * * A decoder ID */ - const QString& decoder(); + const QString& decoder() const; /** * @brief Used by decoders when they Probe to attach itself to this Footage From ffdd56c58281355ae3aeafc1dcf93b91b7ec638c Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 6 Dec 2019 13:29:09 +1100 Subject: [PATCH 2/7] created StreamID object Intended as a more robust solution than mapping to the Stream* pointer, this will map directly to the file and its index regardless of the Stream* pointer. --- app/project/item/footage/stream.cpp | 12 ++++++++++++ app/project/item/footage/stream.h | 13 +++++++++++++ 2 files changed, 25 insertions(+) diff --git a/app/project/item/footage/stream.cpp b/app/project/item/footage/stream.cpp index eb364d1f0..3ce60ecc6 100644 --- a/app/project/item/footage/stream.cpp +++ b/app/project/item/footage/stream.cpp @@ -20,6 +20,7 @@ #include "stream.h" +#include "footage.h" #include "ui/icons/icons.h" Stream::Stream() : @@ -114,3 +115,14 @@ QIcon Stream::IconFromType(const Stream::Type &type) return QIcon(); } + +StreamID Stream::ToID() const +{ + return StreamID(footage_->filename(), index_); +} + +StreamID::StreamID(const QString &filename, const int &stream_index) : + filename_(filename), + stream_index_(stream_index) +{ +} diff --git a/app/project/item/footage/stream.h b/app/project/item/footage/stream.h index 2fcca5075..80e555133 100644 --- a/app/project/item/footage/stream.h +++ b/app/project/item/footage/stream.h @@ -29,6 +29,17 @@ class Footage; +class StreamID { +public: + StreamID(const QString& filename, const int& stream_index); + +private: + QString filename_; + + int stream_index_; + +}; + /** * @brief A base class for keeping metadata about a media stream. * @@ -84,6 +95,8 @@ public: static QIcon IconFromType(const Type& type); + StreamID ToID() const; + private: Footage* footage_; From b8b9ae878bbad8be3e7fe71e43a6141b0f98f461 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 6 Dec 2019 13:30:57 +1100 Subject: [PATCH 3/7] generalized object render caches so they can be used for any object types --- app/render/backend/CMakeLists.txt | 7 ++++--- app/render/backend/colorprocessorcache.h | 10 ++++++++++ app/render/backend/decodercache.cpp | 21 --------------------- app/render/backend/decodercache.h | 20 ++------------------ app/render/backend/rendercache.h | 22 ++++++++++++++++++++++ 5 files changed, 38 insertions(+), 42 deletions(-) create mode 100644 app/render/backend/colorprocessorcache.h delete mode 100644 app/render/backend/decodercache.cpp create mode 100644 app/render/backend/rendercache.h diff --git a/app/render/backend/CMakeLists.txt b/app/render/backend/CMakeLists.txt index 663d5c548..4df8c6e0c 100644 --- a/app/render/backend/CMakeLists.txt +++ b/app/render/backend/CMakeLists.txt @@ -21,9 +21,6 @@ add_subdirectory(vulkan) set(OLIVE_SOURCES ${OLIVE_SOURCES} - render/backend/decodercache.h - render/backend/decodercache.cpp - render/backend/renderbackend.h render/backend/renderbackend.cpp render/backend/renderworker.h @@ -40,6 +37,10 @@ set(OLIVE_SOURCES render/backend/videorenderframecache.cpp render/backend/videorenderworker.h render/backend/videorenderworker.cpp + + render/backend/rendercache.h + render/backend/colorprocessorcache.h + render/backend/decodercache.h PARENT_SCOPE ) diff --git a/app/render/backend/colorprocessorcache.h b/app/render/backend/colorprocessorcache.h new file mode 100644 index 000000000..70512d02e --- /dev/null +++ b/app/render/backend/colorprocessorcache.h @@ -0,0 +1,10 @@ +#ifndef COLORPROCESSORCACHE_H +#define COLORPROCESSORCACHE_H + +#include "project/item/footage/stream.h" +#include "render/colorprocessor.h" +#include "rendercache.h" + +using ColorProcessorCache = RenderCache; + +#endif // COLORPROCESSORCACHE_H diff --git a/app/render/backend/decodercache.cpp b/app/render/backend/decodercache.cpp deleted file mode 100644 index 29939c289..000000000 --- a/app/render/backend/decodercache.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "decodercache.h" - -DecoderCache::DecoderCache() -{ - -} - -void DecoderCache::Clear() -{ - decoders_.clear(); -} - -void DecoderCache::AddDecoder(Stream *stream, DecoderPtr shader) -{ - decoders_.insert(stream, shader); -} - -DecoderPtr DecoderCache::GetDecoder(Stream *stream) -{ - return decoders_.value(stream); -} diff --git a/app/render/backend/decodercache.h b/app/render/backend/decodercache.h index 47011e965..6cda4c30f 100644 --- a/app/render/backend/decodercache.h +++ b/app/render/backend/decodercache.h @@ -3,24 +3,8 @@ #include "decoder/decoder.h" #include "project/item/footage/stream.h" +#include "rendercache.h" -/** - * @brief Thread-safe cache of decoders - */ -class DecoderCache -{ -public: - DecoderCache(); - - void Clear(); - - void AddDecoder(Stream* stream, DecoderPtr shader); - - DecoderPtr GetDecoder(Stream* stream); - -private: - QMap decoders_; - -}; +using DecoderCache = RenderCache; #endif // DECODERCACHE_H diff --git a/app/render/backend/rendercache.h b/app/render/backend/rendercache.h new file mode 100644 index 000000000..e38f7bf56 --- /dev/null +++ b/app/render/backend/rendercache.h @@ -0,0 +1,22 @@ +#ifndef RENDERCACHE_H +#define RENDERCACHE_H + +#include + +template +class RenderCache +{ +public: + RenderCache() = default; + + void Clear(){values_.clear();} + + void Add(K stream, V shader){values_.insert(stream, shader);} + + V Get(K stream){return values_.value(stream);} + +private: + QMap values_; +}; + +#endif // RENDERCACHE_H From 927e2252f3aaef9112e5050ce2ee771acd0fc0a0 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 6 Dec 2019 13:32:02 +1100 Subject: [PATCH 4/7] fixed bug where several frames being cached at the same time would lead to most of them returning blank --- app/render/backend/opengl/openglbackend.cpp | 15 +++++++-------- app/render/backend/opengl/openglbackend.h | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/app/render/backend/opengl/openglbackend.cpp b/app/render/backend/opengl/openglbackend.cpp index 63b25a0ee..8c2c9607f 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); } @@ -135,8 +135,6 @@ bool OpenGLBackend::TimeIsCached(const TimeRange &time) void OpenGLBackend::ThreadCompletedFrame(NodeDependency path, QByteArray hash, NodeValueTable table) { - caching_ = false; - QVariant value = table.Get(NodeParam::kTexture); OpenGLTexturePtr texture = value.value(); @@ -168,7 +166,7 @@ void OpenGLBackend::ThreadCompletedFrame(NodeDependency path, QByteArray hash, N emit CachedFrameReady(path.in(), value); } - CacheNext(); + CompletedFrame(); } void OpenGLBackend::ThreadCompletedDownload(NodeDependency dep, QByteArray hash) @@ -178,15 +176,16 @@ void OpenGLBackend::ThreadCompletedDownload(NodeDependency dep, QByteArray hash) emit CachedTimeReady(dep.in()); } -void OpenGLBackend::ThreadSkippedFrame() +void OpenGLBackend::ThreadSkippedFrame(NodeDependency dep, QByteArray hash) { - caching_ = false; - CacheNext(); + frame_cache()->SetHash(dep.in(), hash); + + CompletedFrame(); } void OpenGLBackend::ThreadHashAlreadyExists(NodeDependency dep, QByteArray hash) { ThreadCompletedDownload(dep, hash); - ThreadSkippedFrame(); + CompletedFrame(); } diff --git a/app/render/backend/opengl/openglbackend.h b/app/render/backend/opengl/openglbackend.h index acb8a807f..099f3415a 100644 --- a/app/render/backend/opengl/openglbackend.h +++ b/app/render/backend/opengl/openglbackend.h @@ -37,7 +37,7 @@ private: private slots: void ThreadCompletedFrame(NodeDependency path, QByteArray hash, NodeValueTable table); void ThreadCompletedDownload(NodeDependency dep, QByteArray hash); - void ThreadSkippedFrame(); + void ThreadSkippedFrame(NodeDependency dep, QByteArray hash); void ThreadHashAlreadyExists(NodeDependency dep, QByteArray hash); }; From 33f73e0e9046501f04c5d79377f77ca0c992e24c Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 6 Dec 2019 13:33:53 +1100 Subject: [PATCH 5/7] reimplemented color management and alpha association in new renderer system Color management is now as functional as it was before. --- app/render/backend/audio/audioworker.cpp | 3 +- app/render/backend/audio/audioworker.h | 2 +- app/render/backend/opengl/CMakeLists.txt | 2 + .../backend/opengl/openglcolorprocessor.cpp | 66 ++++++++++++++ .../backend/opengl/openglcolorprocessor.h | 45 ++++++++++ app/render/backend/opengl/openglworker.cpp | 88 ++++++++++++++++--- app/render/backend/opengl/openglworker.h | 3 +- app/render/backend/renderworker.cpp | 21 +++-- app/render/backend/renderworker.h | 4 +- app/render/backend/videorenderbackend.cpp | 13 ++- app/render/backend/videorenderbackend.h | 7 ++ app/render/backend/videorenderworker.cpp | 29 ++++-- app/render/backend/videorenderworker.h | 9 +- app/render/colorprocessor.h | 4 + 14 files changed, 261 insertions(+), 35 deletions(-) create mode 100644 app/render/backend/opengl/openglcolorprocessor.cpp create mode 100644 app/render/backend/opengl/openglcolorprocessor.h diff --git a/app/render/backend/audio/audioworker.cpp b/app/render/backend/audio/audioworker.cpp index 70a21836f..f48f43f50 100644 --- a/app/render/backend/audio/audioworker.cpp +++ b/app/render/backend/audio/audioworker.cpp @@ -5,7 +5,8 @@ AudioWorker::AudioWorker(DecoderCache *decoder_cache, QObject *parent) : { } -void AudioWorker::FrameToValue(FramePtr frame, NodeValueTable *table) +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 a9598d06b..686888dee 100644 --- a/app/render/backend/audio/audioworker.h +++ b/app/render/backend/audio/audioworker.h @@ -9,7 +9,7 @@ public: AudioWorker(DecoderCache* decoder_cache, QObject* parent = nullptr); protected: - virtual void FrameToValue(FramePtr frame, NodeValueTable* table) override; + virtual void FrameToValue(StreamPtr stream, FramePtr frame, NodeValueTable* table) override; private: diff --git a/app/render/backend/opengl/CMakeLists.txt b/app/render/backend/opengl/CMakeLists.txt index 9bc6fd91e..631879dcf 100644 --- a/app/render/backend/opengl/CMakeLists.txt +++ b/app/render/backend/opengl/CMakeLists.txt @@ -20,6 +20,8 @@ set(OLIVE_SOURCES render/backend/opengl/functions.cpp render/backend/opengl/openglbackend.h render/backend/opengl/openglbackend.cpp + render/backend/opengl/openglcolorprocessor.h + render/backend/opengl/openglcolorprocessor.cpp render/backend/opengl/openglframebuffer.h render/backend/opengl/openglframebuffer.cpp render/backend/opengl/openglshader.h diff --git a/app/render/backend/opengl/openglcolorprocessor.cpp b/app/render/backend/opengl/openglcolorprocessor.cpp new file mode 100644 index 000000000..052f301b1 --- /dev/null +++ b/app/render/backend/opengl/openglcolorprocessor.cpp @@ -0,0 +1,66 @@ +#include "openglcolorprocessor.h" + +#include +#include + +#include "functions.h" + +void OpenGLColorProcessor::Enable(QOpenGLContext *context, bool alpha_is_associated) +{ + if (IsEnabled()) { + return; + } + + context_ = context; + + pipeline_ = OpenGLShader::CreateOCIO(context_, + ocio_lut_, + GetProcessor(), + alpha_is_associated); +} + +bool OpenGLColorProcessor::IsEnabled() const +{ + return ocio_lut_; +} + +OpenGLShaderPtr OpenGLColorProcessor::pipeline() const +{ + return pipeline_; +} + +void OpenGLColorProcessor::ProcessOpenGL() +{ + olive::gl::OCIOBlit(pipeline_, ocio_lut_); +} + +OpenGLColorProcessor::OpenGLColorProcessor(const QString &source_space, const QString &dest_space) : + ColorProcessor(source_space, dest_space), + ocio_lut_(0) +{ +} + +OpenGLColorProcessor::OpenGLColorProcessor(const QString &source_space, QString display, QString view, const QString &look) : + ColorProcessor(source_space, display, view, look), + ocio_lut_(0) +{ +} + +OpenGLColorProcessor::~OpenGLColorProcessor() +{ + if (IsEnabled()) { + // Clean up OCIO LUT texture and shader + context_->functions()->glDeleteTextures(1, &ocio_lut_); + pipeline_ = nullptr; + } +} + +OpenGLColorProcessorPtr OpenGLColorProcessor::CreateOpenGL(const QString &source_space, const QString &dest_space) +{ + return std::make_shared(source_space, dest_space); +} + +OpenGLColorProcessorPtr OpenGLColorProcessor::CreateOpenGL(const QString &source_space, const QString &display, const QString &view, const QString &look) +{ + return std::make_shared(source_space, display, view, look); +} diff --git a/app/render/backend/opengl/openglcolorprocessor.h b/app/render/backend/opengl/openglcolorprocessor.h new file mode 100644 index 000000000..9ab7d955f --- /dev/null +++ b/app/render/backend/opengl/openglcolorprocessor.h @@ -0,0 +1,45 @@ +#ifndef OPENGLCOLORPROCESSOR_H +#define OPENGLCOLORPROCESSOR_H + +#include "openglshader.h" +#include "render/colorprocessor.h" + +class OpenGLColorProcessor; +using OpenGLColorProcessorPtr = std::shared_ptr; + +class OpenGLColorProcessor : public ColorProcessor +{ +public: + OpenGLColorProcessor(const QString &source_space, const QString &dest_space); + + OpenGLColorProcessor(const QString& source_space, + QString display, + QString view, + const QString& look); + + ~OpenGLColorProcessor(); + + static OpenGLColorProcessorPtr CreateOpenGL(const QString& source_space, const QString& dest_space); + + static OpenGLColorProcessorPtr CreateOpenGL(const QString& source_space, + const QString& display, + const QString& view, + const QString& look); + + void Enable(QOpenGLContext* context, bool alpha_is_associated); + bool IsEnabled() const; + + OpenGLShaderPtr pipeline() const; + + void ProcessOpenGL(); + +private: + QOpenGLContext* context_; + + GLuint ocio_lut_; + + OpenGLShaderPtr pipeline_; + +}; + +#endif // OPENGLCOLORPROCESSOR_H diff --git a/app/render/backend/opengl/openglworker.cpp b/app/render/backend/opengl/openglworker.cpp index 9f95b0abd..dda783874 100644 --- a/app/render/backend/opengl/openglworker.cpp +++ b/app/render/backend/opengl/openglworker.cpp @@ -1,11 +1,14 @@ #include "openglworker.h" +#include "core.h" #include "functions.h" #include "node/node.h" +#include "openglcolorprocessor.h" +#include "render/colormanager.h" #include "render/pixelservice.h" -OpenGLWorker::OpenGLWorker(QOpenGLContext *share_ctx, OpenGLShaderCache *shader_cache, DecoderCache *decoder_cache, VideoRenderFrameCache *frame_cache, QObject *parent) : - VideoRenderWorker(decoder_cache, frame_cache, parent), +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), share_ctx_(share_ctx), ctx_(nullptr), functions_(nullptr), @@ -47,18 +50,79 @@ bool OpenGLWorker::InitInternal() return true; } -void OpenGLWorker::FrameToValue(FramePtr frame, NodeValueTable *table) +void OpenGLWorker::FrameToValue(StreamPtr stream, FramePtr frame, NodeValueTable *table) { + // Set up OCIO context + OpenGLColorProcessorPtr color_processor = std::static_pointer_cast(color_cache()->Get(stream.get())); + + // Ensure stream is video or image type + if (stream->type() != Stream::kVideo && stream->type() != Stream::kImage) { + return; + } + + ImageStreamPtr video_stream = std::static_pointer_cast(stream); + + if (!color_processor) { + QString input_colorspace = video_stream->colorspace(); + if (input_colorspace.isEmpty()) { + // FIXME: Should use Stream->Footage to find the Project* since that's a direct chain + input_colorspace = olive::core.GetActiveProject()->default_input_colorspace(); + } + + color_processor = OpenGLColorProcessor::CreateOpenGL(input_colorspace, + OCIO::ROLE_SCENE_LINEAR); + color_cache()->Add(stream.get(), color_processor); + } + + // OCIO's CPU conversion is more accurate, so for online we render on CPU but offline we render GPU + if (video_params().mode() == olive::kOnline) { + // If alpha is associated, disassociate for the color transform + if (video_stream->premultiplied_alpha()) { + ColorManager::DisassociateAlpha(frame); + } + + // Convert frame to float for OCIO + frame = PixelService::ConvertPixelFormat(frame, olive::PIX_FMT_RGBA32F); + + // Perform color transform + color_processor->ConvertFrame(frame); + + // Associate alpha + if (video_stream->premultiplied_alpha()) { + ColorManager::ReassociateAlpha(frame); + } else { + ColorManager::AssociateAlpha(frame); + } + } + OpenGLTexturePtr footage_tex = std::make_shared(); footage_tex->Create(ctx_, frame); - // OCIO's CPU conversion is more accurate, so for online we render on CPU but offline we render GPU - //if (video_params().mode() == olive::kOnline) { - // Convert frame to float for - //frame = PixelService::ConvertPixelFormat(frame, olive::PIX_FMT_RGBA32F); - //} + if (video_params().mode() == olive::kOffline) { + if (!color_processor->IsEnabled()) { + color_processor->Enable(ctx_, video_stream->premultiplied_alpha()); + } - // FIXME: Alpha association and color management + // Create destination texture + OpenGLTexturePtr associated_tex = std::make_shared(); + associated_tex->Create(ctx_, footage_tex->width(), footage_tex->height(), footage_tex->format()); + + // Set viewport for texture size + functions_->glViewport(0, 0, footage_tex->width(), footage_tex->height()); + + buffer_.Attach(associated_tex); + buffer_.Bind(); + footage_tex->Bind(); + + // Blit old texture to new texture through OCIO shader + color_processor->ProcessOpenGL(); + + footage_tex->Release(); + buffer_.Release(); + buffer_.Detach(); + + footage_tex = associated_tex; + } table->Push(NodeParam::kTexture, QVariant::fromValue(footage_tex)); } @@ -66,7 +130,6 @@ void OpenGLWorker::FrameToValue(FramePtr frame, NodeValueTable *table) void OpenGLWorker::CloseInternal() { buffer_.Destroy(); - functions_ = nullptr; delete ctx_; } @@ -184,7 +247,10 @@ void OpenGLWorker::RunNodeAccelerated(Node *node, const NodeValueDatabase *input } } - //qDebug() << " Blitting with shader!"; + // Ensure viewport is correct + functions_->glViewport(0, 0, video_params().effective_width(), video_params().effective_height()); + + // Blit this texture through this shader olive::gl::Blit(shader); // Release any textures we bound before diff --git a/app/render/backend/opengl/openglworker.h b/app/render/backend/opengl/openglworker.h index a509ed018..e0dfe013f 100644 --- a/app/render/backend/opengl/openglworker.h +++ b/app/render/backend/opengl/openglworker.h @@ -14,6 +14,7 @@ public: OpenGLWorker(QOpenGLContext* share_ctx, OpenGLShaderCache* shader_cache, DecoderCache* decoder_cache, + ColorProcessorCache *color_cache, VideoRenderFrameCache* frame_cache, QObject* parent = nullptr); @@ -45,7 +46,7 @@ protected: virtual void CloseInternal() override; - virtual void FrameToValue(FramePtr frame, NodeValueTable* table) override; + virtual void FrameToValue(StreamPtr stream, FramePtr frame, NodeValueTable* table) override; virtual void RunNodeAccelerated(Node *node, const NodeValueDatabase *input_params, NodeValueTable* output_params) override; diff --git a/app/render/backend/renderworker.cpp b/app/render/backend/renderworker.cpp index c83c0ef71..43da6b949 100644 --- a/app/render/backend/renderworker.cpp +++ b/app/render/backend/renderworker.cpp @@ -98,17 +98,16 @@ StreamPtr RenderWorker::ResolveStreamFromInput(NodeInput *input) return input->get_value_at_time(0).value(); } -DecoderPtr RenderWorker::ResolveDecoderFromInput(NodeInput *input) +DecoderPtr RenderWorker::ResolveDecoderFromInput(StreamPtr stream) { // Access a map of Node inputs and decoder instances and retrieve a frame! - StreamPtr stream = ResolveStreamFromInput(input); - DecoderPtr decoder = decoder_cache()->GetDecoder(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->set_stream(stream); - decoder_cache()->AddDecoder(stream.get(), decoder); + decoder_cache()->Add(stream.get(), decoder); } return decoder; @@ -146,13 +145,17 @@ NodeValueTable RenderWorker::ProcessNodeNormally(const NodeDependency& dep) // Exception for Footage types where we actually retrieve some Footage data from a decoder if (input->data_type() == NodeParam::kFootage) { - DecoderPtr decoder = ResolveDecoderFromInput(input); + StreamPtr stream = ResolveStreamFromInput(input); - if (decoder) { - FramePtr frame = RetrieveFromDecoder(decoder, input_time); + if (stream) { + DecoderPtr decoder = ResolveDecoderFromInput(stream); - if (frame) { - FrameToValue(frame, &table); + if (decoder) { + FramePtr frame = RetrieveFromDecoder(decoder, input_time); + + if (frame) { + FrameToValue(stream, frame, &table); + } } } } diff --git a/app/render/backend/renderworker.h b/app/render/backend/renderworker.h index c023d38eb..1fcafe3c9 100644 --- a/app/render/backend/renderworker.h +++ b/app/render/backend/renderworker.h @@ -46,11 +46,11 @@ protected: virtual void RunNodeAccelerated(Node *node, const NodeValueDatabase *input_params, NodeValueTable* output_params); StreamPtr ResolveStreamFromInput(NodeInput* input); - DecoderPtr ResolveDecoderFromInput(NodeInput* input); + DecoderPtr ResolveDecoderFromInput(StreamPtr stream); virtual FramePtr RetrieveFromDecoder(DecoderPtr decoder, const TimeRange& range) = 0; - virtual void FrameToValue(FramePtr frame, NodeValueTable* table) = 0; + virtual void FrameToValue(StreamPtr stream, FramePtr frame, NodeValueTable* table) = 0; NodeValueTable ProcessNodeNormally(const NodeDependency &dep); diff --git a/app/render/backend/videorenderbackend.cpp b/app/render/backend/videorenderbackend.cpp index 2d803bcb9..5379140b5 100644 --- a/app/render/backend/videorenderbackend.cpp +++ b/app/render/backend/videorenderbackend.cpp @@ -175,7 +175,7 @@ void VideoRenderBackend::CacheIDChangedEvent(const QString &id) void VideoRenderBackend::ConnectWorkerToThis(RenderWorker *processor) { connect(processor, SIGNAL(CompletedFrame(NodeDependency, QByteArray, NodeValueTable)), this, SLOT(ThreadCompletedFrame(NodeDependency, QByteArray, NodeValueTable))); - connect(processor, SIGNAL(HashAlreadyBeingCached()), this, SLOT(ThreadSkippedFrame())); + connect(processor, SIGNAL(HashAlreadyBeingCached(NodeDependency, QByteArray)), this, SLOT(ThreadSkippedFrame(NodeDependency, QByteArray))); connect(processor, SIGNAL(CompletedDownload(NodeDependency, QByteArray)), this, SLOT(ThreadCompletedDownload(NodeDependency, QByteArray))); connect(processor, SIGNAL(HashAlreadyExists(NodeDependency, QByteArray)), this, SLOT(ThreadHashAlreadyExists(NodeDependency, QByteArray))); } @@ -185,6 +185,11 @@ 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; @@ -232,3 +237,9 @@ 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 c6875e1a9..261f736e7 100644 --- a/app/render/backend/videorenderbackend.h +++ b/app/render/backend/videorenderbackend.h @@ -23,6 +23,7 @@ #include +#include "colorprocessorcache.h" #include "node/output/viewer/viewer.h" #include "renderbackend.h" #include "render/pixelformat.h" @@ -81,6 +82,8 @@ protected: VideoRenderFrameCache* frame_cache(); + ColorProcessorCache* color_cache(); + const VideoRenderingParams& params() const; /** @@ -92,6 +95,8 @@ protected: virtual void ConnectWorkerToThis(RenderWorker* processor) override; + void CompletedFrame(); + signals: void CachedFrameReady(const rational& time, QVariant value); void CachedTimeReady(const rational& time); @@ -103,6 +108,8 @@ private: VideoRenderFrameCache frame_cache_; + ColorProcessorCache color_cache_; + rational last_time_requested_; private slots: diff --git a/app/render/backend/videorenderworker.cpp b/app/render/backend/videorenderworker.cpp index d5b6a399c..b6de16da0 100644 --- a/app/render/backend/videorenderworker.cpp +++ b/app/render/backend/videorenderworker.cpp @@ -4,9 +4,10 @@ #include "node/node.h" #include "render/pixelservice.h" -VideoRenderWorker::VideoRenderWorker(DecoderCache *decoder_cache, VideoRenderFrameCache *frame_cache, QObject *parent) : +VideoRenderWorker::VideoRenderWorker(DecoderCache *decoder_cache, ColorProcessorCache *color_cache, VideoRenderFrameCache *frame_cache, QObject *parent) : RenderWorker(decoder_cache, parent), - frame_cache_(frame_cache) + frame_cache_(frame_cache), + color_cache_(color_cache) { } @@ -36,7 +37,7 @@ NodeValueTable VideoRenderWorker::RenderInternal(const NodeDependency& path) emit CompletedFrame(path, hash, value); } else { // Another thread must be caching this already, nothing to be done - emit HashAlreadyBeingCached(); + emit HashAlreadyBeingCached(path, hash); } return value; @@ -85,7 +86,7 @@ void VideoRenderWorker::HashNodeRecursively(QCryptographicHash *hash, Node* n, c // We have one exception for FOOTAGE types, since we resolve the footage into a frame in the renderer if (input->data_type() == NodeParam::kFootage) { StreamPtr stream = ResolveStreamFromInput(input); - DecoderPtr decoder = ResolveDecoderFromInput(input); + DecoderPtr decoder = ResolveDecoderFromInput(stream); if (decoder != nullptr) { // Add footage details to hash @@ -99,10 +100,19 @@ void VideoRenderWorker::HashNodeRecursively(QCryptographicHash *hash, Node* n, c // Footage stream hash->addData(QString::number(stream->index()).toUtf8()); - // Footage timestamp - hash->addData(QString::number(decoder->GetTimestampFromTime(time)).toUtf8()); + if (stream->type() == Stream::kImage || stream->type() == Stream::kVideo) { + ImageStreamPtr video_stream = std::static_pointer_cast(stream); - // FIXME: Add colorspace and alpha assoc + // Footage timestamp + hash->addData(QString::number(decoder->GetTimestampFromTime(time)).toUtf8()); + + // Current colorspace + // FIXME: Handle empty colorspace... + hash->addData(video_stream->colorspace().toUtf8()); + + // Alpha associated setting + hash->addData(QString::number(video_stream->premultiplied_alpha()).toUtf8()); + } } } } @@ -171,3 +181,8 @@ NodeValueTable VideoRenderWorker::RenderBlock(TrackOutput *track, const TimeRang return table; } + +ColorProcessorCache *VideoRenderWorker::color_cache() const +{ + return color_cache_; +} diff --git a/app/render/backend/videorenderworker.h b/app/render/backend/videorenderworker.h index 7ed95ec5b..320abee66 100644 --- a/app/render/backend/videorenderworker.h +++ b/app/render/backend/videorenderworker.h @@ -3,6 +3,7 @@ #include +#include "colorprocessorcache.h" #include "node/dependency.h" #include "render/videoparams.h" #include "renderworker.h" @@ -11,7 +12,7 @@ class VideoRenderWorker : public RenderWorker { Q_OBJECT public: - VideoRenderWorker(DecoderCache* decoder_cache, VideoRenderFrameCache* frame_cache, QObject* parent = nullptr); + VideoRenderWorker(DecoderCache* decoder_cache, ColorProcessorCache* color_cache, VideoRenderFrameCache* frame_cache, QObject* parent = nullptr); void SetParameters(const VideoRenderingParams& video_params); @@ -23,7 +24,7 @@ signals: void CompletedDownload(NodeDependency path, QByteArray hash); - void HashAlreadyBeingCached(); + void HashAlreadyBeingCached(NodeDependency path, QByteArray hash); void HashAlreadyExists(NodeDependency path, QByteArray hash); @@ -44,6 +45,8 @@ protected: virtual NodeValueTable RenderBlock(TrackOutput *track, const TimeRange& range) override; + ColorProcessorCache* color_cache() const; + private: void ProcessNode(); @@ -53,6 +56,8 @@ private: VideoRenderFrameCache* frame_cache_; + ColorProcessorCache* color_cache_; + QByteArray download_buffer_; private slots: diff --git a/app/render/colorprocessor.h b/app/render/colorprocessor.h index e575e2574..001b449fb 100644 --- a/app/render/colorprocessor.h +++ b/app/render/colorprocessor.h @@ -4,8 +4,10 @@ #include namespace OCIO = OCIO_NAMESPACE::v1; +#include "common/constructors.h" #include "decoder/frame.h" + class ColorProcessor; using ColorProcessorPtr = std::shared_ptr; @@ -19,6 +21,8 @@ public: QString view, const QString& look); + DISABLE_COPY_MOVE(ColorProcessor) + static ColorProcessorPtr Create(const QString& source_space, const QString& dest_space); static ColorProcessorPtr Create(const QString& source_space, From 5227e10f39a7749125a95d36485515d0492cd4f6 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 6 Dec 2019 15:37:31 +1100 Subject: [PATCH 6/7] 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_; From f6064b40dfe9c855d5fc4f2c0871e0064d56afc1 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 6 Dec 2019 19:38:57 +1100 Subject: [PATCH 7/7] hold worker busy state in renderbackend rather than in renderworker Workers run in different threads and the backends can poll whether the worker is currently busy or not. However the previous iteration has the worker (and an atomic int) provide the busy state which could easily desync with the main thread (since all workers run in different threads). By holding the busy states in the main thread, the main thread will always be able to poll the busy state accurately. --- app/render/backend/audio/audiobackend.cpp | 2 ++ app/render/backend/opengl/openglbackend.cpp | 28 ++++++++--------- app/render/backend/opengl/openglbackend.h | 2 ++ app/render/backend/renderbackend.cpp | 34 ++++++++++----------- app/render/backend/renderbackend.h | 7 +++-- app/render/backend/renderworker.cpp | 12 -------- app/render/backend/renderworker.h | 6 ---- app/render/backend/videorenderworker.cpp | 6 ---- app/widget/timelinewidget/tool/pointer.cpp | 2 +- 9 files changed, 41 insertions(+), 58 deletions(-) diff --git a/app/render/backend/audio/audiobackend.cpp b/app/render/backend/audio/audiobackend.cpp index f1c5ae55d..19c0cbce2 100644 --- a/app/render/backend/audio/audiobackend.cpp +++ b/app/render/backend/audio/audiobackend.cpp @@ -67,6 +67,8 @@ void AudioBackend::ConnectWorkerToThis(RenderWorker *worker) void AudioBackend::ThreadCompletedCache(NodeDependency dep, NodeValueTable data) { + SetWorkerBusyState(static_cast(sender()), false); + QByteArray cached_samples = data.Get(NodeParam::kSamples).toByteArray(); int offset = params().time_to_bytes(dep.in()); diff --git a/app/render/backend/opengl/openglbackend.cpp b/app/render/backend/opengl/openglbackend.cpp index dae35f97e..120c610f5 100644 --- a/app/render/backend/opengl/openglbackend.cpp +++ b/app/render/backend/opengl/openglbackend.cpp @@ -6,7 +6,8 @@ #include "functions.h" OpenGLBackend::OpenGLBackend(QObject *parent) : - VideoRenderBackend(parent) + VideoRenderBackend(parent), + last_download_thread_(0) { } @@ -135,6 +136,8 @@ bool OpenGLBackend::TimeIsCached(const TimeRange &time) void OpenGLBackend::ThreadCompletedFrame(NodeDependency path, QByteArray hash, NodeValueTable table) { + SetWorkerBusyState(static_cast(sender()), false); + QVariant value = table.Get(NodeParam::kTexture); OpenGLTexturePtr texture = value.value(); @@ -146,19 +149,14 @@ void OpenGLBackend::ThreadCompletedFrame(NodeDependency path, QByteArray hash, N QString cache_fn = frame_cache()->CachePathName(hash); // Find an available worker to download this texture - foreach (RenderWorker* worker, processors_) { - // Check if one is available, but worst case if none of them are available, just queue it on the last worker since - // it's the least likely to get work - if (worker->IsAvailable() || worker == processors_.last()) { - QMetaObject::invokeMethod(worker, - "Download", - Q_ARG(NodeDependency, path), - Q_ARG(QByteArray, hash), - Q_ARG(QVariant, QVariant::fromValue(texture)), - Q_ARG(QString, cache_fn)); - break; - } - } + QMetaObject::invokeMethod(processors_.at(last_download_thread_%processors_.size()), + "Download", + Q_ARG(NodeDependency, path), + Q_ARG(QByteArray, hash), + Q_ARG(QVariant, QVariant::fromValue(texture)), + Q_ARG(QString, cache_fn)); + + last_download_thread_++; } // Set as push texture @@ -184,6 +182,7 @@ void OpenGLBackend::ThreadCompletedDownload(NodeDependency dep, QByteArray hash) void OpenGLBackend::ThreadSkippedFrame(NodeDependency dep, QByteArray hash) { frame_cache()->SetHash(dep.in(), hash); + SetWorkerBusyState(static_cast(sender()), false); // Queue up a new frame for this worker CacheNext(); @@ -192,6 +191,7 @@ void OpenGLBackend::ThreadSkippedFrame(NodeDependency dep, QByteArray hash) void OpenGLBackend::ThreadHashAlreadyExists(NodeDependency dep, QByteArray hash) { ThreadCompletedDownload(dep, hash); + SetWorkerBusyState(static_cast(sender()), false); // Queue up a new frame for this worker CacheNext(); diff --git a/app/render/backend/opengl/openglbackend.h b/app/render/backend/opengl/openglbackend.h index 099f3415a..e05303d74 100644 --- a/app/render/backend/opengl/openglbackend.h +++ b/app/render/backend/opengl/openglbackend.h @@ -34,6 +34,8 @@ private: OpenGLShaderCache shader_cache_; + int last_download_thread_; + private slots: void ThreadCompletedFrame(NodeDependency path, QByteArray hash, NodeValueTable table); void ThreadCompletedDownload(NodeDependency dep, QByteArray hash); diff --git a/app/render/backend/renderbackend.cpp b/app/render/backend/renderbackend.cpp index ad4b56ac4..63e6e4687 100644 --- a/app/render/backend/renderbackend.cpp +++ b/app/render/backend/renderbackend.cpp @@ -240,7 +240,7 @@ void RenderBackend::CacheNext() break; } - if (worker->IsAvailable()) { + if (!WorkerIsBusy(worker)) { TimeRange cache_frame = cache_queue_.takeFirst(); NodeDependency dep = NodeDependency(GetDependentInput()->get_connected_node(), cache_frame.in(), cache_frame.out()); @@ -249,6 +249,8 @@ void RenderBackend::CacheNext() "Render", Qt::QueuedConnection, Q_ARG(NodeDependency, dep)); + + SetWorkerBusyState(worker, true); } } } @@ -288,10 +290,20 @@ void RenderBackend::UpdateNodeInputs() } } +bool RenderBackend::WorkerIsBusy(RenderWorker *worker) const +{ + return processor_busy_state_.at(processors_.indexOf(worker)); +} + +void RenderBackend::SetWorkerBusyState(RenderWorker *worker, bool busy) +{ + processor_busy_state_.replace(processors_.indexOf(worker), busy); +} + bool RenderBackend::AllProcessorsAreAvailable() const { - foreach (RenderWorker* worker, processors_) { - if (!worker->IsAvailable()) { + foreach (bool busy, processor_busy_state_) { + if (busy) { return false; } } @@ -316,7 +328,6 @@ void RenderBackend::InitWorkers() QThread* thread = threads().at(i); // Connect to it - connect(processor, SIGNAL(RequestSibling(NodeDependency)), this, SLOT(ThreadRequestedSibling(NodeDependency))); ConnectWorkerToThis(processor); // Finally, we can move it to its own thread @@ -325,20 +336,9 @@ void RenderBackend::InitWorkers() // This function blocks the main thread intentionally. See the documentation for this function to see why. processor->Init(); } -} -void RenderBackend::ThreadRequestedSibling(NodeDependency dep) -{ - // Try to queue another thread to run this dep in advance - foreach (RenderWorker* worker, processors_) { - if (worker->IsAvailable()) { - QMetaObject::invokeMethod(worker, - "RenderAsSibling", - Qt::QueuedConnection, - Q_ARG(NodeDependency, dep)); - return; - } - } + processor_busy_state_.resize(processors_.size()); + processor_busy_state_.fill(false); } void RenderBackend::QueueRecompile() diff --git a/app/render/backend/renderbackend.h b/app/render/backend/renderbackend.h index f159149db..d9d913381 100644 --- a/app/render/backend/renderbackend.h +++ b/app/render/backend/renderbackend.h @@ -86,6 +86,9 @@ protected: void UpdateNodeInputs(); + bool WorkerIsBusy(RenderWorker* worker) const; + void SetWorkerBusyState(RenderWorker* worker, bool busy); + QList cache_queue_; QVector processors_; @@ -133,9 +136,9 @@ private: bool recompile_queued_; bool input_update_queued_; -private slots: - void ThreadRequestedSibling(NodeDependency dep); + QVector processor_busy_state_; +private slots: void QueueRecompile(); }; diff --git a/app/render/backend/renderworker.cpp b/app/render/backend/renderworker.cpp index 913b2defb..49d02b8cd 100644 --- a/app/render/backend/renderworker.cpp +++ b/app/render/backend/renderworker.cpp @@ -6,16 +6,10 @@ RenderWorker::RenderWorker(QObject *parent) : QObject(parent), - working_(0), started_(false) { } -bool RenderWorker::IsAvailable() -{ - return !working_; -} - bool RenderWorker::Init() { if (started_) { @@ -51,9 +45,6 @@ NodeValueTable RenderWorker::RenderAsSibling(NodeDependency dep) //qDebug() << "Processing" << node->id(); - // Set working state - working_++; - // Firstly we check if this node is a "Block", if it is that means it's part of a linked list of mutually exclusive // nodes based on time and we might need to locate which Block to attach to if (node->IsTrack()) { @@ -65,9 +56,6 @@ NodeValueTable RenderWorker::RenderAsSibling(NodeDependency dep) // We're done! - // End this working state - working_--; - return value; } diff --git a/app/render/backend/renderworker.h b/app/render/backend/renderworker.h index 0664012d4..21530c069 100644 --- a/app/render/backend/renderworker.h +++ b/app/render/backend/renderworker.h @@ -20,8 +20,6 @@ public: bool IsStarted(); - bool IsAvailable(); - public slots: void Close(); @@ -30,8 +28,6 @@ public slots: NodeValueTable RenderAsSibling(NodeDependency dep); signals: - void RequestSibling(NodeDependency path); - void CompletedCache(NodeDependency dep, NodeValueTable data); protected: @@ -56,8 +52,6 @@ protected: virtual NodeValueTable RenderBlock(TrackOutput *track, const TimeRange& range) = 0; - QAtomicInt working_; - private: bool started_; diff --git a/app/render/backend/videorenderworker.cpp b/app/render/backend/videorenderworker.cpp index acfd9035c..b5a33c2d1 100644 --- a/app/render/backend/videorenderworker.cpp +++ b/app/render/backend/videorenderworker.cpp @@ -18,8 +18,6 @@ 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); @@ -141,8 +139,6 @@ void VideoRenderWorker::CloseInternal() void VideoRenderWorker::Download(NodeDependency dep, QByteArray hash, QVariant texture, QString filename) { - working_++; - PixelFormatInfo format_info = PixelService::GetPixelFormatInfo(video_params().format()); // Set up OIIO::ImageSpec for compressing cached images on disk @@ -164,8 +160,6 @@ void VideoRenderWorker::Download(NodeDependency dep, QByteArray hash, QVariant t } else { qWarning() << "Failed to open output file:" << filename; } - - working_--; } NodeValueTable VideoRenderWorker::RenderBlock(TrackOutput *track, const TimeRange &range) diff --git a/app/widget/timelinewidget/tool/pointer.cpp b/app/widget/timelinewidget/tool/pointer.cpp index 1493861b9..147c96b30 100644 --- a/app/widget/timelinewidget/tool/pointer.cpp +++ b/app/widget/timelinewidget/tool/pointer.cpp @@ -239,7 +239,7 @@ void TimelineWidget::PointerTool::InitiateDrag(const TimelineCoordinate &mouse_p olive::timeline::MovementMode trim_mode = olive::timeline::kNone; // FIXME: Hardcoded number - const int kTrimHandle = 20; + const int kTrimHandle = 10; qreal mouse_x = parent()->TimeToScene(mouse_pos.GetFrame());