diff --git a/app/render/backend/CMakeLists.txt b/app/render/backend/CMakeLists.txt index 5a3fcd7e0..346de03f0 100644 --- a/app/render/backend/CMakeLists.txt +++ b/app/render/backend/CMakeLists.txt @@ -27,14 +27,10 @@ set(OLIVE_SOURCES render/backend/videorenderbackend.cpp # FIXME: Remove these - render/backend/videorendererdownloadthread.h - render/backend/videorendererdownloadthread.cpp - render/backend/videorendererprocessthread.h - render/backend/videorendererprocessthread.cpp 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.h b/app/render/backend/opengl/openglbackend.h index 84e7b8fde..57b827f3a 100644 --- a/app/render/backend/opengl/openglbackend.h +++ b/app/render/backend/opengl/openglbackend.h @@ -20,11 +20,16 @@ public: void SetParameters(const VideoRenderingParams& video_params); + + public slots: void Init(); void Close(); +signals: + + private: void UpdateViewportFromParams(); diff --git a/app/render/backend/videorendererdownloadthread.cpp b/app/render/backend/videorendererdownloadthread.cpp deleted file mode 100644 index bf1315d44..000000000 --- a/app/render/backend/videorendererdownloadthread.cpp +++ /dev/null @@ -1,124 +0,0 @@ -#include "videorendererdownloadthread.h" - -#include -#include -#include - -#include "common/define.h" -#include "render/pixelservice.h" - -VideoRendererDownloadThread::VideoRendererDownloadThread(QOpenGLContext *share_ctx, - const VideoRenderingParams& params) : - VideoRendererThreadBase(share_ctx, params), - cancelled_(false) -{ -} - -void VideoRendererDownloadThread::Queue(RenderTexturePtr texture, const QString& fn, const QByteArray &hash) -{ - texture_queue_lock_.lock(); - - texture_queue_.append({texture, fn, hash}); - - wait_cond_.wakeAll(); - - texture_queue_lock_.unlock(); -} - -void VideoRendererDownloadThread::Cancel() -{ - cancelled_ = true; - - texture_queue_lock_.lock(); - wait_cond_.wakeAll(); - texture_queue_lock_.unlock(); - - wait(); -} - -void VideoRendererDownloadThread::ProcessLoop() -{ - QOpenGLFunctions* f = render_instance()->context()->functions(); - QOpenGLExtraFunctions* xf = render_instance()->context()->extraFunctions(); - - f->glGenFramebuffers(1, &read_buffer_); - - DownloadQueueEntry entry; - - int buffer_size = PixelService::GetBufferSize(render_instance()->params().format(), - render_instance()->params().width(), - render_instance()->params().height()); - - QVector data_buffer; - data_buffer.resize(buffer_size); - - PixelFormatInfo format_info = PixelService::GetPixelFormatInfo(render_instance()->params().format()); - - // Set up OIIO::ImageSpec for compressing cached images on disk - OIIO::ImageSpec spec(render_instance()->params().width(), render_instance()->params().height(), kRGBAChannels, format_info.oiio_desc); - spec.attribute("compression", "dwaa:200"); - - while (!cancelled_) { - // Check queue for textures to download (use mutex to prevent collisions) - texture_queue_lock_.lock(); - - while (texture_queue_.isEmpty()) { - // Main waiting condition - wait_cond_.wait(&texture_queue_lock_); - - if (cancelled_) { - break; - } - } - if (cancelled_) { - texture_queue_lock_.unlock(); - break; - } - - entry = texture_queue_.takeFirst(); - - texture_queue_lock_.unlock(); - - // Download the texture - - f->glBindFramebuffer(GL_READ_FRAMEBUFFER, read_buffer_); - - xf->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, - entry.texture->texture(), - 0); - - f->glReadPixels(0, - 0, - entry.texture->width(), - entry.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 = entry.filename.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(entry.hash); - } else { - qWarning() << QStringLiteral("Failed to open output file \"%1\"").arg(entry.filename); - } - } - - f->glDeleteFramebuffers(1, &read_buffer_); -} diff --git a/app/render/backend/videorendererdownloadthread.h b/app/render/backend/videorendererdownloadthread.h deleted file mode 100644 index 556d4d6be..000000000 --- a/app/render/backend/videorendererdownloadthread.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef RENDERERDOWNLOADTHREAD_H -#define RENDERERDOWNLOADTHREAD_H - -#include "videorendererthreadbase.h" - -class VideoRendererDownloadThread : public VideoRendererThreadBase -{ - Q_OBJECT -public: - VideoRendererDownloadThread(QOpenGLContext* share_ctx, - const VideoRenderingParams ¶ms); - - void Queue(RenderTexturePtr texture, const QString &fn, const QByteArray &hash); - -public slots: - virtual void Cancel() override; - -signals: - void Downloaded(const QByteArray& hash); - -protected: - virtual void ProcessLoop() override; - -private: - struct DownloadQueueEntry { - RenderTexturePtr texture; - QString filename; - QByteArray hash; - }; - - GLuint read_buffer_; - - QVector texture_queue_; - - QMutex texture_queue_lock_; - - QAtomicInt cancelled_; - - QByteArray hash_; - -}; - -using RendererDownloadThreadPtr = std::shared_ptr; - -#endif // RENDERERDOWNLOADTHREAD_H diff --git a/app/render/backend/videorendererprocessthread.cpp b/app/render/backend/videorendererprocessthread.cpp deleted file mode 100644 index f1c6f691e..000000000 --- a/app/render/backend/videorendererprocessthread.cpp +++ /dev/null @@ -1,151 +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 "videorendererprocessthread.h" - -#include "videorenderbackend.h" - -RendererProcessThread::RendererProcessThread(VideoRenderBackend* parent, - QOpenGLContext *share_ctx, - const VideoRenderingParams ¶ms) : - VideoRendererThreadBase(share_ctx, params), - parent_(parent), - cancelled_(false) -{ - -} - -bool RendererProcessThread::Queue(const NodeDependency& dep, bool wait, bool sibling) -{ - if (wait) { - // Wait for thread to be available - mutex_.lock(); - } else if (!mutex_.tryLock()) { - return false; - } - - // We can now change params without the other thread using them - path_ = dep; - sibling_ = sibling; - - // Prepare to wait for thread to respond - caller_mutex_.lock(); - - // Wake up our main thread - wait_cond_.wakeAll(); - mutex_.unlock(); - - // Wait for thread to start before returning - wait_cond_.wait(&caller_mutex_); - caller_mutex_.unlock(); - - return true; -} - -void RendererProcessThread::Cancel() -{ - cancelled_ = true; - - mutex_.lock(); - wait_cond_.wakeAll(); - mutex_.unlock(); - - wait(); -} - -void RendererProcessThread::ProcessLoop() -{ - while (!cancelled_) { - // Main waiting condition - wait_cond_.wait(&mutex_); - - if (cancelled_) { - break; - } - - // Wake up main thread - caller_mutex_.lock(); - wait_cond_.wakeAll(); - caller_mutex_.unlock(); - - // 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; - - 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_); - } - } -} diff --git a/app/render/backend/videorendererprocessthread.h b/app/render/backend/videorendererprocessthread.h deleted file mode 100644 index 5957e2c06..000000000 --- a/app/render/backend/videorendererprocessthread.h +++ /dev/null @@ -1,68 +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 RENDERERPROCESSTHREAD_H -#define RENDERERPROCESSTHREAD_H - -#include "videorendererthreadbase.h" - -class VideoRenderBackend; - -class RendererProcessThread : public VideoRendererThreadBase -{ - Q_OBJECT -public: - RendererProcessThread(VideoRenderBackend* parent, - QOpenGLContext* share_ctx, - const VideoRenderingParams ¶ms); - - bool Queue(const NodeDependency &dep, bool wait, bool sibling); - -public slots: - virtual void Cancel() override; - -protected: - virtual void ProcessLoop() override; - -signals: - void RequestSibling(NodeDependency dep); - - void CachedFrame(RenderTexturePtr texture, const rational& time, const QByteArray& hash); - - void FrameSkipped(const rational& time, const QByteArray& hash); - -private: - VideoRenderBackend* parent_; - - NodeDependency path_; - - QByteArray hash_; - - RenderTexturePtr texture_; - - QAtomicInt cancelled_; - - bool sibling_; - -}; - -using RendererProcessThreadPtr = std::shared_ptr; - -#endif // RENDERERPROCESSTHREAD_H diff --git a/app/render/backend/videorendererthreadbase.cpp b/app/render/backend/videorendererthreadbase.cpp index 602d7a229..148fcb09d 100644 --- a/app/render/backend/videorendererthreadbase.cpp +++ b/app/render/backend/videorendererthreadbase.cpp @@ -21,8 +21,13 @@ #include "videorendererthreadbase.h" #include +#include -VideoRendererThreadBase::VideoRendererThreadBase(QOpenGLContext *share_ctx, const VideoRenderingParams ¶ms) : +#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) { @@ -34,50 +39,150 @@ RenderInstance *VideoRendererThreadBase::render_instance() return &render_instance_; } -void VideoRendererThreadBase::run() +void VideoRendererThreadBase::Start() { - // Lock mutex for main loop - mutex_.lock(); - render_instance_.SetShareContext(share_ctx_); // Allocate and create resources - bool started = render_instance_.Start(); + render_instance_.Start(); - // Signal that main thread can continue now - WakeCaller(); - if (started) { + // Set up download functions + f = render_instance()->context()->functions(); + xf = render_instance()->context()->extraFunctions(); - // Main loop (use Cancel() to exit it) - ProcessLoop(); + 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(); - // Unlock mutex before exiting - mutex_.unlock(); + thread()->quit(); } -void VideoRendererThreadBase::WakeCaller() +void VideoRendererThreadBase::Process(const NodeDependency &path, bool sibling) { - // Signal that main thread can continue now - caller_mutex_.lock(); - wait_cond_.wakeAll(); - caller_mutex_.unlock(); + // 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::StartThread(QThread::Priority priority) +void VideoRendererThreadBase::Download(RenderTexturePtr texture, const QString &fn, const QByteArray &hash) { - caller_mutex_.lock(); + // Download the texture - // Start the thread - QThread::start(priority); + f->glBindFramebuffer(GL_READ_FRAMEBUFFER, read_buffer_); - // Wait for thread to finish completion - wait_cond_.wait(&caller_mutex_); + xf->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + texture->texture(), + 0); - caller_mutex_.unlock(); + 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 index 47a129894..e7bbca4d8 100644 --- a/app/render/backend/videorendererthreadbase.h +++ b/app/render/backend/videorendererthreadbase.h @@ -22,45 +22,62 @@ #define RENDERTHREAD_H #include -#include -#include -#include +#include #include "node/node.h" #include "render/videoparams.h" #include "renderinstance.h" +#include "render/pixelservice.h" -class VideoRendererThreadBase : public QThread +class VideoRenderBackend; + +class VideoRendererThreadBase : public QObject { Q_OBJECT public: - VideoRendererThreadBase(QOpenGLContext* share_ctx, const VideoRenderingParams& params); + VideoRendererThreadBase(VideoRenderBackend* parent, QOpenGLContext* share_ctx, const VideoRenderingParams& params); RenderInstance* render_instance(); - void StartThread(Priority priority = InheritPriority); - - virtual void run() override; - public slots: - virtual void Cancel() = 0; + void Start(); -protected: - virtual void ProcessLoop() = 0; + void Stop(); - QWaitCondition wait_cond_; + void Process(const NodeDependency &dep, bool sibling); - QMutex mutex_; + void Download(RenderTexturePtr texture, const QString &fn, const QByteArray &hash); - QMutex caller_mutex_; +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;