From ceb70a1870f52b5baadeaef8fed3da55ecb2a99d Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 16 Feb 2020 18:22:43 +1100 Subject: [PATCH] viewer: use internal texture to allow viewer to control texture as part of its context More intuitive code flow and allows the user to undock the viewer (which forcibly destroys and recreates the context) and the viewer will handle creation of the new texture in said new context. --- app/render/backend/exporter.cpp | 3 +- app/render/backend/opengl/openglbackend.cpp | 70 --------------------- app/render/backend/opengl/openglbackend.h | 11 ---- app/render/backend/opengl/opengltexture.cpp | 45 ++++--------- app/render/backend/renderbackend.cpp | 9 +++ app/render/backend/renderbackend.h | 4 +- app/render/backend/videorenderbackend.cpp | 52 ++------------- app/render/backend/videorenderbackend.h | 21 +------ app/render/pixelservice.cpp | 15 +++++ app/render/pixelservice.h | 2 + app/widget/viewer/viewer.cpp | 25 +++----- app/widget/viewer/viewer.h | 10 --- app/widget/viewer/viewerglwidget.cpp | 67 +++++++++++++++----- app/widget/viewer/viewerglwidget.h | 9 ++- 14 files changed, 116 insertions(+), 227 deletions(-) diff --git a/app/render/backend/exporter.cpp b/app/render/backend/exporter.cpp index 0d2e48e46..e63f02aa2 100644 --- a/app/render/backend/exporter.cpp +++ b/app/render/backend/exporter.cpp @@ -278,7 +278,8 @@ void Exporter::VideoHashesComplete() video_backend_->SetOperatingMode(VideoRenderWorker::kRenderOnly); video_backend_->SetOnlySignalLastFrameRequested(false); - connect(video_backend_, &VideoRenderBackend::CachedFrameReady, this, &Exporter::FrameRendered); + // FIXME: Exporting is now broken because of this + //connect(video_backend_, &VideoRenderBackend::CachedFrameReady, this, &Exporter::FrameRendered); foreach (const TimeRange& range, ranges) { video_backend_->InvalidateCache(range.in(), range.out()); diff --git a/app/render/backend/opengl/openglbackend.cpp b/app/render/backend/opengl/openglbackend.cpp index a79cc6d09..d0542e0a7 100644 --- a/app/render/backend/opengl/openglbackend.cpp +++ b/app/render/backend/opengl/openglbackend.cpp @@ -7,7 +7,6 @@ OpenGLBackend::OpenGLBackend(QObject *parent) : VideoRenderBackend(parent), - master_texture_(nullptr), proxy_(nullptr) { } @@ -49,17 +48,6 @@ bool OpenGLBackend::InitInternal() connect(processor, &OpenGLWorker::RequestRunNodeAccelerated, proxy_, &OpenGLProxy::RunNodeAccelerated, Qt::BlockingQueuedConnection); } - // Create master texture (the one sent to the viewer) - master_texture_ = std::make_shared(); - master_texture_->Create(QOpenGLContext::currentContext(), - params().effective_width(), - params().effective_height(), - params().format()); - - // Create copy buffer/pipeline - copy_buffer_.Create(QOpenGLContext::currentContext()); - copy_pipeline_ = OpenGLShader::CreateDefault(); - return true; } @@ -70,25 +58,9 @@ void OpenGLBackend::CloseInternal() proxy_ = nullptr; } - copy_buffer_.Destroy(); - copy_pipeline_ = nullptr; - master_texture_ = nullptr; - VideoRenderBackend::CloseInternal(); } -OpenGLTexturePtr OpenGLBackend::GetCachedFrameAsTexture(const rational &time) -{ - const char* cached_frame = GetCachedFrame(time); - - if (cached_frame) { - master_texture_->Upload(cached_frame); - return master_texture_; - } - - return nullptr; -} - bool OpenGLBackend::CompileInternal() { return true; @@ -98,52 +70,10 @@ void OpenGLBackend::DecompileInternal() { } -void OpenGLBackend::EmitCachedFrameReady(const rational &time, const QVariant &value, qint64 job_time) -{ - OpenGLTextureCache::ReferencePtr ref = value.value(); - OpenGLTexturePtr tex; - - if (ref && ref->texture()) { - tex = CopyTexture(ref->texture()); - } else { - tex = nullptr; - } - - emit CachedFrameReady(time, QVariant::fromValue(tex), job_time); -} - void OpenGLBackend::ParamsChangedEvent() { // If we're initiated, we need to recreate the texture. Otherwise this backend isn't active so it doesn't matter. if (IsInitiated()) { - master_texture_->Destroy(); - master_texture_->Create(QOpenGLContext::currentContext(), - params().effective_width(), - params().effective_height(), - params().format()); - proxy_->SetParameters(params()); } } - -OpenGLTexturePtr OpenGLBackend::CopyTexture(OpenGLTexturePtr input) -{ - QOpenGLContext* ctx = QOpenGLContext::currentContext(); - - OpenGLTexturePtr copy = std::make_shared(); - copy->Create(ctx, input->width(), input->height(), input->format()); - - ctx->functions()->glViewport(0, 0, input->width(), input->height()); - - copy_buffer_.Attach(copy); - copy_buffer_.Bind(); - input->Bind(); - - OpenGLRenderFunctions::Blit(copy_pipeline_); - - input->Release(); - copy_buffer_.Release(); - copy_buffer_.Detach(); - - return copy; -} diff --git a/app/render/backend/opengl/openglbackend.h b/app/render/backend/opengl/openglbackend.h index f4b010230..43260b659 100644 --- a/app/render/backend/opengl/openglbackend.h +++ b/app/render/backend/opengl/openglbackend.h @@ -16,8 +16,6 @@ public: virtual ~OpenGLBackend() override; - OpenGLTexturePtr GetCachedFrameAsTexture(const rational& time); - protected: virtual bool InitInternal() override; @@ -27,18 +25,9 @@ protected: virtual void DecompileInternal() override; - virtual void EmitCachedFrameReady(const rational &time, const QVariant& value, qint64 job_time) override; - virtual void ParamsChangedEvent() override; private: - OpenGLTexturePtr CopyTexture(OpenGLTexturePtr input); - - OpenGLTexturePtr master_texture_; - - OpenGLFramebuffer copy_buffer_; - OpenGLShaderPtr copy_pipeline_; - OpenGLProxy* proxy_; }; diff --git a/app/render/backend/opengl/opengltexture.cpp b/app/render/backend/opengl/opengltexture.cpp index 1810b8704..03ebbfdb0 100644 --- a/app/render/backend/opengl/opengltexture.cpp +++ b/app/render/backend/opengl/opengltexture.cpp @@ -58,7 +58,7 @@ void OpenGLTexture::Create(QOpenGLContext *ctx, int width, int height, const Pix height_ = height; format_ = format; - connect(created_ctx_, SIGNAL(aboutToBeDestroyed()), this, SLOT(Destroy())); + connect(created_ctx_, SIGNAL(aboutToBeDestroyed()), this, SLOT(Destroy()), Qt::DirectConnection); // Create main texture CreateInternal(created_ctx_, &texture_, data); @@ -83,26 +83,12 @@ void OpenGLTexture::Destroy() void OpenGLTexture::Bind() { - QOpenGLContext* context = QOpenGLContext::currentContext(); - - if (!context) { - qWarning() << "OpenGLTexture::Bind() called with an invalid context"; - return; - } - - context->functions()->glBindTexture(GL_TEXTURE_2D, texture_); + created_ctx_->functions()->glBindTexture(GL_TEXTURE_2D, texture_); } void OpenGLTexture::Release() { - QOpenGLContext* context = QOpenGLContext::currentContext(); - - if (!context) { - qWarning() << "OpenGLTexture::Release() called with an invalid context"; - return; - } - - context->functions()->glBindTexture(GL_TEXTURE_2D, 0); + created_ctx_->functions()->glBindTexture(GL_TEXTURE_2D, 0); } const int &OpenGLTexture::width() const @@ -132,26 +118,19 @@ void OpenGLTexture::Upload(const void *data) return; } - QOpenGLContext* context = QOpenGLContext::currentContext(); - - if (!context) { - qWarning() << "OpenGLTexture::Release() called with an invalid context"; - return; - } - Bind(); PixelFormat::Info info = PixelService::GetPixelFormatInfo(format_); - context->functions()->glTexSubImage2D(GL_TEXTURE_2D, - 0, - 0, - 0, - width_, - height_, - info.pixel_format, - info.gl_pixel_type, - data); + created_ctx_->functions()->glTexSubImage2D(GL_TEXTURE_2D, + 0, + 0, + 0, + width_, + height_, + info.pixel_format, + info.gl_pixel_type, + data); Release(); } diff --git a/app/render/backend/renderbackend.cpp b/app/render/backend/renderbackend.cpp index 93fd9d566..a0e666cfb 100644 --- a/app/render/backend/renderbackend.cpp +++ b/app/render/backend/renderbackend.cpp @@ -202,6 +202,15 @@ void RenderBackend::RegenerateCacheID() CacheIDChangedEvent(cache_id_); } +bool RenderBackend::InitInternal() +{ + return true; +} + +void RenderBackend::CloseInternal() +{ +} + bool RenderBackend::CanRender() { return true; diff --git a/app/render/backend/renderbackend.h b/app/render/backend/renderbackend.h index db58f1cf9..8c09c464b 100644 --- a/app/render/backend/renderbackend.h +++ b/app/render/backend/renderbackend.h @@ -43,9 +43,9 @@ signals: protected: void RegenerateCacheID(); - virtual bool InitInternal() = 0; + virtual bool InitInternal(); - virtual void CloseInternal() = 0; + virtual void CloseInternal(); virtual bool CompileInternal() = 0; diff --git a/app/render/backend/videorenderbackend.cpp b/app/render/backend/videorenderbackend.cpp index d74bdb72d..1738f6e1b 100644 --- a/app/render/backend/videorenderbackend.cpp +++ b/app/render/backend/videorenderbackend.cpp @@ -42,17 +42,6 @@ VideoRenderBackend::VideoRenderBackend(QObject *parent) : connect(DiskManager::instance(), &DiskManager::DeletedFrame, this, &VideoRenderBackend::FrameRemovedFromDiskCache); } -bool VideoRenderBackend::InitInternal() -{ - ResizeCacheLoadBuffer(); - return true; -} - -void VideoRenderBackend::CloseInternal() -{ - cache_frame_load_buffer_.clear(); -} - void VideoRenderBackend::ConnectViewer(ViewerOutput *node) { connect(node, &ViewerOutput::VideoChangedBetween, this, &VideoRenderBackend::InvalidateCache); @@ -81,9 +70,6 @@ void VideoRenderBackend::SetParameters(const VideoRenderingParams& params) // Set new parameters params_ = params; - // Resize frame load buffer - ResizeCacheLoadBuffer(); - // Handle custom events from derivatives ParamsChangedEvent(); @@ -154,11 +140,6 @@ void VideoRenderBackend::ConnectWorkerToThis(RenderWorker *processor) connect(video_processor, &VideoRenderWorker::HashAlreadyExists, this, &VideoRenderBackend::ThreadHashAlreadyExists, Qt::QueuedConnection); } -void VideoRenderBackend::EmitCachedFrameReady(const rational &time, const QVariant &value, qint64 job_time) -{ - emit CachedFrameReady(time, value, job_time); -} - void VideoRenderBackend::InvalidateCacheInternal(const rational &start_range, const rational &end_range) { TimeRange invalidated(start_range, end_range); @@ -179,7 +160,7 @@ VideoRenderFrameCache *VideoRenderBackend::frame_cache() return &frame_cache_; } -const char *VideoRenderBackend::GetCachedFrame(const rational &time) +QString VideoRenderBackend::GetCachedFrame(const rational &time) { last_time_requested_ = time; @@ -204,30 +185,12 @@ const char *VideoRenderBackend::GetCachedFrame(const rational &time) QByteArray frame_hash = frame_cache_.TimeToHash(time); if (!frame_hash.isEmpty()) { - QString fn = frame_cache_.CachePathName(frame_hash, params_.format()); + DiskManager::instance()->Accessed(frame_hash); - if (QFileInfo::exists(fn)) { - auto in = OIIO::ImageInput::open(fn.toStdString()); - - if (in) { - DiskManager::instance()->Accessed(frame_hash); - - in->read_image(PixelService::GetPixelFormatInfo(params_.format()).oiio_desc, cache_frame_load_buffer_.data()); - - in->close(); - -#if OIIO_VERSION < 10903 - OIIO::ImageInput::destroy(in); -#endif - - return cache_frame_load_buffer_.constData(); - } else { - qWarning() << "OIIO Error:" << OIIO::geterror().c_str(); - } - } + return frame_cache_.CachePathName(frame_hash, params_.format()); } - return nullptr; + return QString(); } NodeInput *VideoRenderBackend::GetDependentInput() @@ -368,7 +331,7 @@ void VideoRenderBackend::TruncateFrameCacheLength(const rational &length) // If the playhead is past the length, update the viewer to a null texture because it won't be cached through the // queue, but will now be a null texture if (last_time_requested_ >= length) { - emit CachedFrameReady(last_time_requested_, QVariant(), QDateTime::currentMSecsSinceEpoch()); + emit CachedTimeReady(last_time_requested_, QDateTime::currentMSecsSinceEpoch()); } // Adjust queue for new invalidated range @@ -422,8 +385,3 @@ void VideoRenderBackend::Requeue() CacheNext(); } - -void VideoRenderBackend::ResizeCacheLoadBuffer() -{ - cache_frame_load_buffer_.resize(PixelService::GetBufferSize(params_.format(), params_.effective_width(), params_.effective_height())); -} diff --git a/app/render/backend/videorenderbackend.h b/app/render/backend/videorenderbackend.h index 5f151ff11..69ace2976 100644 --- a/app/render/backend/videorenderbackend.h +++ b/app/render/backend/videorenderbackend.h @@ -60,21 +60,13 @@ public: bool IsRendered(const rational& time) const; + QString GetCachedFrame(const rational& time); + VideoRenderFrameCache* frame_cache(); const VideoRenderingParams& params() const; protected: - /** - * @brief Allocate and start the multithreaded backend - */ - virtual bool InitInternal() override; - - /** - * @brief Terminate and deallocate the multithreaded backend - */ - virtual void CloseInternal() override; - struct HashTimeMapping { rational time; QByteArray hash; @@ -84,8 +76,6 @@ protected: virtual void DisconnectViewer(ViewerOutput* node) override; - const char *GetCachedFrame(const rational& time); - virtual NodeInput* GetDependentInput() override; virtual bool CanRender() override; @@ -101,8 +91,6 @@ protected: virtual void ConnectWorkerToThis(RenderWorker* processor) override; - virtual void EmitCachedFrameReady(const rational &time, const QVariant& value, qint64 job_time); - virtual void InvalidateCacheInternal(const rational &start_range, const rational &end_range) override; virtual void ParamsChangedEvent(); @@ -110,7 +98,6 @@ protected: VideoRenderWorker::OperatingMode operating_mode_; signals: - void CachedFrameReady(const rational& time, QVariant value, qint64 job_time); void CachedTimeReady(const rational& time, qint64 job_time); void RangeInvalidated(const TimeRange& range); @@ -124,12 +111,8 @@ private: void Requeue(); - void ResizeCacheLoadBuffer(); - VideoRenderingParams params_; - QByteArray cache_frame_load_buffer_; - VideoRenderFrameCache frame_cache_; TimeRangeList invalidated_; diff --git a/app/render/pixelservice.cpp b/app/render/pixelservice.cpp index 033e79a33..53e58437b 100644 --- a/app/render/pixelservice.cpp +++ b/app/render/pixelservice.cpp @@ -58,6 +58,21 @@ void PixelService::SetConfiguredFormatForMode(RenderMode::Mode mode, PixelFormat } } +PixelFormat::Format PixelService::OIIOFormatToOliveFormat(OIIO::TypeDesc desc) +{ + if (desc == OIIO::TypeDesc::UINT8) { + return PixelFormat::PIX_FMT_RGBA8; + } else if (desc == OIIO::TypeDesc::UINT16) { + return PixelFormat::PIX_FMT_RGBA16U; + } else if (desc == OIIO::TypeDesc::HALF) { + return PixelFormat::PIX_FMT_RGBA16F; + } else if (desc == OIIO::TypeDesc::FLOAT) { + return PixelFormat::PIX_FMT_RGBA32F; + } + + return PixelFormat::PIX_FMT_INVALID; +} + PixelFormat::Info PixelService::GetPixelFormatInfo(const PixelFormat::Format &format) { PixelFormat::Info info; diff --git a/app/render/pixelservice.h b/app/render/pixelservice.h index ee84ed317..0a874f444 100644 --- a/app/render/pixelservice.h +++ b/app/render/pixelservice.h @@ -41,6 +41,8 @@ public: PixelFormat::Format GetConfiguredFormatForMode(RenderMode::Mode mode); void SetConfiguredFormatForMode(RenderMode::Mode mode, PixelFormat::Format format); + static PixelFormat::Format OIIOFormatToOliveFormat(OIIO::TypeDesc desc); + /** * @brief Return a PixelFormatInfo containing information for a certain format * diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 559761529..25361fb34 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -83,7 +83,6 @@ ViewerWidget::ViewerWidget(QWidget *parent) : // Start background renderers video_renderer_ = new OpenGLBackend(this); - connect(video_renderer_, &VideoRenderBackend::CachedFrameReady, this, &ViewerWidget::RendererCachedFrame); connect(video_renderer_, &VideoRenderBackend::CachedTimeReady, this, &ViewerWidget::RendererCachedTime); connect(video_renderer_, &VideoRenderBackend::CachedTimeReady, ruler(), &TimeRuler::CacheTimeReady); connect(video_renderer_, &VideoRenderBackend::RangeInvalidated, ruler(), &TimeRuler::CacheInvalidatedRange); @@ -215,17 +214,16 @@ VideoRenderBackend *ViewerWidget::video_renderer() const return video_renderer_; } -void ViewerWidget::SetTexture(OpenGLTexturePtr tex) -{ - gl_widget_->SetTexture(tex); -} - void ViewerWidget::UpdateTextureFromNode(const rational& time) { - if (!GetConnectedNode()) { - SetTexture(nullptr); + if (!GetConnectedNode() || time >= GetConnectedNode()->Length()) { + gl_widget_->SetImage(QString()); } else { - SetTexture(video_renderer_->GetCachedFrameAsTexture(time)); + QString frame_fn = video_renderer_->GetCachedFrame(time); + + if (!frame_fn.isEmpty()) { + gl_widget_->SetImage(frame_fn); + } } } @@ -466,15 +464,6 @@ void ViewerWidget::PlaybackTimerUpdate() } } -void ViewerWidget::RendererCachedFrame(const rational &time, QVariant value, qint64 job_time) -{ - if (GetTime() == time && job_time > frame_cache_job_time_) { - frame_cache_job_time_ = job_time; - - SetTexture(value.value()); - } -} - void ViewerWidget::RendererCachedTime(const rational &time, qint64 job_time) { if (GetTime() == time && job_time > frame_cache_job_time_) { diff --git a/app/widget/viewer/viewer.h b/app/widget/viewer/viewer.h index 17cd5c620..77ee1f967 100644 --- a/app/widget/viewer/viewer.h +++ b/app/widget/viewer/viewer.h @@ -72,15 +72,6 @@ public: VideoRenderBackend* video_renderer() const; public slots: - /** - * @brief Set the texture to draw and draw it - * - * Wrapper function for ViewerGLWidget::SetTexture(). - * - * @param tex - */ - void SetTexture(OpenGLTexturePtr tex); - void Play(); void Pause(); @@ -166,7 +157,6 @@ private: private slots: void PlaybackTimerUpdate(); - void RendererCachedFrame(const rational& time, QVariant value, qint64 job_time); void RendererCachedTime(const rational& time, qint64 job_time); void SizeChangedSlot(int width, int height); diff --git a/app/widget/viewer/viewerglwidget.cpp b/app/widget/viewer/viewerglwidget.cpp index 7798731c9..afcc0cb76 100644 --- a/app/widget/viewer/viewerglwidget.cpp +++ b/app/widget/viewer/viewerglwidget.cpp @@ -20,6 +20,8 @@ #include "viewerglwidget.h" +#include +#include #include #include #include @@ -27,10 +29,10 @@ #include "render/backend/opengl/openglrenderfunctions.h" #include "render/backend/opengl/openglshader.h" +#include "render/pixelservice.h" ViewerGLWidget::ViewerGLWidget(QWidget *parent) : QOpenGLWidget(parent), - texture_(0), ocio_lut_(0), color_manager_(nullptr) { @@ -68,6 +70,51 @@ void ViewerGLWidget::SetMatrix(const QMatrix4x4 &mat) update(); } +void ViewerGLWidget::SetImage(const QString &fn) +{ + OIIO::ImageBuf* in; + + if (fn.isEmpty()) { + // Backend had no filename + goto end; + } + + if (!QFileInfo::exists(fn)) { + goto end; + } + + in = new OIIO::ImageBuf(fn.toStdString()); + + if (in->read(0, 0, true)) { + + PixelFormat::Format image_format = PixelService::OIIOFormatToOliveFormat(in->spec().format); + + // Ensure the following texture operations are done in our context (in case we're in a separate window for instance) + makeCurrent(); + + if (!texture_.IsCreated() + || texture_.width() != in->spec().width + || texture_.height() != in->spec().height + || texture_.format() != image_format) { + texture_.Destroy(); + + texture_.Create(context(), in->spec().width, in->spec().height, image_format); + } + + texture_.Upload(in->localpixels()); + + doneCurrent(); + + } else { + qWarning() << "OIIO Error:" << OIIO::geterror().c_str(); + } + + delete in; + +end: + update(); +} + void ViewerGLWidget::SetOCIODisplay(const QString &display) { ocio_display_ = display; @@ -109,15 +156,6 @@ const QString &ViewerGLWidget::ocio_look() const return ocio_look_; } -void ViewerGLWidget::SetTexture(OpenGLTexturePtr tex) -{ - // Update the texture - texture_ = tex; - - // Paint the texture - update(); -} - void ViewerGLWidget::SetOCIOParameters(const QString &display, const QString &view, const QString &look) { ocio_display_ = display; @@ -144,15 +182,16 @@ void ViewerGLWidget::paintGL() f->glClear(GL_COLOR_BUFFER_BIT); // We only draw if we have a pipeline - if (!pipeline_ || !texture_) { + if (!pipeline_ || !texture_.IsCreated()) { return; } // Bind retrieved texture - f->glBindTexture(GL_TEXTURE_2D, texture_->texture()); + f->glBindTexture(GL_TEXTURE_2D, texture_.texture()); // Blit using the pipeline retrieved in initializeGL() - OpenGLRenderFunctions::OCIOBlit(pipeline_, ocio_lut_, true, matrix_); + //OpenGLRenderFunctions::OCIOBlit(pipeline_, ocio_lut_, true, matrix_); + OpenGLRenderFunctions::Blit(pipeline_, true, matrix_); // Release retrieved texture f->glBindTexture(GL_TEXTURE_2D, 0); @@ -240,7 +279,7 @@ void ViewerGLWidget::ContextCleanup() makeCurrent(); ClearOCIOLutTexture(); - + texture_.Destroy(); pipeline_ = nullptr; doneCurrent(); diff --git a/app/widget/viewer/viewerglwidget.h b/app/widget/viewer/viewerglwidget.h index 18f82bda1..b26414b2c 100644 --- a/app/widget/viewer/viewerglwidget.h +++ b/app/widget/viewer/viewerglwidget.h @@ -74,6 +74,11 @@ public: */ void SetMatrix(const QMatrix4x4& mat); + /** + * @brief Set an image to load and display on screen + */ + void SetImage(const QString& fn); + public slots: /** * @brief Set the texture to draw and draw it @@ -82,7 +87,7 @@ public slots: * * @param tex */ - void SetTexture(OpenGLTexturePtr tex); + //void SetTexture(OpenGLTexturePtr tex); void SetOCIOParameters(const QString& display, const QString& view, const QString& look); @@ -157,7 +162,7 @@ private: /** * @brief Internal reference to the OpenGL texture to draw. Set in SetTexture() and used in paintGL(). */ - OpenGLTexturePtr texture_; + OpenGLTexture texture_; /** * @brief Internal shader object to use as the pipeline shader