From 429514b1fdc622ab41020e0b60a5140e8924a6ad Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 1 Nov 2019 12:58:38 +1100 Subject: [PATCH] separation of previous renderer into base and derivation complete The previous iteration was fairly OpenGL-heavy. It's now been separated into a base class that is OpenGL independent and a derived class that is OpenGL-based. Over time this should allow for portability away from OpenGL if necessary. --- app/core.cpp | 2 +- app/panel/viewer/viewer.cpp | 5 - app/panel/viewer/viewer.h | 9 - app/render/backend/CMakeLists.txt | 7 - app/render/backend/opengl/openglbackend.cpp | 172 +++++++++++++- app/render/backend/opengl/openglbackend.h | 31 ++- .../backend/opengl/openglframebuffer.cpp | 4 +- app/render/backend/opengl/openglframebuffer.h | 6 +- app/render/backend/opengl/opengltexture.h | 4 +- app/render/backend/renderbackend.h | 2 - app/render/backend/renderinstance.cpp | 125 ----------- app/render/backend/renderinstance.h | 80 ------- app/render/backend/videorenderbackend.cpp | 212 +++--------------- app/render/backend/videorenderbackend.h | 99 ++++---- .../backend/videorendererthreadbase.cpp | 188 ---------------- app/render/backend/videorendererthreadbase.h | 85 ------- app/widget/viewer/viewer.cpp | 6 +- app/widget/viewer/viewer.h | 7 +- 18 files changed, 267 insertions(+), 777 deletions(-) delete mode 100644 app/render/backend/renderinstance.cpp delete mode 100644 app/render/backend/renderinstance.h delete mode 100644 app/render/backend/videorendererthreadbase.cpp delete mode 100644 app/render/backend/videorendererthreadbase.h diff --git a/app/core.cpp b/app/core.cpp index d85a470ee..2382ee77d 100644 --- a/app/core.cpp +++ b/app/core.cpp @@ -317,7 +317,7 @@ void Core::DeclareTypesForQt() qRegisterMetaType("Task::Status"); qRegisterMetaType(); qRegisterMetaType(); - qRegisterMetaType(); + qRegisterMetaType(); } void Core::StartGUI(bool full_screen) diff --git a/app/panel/viewer/viewer.cpp b/app/panel/viewer/viewer.cpp index 409bd72c2..f18893298 100644 --- a/app/panel/viewer/viewer.cpp +++ b/app/panel/viewer/viewer.cpp @@ -112,11 +112,6 @@ void ViewerPanel::SetTime(const int64_t ×tamp) viewer_->SetTime(timestamp); } -void ViewerPanel::SetTexture(RenderTexturePtr tex) -{ - viewer_->SetTexture(tex); -} - void ViewerPanel::changeEvent(QEvent *e) { if (e->type() == QEvent::LanguageChange) { diff --git a/app/panel/viewer/viewer.h b/app/panel/viewer/viewer.h index 0a4e159c1..1d4b79678 100644 --- a/app/panel/viewer/viewer.h +++ b/app/panel/viewer/viewer.h @@ -63,15 +63,6 @@ public: rational GetTime(); public slots: - /** - * @brief Set the texture to draw and draw it - * - * Wrapper function for Viewer::SetTexture(). - * - * @param tex - */ - void SetTexture(RenderTexturePtr tex); - void SetTime(const int64_t& timestamp); protected: diff --git a/app/render/backend/CMakeLists.txt b/app/render/backend/CMakeLists.txt index 346de03f0..03456983d 100644 --- a/app/render/backend/CMakeLists.txt +++ b/app/render/backend/CMakeLists.txt @@ -25,12 +25,5 @@ set(OLIVE_SOURCES render/backend/audiorenderbackend.cpp render/backend/videorenderbackend.h render/backend/videorenderbackend.cpp - - # FIXME: Remove these - render/backend/videorendererthreadbase.h - render/backend/videorendererthreadbase.cpp - render/backend/renderinstance.h - render/backend/renderinstance.cpp - PARENT_SCOPE ) diff --git a/app/render/backend/opengl/openglbackend.cpp b/app/render/backend/opengl/openglbackend.cpp index 030f4b2ba..f92d716c3 100644 --- a/app/render/backend/opengl/openglbackend.cpp +++ b/app/render/backend/opengl/openglbackend.cpp @@ -2,8 +2,12 @@ #include -OpenGLBackend::OpenGLBackend(QOpenGLContext *share_ctx) : - share_ctx_(share_ctx) +#include "functions.h" + +OpenGLBackend::OpenGLBackend(QOpenGLContext *share_ctx, QObject *parent) : + VideoRenderBackend(parent), + share_ctx_(share_ctx), + push_time_(-1) { } @@ -14,7 +18,9 @@ OpenGLBackend::~OpenGLBackend() bool OpenGLBackend::Init() { - threads_.resize(QThread::idealThreadCount()); + if (!OpenGLBackend::Init()) { + return false; + } // Some OpenGL implementations (notably wgl) require the context not to be current before sharing. We block the main // thread here to prevent QOpenGLWidget trying to reclaim the context before we're done @@ -22,16 +28,14 @@ bool OpenGLBackend::Init() share_ctx_->doneCurrent(); // Initiate one thread per CPU core - for (int i=0;iSetParameters(VideoRenderingParams(viewer_node()->video_params(), olive::PIX_FMT_RGBA16F, olive::kOffline)); + processor->SetParameters(params()); // Finally, we can move it to its own thread processor->moveToThread(thread); @@ -40,23 +44,64 @@ bool OpenGLBackend::Init() // We've finished creating shared contexts, we can now restore the context to its previous current state share_ctx_->makeCurrent(old_surface); + // Create master texture (the one sent to the viewer) + master_texture_ = std::make_shared(); + master_texture_->Create(share_ctx_, params().effective_width(), params().effective_height(), params().format()); + + // Create internal FBO for copying textures + copy_buffer_.Create(share_ctx_); + copy_buffer_.Attach(master_texture_); + copy_pipeline_ = OpenGLShader::CreateDefault(); + return true; } void OpenGLBackend::GenerateFrame(const rational &time) { Q_UNUSED(time) + + /*threads().first()->Queue(NodeDependency(viewer_node()->texture_input()->get_connected_output(), time, time), + true, + false);*/ } void OpenGLBackend::Close() { + if (!IsStarted()) { + return; + } + Decompile(); - // Clear all cores - for (int i=0;i= 0) { + rational temp_push_time = push_time_; + push_time_ = -1; + + if (time == temp_push_time) { + return master_texture_; + } } - threads_.clear(); + + const char* cached_frame = GetCachedFrame(time); + + if (cached_frame != nullptr) { + master_texture_->Upload(cached_frame); + + return master_texture_; + } + + return nullptr; } bool OpenGLBackend::Compile() @@ -160,6 +205,109 @@ QString OpenGLBackend::GenerateShaderID(NodeOutput *output) return QString("%1:%2").arg(output->parent()->id(), output->id()); } +void OpenGLBackend::ThreadCallback(OpenGLTexturePtr texture, const rational& time, const QByteArray& hash) +{ + // Threads are all done now, time to proceed + caching_ = false; + + DeferMap(time, hash); + + if (texture != nullptr) { + // We received a texture, time to start downloading it + QString fn = CachePathName(hash); + + /* + download_threads_[last_download_thread_%download_threads_.size()]->Queue(texture, + fn, + hash); + + last_download_thread_++; + */ + } else { + // There was no texture here, we must update the viewer + DownloadThreadComplete(hash); + } + + // If the connected output is using this time, signal it to update + if (last_time_requested_ == time) { + + copy_buffer_.Bind(); + + QOpenGLFunctions* f = QOpenGLContext::currentContext()->functions(); + + if (texture == nullptr) { + + // No texture, clear the master and push it + f->glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + f->glClear(GL_COLOR_BUFFER_BIT); + + } else { + texture->Bind(); + + f->glViewport(0, 0, master_texture_->width(), master_texture_->height()); + + olive::gl::Blit(copy_pipeline_); + + texture->Release(); + } + + copy_buffer_.Release(); + + push_time_ = time; + + emit CachedFrameReady(time); + } + + CacheNext(); +} + +void OpenGLBackend::ThreadRequestSibling(NodeDependency dep) +{ + Q_UNUSED(dep) + + // Try to queue another thread to run this dep in advance + for (int i=1;iQueue(dep, false, true)) { + return; + }*/ + } +} + +void OpenGLBackend::ThreadSkippedFrame(const rational& time, const QByteArray& hash) +{ + caching_ = false; + + DeferMap(time, hash); + + if (!IsCaching(hash)) { + DownloadThreadComplete(hash); + + // Signal output to update value + emit CachedFrameReady(time); + } + + CacheNext(); +} + +void OpenGLBackend::DownloadThreadComplete(const QByteArray &hash) +{ + cache_hash_list_mutex_.lock(); + cache_hash_list_.removeAll(hash); + cache_hash_list_mutex_.unlock(); + + for (int i=0;i #include -#include "../renderbackend.h" +#include "../videorenderbackend.h" #include "openglframebuffer.h" +#include "opengltexture.h" +#include "openglshader.h" class OpenGLProcessor : public QObject { public: @@ -45,24 +47,27 @@ private: VideoRenderingParams video_params_; }; -class OpenGLBackend : public RenderBackend +class OpenGLBackend : public VideoRenderBackend { public: - OpenGLBackend(QOpenGLContext* share_ctx); + OpenGLBackend(QOpenGLContext* share_ctx, QObject* parent = nullptr); virtual ~OpenGLBackend() override; virtual bool Init() override; - virtual void GenerateFrame(const rational& time) override; - virtual void Close() override; + OpenGLTexturePtr GetCachedFrameAsTexture(const rational& time); + public slots: virtual bool Compile() override; virtual void Decompile() override; +protected: + virtual void GenerateFrame(const rational& time) override; + private: QOpenGLContext* share_ctx_; @@ -79,8 +84,22 @@ private: QList compiled_nodes_; - QVector threads_; QVector processors_; + + OpenGLTexturePtr master_texture_; + rational push_time_; + + OpenGLFramebuffer copy_buffer_; + OpenGLShaderPtr copy_pipeline_; + +private slots: + void ThreadCallback(OpenGLTexturePtr texture, const rational& time, const QByteArray& hash); + + void ThreadRequestSibling(NodeDependency dep); + + void ThreadSkippedFrame(const rational &time, const QByteArray &hash); + + void DownloadThreadComplete(const QByteArray &hash); }; #endif // OPENGLBACKEND_H diff --git a/app/render/backend/opengl/openglframebuffer.cpp b/app/render/backend/opengl/openglframebuffer.cpp index 426c288b0..4f23b19e6 100644 --- a/app/render/backend/opengl/openglframebuffer.cpp +++ b/app/render/backend/opengl/openglframebuffer.cpp @@ -88,7 +88,7 @@ void OpenGLFramebuffer::Release() context_->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); } -void OpenGLFramebuffer::Attach(RenderTexturePtr texture) +void OpenGLFramebuffer::Attach(OpenGLTexturePtr texture) { if (context_ == nullptr) { return; @@ -98,7 +98,7 @@ void OpenGLFramebuffer::Attach(RenderTexturePtr texture) AttachInternal(texture_->texture(), false); } -void OpenGLFramebuffer::AttachBackBuffer(RenderTexturePtr texture) +void OpenGLFramebuffer::AttachBackBuffer(OpenGLTexturePtr texture) { if (context_ == nullptr) { return; diff --git a/app/render/backend/opengl/openglframebuffer.h b/app/render/backend/opengl/openglframebuffer.h index c29c08ef1..94a909ad0 100644 --- a/app/render/backend/opengl/openglframebuffer.h +++ b/app/render/backend/opengl/openglframebuffer.h @@ -42,9 +42,9 @@ public: void Release(); - void Attach(RenderTexturePtr texture); + void Attach(OpenGLTexturePtr texture); - void AttachBackBuffer(RenderTexturePtr texture); + void AttachBackBuffer(OpenGLTexturePtr texture); void Detach(); @@ -60,7 +60,7 @@ private: GLuint buffer_; - RenderTexturePtr texture_; + OpenGLTexturePtr texture_; }; #endif // OPENGLFRAMEBUFFER_H diff --git a/app/render/backend/opengl/opengltexture.h b/app/render/backend/opengl/opengltexture.h index 72c59def4..f3f41e6f2 100644 --- a/app/render/backend/opengl/opengltexture.h +++ b/app/render/backend/opengl/opengltexture.h @@ -84,7 +84,7 @@ private: olive::PixelFormat format_; }; -using RenderTexturePtr = std::shared_ptr; -Q_DECLARE_METATYPE(RenderTexturePtr) +using OpenGLTexturePtr = std::shared_ptr; +Q_DECLARE_METATYPE(OpenGLTexturePtr) #endif // OPENGLTEXTURE_H diff --git a/app/render/backend/renderbackend.h b/app/render/backend/renderbackend.h index fc84c062c..c3bb47e0a 100644 --- a/app/render/backend/renderbackend.h +++ b/app/render/backend/renderbackend.h @@ -14,8 +14,6 @@ public: virtual bool Init() = 0; - virtual void GenerateFrame(const rational& time) = 0; - virtual void Close() = 0; const QString& GetError() const; diff --git a/app/render/backend/renderinstance.cpp b/app/render/backend/renderinstance.cpp deleted file mode 100644 index e08fbaa12..000000000 --- a/app/render/backend/renderinstance.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#include "renderinstance.h" - -#include - -#include "opengl/openglshader.h" - -RenderInstance::RenderInstance(const VideoRenderingParams& params) : - share_ctx_(nullptr), - params_(params) -{ - // Create offscreen surface - surface_.create(); -} - -RenderInstance::~RenderInstance() -{ - // Destroy offscreen surface - surface_.destroy(); -} - -void RenderInstance::SetShareContext(QOpenGLContext *share) -{ - Q_ASSERT(!IsStarted()); - - share_ctx_ = share; -} - -bool RenderInstance::Start() -{ - if (IsStarted()) { - return true; - } - - // Create context object - ctx_ = new QOpenGLContext(); - - // If we're sharing resources, set this up now - if (share_ctx_ != nullptr) { - ctx_->setShareContext(share_ctx_); - } - - // Create OpenGL context (automatically destroys any existing if there is one) - if (!ctx_->create()) { - qWarning() << QStringLiteral("Failed to create OpenGL context in thread %1").arg(reinterpret_cast(this)); - return false; - } - - // Make context current on that surface - if (!ctx_->makeCurrent(&surface_)) { - qWarning() << QStringLiteral("Failed to makeCurrent() on offscreen surface in thread %1").arg(reinterpret_cast(this)); - return false; - } - - buffer_.Create(ctx_); - - // Set viewport to the compositing dimensions - ctx_->functions()->glViewport(0, 0, params_.effective_width(), params_.effective_height()); - ctx_->functions()->glEnable(GL_BLEND); - - // Set up default pipeline - default_pipeline_ = OpenGLShader::CreateDefault(); - - return true; -} - -void RenderInstance::Stop() -{ - if (IsStarted()) { - return; - } - - // Destroy pipeline - default_pipeline_ = nullptr; - - // Destroy buffer - buffer_.Destroy(); - - // Destroy context - delete ctx_; -} - -bool RenderInstance::IsStarted() -{ - return buffer_.IsCreated(); -} - -OpenGLFramebuffer *RenderInstance::buffer() -{ - return &buffer_; -} - -QOpenGLContext *RenderInstance::context() -{ - return ctx_; -} - -const VideoRenderingParams &RenderInstance::params() const -{ - return params_; -} - -OpenGLShaderPtr RenderInstance::default_pipeline() const -{ - return default_pipeline_; -} diff --git a/app/render/backend/renderinstance.h b/app/render/backend/renderinstance.h deleted file mode 100644 index 23a677d27..000000000 --- a/app/render/backend/renderinstance.h +++ /dev/null @@ -1,80 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#ifndef GLINSTANCE_H -#define GLINSTANCE_H - -#include -#include -#include - -#include "opengl/openglshader.h" -#include "opengl/openglframebuffer.h" -#include "render/rendermodes.h" -#include "render/videoparams.h" - -/** - * @brief An object containing all resources necessary for each thread to support hardware accelerated rendering - * - * RenderInstance contains everything that Nodes will need to draw with on a per-thread basis. - * - * Due to its usage of QOffscreenSurface, a RenderInstance instance must be constructed in the main (GUI) thread. From - * there it is safe to call Start() on in another thread. - */ -class RenderInstance : public QObject -{ -public: - RenderInstance(const VideoRenderingParams ¶ms); - - virtual ~RenderInstance() override; - - Q_DISABLE_COPY_MOVE(RenderInstance) - - void SetShareContext(QOpenGLContext* share); - - bool Start(); - - void Stop(); - - bool IsStarted(); - - OpenGLFramebuffer* buffer(); - - QOpenGLContext* context(); - - const VideoRenderingParams& params() const; - - OpenGLShaderPtr default_pipeline() const; - -private: - QOpenGLContext* ctx_; - - QOpenGLContext* share_ctx_; - - QOffscreenSurface surface_; - - OpenGLFramebuffer buffer_; - - VideoRenderingParams params_; - - OpenGLShaderPtr default_pipeline_; -}; - -#endif // GLINSTANCE_H diff --git a/app/render/backend/videorenderbackend.cpp b/app/render/backend/videorenderbackend.cpp index fb936b49c..78f7455f3 100644 --- a/app/render/backend/videorenderbackend.cpp +++ b/app/render/backend/videorenderbackend.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include "common/filefunctions.h" #include "opengl/functions.h" @@ -34,10 +35,8 @@ VideoRenderBackend::VideoRenderBackend(QObject *parent) : RenderBackend(parent), - started_(false), caching_(false), - push_time_(-1), - starting_(false) + started_(false) { // FIXME: Cache name should actually be the name of the sequence SetCacheName("Test"); @@ -132,6 +131,16 @@ void VideoRenderBackend::ViewerNodeChangedEvent(ViewerOutput *node) } } +const QVector &VideoRenderBackend::threads() +{ + return threads_; +} + +const VideoRenderingParams &VideoRenderBackend::params() const +{ + return params_; +} + void VideoRenderBackend::SetParameters(const VideoRenderingParams& params) { // Since we're changing parameters, all the existing threads are invalid and must be removed. They will start again @@ -151,69 +160,16 @@ bool VideoRenderBackend::Init() return true; } - QOpenGLContext* ctx = QOpenGLContext::currentContext(); - - int background_thread_count = QThread::idealThreadCount(); - - // Some OpenGL implementations (notably wgl) require the context not to be current before sharing - QSurface* old_surface = ctx->surface(); - ctx->doneCurrent(); - - threads_.resize(background_thread_count); + threads_.resize(QThread::idealThreadCount()); for (int i=0;i(this, ctx, params_); - threads_[i]->StartThread(QThread::LowPriority); + QThread* thread = new QThread(this); + threads_.replace(i, thread); - // Ensure this connection is "Queued" so that it always runs in this object's threaded rather than any of the - // other threads - connect(threads_.at(i).get(), - SIGNAL(RequestSibling(NodeDependency)), - this, - SLOT(ThreadRequestSibling(NodeDependency)), - Qt::QueuedConnection); + // We use low priority to keep the app responsive at all times (GUI thread should always prioritize over this one) + thread->start(QThread::LowPriority); } - // Connect first thread (master thread) to the callback - connect(threads_.first().get(), - SIGNAL(CachedFrame(RenderTexturePtr, const rational&, const QByteArray&)), - this, - SLOT(ThreadCallback(RenderTexturePtr, const rational&, const QByteArray&)), - Qt::QueuedConnection); - connect(threads_.first().get(), - SIGNAL(FrameSkipped(const rational&, const QByteArray&)), - this, - SLOT(ThreadSkippedFrame(const rational&, const QByteArray&)), - Qt::QueuedConnection); - - download_threads_.resize(background_thread_count); - - for (int i=0;i(ctx, params_); - download_threads_[i]->StartThread(QThread::LowPriority); - - connect(download_threads_[i].get(), - SIGNAL(Downloaded(const QByteArray&)), - this, - SLOT(DownloadThreadComplete(const QByteArray&)), - Qt::QueuedConnection); - } - - last_download_thread_ = 0; - - // Restore context now that thread creation is complete - ctx->makeCurrent(old_surface); - - // Create master texture (the one sent to the viewer) - master_texture_ = std::make_shared(); - master_texture_->Create(ctx, params_.effective_width(), params_.effective_height(), params_.format()); - - // Create internal FBO for copying textures - copy_buffer_.Create(ctx); - copy_buffer_.Attach(master_texture_); - copy_pipeline_ = OpenGLShader::CreateDefault(); - cache_frame_load_buffer_.resize(PixelService::GetBufferSize(params_.format(), params_.effective_width(), params_.effective_height())); started_ = true; @@ -229,20 +185,11 @@ void VideoRenderBackend::Close() started_ = false; - foreach (RendererDownloadThreadPtr download_thread_, download_threads_) { - download_thread_->Cancel(); - } - download_threads_.clear(); - - foreach (RendererProcessThreadPtr process_thread, threads_) { - process_thread->Cancel(); + foreach (QThread* thread, threads_) { + thread->quit(); } threads_.clear(); - copy_buffer_.Destroy(); - master_texture_ = nullptr; - copy_pipeline_ = nullptr; - cache_frame_load_buffer_.clear(); } @@ -278,7 +225,7 @@ void VideoRenderBackend::CacheNext() qDebug() << "Caching" << cache_frame.toDouble(); - threads_.first()->Queue(NodeDependency(viewer_node()->texture_input()->get_connected_output(), cache_frame, cache_frame), true, false); + GenerateFrame(cache_frame); caching_ = true; } @@ -329,118 +276,10 @@ bool VideoRenderBackend::TryCache(const QByteArray &hash) return !is_caching; } -void VideoRenderBackend::ThreadCallback(RenderTexturePtr texture, const rational& time, const QByteArray& hash) -{ - // Threads are all done now, time to proceed - caching_ = false; - - DeferMap(time, hash); - - if (texture != nullptr) { - // We received a texture, time to start downloading it - QString fn = CachePathName(hash); - - download_threads_[last_download_thread_%download_threads_.size()]->Queue(texture, - fn, - hash); - - last_download_thread_++; - } else { - // There was no texture here, we must update the viewer - DownloadThreadComplete(hash); - } - - // If the connected output is using this time, signal it to update - if (last_time_requested_ == time) { - - copy_buffer_.Bind(); - - QOpenGLFunctions* f = QOpenGLContext::currentContext()->functions(); - - if (texture == nullptr) { - - // No texture, clear the master and push it - f->glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - f->glClear(GL_COLOR_BUFFER_BIT); - - } else { - texture->Bind(); - - f->glViewport(0, 0, master_texture_->width(), master_texture_->height()); - - olive::gl::Blit(copy_pipeline_); - - texture->Release(); - } - - copy_buffer_.Release(); - - push_time_ = time; - - emit CachedFrameReady(time); - } - - CacheNext(); -} - -void VideoRenderBackend::ThreadRequestSibling(NodeDependency dep) -{ - // Try to queue another thread to run this dep in advance - for (int i=1;iQueue(dep, false, true)) { - return; - } - } -} - -void VideoRenderBackend::ThreadSkippedFrame(const rational& time, const QByteArray& hash) -{ - caching_ = false; - - DeferMap(time, hash); - - if (!IsCaching(hash)) { - DownloadThreadComplete(hash); - - // Signal output to update value - emit CachedFrameReady(time); - } - - CacheNext(); -} - -void VideoRenderBackend::DownloadThreadComplete(const QByteArray &hash) -{ - cache_hash_list_mutex_.lock(); - cache_hash_list_.removeAll(hash); - cache_hash_list_mutex_.unlock(); - - for (int i=0;i= 0) { - rational temp_push_time = push_time_; - push_time_ = -1; - - if (time == temp_push_time) { - return master_texture_; - } - } - if (viewer_node() == nullptr) { // Nothing is connected - nothing to show or render return nullptr; @@ -468,9 +307,7 @@ RenderTexturePtr VideoRenderBackend::GetCachedFrame(const rational &time) in->close(); - master_texture_->Upload(cache_frame_load_buffer_.data()); - - return master_texture_; + return cache_frame_load_buffer_.constData(); } else { qWarning() << "OIIO Error:" << OIIO::geterror().c_str(); } @@ -479,3 +316,8 @@ RenderTexturePtr VideoRenderBackend::GetCachedFrame(const rational &time) return nullptr; } + +bool VideoRenderBackend::IsStarted() +{ + return started_; +} diff --git a/app/render/backend/videorenderbackend.h b/app/render/backend/videorenderbackend.h index 576f34912..e94185ca4 100644 --- a/app/render/backend/videorenderbackend.h +++ b/app/render/backend/videorenderbackend.h @@ -22,17 +22,11 @@ #define VIDEORENDERERBACKEND_H #include -#include #include "node/output/viewer/viewer.h" #include "renderbackend.h" #include "render/pixelformat.h" #include "render/rendermodes.h" -#include "opengl/openglframebuffer.h" -#include "opengl/openglshader.h" -#include "opengl/opengltexture.h" -#include "videorendererdownloadthread.h" -#include "videorendererprocessthread.h" /** * @brief A multithreaded OpenGL based renderer for node systems @@ -47,7 +41,7 @@ public: * Constructing a Renderer object will not start any threads/backend on its own. Use Start() to do this and Stop() * when the Renderer is about to be destroyed. */ - VideoRenderBackend(QObject* parent); + VideoRenderBackend(QObject* parent = nullptr); virtual ~VideoRenderBackend() override; @@ -98,42 +92,22 @@ public: */ bool TryCache(const QByteArray& hash); - RenderTexturePtr GetCachedFrame(const rational& time); - - virtual void GenerateFrame(const rational&) override {} + bool IsStarted(); public slots: virtual void InvalidateCache(const rational &start_range, const rational &end_range) override; - virtual bool Compile() override {return true;} - - virtual void Decompile() override {} - protected: - virtual void ViewerNodeChangedEvent(ViewerOutput* node) override; - -signals: - void CachedFrameReady(const rational& time); - -private: struct HashTimeMapping { rational time; QByteArray hash; }; - /** - * @brief Internal function for generating the cache ID - */ - void GenerateCacheIDInternal(); + virtual void ViewerNodeChangedEvent(ViewerOutput* node) override; - /** - * @brief Function called when there are frames in the queue to cache - * - * This function is NOT thread-safe and should only be called in the main thread. - */ - void CacheNext(); + virtual void GenerateFrame(const rational&) = 0; - bool ShouldPushTexture(const rational &time); + const char *GetCachedFrame(const rational& time); /** * @brief Return the path of the cached image at this time @@ -142,10 +116,41 @@ private: void DeferMap(const rational &time, const QByteArray &hash); + /** + * @brief Function called when there are frames in the queue to cache + * + * This function is NOT thread-safe and should only be called in the main thread. + */ + void CacheNext(); + + const QVector& threads(); + + const VideoRenderingParams& params() const; + + QMap time_hash_map_; + + QList deferred_maps_; + + QMutex cache_hash_list_mutex_; + QVector cache_hash_list_; + + rational last_time_requested_; + + bool caching_; + +signals: + void CachedFrameReady(const rational& time); + +private: + /** + * @brief Internal function for generating the cache ID + */ + void GenerateCacheIDInternal(); + /** * @brief Internal list of RenderProcessThreads */ - QVector threads_; + QVector threads_; /** * @brief Internal variable that contains whether the Renderer has started or not @@ -154,42 +159,18 @@ private: VideoRenderingParams params_; - rational last_time_requested_; - QLinkedList cache_queue_; QString cache_name_; qint64 cache_time_; QString cache_id_; - bool caching_; - QVector cache_frame_load_buffer_; + QByteArray cache_frame_load_buffer_; - QVector download_threads_; - int last_download_thread_; - - RenderTexturePtr master_texture_; - rational push_time_; - - OpenGLFramebuffer copy_buffer_; - OpenGLShaderPtr copy_pipeline_; - - QMap time_hash_map_; - - QMutex cache_hash_list_mutex_; - QVector cache_hash_list_; - - QList deferred_maps_; - - bool starting_; + /*QVector download_threads_; + int last_download_thread_;*/ private slots: - void ThreadCallback(RenderTexturePtr texture, const rational& time, const QByteArray& hash); - void ThreadRequestSibling(NodeDependency dep); - - void ThreadSkippedFrame(const rational &time, const QByteArray &hash); - - void DownloadThreadComplete(const QByteArray &hash); }; diff --git a/app/render/backend/videorendererthreadbase.cpp b/app/render/backend/videorendererthreadbase.cpp deleted file mode 100644 index 148fcb09d..000000000 --- a/app/render/backend/videorendererthreadbase.cpp +++ /dev/null @@ -1,188 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#include "videorendererthreadbase.h" - -#include -#include - -#include "common/define.h" -#include "videorenderbackend.h" - -VideoRendererThreadBase::VideoRendererThreadBase(VideoRenderBackend* parent, QOpenGLContext *share_ctx, const VideoRenderingParams ¶ms) : - parent_(parent), - share_ctx_(share_ctx), - render_instance_(params) -{ - connect(share_ctx_, SIGNAL(aboutToBeDestroyed()), this, SLOT(Cancel())); -} - -RenderInstance *VideoRendererThreadBase::render_instance() -{ - return &render_instance_; -} - -void VideoRendererThreadBase::Start() -{ - render_instance_.SetShareContext(share_ctx_); - - // Allocate and create resources - render_instance_.Start(); - - - // Set up download functions - f = render_instance()->context()->functions(); - xf = render_instance()->context()->extraFunctions(); - - f->glGenFramebuffers(1, &read_buffer_); - - int buffer_size = PixelService::GetBufferSize(render_instance()->params().format(), - render_instance()->params().width(), - render_instance()->params().height()); - - data_buffer_.resize(buffer_size); - - format_info_ = PixelService::GetPixelFormatInfo(render_instance()->params().format()); - - // Set up OIIO::ImageSpec for compressing cached images on disk - spec_ = OIIO::ImageSpec(render_instance()->params().width(), render_instance()->params().height(), kRGBAChannels, format_info_.oiio_desc); - spec_.attribute("compression", "dwaa:200"); -} - -void VideoRendererThreadBase::Stop() -{ - f->glDeleteFramebuffers(1, &read_buffer_); - - // Free all resources - render_instance_.Stop(); - - thread()->quit(); -} - -void VideoRendererThreadBase::Process(const NodeDependency &path, bool sibling) -{ - // Process the Node - NodeOutput* output_to_process = path.node(); - Node* node_to_process = output_to_process->parent(); - - texture_ = nullptr; - - QList all_deps; - bool has_hash = false; - bool can_cache = true; - - QByteArray hash; - - if (!sibling) { - node_to_process->Lock(); - - all_deps = node_to_process->GetDependencies(); - foreach (Node* dep, all_deps) { - dep->Lock(); - } - - // Check hash - QCryptographicHash hasher(QCryptographicHash::Sha1); - node_to_process->Hash(&hasher, output_to_process, path.in()); - hash = hasher.result(); - - has_hash = parent_->HasHash(hash); - can_cache = false; - } - - if (!has_hash){ - - if ((can_cache = parent_->TryCache(hash))) { - - QList deps = node_to_process->RunDependencies(output_to_process, path.in()); - - // Ask for other threads to run these deps while we're here - if (!deps.isEmpty()) { - for (int i=1;iget_value(path.in(), path.in()).value(); - - render_instance()->context()->functions()->glFinish(); - } - } - - if (!sibling) { - foreach (Node* dep, all_deps) { - dep->Unlock(); - } - - node_to_process->Unlock(); - } - - if (can_cache) { - // We cached this frame, signal that it will need to be downloaded to disk - emit CachedFrame(texture_, path.in(), hash); - } else { - // This hash already exists, no need to cache, just map it - emit FrameSkipped(path.in(), hash); - } -} - -void VideoRendererThreadBase::Download(RenderTexturePtr texture, const QString &fn, const QByteArray &hash) -{ - // Download the texture - - f->glBindFramebuffer(GL_READ_FRAMEBUFFER, read_buffer_); - - xf->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, - texture->texture(), - 0); - - f->glReadPixels(0, - 0, - texture->width(), - texture->height(), - format_info_.pixel_format, - format_info_.gl_pixel_type, - data_buffer_.data()); - - xf->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, - 0, - 0); - - f->glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - - std::string working_fn_std = fn.toStdString(); - - std::unique_ptr out = OIIO::ImageOutput::create(working_fn_std); - - if (out) { - out->open(working_fn_std, spec_); - out->write_image(format_info_.oiio_desc, data_buffer_.data()); - out->close(); - - emit Downloaded(hash); - } else { - qWarning() << QStringLiteral("Failed to open output file \"%1\"").arg(fn); - } -} diff --git a/app/render/backend/videorendererthreadbase.h b/app/render/backend/videorendererthreadbase.h deleted file mode 100644 index e7bbca4d8..000000000 --- a/app/render/backend/videorendererthreadbase.h +++ /dev/null @@ -1,85 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#ifndef RENDERTHREAD_H -#define RENDERTHREAD_H - -#include -#include - -#include "node/node.h" -#include "render/videoparams.h" -#include "renderinstance.h" -#include "render/pixelservice.h" - -class VideoRenderBackend; - -class VideoRendererThreadBase : public QObject -{ - Q_OBJECT -public: - VideoRendererThreadBase(VideoRenderBackend* parent, QOpenGLContext* share_ctx, const VideoRenderingParams& params); - - RenderInstance* render_instance(); - -public slots: - void Start(); - - void Stop(); - - void Process(const NodeDependency &dep, bool sibling); - - void Download(RenderTexturePtr texture, const QString &fn, const QByteArray &hash); - -signals: - void RequestSibling(NodeDependency dep); - - void CachedFrame(RenderTexturePtr texture, const rational& time, const QByteArray& hash); - - void FrameSkipped(const rational& time, const QByteArray& hash); - - void Downloaded(const QByteArray& hash); - -private: - void WakeCaller(); - - VideoRenderBackend* parent_; - - QOpenGLContext* share_ctx_; - - RenderInstance render_instance_; - - RenderTexturePtr texture_; - - GLuint read_buffer_; - - QOpenGLFunctions* f; - QOpenGLExtraFunctions* xf; - - PixelFormatInfo format_info_; - OIIO::ImageSpec spec_; - - QVector data_buffer_; - -}; - -using RendererThreadPtr = std::shared_ptr; - -#endif // RENDERTHREAD_H diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 1ca47b768..486138ba5 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -84,7 +84,7 @@ ViewerWidget::ViewerWidget(QWidget *parent) : ruler_->SetScale(48.0); // Start background renderers - video_renderer_ = new VideoRenderBackend(this); + video_renderer_ = new OpenGLBackend(gl_widget_->context(), this); connect(video_renderer_, SIGNAL(CachedFrameReady(const rational&)), this, SLOT(RendererCachedFrame(const rational&))); } @@ -168,7 +168,7 @@ void ViewerWidget::DisconnectViewerNode() ConnectViewerNode(nullptr); } -void ViewerWidget::SetTexture(RenderTexturePtr tex) +void ViewerWidget::SetTexture(OpenGLTexturePtr tex) { if (tex == nullptr) { gl_widget_->SetTexture(0); @@ -197,7 +197,7 @@ void ViewerWidget::UpdateTextureFromNode(const rational& time) if (viewer_node_ == nullptr) { SetTexture(nullptr); } else { - SetTexture(video_renderer_->GetCachedFrame(time)); + SetTexture(video_renderer_->GetCachedFrameAsTexture(time)); } } diff --git a/app/widget/viewer/viewer.h b/app/widget/viewer/viewer.h index d77a3782d..b9463341d 100644 --- a/app/widget/viewer/viewer.h +++ b/app/widget/viewer/viewer.h @@ -30,7 +30,8 @@ #include "common/rational.h" #include "node/output/viewer/viewer.h" -#include "render/backend/videorenderbackend.h" +#include "render/backend/opengl/openglbackend.h" +#include "render/backend/opengl/opengltexture.h" #include "viewerglwidget.h" #include "viewersizer.h" #include "widget/playbackcontrols/playbackcontrols.h" @@ -73,7 +74,7 @@ public slots: * * @param tex */ - void SetTexture(RenderTexturePtr tex); + void SetTexture(OpenGLTexturePtr tex); void SetTimebase(const rational& r); @@ -110,7 +111,7 @@ private: void PushScrubbedAudio(); - VideoRenderBackend* video_renderer_; + OpenGLBackend* video_renderer_; ViewerSizer* sizer_;