diff --git a/app/dialog/sequence/sequencedialogparametertab.cpp b/app/dialog/sequence/sequencedialogparametertab.cpp index 98689b16c..7487ba294 100644 --- a/app/dialog/sequence/sequencedialogparametertab.cpp +++ b/app/dialog/sequence/sequencedialogparametertab.cpp @@ -75,7 +75,7 @@ SequenceDialogParameterTab::SequenceDialogParameterTab(Sequence* sequence, QWidg preview_layout->addWidget(preview_resolution_label_, row, 2); row++; preview_layout->addWidget(new QLabel(tr("Format:")), row, 0); - preview_format_field_ = new PixelFormatComboBox(true, true); + preview_format_field_ = new PixelFormatComboBox(true); preview_layout->addWidget(preview_format_field_, row, 1, 1, 2); layout->addWidget(preview_group); diff --git a/app/render/backend/opengl/openglrenderer.cpp b/app/render/backend/opengl/openglrenderer.cpp index 8280f6518..533408e01 100644 --- a/app/render/backend/opengl/openglrenderer.cpp +++ b/app/render/backend/opengl/openglrenderer.cpp @@ -62,6 +62,7 @@ OpenGLRenderer::OpenGLRenderer(QObject* parent) : OpenGLRenderer::~OpenGLRenderer() { + Destroy(); } void OpenGLRenderer::Init(QOpenGLContext *existing_ctx) @@ -97,13 +98,14 @@ bool OpenGLRenderer::Init() void OpenGLRenderer::PostInit() { // Make context current on that surface - if (!context_->makeCurrent(&surface_)) { + if (context_->parent() == this && !context_->makeCurrent(&surface_)) { qCritical() << "Failed to makeCurrent() on offscreen surface in thread" << thread(); return; } - // Store OpenGL functions instance functions_ = context_->functions(); + + // Store OpenGL functions instance functions_->glBlendFunc(GL_ONE, GL_ZERO); // Set up framebuffer used for various things @@ -127,29 +129,58 @@ void OpenGLRenderer::PostInit() void OpenGLRenderer::Destroy() { - // Delete vertex array object - vao_.destroy(); + if (context_) { + // Delete buffers + vert_vbo_.destroy(); + frag_vbo_.destroy(); - // Delete framebuffer - functions_->glDeleteFramebuffers(1, &framebuffer_); + // Delete vertex array object + vao_.destroy(); - // Delete all shaders - qDeleteAll(shader_cache_); - shader_cache_.clear(); + // Delete framebuffer + functions_->glDeleteFramebuffers(1, &framebuffer_); - // Delete context if it belongs to us - if (context_->parent() == this) { - delete context_; - } - context_ = nullptr; + // Delete all shaders + qDeleteAll(shader_cache_); + shader_cache_.clear(); - // Destroy surface if we created it - if (surface_.isValid()) { - surface_.destroy(); + // Delete context if it belongs to us + if (context_->parent() == this) { + delete context_; + } + context_ = nullptr; + + // Destroy surface if we created it + if (surface_.isValid()) { + surface_.destroy(); + } } } -QVariant OpenGLRenderer::CreateNativeTexture(const VideoParams &p, void *data, int linesize) +void OpenGLRenderer::ClearDestination(double r, double g, double b, double a) +{ + functions_->glClearColor(r, g, b, a); + functions_->glClear(GL_COLOR_BUFFER_BIT); +} + +void OpenGLRenderer::AttachTextureAsDestination(Renderer::Texture* texture) +{ + functions_->glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_); + functions_->glFramebufferTexture2D(GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + texture->id().value(), + 0); + + SetViewport(texture->width(), texture->height()); +} + +void OpenGLRenderer::DetachTextureAsDestination() +{ + functions_->glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +QVariant OpenGLRenderer::CreateNativeTexture(VideoParams p, void *data, int linesize) { GLuint texture; functions_->glGenTextures(1, &texture); @@ -157,7 +188,7 @@ QVariant OpenGLRenderer::CreateNativeTexture(const VideoParams &p, void *data, i functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, linesize); functions_->glTexImage2D(GL_TEXTURE_2D, 0, GetInternalFormat(p.format()), - p.width(), p.height(), 0, GetPixelFormat(p.format()), + p.width(), p.height(), 0, GL_RGBA, GetPixelType(p.format()), data); functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); @@ -171,6 +202,37 @@ void OpenGLRenderer::DestroyNativeTexture(QVariant texture) functions_->glDeleteTextures(1, &t); } +QVariant OpenGLRenderer::CreateNativeShader(ShaderCode code) +{ + QOpenGLShaderProgram* program = new QOpenGLShaderProgram(context_); + + if (!program->addShaderFromSourceCode(QOpenGLShader::Vertex, code.vert_code())) { + qCritical() << "Failed to add vertex code to shader"; + goto error; + } + + if (!program->addShaderFromSourceCode(QOpenGLShader::Fragment, code.frag_code())) { + qCritical() << "Failed to add fragment code to shader"; + goto error; + } + + if (!program->link()) { + qCritical() << "Failed to link shader"; + goto error; + } + + return Node::PtrToValue(program); + +error: + delete program; + return QVariant(); +} + +void OpenGLRenderer::DestroyNativeShader(QVariant shader) +{ + delete Node::ValueToPtr(shader); +} + void OpenGLRenderer::UploadToTexture(Texture *texture, void *data, int linesize) { GLuint t = texture->id().value(); @@ -186,7 +248,7 @@ void OpenGLRenderer::UploadToTexture(Texture *texture, void *data, int linesize) functions_->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, p.effective_width(), p.effective_height(), - GetPixelFormat(p.format()), GetPixelType(p.format()), + GL_RGBA, GetPixelType(p.format()), data); functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); @@ -210,7 +272,7 @@ void OpenGLRenderer::DownloadFromTexture(Texture* texture, void *data, int lines 0, p.width(), p.height(), - GetPixelFormat(p.format()), + GL_RGBA, GetPixelType(p.format()), data); @@ -219,7 +281,7 @@ void OpenGLRenderer::DownloadFromTexture(Texture* texture, void *data, int lines functions_->glBindTexture(GL_TEXTURE_2D, current_tex); } -Renderer::TexturePtr OpenGLRenderer::ProcessShader(const Node *node, const TimeRange &range, const ShaderJob &job, const VideoParams ¶ms) +Renderer::TexturePtr OpenGLRenderer::ProcessShader(const Node *node, ShaderJob job, VideoParams params) { // If this node is iterative, we'll pick up which input here GLuint iterative_input = 0; @@ -342,12 +404,6 @@ Renderer::TexturePtr OpenGLRenderer::ProcessShader(const Node *node, const TimeR { TexturePtr texture = value.value(); - if (texture) { - if (PixelFormat::FormatHasAlphaChannel(texture->format())) { - input_textures_have_alpha = true; - } - } - // Set value to bound texture shader->setUniformValue(variable_location, textures_to_bind.size()); @@ -359,6 +415,10 @@ Renderer::TexturePtr OpenGLRenderer::ProcessShader(const Node *node, const TimeR GLuint tex_id = texture ? texture->id().value() : 0; textures_to_bind.append(tex_id); + if (texture && texture->has_meaningful_alpha()) { + input_textures_have_alpha = true; + } + // Set enable flag if shader wants it int enable_param_location = shader->uniformLocation(QStringLiteral("%1_enabled").arg(it.key())); if (enable_param_location > -1) { @@ -412,17 +472,6 @@ Renderer::TexturePtr OpenGLRenderer::ProcessShader(const Node *node, const TimeR static_cast(params.height())); // Create the output textures - PixelFormat::Format output_format = (input_textures_have_alpha || job.GetAlphaChannelRequired()) - ? PixelFormat::GetFormatWithAlphaChannel(params.format()) - : PixelFormat::GetFormatWithoutAlphaChannel(params.format()); - VideoParams output_params(params.width(), - params.height(), - params.time_base(), - output_format, - params.pixel_aspect_ratio(), - params.interlacing(), - params.divider()); - int real_iteration_count; if (job.GetIterationCount() > 1 && job.GetIterativeInput()) { real_iteration_count = job.GetIterationCount(); @@ -431,11 +480,11 @@ Renderer::TexturePtr OpenGLRenderer::ProcessShader(const Node *node, const TimeR } TexturePtr dst_refs[2]; - dst_refs[0] = CreateTexture(output_params); + dst_refs[0] = CreateTexture(params); // If this node requires multiple iterations, get a texture for it too if (real_iteration_count > 1) { - dst_refs[1] = CreateTexture(output_params); + dst_refs[1] = CreateTexture(params); } // Some nodes use multiple iterations for optimization @@ -483,20 +532,15 @@ Renderer::TexturePtr OpenGLRenderer::ProcessShader(const Node *node, const TimeR PrepareInputTexture(job.GetBilinearFiltering()); } - functions_->glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_); - functions_->glFramebufferTexture2D(GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, - output_tex->id().value(), - 0); + AttachTextureAsDestination(output_tex.get()); // Blit this texture through this shader functions_->glDrawArrays(GL_TRIANGLES, 0, blit_vertices.size() / 3); - - // Reset framebuffer to default - functions_->glBindFramebuffer(GL_FRAMEBUFFER, 0); } + // Reset framebuffer to default + DetachTextureAsDestination(); + // Release any textures we bound before for (int i=textures_to_bind.size()-1; i>=0; i--) { functions_->glActiveTexture(GL_TEXTURE0 + i); @@ -509,26 +553,58 @@ Renderer::TexturePtr OpenGLRenderer::ProcessShader(const Node *node, const TimeR // Release shader shader->release(); + output_tex->set_has_meaningful_alpha((input_textures_have_alpha || job.GetAlphaChannelRequired())); + return output_tex; } +void OpenGLRenderer::SetViewport(int width, int height) +{ + functions_->glViewport(0, 0, width, height); +} + +void OpenGLRenderer::BlitColorManaged(ColorProcessorPtr color_processor, Texture *source, Renderer::Texture* destination) +{ + qCritical() << "OpenGLRenderer::BlitColorMangaed is a stub!"; +} + +void OpenGLRenderer::Blit(Renderer::Texture *source, QVariant shader, Renderer::ShaderUniformMap parameters, Renderer::Texture *destination) +{ + QOpenGLShaderProgram* program = Node::ValueToPtr(shader); + + if (!program) { + qCritical() << "Attempted to blit with a null shader"; + return; + } + + if (destination) { + AttachTextureAsDestination(destination); + } + + functions_->glBindTexture(GL_TEXTURE_2D, source->id().value()); + + program->bind(); + + qCritical() << "OpenGLRenderer::Blit is a stub!"; + + program->release(); + + functions_->glBindTexture(GL_TEXTURE_2D, 0); + + if (destination) { + DetachTextureAsDestination(); + } +} + GLint OpenGLRenderer::GetInternalFormat(PixelFormat::Format format) { switch (format) { - case PixelFormat::PIX_FMT_RGB8: - return GL_RGB8; case PixelFormat::PIX_FMT_RGBA8: return GL_RGBA8; - case PixelFormat::PIX_FMT_RGB16U: - return GL_RGB16; case PixelFormat::PIX_FMT_RGBA16U: return GL_RGBA16; - case PixelFormat::PIX_FMT_RGB16F: - return GL_RGB16F; case PixelFormat::PIX_FMT_RGBA16F: return GL_RGBA16F; - case PixelFormat::PIX_FMT_RGB32F: - return GL_RGB32F; case PixelFormat::PIX_FMT_RGBA32F: return GL_RGBA32F; @@ -540,28 +616,15 @@ GLint OpenGLRenderer::GetInternalFormat(PixelFormat::Format format) return GL_INVALID_VALUE; } -GLenum OpenGLRenderer::GetPixelFormat(PixelFormat::Format format) -{ - if (PixelFormat::FormatHasAlphaChannel(format)) { - return GL_RGBA; - } else { - return GL_RGB; - } -} - GLenum OpenGLRenderer::GetPixelType(PixelFormat::Format format) { switch (format) { - case PixelFormat::PIX_FMT_RGB8: case PixelFormat::PIX_FMT_RGBA8: return GL_UNSIGNED_BYTE; - case PixelFormat::PIX_FMT_RGB16U: case PixelFormat::PIX_FMT_RGBA16U: return GL_UNSIGNED_SHORT; - case PixelFormat::PIX_FMT_RGB16F: case PixelFormat::PIX_FMT_RGBA16F: return GL_HALF_FLOAT; - case PixelFormat::PIX_FMT_RGB32F: case PixelFormat::PIX_FMT_RGBA32F: return GL_FLOAT; diff --git a/app/render/backend/opengl/openglrenderer.h b/app/render/backend/opengl/openglrenderer.h index 00cf5275d..d0358a680 100644 --- a/app/render/backend/opengl/openglrenderer.h +++ b/app/render/backend/opengl/openglrenderer.h @@ -49,26 +49,37 @@ public slots: virtual void Destroy() override; - virtual QVariant CreateNativeTexture(const VideoParams& p, void* data = nullptr, int linesize = 0) override; + virtual void ClearDestination(double r = 0.0, double g = 0.0, double b = 0.0, double a = 0.0) override; + + virtual void AttachTextureAsDestination(OLIVE_NAMESPACE::Renderer::Texture* texture) override; + + virtual void DetachTextureAsDestination() override; + + virtual QVariant CreateNativeTexture(OLIVE_NAMESPACE::VideoParams param, void* data = nullptr, int linesize = 0) override; virtual void DestroyNativeTexture(QVariant texture) override; - virtual void UploadToTexture(Texture* texture, void* data, int linesize) override; + virtual QVariant CreateNativeShader(OLIVE_NAMESPACE::ShaderCode code) override; - virtual void DownloadFromTexture(Texture* texture, void* data, int linesize) override; + virtual void DestroyNativeShader(QVariant shader) override; + + virtual void UploadToTexture(OLIVE_NAMESPACE::Renderer::Texture* texture, void* data, int linesize) override; + + virtual void DownloadFromTexture(OLIVE_NAMESPACE::Renderer::Texture* texture, void* data, int linesize) override; virtual TexturePtr ProcessShader(const OLIVE_NAMESPACE::Node* node, - const OLIVE_NAMESPACE::TimeRange &range, - const OLIVE_NAMESPACE::ShaderJob &job, - const OLIVE_NAMESPACE::VideoParams ¶ms) override; + OLIVE_NAMESPACE::ShaderJob job, + OLIVE_NAMESPACE::VideoParams params) override; - virtual TexturePtr TransformColor(Texture* texture, OLIVE_NAMESPACE::ColorProcessorPtr processor) override; + virtual void SetViewport(int width, int height) override; + + virtual void BlitColorManaged(OLIVE_NAMESPACE::ColorProcessorPtr color_processor, OLIVE_NAMESPACE::Renderer::Texture* source, OLIVE_NAMESPACE::Renderer::Texture *destination = nullptr) override; + + virtual void Blit(OLIVE_NAMESPACE::Renderer::Texture* source, QVariant shader, OLIVE_NAMESPACE::Renderer::ShaderUniformMap parameters, OLIVE_NAMESPACE::Renderer::Texture* destination = nullptr) override; private: static GLint GetInternalFormat(PixelFormat::Format format); - static GLenum GetPixelFormat(PixelFormat::Format format); - static GLenum GetPixelType(PixelFormat::Format format); void PrepareInputTexture(bool bilinear); @@ -93,6 +104,4 @@ private: OLIVE_NAMESPACE_EXIT -Q_DECLARE_METATYPE(OLIVE_NAMESPACE::OpenGLRenderer::TexturePtr); - #endif // OPENGLCONTEXT_H diff --git a/app/render/backend/renderer.h b/app/render/backend/renderer.h index e30bb0c9d..0d8088af8 100644 --- a/app/render/backend/renderer.h +++ b/app/render/backend/renderer.h @@ -38,8 +38,6 @@ class Renderer : public QObject public: Renderer(QObject* parent = nullptr); - virtual ~Renderer() override; - virtual bool Init() = 0; class Texture @@ -48,7 +46,8 @@ public: Texture(Renderer* renderer, const QVariant& native, const VideoParams& param) : renderer_(renderer), params_(param), - id_(native) + id_(native), + meaningful_alpha_(true) { } @@ -67,7 +66,7 @@ public: return params_; } - void Upload(void* data, int linesize = 0) + void Upload(void* data, int linesize) { renderer_->UploadToTexture(this, data, linesize); } @@ -97,6 +96,16 @@ public: return params_.pixel_aspect_ratio(); } + bool has_meaningful_alpha() const + { + return meaningful_alpha_; + } + + void set_has_meaningful_alpha(bool e) + { + meaningful_alpha_ = e; + } + private: Renderer* renderer_; @@ -104,39 +113,53 @@ public: QVariant id_; + bool meaningful_alpha_; + }; using TexturePtr = std::shared_ptr; TexturePtr CreateTexture(const VideoParams& param, void* data = nullptr, int linesize = 0); + struct ShaderValue { + QVariant data; + NodeParam::DataType type; + }; + + using ShaderUniformMap = QHash; + public slots: virtual void PostInit() = 0; virtual void Destroy() = 0; - virtual QVariant CreateNativeTexture(const VideoParams& param, void* data = nullptr, int linesize = 0) = 0; + virtual void ClearDestination(double r = 0.0, double g = 0.0, double b = 0.0, double a = 0.0) = 0; + + virtual void AttachTextureAsDestination(OLIVE_NAMESPACE::Renderer::Texture* texture) = 0; + + virtual void DetachTextureAsDestination() = 0; + + virtual QVariant CreateNativeTexture(OLIVE_NAMESPACE::VideoParams param, void* data = nullptr, int linesize = 0) = 0; virtual void DestroyNativeTexture(QVariant texture) = 0; - virtual QVariant CreateNativeShader(const ShaderCode& code) = 0; + virtual QVariant CreateNativeShader(OLIVE_NAMESPACE::ShaderCode code) = 0; virtual void DestroyNativeShader(QVariant shader) = 0; - virtual void UploadToTexture(Texture* texture, void* data, int linesize) = 0; + virtual void UploadToTexture(OLIVE_NAMESPACE::Renderer::Texture* texture, void* data, int linesize) = 0; - virtual void DownloadFromTexture(Texture* texture, void* data, int linesize) = 0; + virtual void DownloadFromTexture(OLIVE_NAMESPACE::Renderer::Texture* texture, void* data, int linesize) = 0; virtual TexturePtr ProcessShader(const OLIVE_NAMESPACE::Node* node, - const OLIVE_NAMESPACE::TimeRange &range, - const OLIVE_NAMESPACE::ShaderJob &job, - const OLIVE_NAMESPACE::VideoParams ¶ms) = 0; + OLIVE_NAMESPACE::ShaderJob job, + OLIVE_NAMESPACE::VideoParams params) = 0; - virtual TexturePtr TransformColor(Texture* texture, OLIVE_NAMESPACE::ColorProcessorPtr processor) = 0; + virtual void SetViewport(int width, int height) = 0; - virtual void Render() = 0; + virtual void BlitColorManaged(OLIVE_NAMESPACE::ColorProcessorPtr color_processor, OLIVE_NAMESPACE::Renderer::Texture* source, OLIVE_NAMESPACE::Renderer::Texture *destination = nullptr) = 0; - virtual void RenderToTexture(Texture* destination) = 0; + virtual void Blit(OLIVE_NAMESPACE::Renderer::Texture* source, QVariant shader, OLIVE_NAMESPACE::Renderer::ShaderUniformMap parameters, OLIVE_NAMESPACE::Renderer::Texture* destination = nullptr) = 0; private: @@ -145,4 +168,6 @@ private: OLIVE_NAMESPACE_EXIT +Q_DECLARE_METATYPE(OLIVE_NAMESPACE::Renderer::TexturePtr); + #endif // RENDERCONTEXT_H diff --git a/app/render/backend/rendererthreadwrapper.cpp b/app/render/backend/rendererthreadwrapper.cpp index d344b16c5..eed470ec7 100644 --- a/app/render/backend/rendererthreadwrapper.cpp +++ b/app/render/backend/rendererthreadwrapper.cpp @@ -22,12 +22,11 @@ OLIVE_NAMESPACE_ENTER -/*RendererThreadWrapper::RendererThreadWrapper(Renderer *inner, QObject *parent) : +RendererThreadWrapper::RendererThreadWrapper(Renderer *inner, QObject *parent) : Renderer(parent), inner_(inner), thread_(nullptr) { - inner_->setParent(this); } bool RendererThreadWrapper::Init() @@ -38,11 +37,11 @@ bool RendererThreadWrapper::Init() } // Create thread - QThread* thread = new QThread(this); - thread->start(QThread::IdlePriority); + thread_ = new QThread(this); + thread_->start(QThread::IdlePriority); // Move context to thread - inner_->moveToThread(thread); + inner_->moveToThread(thread_); // Queue post-init in new thread QMetaObject::invokeMethod(inner_, "PostInit", Qt::BlockingQueuedConnection); @@ -50,10 +49,16 @@ bool RendererThreadWrapper::Init() return true; } +void RendererThreadWrapper::PostInit() +{ + // Do nothing +} + void RendererThreadWrapper::Destroy() { if (thread_) { QMetaObject::invokeMethod(inner_, "Destroy", Qt::BlockingQueuedConnection); + inner_ = nullptr; thread_->quit(); thread_->wait(); @@ -62,76 +67,113 @@ void RendererThreadWrapper::Destroy() } } -QVariant RendererThreadWrapper::CreateTexture(const VideoParams ¶m, void *data, int linesize) +void RendererThreadWrapper::ClearDestination(double r, double g, double b, double a) +{ + QMetaObject::invokeMethod(inner_, "ClearDestination", Qt::BlockingQueuedConnection, + Q_ARG(double, r), + Q_ARG(double, g), + Q_ARG(double, b), + Q_ARG(double, a)); +} + +void RendererThreadWrapper::AttachTextureAsDestination(Renderer::Texture *texture) +{ + QMetaObject::invokeMethod(inner_, "AttachTextureAsDestination", Qt::BlockingQueuedConnection, + OLIVE_NS_ARG(Renderer::Texture*, texture)); +} + +void RendererThreadWrapper::DetachTextureAsDestination() +{ + QMetaObject::invokeMethod(inner_, "DetachTextureAsDestination", Qt::BlockingQueuedConnection); +} + +QVariant RendererThreadWrapper::CreateNativeTexture(VideoParams param, void *data, int linesize) { QVariant v; - QMetaObject::invokeMethod(inner_, "CreateTexture", Qt::BlockingQueuedConnection, + QMetaObject::invokeMethod(inner_, "CreateNativeTexture", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QVariant, v), - OLIVE_NS_CONST_ARG(VideoParams&, param), + OLIVE_NS_ARG(VideoParams, param), Q_ARG(void*, data), Q_ARG(int, linesize)); return v; } -void RendererThreadWrapper::DestroyTexture(QVariant texture) +void RendererThreadWrapper::DestroyNativeTexture(QVariant texture) { - QMetaObject::invokeMethod(inner_, "DestroyTexture", Qt::BlockingQueuedConnection, + QMetaObject::invokeMethod(inner_, "DestroyNativeTexture", Qt::BlockingQueuedConnection, Q_ARG(QVariant, texture)); } -void RendererThreadWrapper::UploadToTexture(QVariant texture, void *data, int linesize) +QVariant RendererThreadWrapper::CreateNativeShader(ShaderCode code) +{ + QVariant v; + + QMetaObject::invokeMethod(inner_, "CreateNativeShader", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QVariant, v), + OLIVE_NS_ARG(ShaderCode, code)); + + return v; +} + +void RendererThreadWrapper::DestroyNativeShader(QVariant shader) +{ + QMetaObject::invokeMethod(inner_, "DestroyNativeShader", Qt::BlockingQueuedConnection, + Q_ARG(QVariant, shader)); +} + +void RendererThreadWrapper::UploadToTexture(Renderer::Texture *texture, void *data, int linesize) { QMetaObject::invokeMethod(inner_, "UploadToTexture", Qt::BlockingQueuedConnection, - Q_ARG(QVariant, texture), + OLIVE_NS_ARG(Renderer::Texture*, texture), Q_ARG(void*, data), Q_ARG(int, linesize)); } -void RendererThreadWrapper::DownloadFromTexture(QVariant texture, void *data, int linesize) +void RendererThreadWrapper::DownloadFromTexture(Renderer::Texture *texture, void *data, int linesize) { QMetaObject::invokeMethod(inner_, "DownloadFromTexture", Qt::BlockingQueuedConnection, - Q_ARG(QVariant, texture), + OLIVE_NS_ARG(Renderer::Texture*, texture), Q_ARG(void*, data), Q_ARG(int, linesize)); } -QVariant RendererThreadWrapper::ProcessShader(const Node *node, const TimeRange &range, const ShaderJob &job, const VideoParams ¶ms) +Renderer::TexturePtr RendererThreadWrapper::ProcessShader(const Node *node, ShaderJob job, VideoParams params) { - QVariant v; + Renderer::TexturePtr tex; - QMetaObject::invokeMethod(inner_, "ProcessShader", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(QVariant, v), + QMetaObject::invokeMethod(inner_, "Blit", Qt::BlockingQueuedConnection, + OLIVE_NS_RETURN_ARG(Renderer::TexturePtr, tex), OLIVE_NS_CONST_ARG(Node*, node), - OLIVE_NS_CONST_ARG(TimeRange&, range), - OLIVE_NS_CONST_ARG(ShaderJob&, job), - OLIVE_NS_CONST_ARG(VideoParams&, params)); + OLIVE_NS_ARG(ShaderJob, job), + OLIVE_NS_ARG(VideoParams, params)); - return v; + return tex; } -QVariant RendererThreadWrapper::TransformColor(QVariant texture, ColorProcessorPtr processor) +void RendererThreadWrapper::SetViewport(int width, int height) { - QVariant v; - - QMetaObject::invokeMethod(inner_, "ProcessShader", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(QVariant, v), - Q_ARG(QVariant, texture), - OLIVE_NS_ARG(ColorProcessorPtr, processor)); - - return v; + QMetaObject::invokeMethod(inner_, "SetViewport", Qt::BlockingQueuedConnection, + Q_ARG(int, width), + Q_ARG(int, height)); } -VideoParams RendererThreadWrapper::GetParamsFromTexture(QVariant texture) +void RendererThreadWrapper::BlitColorManaged(ColorProcessorPtr color_processor, Renderer::Texture *source, Renderer::Texture *destination) { - VideoParams p; + QMetaObject::invokeMethod(inner_, "BlitColorManaged", Qt::BlockingQueuedConnection, + OLIVE_NS_ARG(ColorProcessorPtr, color_processor), + OLIVE_NS_ARG(Renderer::Texture*, source), + OLIVE_NS_ARG(Renderer::Texture*, destination)); +} - QMetaObject::invokeMethod(inner_, "GetParamsFromTexture", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(VideoParams, p), - Q_ARG(QVariant, texture)); - - return p; -}*/ +void RendererThreadWrapper::Blit(Renderer::Texture *source, QVariant shader, Renderer::ShaderUniformMap parameters, Renderer::Texture *destination) +{ + QMetaObject::invokeMethod(inner_, "Blit", Qt::BlockingQueuedConnection, + OLIVE_NS_ARG(Renderer::Texture*, source), + Q_ARG(QVariant, shader), + Q_ARG(Renderer::ShaderUniformMap, parameters), + OLIVE_NS_ARG(Renderer::Texture*, destination)); +} OLIVE_NAMESPACE_EXIT diff --git a/app/render/backend/rendererthreadwrapper.h b/app/render/backend/rendererthreadwrapper.h index 5a0e8d5cb..d8bd05bb0 100644 --- a/app/render/backend/rendererthreadwrapper.h +++ b/app/render/backend/rendererthreadwrapper.h @@ -27,7 +27,7 @@ OLIVE_NAMESPACE_ENTER -/*class RendererThreadWrapper : public Renderer +class RendererThreadWrapper : public Renderer { public: RendererThreadWrapper(Renderer* inner, QObject* parent = nullptr); @@ -35,39 +35,50 @@ public: virtual ~RendererThreadWrapper() override { Destroy(); + delete inner_; } virtual bool Init() override; public slots: - virtual void PostInit() override{} + virtual void PostInit() override; virtual void Destroy() override; - virtual QVariant CreateTexture(const VideoParams& param, void* data, int linesize) override; + virtual void ClearDestination(double r = 0.0, double g = 0.0, double b = 0.0, double a = 0.0) override; - virtual void DestroyTexture(QVariant texture) override; + virtual void AttachTextureAsDestination(OLIVE_NAMESPACE::Renderer::Texture* texture) override; - virtual void UploadToTexture(QVariant texture, void* data, int linesize) override; + virtual void DetachTextureAsDestination() override; - virtual void DownloadFromTexture(QVariant texture, void* data, int linesize) override; + virtual QVariant CreateNativeTexture(OLIVE_NAMESPACE::VideoParams param, void* data = nullptr, int linesize = 0) override; - virtual QVariant ProcessShader(const OLIVE_NAMESPACE::Node* node, - const OLIVE_NAMESPACE::TimeRange &range, - const OLIVE_NAMESPACE::ShaderJob &job, - const OLIVE_NAMESPACE::VideoParams ¶ms) override; + virtual void DestroyNativeTexture(QVariant texture) override; - virtual QVariant TransformColor(QVariant texture, - OLIVE_NAMESPACE::ColorProcessorPtr processor) override; + virtual QVariant CreateNativeShader(OLIVE_NAMESPACE::ShaderCode code) override; - //virtual VideoParams GetParamsFromTexture(QVariant texture) override; + virtual void DestroyNativeShader(QVariant shader) override; + + virtual void UploadToTexture(OLIVE_NAMESPACE::Renderer::Texture* texture, void* data, int linesize) override; + + virtual void DownloadFromTexture(OLIVE_NAMESPACE::Renderer::Texture* texture, void* data, int linesize) override; + + virtual TexturePtr ProcessShader(const OLIVE_NAMESPACE::Node* node, + OLIVE_NAMESPACE::ShaderJob job, + OLIVE_NAMESPACE::VideoParams params) override; + + virtual void SetViewport(int width, int height) override; + + virtual void BlitColorManaged(OLIVE_NAMESPACE::ColorProcessorPtr color_processor, OLIVE_NAMESPACE::Renderer::Texture* source, OLIVE_NAMESPACE::Renderer::Texture *destination = nullptr) override; + + virtual void Blit(OLIVE_NAMESPACE::Renderer::Texture* source, QVariant shader, OLIVE_NAMESPACE::Renderer::ShaderUniformMap parameters, OLIVE_NAMESPACE::Renderer::Texture* destination = nullptr) override; private: Renderer* inner_; QThread* thread_; -};*/ +}; OLIVE_NAMESPACE_EXIT diff --git a/app/render/color.cpp b/app/render/color.cpp index e8e4cbfca..3c47f7e81 100644 --- a/app/render/color.cpp +++ b/app/render/color.cpp @@ -196,11 +196,11 @@ float Color::lightness() const void Color::toData(char *data, const PixelFormat::Format &format) const { - OIIO::convert_types(PixelFormat::GetOIIOTypeDesc(PixelFormat::PIX_FMT_RGB32F), + OIIO::convert_types(OIIO::TypeDesc::FLOAT, data_, PixelFormat::GetOIIOTypeDesc(format), data, - PixelFormat::FormatHasAlphaChannel(format) ? kRGBAChannels : kRGBChannels); + kRGBAChannels); } Color Color::fromData(const char *data, const PixelFormat::Format &format) @@ -209,13 +209,9 @@ Color Color::fromData(const char *data, const PixelFormat::Format &format) OIIO::convert_types(PixelFormat::GetOIIOTypeDesc(format), data, - PixelFormat::GetOIIOTypeDesc(PixelFormat::PIX_FMT_RGB32F), + OIIO::TypeDesc::FLOAT, c.data_, - PixelFormat::FormatHasAlphaChannel(format) ? kRGBAChannels : kRGBChannels); - - if (!PixelFormat::FormatHasAlphaChannel(format)) { - c.set_alpha(1.0f); - } + kRGBAChannels); return c; } diff --git a/app/render/colormanager.cpp b/app/render/colormanager.cpp index 2e4f496dd..b6fb12e1c 100644 --- a/app/render/colormanager.cpp +++ b/app/render/colormanager.cpp @@ -332,11 +332,6 @@ void ColorManager::SetOCIOMethodForMode(RenderMode::Mode mode, ColorManager::OCI void ColorManager::AssociateAlphaPixFmtFilter(ColorManager::AlphaAction action, FramePtr f) { - if (!PixelFormat::FormatHasAlphaChannel(f->format())) { - // This frame has no alpha channel, do nothing - return; - } - int pixel_count = f->width() * f->height() * kRGBAChannels; switch (static_cast(f->format())) { @@ -344,19 +339,15 @@ void ColorManager::AssociateAlphaPixFmtFilter(ColorManager::AlphaAction action, case PixelFormat::PIX_FMT_COUNT: qWarning() << "Alpha association functions received an invalid pixel format"; break; - case PixelFormat::PIX_FMT_RGB8: case PixelFormat::PIX_FMT_RGBA8: - case PixelFormat::PIX_FMT_RGB16U: case PixelFormat::PIX_FMT_RGBA16U: qWarning() << "Alpha association functions only works on float-based pixel formats at this time"; break; - case PixelFormat::PIX_FMT_RGB16F: case PixelFormat::PIX_FMT_RGBA16F: { AssociateAlphaInternal(action, reinterpret_cast(f->data()), pixel_count); break; } - case PixelFormat::PIX_FMT_RGB32F: case PixelFormat::PIX_FMT_RGBA32F: { AssociateAlphaInternal(action, reinterpret_cast(f->data()), pixel_count); diff --git a/app/render/colorprocessor.cpp b/app/render/colorprocessor.cpp index 4b98a5133..cee97fb43 100644 --- a/app/render/colorprocessor.cpp +++ b/app/render/colorprocessor.cpp @@ -61,7 +61,7 @@ void ColorProcessor::ConvertFrame(Frame *f) OCIO::PackedImageDesc img(reinterpret_cast(f->data()), f->width(), f->height(), - PixelFormat::ChannelCount(f->format()), + kRGBAChannels, OCIO::AutoStride, OCIO::AutoStride, f->linesize_bytes()); diff --git a/app/render/decodercache.h b/app/render/decodercache.h index 6e0890f43..e43023274 100644 --- a/app/render/decodercache.h +++ b/app/render/decodercache.h @@ -26,7 +26,18 @@ OLIVE_NAMESPACE_ENTER -using DecoderCache = QHash; +class DecoderCache : public QHash +{ +public: + QMutex *mutex() + { + return &mutex_; + } + +private: + QMutex mutex_; + +}; OLIVE_NAMESPACE_EXIT diff --git a/app/render/framehashcache.cpp b/app/render/framehashcache.cpp index 37a656acf..d555d2a38 100644 --- a/app/render/framehashcache.cpp +++ b/app/render/framehashcache.cpp @@ -245,17 +245,9 @@ FramePtr FrameHashCache::LoadCacheFrame(const QString &fn) PixelFormat::Format image_format; if (pix_type == Imf::HALF) { - if (has_alpha) { - image_format = PixelFormat::PIX_FMT_RGBA16F; - } else { - image_format = PixelFormat::PIX_FMT_RGB16F; - } + image_format = PixelFormat::PIX_FMT_RGBA16F; } else { - if (has_alpha) { - image_format = PixelFormat::PIX_FMT_RGBA32F; - } else { - image_format = PixelFormat::PIX_FMT_RGB32F; - } + image_format = PixelFormat::PIX_FMT_RGBA32F; } frame = Frame::Create(); @@ -268,7 +260,7 @@ FramePtr FrameHashCache::LoadCacheFrame(const QString &fn) int bpc = PixelFormat::BytesPerChannel(image_format); - size_t xs = PixelFormat::ChannelCount(image_format) * bpc; + size_t xs = kRGBAChannels * bpc; size_t ys = frame->linesize_bytes(); Imf::FrameBuffer framebuffer; @@ -411,8 +403,7 @@ bool FrameHashCache::SaveCacheFrame(const QString &filename, char *data, const V // Floating point types are stored in EXR Imf::PixelType pix_type; - if (vparam.format() == PixelFormat::PIX_FMT_RGB16F - || vparam.format() == PixelFormat::PIX_FMT_RGBA16F) { + if (vparam.format() == PixelFormat::PIX_FMT_RGBA16F) { pix_type = Imf::HALF; } else { pix_type = Imf::FLOAT; @@ -423,9 +414,7 @@ bool FrameHashCache::SaveCacheFrame(const QString &filename, char *data, const V header.channels().insert("R", Imf::Channel(pix_type)); header.channels().insert("G", Imf::Channel(pix_type)); header.channels().insert("B", Imf::Channel(pix_type)); - if (PixelFormat::FormatHasAlphaChannel(vparam.format())) { - header.channels().insert("A", Imf::Channel(pix_type)); - } + header.channels().insert("A", Imf::Channel(pix_type)); header.compression() = Imf::DWAA_COMPRESSION; header.insert("dwaCompressionLevel", Imf::FloatAttribute(200.0f)); @@ -435,16 +424,14 @@ bool FrameHashCache::SaveCacheFrame(const QString &filename, char *data, const V int bpc = PixelFormat::BytesPerChannel(vparam.format()); - size_t xs = PixelFormat::ChannelCount(vparam.format()) * bpc; + size_t xs = kRGBAChannels * bpc; size_t ys = linesize_bytes; Imf::FrameBuffer framebuffer; framebuffer.insert("R", Imf::Slice(pix_type, data, xs, ys)); framebuffer.insert("G", Imf::Slice(pix_type, data + bpc, xs, ys)); framebuffer.insert("B", Imf::Slice(pix_type, data + 2*bpc, xs, ys)); - if (PixelFormat::FormatHasAlphaChannel(vparam.format())) { - framebuffer.insert("A", Imf::Slice(pix_type, data + 3*bpc, xs, ys)); - } + framebuffer.insert("A", Imf::Slice(pix_type, data + 3*bpc, xs, ys)); out.setFrameBuffer(framebuffer); out.writePixels(vparam.effective_height()); diff --git a/app/render/pixelformat.cpp b/app/render/pixelformat.cpp index 01abf0aff..9cde0a269 100644 --- a/app/render/pixelformat.cpp +++ b/app/render/pixelformat.cpp @@ -25,44 +25,20 @@ #include #include -#include "codec/oiio/oiiodecoder.h" +#include "codec/oiio/oiiocommon.h" #include "common/define.h" #include "core.h" OLIVE_NAMESPACE_ENTER -bool PixelFormat::FormatHasAlphaChannel(const PixelFormat::Format &format) -{ - switch (format) { - case PixelFormat::PIX_FMT_RGBA8: - case PixelFormat::PIX_FMT_RGBA16U: - case PixelFormat::PIX_FMT_RGBA16F: - case PixelFormat::PIX_FMT_RGBA32F: - return true; - case PixelFormat::PIX_FMT_RGB8: - case PixelFormat::PIX_FMT_RGB16U: - case PixelFormat::PIX_FMT_RGB16F: - case PixelFormat::PIX_FMT_RGB32F: - case PixelFormat::PIX_FMT_INVALID: - case PixelFormat::PIX_FMT_COUNT: - break; - } - - return false; -} - bool PixelFormat::FormatIsFloat(const PixelFormat::Format &format) { switch (format) { - case PixelFormat::PIX_FMT_RGB16F: case PixelFormat::PIX_FMT_RGBA16F: - case PixelFormat::PIX_FMT_RGB32F: case PixelFormat::PIX_FMT_RGBA32F: return true; - case PixelFormat::PIX_FMT_RGB8: case PixelFormat::PIX_FMT_RGBA8: - case PixelFormat::PIX_FMT_RGB16U: case PixelFormat::PIX_FMT_RGBA16U: case PixelFormat::PIX_FMT_INVALID: case PixelFormat::PIX_FMT_COUNT: @@ -75,16 +51,12 @@ bool PixelFormat::FormatIsFloat(const PixelFormat::Format &format) OIIO::TypeDesc::BASETYPE PixelFormat::GetOIIOTypeDesc(const PixelFormat::Format &format) { switch (format) { - case PixelFormat::PIX_FMT_RGB8: case PixelFormat::PIX_FMT_RGBA8: return OIIO::TypeDesc::UINT8; - case PixelFormat::PIX_FMT_RGB16U: case PixelFormat::PIX_FMT_RGBA16U: return OIIO::TypeDesc::UINT16; - case PixelFormat::PIX_FMT_RGB16F: case PixelFormat::PIX_FMT_RGBA16F: return OIIO::TypeDesc::HALF; - case PixelFormat::PIX_FMT_RGB32F: case PixelFormat::PIX_FMT_RGBA32F: return OIIO::TypeDesc::FLOAT; case PixelFormat::PIX_FMT_INVALID: @@ -98,16 +70,12 @@ OIIO::TypeDesc::BASETYPE PixelFormat::GetOIIOTypeDesc(const PixelFormat::Format QString PixelFormat::GetName(const PixelFormat::Format &format) { switch (format) { - case PixelFormat::PIX_FMT_RGB8: case PixelFormat::PIX_FMT_RGBA8: return tr("8-bit"); - case PixelFormat::PIX_FMT_RGB16U: case PixelFormat::PIX_FMT_RGBA16U: return tr("16-bit Integer"); - case PixelFormat::PIX_FMT_RGB16F: case PixelFormat::PIX_FMT_RGBA16F: return tr("Half-Float (16-bit)"); - case PixelFormat::PIX_FMT_RGB32F: case PixelFormat::PIX_FMT_RGBA32F: return tr("Full-Float (32-bit)"); case PixelFormat::PIX_FMT_INVALID: @@ -149,67 +117,21 @@ void PixelFormat::SetConfiguredFormatForMode(RenderMode::Mode mode, PixelFormat: } } -PixelFormat::Format PixelFormat::OIIOFormatToOliveFormat(OIIO::TypeDesc desc, bool has_alpha) +PixelFormat::Format PixelFormat::OIIOFormatToOliveFormat(OIIO::TypeDesc desc) { if (desc == OIIO::TypeDesc::UINT8) { - return has_alpha ? PixelFormat::PIX_FMT_RGBA8 : PixelFormat::PIX_FMT_RGB8; + return PixelFormat::PIX_FMT_RGBA8; } else if (desc == OIIO::TypeDesc::UINT16) { - return has_alpha ? PixelFormat::PIX_FMT_RGBA16U : PixelFormat::PIX_FMT_RGB16U; + return PixelFormat::PIX_FMT_RGBA16U; } else if (desc == OIIO::TypeDesc::HALF) { - return has_alpha ? PixelFormat::PIX_FMT_RGBA16F : PixelFormat::PIX_FMT_RGB16F; + return PixelFormat::PIX_FMT_RGBA16F; } else if (desc == OIIO::TypeDesc::FLOAT) { - return has_alpha ? PixelFormat::PIX_FMT_RGBA32F : PixelFormat::PIX_FMT_RGB32F; + return PixelFormat::PIX_FMT_RGBA32F; } return PixelFormat::PIX_FMT_INVALID; } -PixelFormat::Format PixelFormat::GetFormatWithAlphaChannel(PixelFormat::Format f) -{ - switch (f) { - case PIX_FMT_INVALID: - case PIX_FMT_COUNT: - break; - case PIX_FMT_RGB8: - case PIX_FMT_RGBA8: - return PIX_FMT_RGBA8; - case PIX_FMT_RGB16U: - case PIX_FMT_RGBA16U: - return PIX_FMT_RGBA16U; - case PIX_FMT_RGB16F: - case PIX_FMT_RGBA16F: - return PIX_FMT_RGBA16F; - case PIX_FMT_RGB32F: - case PIX_FMT_RGBA32F: - return PIX_FMT_RGBA32F; - } - - return PIX_FMT_INVALID; -} - -PixelFormat::Format PixelFormat::GetFormatWithoutAlphaChannel(PixelFormat::Format f) -{ - switch (f) { - case PIX_FMT_INVALID: - case PIX_FMT_COUNT: - break; - case PIX_FMT_RGB8: - case PIX_FMT_RGBA8: - return PIX_FMT_RGB8; - case PIX_FMT_RGB16U: - case PIX_FMT_RGBA16U: - return PIX_FMT_RGB16U; - case PIX_FMT_RGB16F: - case PIX_FMT_RGBA16F: - return PIX_FMT_RGB16F; - case PIX_FMT_RGB32F: - case PIX_FMT_RGBA32F: - return PIX_FMT_RGB32F; - } - - return PIX_FMT_INVALID; -} - int PixelFormat::GetBufferSize(const PixelFormat::Format &format, const int &width, const int &height) { return BytesPerPixel(format) * width * height; @@ -217,21 +139,17 @@ int PixelFormat::GetBufferSize(const PixelFormat::Format &format, const int &wid int PixelFormat::BytesPerPixel(const PixelFormat::Format &format) { - return BytesPerChannel(format) * ChannelCount(format); + return BytesPerChannel(format) * kRGBAChannels; } int PixelFormat::BytesPerChannel(const PixelFormat::Format &format) { switch (format) { - case PixelFormat::PIX_FMT_RGB8: case PixelFormat::PIX_FMT_RGBA8: return 1; - case PixelFormat::PIX_FMT_RGB16U: - case PixelFormat::PIX_FMT_RGB16F: case PixelFormat::PIX_FMT_RGBA16U: case PixelFormat::PIX_FMT_RGBA16F: return 2; - case PixelFormat::PIX_FMT_RGB32F: case PixelFormat::PIX_FMT_RGBA32F: return 4; case PixelFormat::PIX_FMT_INVALID: @@ -245,15 +163,6 @@ int PixelFormat::BytesPerChannel(const PixelFormat::Format &format) return 0; } -int PixelFormat::ChannelCount(const PixelFormat::Format &format) -{ - if (PixelFormat::FormatHasAlphaChannel(format)) { - return kRGBAChannels; - } else { - return kRGBChannels; - } -} - FramePtr PixelFormat::ConvertPixelFormat(FramePtr frame, const PixelFormat::Format &dest_format) { if (frame->format() == dest_format) { @@ -271,23 +180,23 @@ FramePtr PixelFormat::ConvertPixelFormat(FramePtr frame, const PixelFormat::Form // Do the conversion through OIIO - create a buffer for the source image OIIO::ImageBuf src(OIIO::ImageSpec(frame->width(), frame->height(), - ChannelCount(frame->format()), + kRGBAChannels, GetOIIOTypeDesc(frame->format()))); // Set the pixels (this is necessary as opposed to an OIIO buffer wrapper since Frame has // linesizes) - OIIODecoder::FrameToBuffer(frame, &src); + OIIOCommon::FrameToBuffer(frame, &src); // Create a destination OIIO buffer with our destination format OIIO::ImageBuf dst(OIIO::ImageSpec(converted->width(), converted->height(), - ChannelCount(converted->format()), + kRGBAChannels, GetOIIOTypeDesc(converted->format()))); if (dst.copy_pixels(src)) { // Convert our buffer back to a frame - OIIODecoder::BufferToFrame(&dst, converted); + OIIOCommon::BufferToFrame(&dst, converted); return converted; } else { diff --git a/app/render/pixelformat.h b/app/render/pixelformat.h index cb7c53fae..4808aa35a 100644 --- a/app/render/pixelformat.h +++ b/app/render/pixelformat.h @@ -48,11 +48,6 @@ public: PIX_FMT_RGBA16F, PIX_FMT_RGBA32F, - PIX_FMT_RGB8, - PIX_FMT_RGB16U, - PIX_FMT_RGB16F, - PIX_FMT_RGB32F, - PIX_FMT_COUNT }; @@ -66,10 +61,7 @@ public: Format GetConfiguredFormatForMode(RenderMode::Mode mode); void SetConfiguredFormatForMode(RenderMode::Mode mode, PixelFormat::Format format); - static Format OIIOFormatToOliveFormat(OIIO::TypeDesc desc, bool has_alpha); - - static Format GetFormatWithAlphaChannel(Format f); - static Format GetFormatWithoutAlphaChannel(Format f); + static Format OIIOFormatToOliveFormat(OIIO::TypeDesc desc); /** * @brief Returns the minimum buffer size (in bytes) necessary for a given format, width, and height. @@ -102,11 +94,6 @@ public: */ static int BytesPerChannel(const Format& format); - /** - * @brief Return the number of channels in this format - */ - static int ChannelCount(const Format& format); - /** * @brief Convert a frame to a pixel format * @@ -114,11 +101,6 @@ public: */ static FramePtr ConvertPixelFormat(FramePtr frame, const Format &dest_format); - /** - * @brief Simple convenience function returning whether a pixel format has an alpha channel or not - */ - static bool FormatHasAlphaChannel(const Format& format); - /** * @brief Simple convenience function returning whether a pixel format is float-based or integer-based */ diff --git a/app/render/previewautocacher.cpp b/app/render/previewautocacher.cpp index b0945e611..e8e4d72eb 100644 --- a/app/render/previewautocacher.cpp +++ b/app/render/previewautocacher.cpp @@ -3,6 +3,8 @@ #include #include +#include "project/item/sequence/sequence.h" +#include "project/project.h" #include "render/rendermanager.h" #include "render/renderprocessor.h" @@ -13,6 +15,7 @@ PreviewAutoCacher::PreviewAutoCacher() : paused_(false), has_changed_(false), use_custom_range_(false), + single_frame_render_(nullptr), last_update_time_(0), ignore_next_mouse_button_(false), video_params_changed_(false), @@ -24,13 +27,20 @@ PreviewAutoCacher::PreviewAutoCacher() : RenderTicketPtr PreviewAutoCacher::GetSingleFrame(const rational &t) { - RenderTicketPtr ticket = std::make_shared(); + if (single_frame_render_) { + single_frame_render_->Cancel(); + } - ticket->setProperty("time", QVariant::fromValue(t)); + single_frame_render_ = std::make_shared(); + + single_frame_render_->setProperty("time", QVariant::fromValue(t)); + + // Copy because TryRender() might set this to null and we still want to return a handle to this + RenderTicketPtr copy = single_frame_render_; TryRender(); - return ticket; + return copy; } void PreviewAutoCacher::SetPaused(bool paused) @@ -299,6 +309,14 @@ void PreviewAutoCacher::AudioParamsChanged() TryRender(); } +void PreviewAutoCacher::SingleFrameFinished() +{ + RenderTicketWatcher* watcher = static_cast(sender()); + RenderTicketPtr passthrough = watcher->property("passthrough").value(); + passthrough->Finish(watcher->GetTicket()->Get(), watcher->GetTicket()->WasCancelled()); + delete watcher; +} + //#define PRINT_UPDATE_QUEUE_INFO void PreviewAutoCacher::ProcessUpdateQueue() { @@ -554,23 +572,21 @@ void PreviewAutoCacher::TryRender() invalidated_audio_.clear(); } - if (!single_frame_renders_.isEmpty()) { - foreach (RenderTicketPtr ticket, single_frame_renders_) { - RenderTicketWatcher* watcher = new RenderTicketWatcher(); + if (single_frame_render_) { + RenderTicketWatcher* watcher = new RenderTicketWatcher(); - watcher->setProperty("passthrough", QVariant::fromValue(ticket)); + watcher->setProperty("passthrough", QVariant::fromValue(single_frame_render_)); - connect(watcher, &RenderTicketWatcher::Finished, watcher, [watcher]{ - RenderTicketPtr passthrough = watcher->property("passthrough").value(); - passthrough->Finish(watcher->GetTicket()->Get(), watcher->GetTicket()->WasCancelled()); - watcher->deleteLater(); - }); + connect(watcher, &RenderTicketWatcher::Finished, this, &PreviewAutoCacher::SingleFrameFinished); - watcher->SetTicket(RenderManager::instance()->RenderFrame(copied_viewer_node_, - ticket->property("time").value(), - RenderMode::kOffline, true)); - } - single_frame_renders_.clear(); + single_frame_render_->Start(); + + watcher->SetTicket(RenderManager::instance()->RenderFrame(copied_viewer_node_, + static_cast(viewer_node_->parent())->project()->color_manager(), + single_frame_render_->property("time").value(), + RenderMode::kOffline, true)); + + single_frame_render_ = nullptr; } } @@ -607,7 +623,9 @@ void PreviewAutoCacher::RequeueFrames() watcher->setProperty("hash", hash); connect(watcher, &RenderTicketWatcher::Finished, this, &PreviewAutoCacher::VideoRendered); video_tasks_.insert(watcher, hash); - watcher->SetTicket(RenderManager::instance()->RenderFrame(copied_viewer_node_, t, RenderMode::kOffline, false)); + watcher->SetTicket(RenderManager::instance()->RenderFrame(copied_viewer_node_, + static_cast(viewer_node_->parent())->project()->color_manager(), + t, RenderMode::kOffline, false)); } } diff --git a/app/render/previewautocacher.h b/app/render/previewautocacher.h index 0190a4e00..c6b993b5d 100644 --- a/app/render/previewautocacher.h +++ b/app/render/previewautocacher.h @@ -131,7 +131,7 @@ private: TimeRangeList invalidated_video_; TimeRangeList invalidated_audio_; - QVector single_frame_renders_; + RenderTicketPtr single_frame_render_; QList*> hash_tasks_; QMap audio_tasks_; @@ -192,6 +192,8 @@ private slots: void AudioParamsChanged(); + void SingleFrameFinished(); + }; OLIVE_NAMESPACE_EXIT diff --git a/app/render/rendermanager.cpp b/app/render/rendermanager.cpp index 99f7f47be..cacf1ed90 100644 --- a/app/render/rendermanager.cpp +++ b/app/render/rendermanager.cpp @@ -39,10 +39,36 @@ OLIVE_NAMESPACE_ENTER RenderManager* RenderManager::instance_ = nullptr; RenderManager::RenderManager(QObject *parent) : - ThreadPool(QThread::IdlePriority, 0, parent) + ThreadPool(QThread::IdlePriority, 0, parent), + backend_(kOpenGL) { - context_ = new RendererThreadWrapper(new OpenGLRenderer(), this); - context_->Init(); + Renderer* graphics_renderer = nullptr; + + if (backend_ == kOpenGL) { + graphics_renderer = new OpenGLRenderer(); + } + + if (graphics_renderer) { + context_ = new RendererThreadWrapper(graphics_renderer, this); + context_->Init(); + context_->PostInit(); + + still_cache_ = new StillImageCache(); + decoder_cache_ = new DecoderCache(); + } else { + qCritical() << "Tried to initialize unknown graphics backend"; + still_cache_ = nullptr; + decoder_cache_ = nullptr; + } +} + +RenderManager::~RenderManager() +{ + delete decoder_cache_; + delete still_cache_; + + context_->Destroy(); + delete context_; } QByteArray RenderManager::Hash(const Node *n, const VideoParams ¶ms, const rational &time) @@ -50,9 +76,13 @@ QByteArray RenderManager::Hash(const Node *n, const VideoParams ¶ms, const r QCryptographicHash hasher(QCryptographicHash::Sha1); // Embed video parameters into this hash - hasher.addData(reinterpret_cast(¶ms.effective_width()), sizeof(int)); - hasher.addData(reinterpret_cast(¶ms.effective_height()), sizeof(int)); - hasher.addData(reinterpret_cast(¶ms.format()), sizeof(PixelFormat::Format)); + int width = params.effective_width(); + int height = params.effective_height(); + PixelFormat::Format format = params.format(); + + hasher.addData(reinterpret_cast(&width), sizeof(int)); + hasher.addData(reinterpret_cast(&height), sizeof(int)); + hasher.addData(reinterpret_cast(&format), sizeof(PixelFormat::Format)); if (n) { n->Hash(hasher, time); @@ -61,15 +91,18 @@ QByteArray RenderManager::Hash(const Node *n, const VideoParams ¶ms, const r return hasher.result(); } -RenderTicketPtr RenderManager::RenderFrame(ViewerOutput *viewer, const rational &time, RenderMode::Mode mode, bool prioritize) +RenderTicketPtr RenderManager::RenderFrame(ViewerOutput *viewer, ColorManager *color_manager, const rational &time, RenderMode::Mode mode, bool prioritize) { - return RenderFrame(viewer, time, mode, - QSize(), + return RenderFrame(viewer, + color_manager, + time, + mode, + QSize(0, 0), QMatrix4x4(), prioritize); } -RenderTicketPtr RenderManager::RenderFrame(ViewerOutput* viewer, const rational &time, RenderMode::Mode mode, const QSize &force_size, const QMatrix4x4 &matrix, bool prioritize) +RenderTicketPtr RenderManager::RenderFrame(ViewerOutput* viewer, ColorManager* color_manager, const rational &time, RenderMode::Mode mode, const QSize &force_size, const QMatrix4x4 &matrix, bool prioritize) { // Create ticket RenderTicketPtr ticket = std::make_shared(); @@ -81,6 +114,7 @@ RenderTicketPtr RenderManager::RenderFrame(ViewerOutput* viewer, const rational ticket->setProperty("mode", mode); ticket->setProperty("type", kTypeVideo); ticket->setProperty("cache", viewer->video_frame_cache()->GetCacheDirectory()); + ticket->setProperty("colormanager", Node::PtrToValue(color_manager)); // Queue appending the ticket and running the next job on our thread to make this function thread-safe QMetaObject::invokeMethod(this, "AddTicket", Qt::AutoConnection, @@ -128,7 +162,7 @@ RenderTicketPtr RenderManager::SaveFrameToCache(FrameHashCache *cache, FramePtr void RenderManager::RunTicket(RenderTicketPtr ticket) const { - RenderProcessor::Process(ticket, context_); + RenderProcessor::Process(ticket, context_, still_cache_, decoder_cache_); } OLIVE_NAMESPACE_EXIT diff --git a/app/render/rendermanager.h b/app/render/rendermanager.h index f00b6d2b0..b04935b40 100644 --- a/app/render/rendermanager.h +++ b/app/render/rendermanager.h @@ -31,6 +31,7 @@ #include "node/output/viewer/viewer.h" #include "node/traverser.h" #include "render/backend/renderer.h" +#include "stillimagecache.h" #include "threading/threadpool.h" OLIVE_NAMESPACE_ENTER @@ -79,8 +80,8 @@ public: * * This function is thread-safe. */ - RenderTicketPtr RenderFrame(ViewerOutput* viewer, const rational& time, RenderMode::Mode mode, bool prioritize = false); - RenderTicketPtr RenderFrame(ViewerOutput* viewer, const rational& time, RenderMode::Mode mode, const QSize& force_size, const QMatrix4x4& matrix, bool prioritize = false); + RenderTicketPtr RenderFrame(ViewerOutput* viewer, ColorManager* color_manager, const rational& time, RenderMode::Mode mode, bool prioritize = false); + RenderTicketPtr RenderFrame(ViewerOutput* viewer, ColorManager* color_manager, const rational& time, RenderMode::Mode mode, const QSize& force_size, const QMatrix4x4& matrix, bool prioritize = false); /** * @brief Asynchronously generate a chunk of audio @@ -104,15 +105,28 @@ public: kTypeVideoDownload }; + Backend backend() const + { + return backend_; + } + signals: private: RenderManager(QObject* parent = nullptr); + virtual ~RenderManager() override; + static RenderManager* instance_; Renderer* context_; + Backend backend_; + + StillImageCache* still_cache_; + + DecoderCache* decoder_cache_; + }; OLIVE_NAMESPACE_EXIT diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index 8c194b358..1f4c61837 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -24,13 +24,16 @@ #include #include +#include "project/project.h" #include "rendermanager.h" OLIVE_NAMESPACE_ENTER -RenderProcessor::RenderProcessor(RenderTicketPtr ticket, Renderer *render_ctx) : +RenderProcessor::RenderProcessor(RenderTicketPtr ticket, Renderer *render_ctx, StillImageCache* still_image_cache, DecoderCache* decoder_cache) : ticket_(ticket), - render_ctx_(render_ctx) + render_ctx_(render_ctx), + still_image_cache_(still_image_cache), + decoder_cache_(decoder_cache) { } @@ -52,21 +55,17 @@ void RenderProcessor::Run() Renderer::TexturePtr texture = table.Get(NodeParam::kTexture).value(); + VideoParams frame_params = viewer->video_params(); + QSize frame_size = ticket_->property("size").value(); - if (frame_size.isNull()) { - frame_size = QSize(viewer->video_params().effective_width(), - viewer->video_params().effective_height()); + if (!frame_size.isNull()) { + frame_params.set_width(frame_size.width()); + frame_params.set_height(frame_size.height()); } FramePtr frame = Frame::Create(); frame->set_timestamp(time); - frame->set_video_params(VideoParams(frame_size.width(), - frame_size.height(), - viewer->video_params().time_base(), - viewer->video_params().format(), - viewer->video_params().pixel_aspect_ratio(), - viewer->video_params().interlacing(), - viewer->video_params().divider())); + frame->set_video_params(frame_params); frame->allocate(); if (!texture) { @@ -109,13 +108,38 @@ void RenderProcessor::Run() // Fail ticket_->Cancel(); } - - this->deleteLater(); } -void RenderProcessor::Process(RenderTicketPtr ticket, Renderer *render_ctx) +DecoderPtr RenderProcessor::ResolveDecoderFromInput(StreamPtr stream) { - RenderProcessor p(ticket, render_ctx); + if (!stream) { + qWarning() << "Attempted to resolve the decoder of a null stream"; + return nullptr; + } + + QMutexLocker locker(decoder_cache_->mutex()); + + DecoderPtr decoder = decoder_cache_->value(stream.get()); + + if (!decoder) { + // No decoder + decoder = Decoder::CreateFromID(stream->footage()->decoder()); + + if (decoder->Open(stream)) { + decoder_cache_->insert(stream.get(), decoder); + } else { + qWarning() << "Failed to open decoder for" << stream->footage()->filename() + << "::" << stream->index(); + return nullptr; + } + } + + return decoder; +} + +void RenderProcessor::Process(RenderTicketPtr ticket, Renderer *render_ctx, StillImageCache *still_image_cache, DecoderCache *decoder_cache) +{ + RenderProcessor p(ticket, render_ctx, still_image_cache, decoder_cache); p.Run(); } @@ -259,11 +283,24 @@ QVariant RenderProcessor::ProcessVideoFootage(StreamPtr stream, const rational & if (frame) { // Return a texture from the derived class - Renderer::TexturePtr unmanaged_texture = render_ctx_->CreateTexture(frame->video_params(), frame->data(), frame->linesize_pixels()); + Renderer::TexturePtr unmanaged_texture = render_ctx_->CreateTexture(frame->video_params(), + frame->data(), + frame->linesize_pixels()); - Renderer::TexturePtr managed_texture = render_ctx_->TransformColor(unmanaged_texture, ) + // We convert to our rendering pixel format, since that will always be float-based which + // is necessary for correct color conversion + VideoParams managed_params = frame->video_params(); + managed_params.set_format(video_params.format()); + value = render_ctx_->CreateTexture(managed_params); - value = FootageFrameToTexture(stream, frame); + // FIXME: Accessing video_stream->colorspace() + + ColorManager* color_manager = video_stream->footage()->project()->color_manager(); + ColorProcessorPtr processor = ColorProcessor::Create(color_manager, + video_stream->colorspace(), + ColorTransform(OCIO::ROLE_SCENE_LINEAR)); + + render_ctx_->BlitColorManaged(processor, unmanaged_texture.get(), value.get()); still_image_cache_->mutex()->lock(); @@ -290,38 +327,10 @@ QVariant RenderProcessor::ProcessAudioFootage(StreamPtr stream, const TimeRange if (decoder) { const AudioParams& audio_params = Node::ValueToPtr(ticket_->property("viewer"))->audio_params(); - // See if we have a conformed version of this audio - if (!decoder->HasConformedVersion(audio_params)) { + SampleBufferPtr frame = decoder->RetrieveAudio(input_time, audio_params, &IsCancelled()); - // If not, the audio needs to be conformed - // For online rendering/export, it's a waste of time to render the audio until we have - // all we need, so we try to handle the conform ourselves - AudioStreamPtr as = std::static_pointer_cast(stream); - - // Check if any other threads are conforming this audio - if (as->try_start_conforming(audio_params)) { - - // If not, conform it ourselves - decoder->ConformAudio(&IsCancelled(), audio_params); - - } else { - - // If another thread is conforming already, hackily try to wait until it's done. - do { - QThread::msleep(1000); - } while (!as->has_conformed_version(audio_params) && !IsCancelled()); - - } - - } - - if (decoder->HasConformedVersion(audio_params)) { - SampleBufferPtr frame = decoder->RetrieveAudio(input_time.in(), input_time.length(), - audio_params); - - if (frame) { - value = QVariant::fromValue(frame); - } + if (frame) { + value = QVariant::fromValue(frame); } } @@ -330,9 +339,11 @@ QVariant RenderProcessor::ProcessAudioFootage(StreamPtr stream, const TimeRange QVariant RenderProcessor::ProcessShader(const Node *node, const TimeRange &range, const ShaderJob &job) { + Q_UNUSED(range) + const VideoParams& video_params = Node::ValueToPtr(ticket_->property("viewer"))->video_params(); - render_ctx_->ProcessShader(node, range, job, video_params); + return QVariant::fromValue(render_ctx_->ProcessShader(node, job, video_params)); } QVariant RenderProcessor::ProcessSamples(const Node *node, const TimeRange &range, const SampleJob &job) @@ -384,49 +395,39 @@ QVariant RenderProcessor::ProcessFrameGeneration(const Node *node, const Generat const VideoParams& video_params = Node::ValueToPtr(ticket_->property("viewer"))->video_params(); - PixelFormat::Format output_fmt; - if (job.GetAlphaChannelRequired()) { - output_fmt = PixelFormat::GetFormatWithAlphaChannel(video_params.format()); - } else { - output_fmt = PixelFormat::GetFormatWithoutAlphaChannel(video_params.format()); - } - - frame->set_video_params(VideoParams(video_params.width(), - video_params.height(), - video_params.time_base(), - output_fmt, - video_params.pixel_aspect_ratio(), - video_params.interlacing(), - video_params.divider())); + frame->set_video_params(video_params); frame->allocate(); node->GenerateFrame(frame, job); - Renderer::TexturePtr texture = render_ctx_->CreateTexture(frame->video_params(), frame->data(), frame->linesize_pixels()); + Renderer::TexturePtr texture = render_ctx_->CreateTexture(frame->video_params(), + frame->data(), + frame->linesize_pixels()); + + texture->set_has_meaningful_alpha(job.GetAlphaChannelRequired()); return QVariant::fromValue(texture); } QVariant RenderProcessor::GetCachedFrame(const Node *node, const rational &time) { - if (ticket_->property("mode").value() == RenderMode::kOffline - && !cache_path_.isEmpty() + if (ticket_->property("mode").toInt() == RenderMode::kOffline && node->id() == QStringLiteral("org.olivevideoeditor.Olive.videoinput")) { const VideoParams& video_params = Node::ValueToPtr(ticket_->property("viewer"))->video_params(); QByteArray hash = RenderManager::Hash(node, video_params, time); - FramePtr f = FrameHashCache::LoadCacheFrame(cache_path_, hash); + FramePtr f = FrameHashCache::LoadCacheFrame(ticket_->property("cache").toString(), hash); if (f) { // The cached frame won't load with the correct divider by default, so we enforce it here - f->set_video_params(VideoParams(f->width() * video_params.divider(), - f->height() * video_params.divider(), - f->video_params().time_base(), - f->video_params().format(), - f->video_params().pixel_aspect_ratio(), - f->video_params().interlacing(), - video_params.divider())); + VideoParams p = f->video_params(); + + p.set_width(f->width() * video_params.divider()); + p.set_height(f->height() * video_params.divider()); + p.set_divider(video_params.divider()); + + f->set_video_params(p); Renderer::TexturePtr texture = render_ctx_->CreateTexture(f->video_params(), f->data(), f->linesize_pixels()); return QVariant::fromValue(texture); diff --git a/app/render/renderprocessor.h b/app/render/renderprocessor.h index bf6942770..2b85315bd 100644 --- a/app/render/renderprocessor.h +++ b/app/render/renderprocessor.h @@ -21,6 +21,7 @@ #ifndef RENDERPROCESSOR_H #define RENDERPROCESSOR_H +#include "decodercache.h" #include "node/traverser.h" #include "render/backend/renderer.h" #include "stillimagecache.h" @@ -28,11 +29,10 @@ OLIVE_NAMESPACE_ENTER -class RenderProcessor : public QObject, public NodeTraverser +class RenderProcessor : public NodeTraverser { - Q_OBJECT public: - static void Process(RenderTicketPtr ticket, Renderer* render_ctx); + static void Process(RenderTicketPtr ticket, Renderer* render_ctx, StillImageCache* still_image_cache, DecoderCache* decoder_cache); struct RenderedWaveform { const TrackOutput* track; @@ -40,11 +40,6 @@ public: TimeRange range; }; -signals: - void GeneratedFrame(FramePtr frame); - - void GeneratedAudio(SampleBufferPtr audio); - protected: virtual NodeValueTable GenerateBlockTable(const TrackOutput *track, const TimeRange &range) override; @@ -61,16 +56,20 @@ protected: virtual QVariant GetCachedFrame(const Node *node, const rational &time) override; private: - RenderProcessor(RenderTicketPtr ticket, Renderer* render_ctx); + RenderProcessor(RenderTicketPtr ticket, Renderer* render_ctx, StillImageCache* still_image_cache, DecoderCache* decoder_cache); void Run(); + DecoderPtr ResolveDecoderFromInput(StreamPtr stream); + RenderTicketPtr ticket_; Renderer* render_ctx_; StillImageCache* still_image_cache_; + DecoderCache* decoder_cache_; + }; OLIVE_NAMESPACE_EXIT diff --git a/app/render/videoparams.h b/app/render/videoparams.h index fa24e2027..dab019254 100644 --- a/app/render/videoparams.h +++ b/app/render/videoparams.h @@ -43,51 +43,90 @@ public: const PixelFormat::Format& format, const rational& pixel_aspect_ratio = 1, const Interlacing& interlacing = kInterlaceNone, const int& divider = 1); - const int& width() const + int width() const { return width_; } - const int& height() const + void set_width(int width) + { + width_ = width; + calculate_effective_size(); + } + + int height() const { return height_; } + void set_height(int height) + { + height_ = height; + calculate_effective_size(); + } + const rational& time_base() const { return time_base_; } - const int& divider() const + void set_time_base(const rational& r) + { + time_base_ = r; + } + + int divider() const { return divider_; } - const int& effective_width() const + void set_divider(int d) + { + divider_ = d; + calculate_effective_size(); + } + + int effective_width() const { return effective_width_; } - const int& effective_height() const + int effective_height() const { return effective_height_; } - const PixelFormat::Format& format() const + PixelFormat::Format format() const { return format_; } + void set_format(PixelFormat::Format f) + { + format_ = f; + } + const rational& pixel_aspect_ratio() const { return pixel_aspect_ratio_; } + void set_pixel_aspect_ratio(const rational& r) + { + pixel_aspect_ratio_ = r; + validate_pixel_aspect_ratio(); + } + Interlacing interlacing() const { return interlacing_; } + void set_interlacing(Interlacing i) + { + interlacing_ = i; + } + static int generate_auto_divider(qint64 width, qint64 height); bool is_valid() const; diff --git a/app/shaders/rgbhistogram.frag b/app/shaders/rgbhistogram.frag index 435949138..a593caaed 100644 --- a/app/shaders/rgbhistogram.frag +++ b/app/shaders/rgbhistogram.frag @@ -1,9 +1,7 @@ #version 150 uniform sampler2D ove_maintex; -uniform vec2 ove_resolution; -uniform vec2 ove_viewport; - +uniform vec2 viewport; uniform float histogram_scale; in vec2 ove_texcoord; @@ -11,7 +9,7 @@ in vec2 ove_texcoord; out vec4 fragColor; void main(void) { - float histogram_width = ceil(histogram_scale * ove_viewport.y); + float histogram_width = ceil(histogram_scale * viewport.y); float quantisation = 1.0 / (histogram_width - 1.0); vec3 cur_col = vec3(0.0); vec3 sum = vec3(0.0); diff --git a/app/shaders/rgbhistogram.vert b/app/shaders/rgbhistogram.vert index 92536144c..a8e53d253 100644 --- a/app/shaders/rgbhistogram.vert +++ b/app/shaders/rgbhistogram.vert @@ -1,7 +1,6 @@ #version 150 uniform float histogram_scale; -uniform vec2 ove_resolution; in vec4 a_position; in vec2 a_texcoord; @@ -26,4 +25,4 @@ void main() { gl_Position = transform * a_position; ove_texcoord = a_texcoord; -} \ No newline at end of file +} diff --git a/app/shaders/rgbhistogram_secondary.frag b/app/shaders/rgbhistogram_secondary.frag index 474db6b74..0bb8ac75a 100644 --- a/app/shaders/rgbhistogram_secondary.frag +++ b/app/shaders/rgbhistogram_secondary.frag @@ -1,8 +1,7 @@ #version 150 uniform sampler2D ove_maintex; -uniform vec2 ove_resolution; -uniform vec2 ove_viewport; +uniform vec2 viewport; uniform float histogram_scale; uniform float histogram_power; @@ -13,11 +12,11 @@ out vec4 fragColor; void main(void) { vec3 col = vec3(0.0); - float histogram_height = ceil(ove_viewport.y * histogram_scale); + float histogram_height = ceil(viewport.y * histogram_scale); vec3 histogram_ratio = vec3(0.0); vec3 sum = vec3(0.0); float ratio = 0.0; - vec3 total_pixels = vec3(ceil(ove_viewport.x * ove_resolution.y * + vec3 total_pixels = vec3(ceil(viewport.x * viewport.y * histogram_scale)); for (int i = 0; i < histogram_height; i++) { diff --git a/app/shaders/rgbwaveform.frag b/app/shaders/rgbwaveform.frag index e568b37cd..38b41a1f5 100644 --- a/app/shaders/rgbwaveform.frag +++ b/app/shaders/rgbwaveform.frag @@ -1,8 +1,8 @@ #version 150 uniform sampler2D ove_maintex; -uniform vec2 ove_resolution; -uniform vec2 ove_viewport; + +uniform vec2 viewport; uniform vec3 luma_coeffs; uniform float waveform_scale; @@ -12,7 +12,7 @@ in vec2 ove_texcoord; out vec4 fragColor; void main(void) { - float waveform_height = ceil(waveform_scale * ove_viewport.y); + float waveform_height = ceil(waveform_scale * viewport.y); float quantisation = 1.0 / (waveform_height - 1.0); float intensity = 0.10; vec4 col = vec4(0.0); diff --git a/app/task/conform/conform.cpp b/app/task/conform/conform.cpp index fa131be8c..4ddc322f7 100644 --- a/app/task/conform/conform.cpp +++ b/app/task/conform/conform.cpp @@ -33,7 +33,10 @@ ConformTask::ConformTask(AudioStreamPtr stream, const AudioParams& params) : bool ConformTask::Run() { - if (stream_->footage()->decoder().isEmpty()) { + // Conforming is done by the renderer now, but I would like to use something like this just to + // show progress + + /*if (stream_->footage()->decoder().isEmpty()) { SetError(tr("Failed to find decoder to conform audio stream")); return false; } else { @@ -49,7 +52,9 @@ bool ConformTask::Run() } else { return true; } - } + }*/ + + return true; } OLIVE_NAMESPACE_EXIT diff --git a/app/task/export/export.cpp b/app/task/export/export.cpp index 210d1cb23..a74445eab 100644 --- a/app/task/export/export.cpp +++ b/app/task/export/export.cpp @@ -115,17 +115,8 @@ bool ExportTask::Run() void FrameColorConvert(ColorProcessorPtr processor, FramePtr frame) { // OCIO conversion requires a frame in 32F format - if (frame->format() != PixelFormat::PIX_FMT_RGBA32F - && frame->format() != PixelFormat::PIX_FMT_RGB32F) { - PixelFormat::Format dst; - - if (PixelFormat::FormatHasAlphaChannel(frame->format())) { - dst = PixelFormat::PIX_FMT_RGBA32F; - } else { - dst = PixelFormat::PIX_FMT_RGB32F; - } - - frame = PixelFormat::ConvertPixelFormat(frame, dst); + if (frame->format() != PixelFormat::PIX_FMT_RGBA32F) { + frame = PixelFormat::ConvertPixelFormat(frame, PixelFormat::PIX_FMT_RGBA32F); } // Color conversion must be done with unassociated alpha, and the pipeline is always associated diff --git a/app/task/project/import/import.cpp b/app/task/project/import/import.cpp index 5a94e2d38..ab30be115 100644 --- a/app/task/project/import/import.cpp +++ b/app/task/project/import/import.cpp @@ -110,8 +110,8 @@ void ProjectImportTask::Import(Folder *folder, QFileInfoList import, int &counte } else { - FootagePtr item = Decoder::ProbeMedia(model_->project(), file_info.absoluteFilePath(), - &IsCancelled()); + FootagePtr item = Decoder::Probe(model_->project(), file_info.absoluteFilePath(), + &IsCancelled()); if (item) { // See if this footage is an image sequence diff --git a/app/threading/threadpool.cpp b/app/threading/threadpool.cpp index ccd139a3c..b89a00ee9 100644 --- a/app/threading/threadpool.cpp +++ b/app/threading/threadpool.cpp @@ -52,8 +52,6 @@ ThreadPool::~ThreadPool() thread->wait(); delete thread; } - - RunNext(); } void ThreadPool::AddTicket(RenderTicketPtr ticket, bool prioritize) diff --git a/app/threading/threadticket.cpp b/app/threading/threadticket.cpp index 30ae3b5e1..e87b1c57e 100644 --- a/app/threading/threadticket.cpp +++ b/app/threading/threadticket.cpp @@ -83,7 +83,12 @@ void RenderTicket::Finish(QVariant result, bool cancelled) { QMutexLocker locker(&lock_); - if (started_ && !finished_) { + if (!started_) { + qWarning() << "Tried to finish a ticket that hadn't started"; + } else if (finished_) { + // Do nothing + return; + } else { finished_ = true; cancelled_ = cancelled; diff --git a/app/threading/threadticketwatcher.cpp b/app/threading/threadticketwatcher.cpp index 13940668e..804aaef99 100644 --- a/app/threading/threadticketwatcher.cpp +++ b/app/threading/threadticketwatcher.cpp @@ -30,8 +30,15 @@ RenderTicketWatcher::RenderTicketWatcher(QObject *parent) : void RenderTicketWatcher::SetTicket(RenderTicketPtr ticket) { - // Ensure that a ticket has NOT already been set and that this ticket is NOT NULL - Q_ASSERT(!ticket_ && ticket); + if (ticket_) { + qCritical() << "Tried to set a ticket on a RenderTicketWatcher twice"; + return; + } + + if (!ticket) { + qCritical() << "Tried to set a null ticket on a RenderTicketWatcher"; + return; + } ticket_ = ticket; diff --git a/app/widget/manageddisplay/manageddisplay.cpp b/app/widget/manageddisplay/manageddisplay.cpp index 52ba3f356..27b4b3a9d 100644 --- a/app/widget/manageddisplay/manageddisplay.cpp +++ b/app/widget/manageddisplay/manageddisplay.cpp @@ -20,25 +20,59 @@ #include "manageddisplay.h" +#include #include #include "render/backend/opengl/openglrenderer.h" +#include "render/rendermanager.h" OLIVE_NAMESPACE_ENTER ManagedDisplayWidget::ManagedDisplayWidget(QWidget *parent) : - QOpenGLWidget(parent), + QWidget(parent), color_manager_(nullptr), color_service_(nullptr) { setContextMenuPolicy(Qt::CustomContextMenu); - attached_renderer_ = new OpenGLRenderer(); + QHBoxLayout* layout = new QHBoxLayout(this); + layout->setSpacing(0); + layout->setMargin(0); + + if (RenderManager::instance()->backend() == RenderManager::kOpenGL) { + // Create OpenGL widget + inner_widget_ = new ManagedDisplayWidgetOpenGL(); + connect(static_cast(inner_widget_), + &ManagedDisplayWidgetOpenGL::OnInit, + this, &ManagedDisplayWidget::OnInit, Qt::DirectConnection); + connect(static_cast(inner_widget_), + &ManagedDisplayWidgetOpenGL::OnDestroy, + this, &ManagedDisplayWidget::OnDestroy, Qt::DirectConnection); + connect(static_cast(inner_widget_), + &ManagedDisplayWidgetOpenGL::OnPaint, + this, &ManagedDisplayWidget::OnPaint, Qt::DirectConnection); + connect(static_cast(inner_widget_), + &ManagedDisplayWidgetOpenGL::frameSwapped, + this, &ManagedDisplayWidget::frameSwapped, Qt::DirectConnection); + + // Create OpenGL renderer + attached_renderer_ = new OpenGLRenderer(this); + } else { + inner_widget_ = nullptr; + } + + layout->addWidget(inner_widget_); } ManagedDisplayWidget::~ManagedDisplayWidget() { - ContextCleanup(); + OnDestroy(); + + if (RenderManager::instance()->backend() == RenderManager::kOpenGL) { + disconnect(static_cast(inner_widget_), + &ManagedDisplayWidgetOpenGL::OnDestroy, + this, &ManagedDisplayWidget::OnDestroy); + } } void ManagedDisplayWidget::ConnectColorManager(ColorManager *color_manager) @@ -111,17 +145,6 @@ ColorProcessorPtr ManagedDisplayWidget::color_service() return color_service_; } -void ManagedDisplayWidget::ContextCleanup() -{ - makeCurrent(); - - color_service_ = nullptr; - - attached_renderer_->Destroy(); - - doneCurrent(); -} - void ManagedDisplayWidget::ShowDefaultContextMenu() { Menu m(this); @@ -178,24 +201,27 @@ void ManagedDisplayWidget::MenuColorspaceSelect(QAction *action) SetColorTransform(color_manager()->GetCompliantColorSpace(ColorTransform(action->data().toString()))); } -void ManagedDisplayWidget::SetColorTransform(const ColorTransform &transform) +void ManagedDisplayWidget::OnDestroy() { - makeCurrent(); - - color_transform_ = transform; - SetupColorProcessor(); - ColorProcessorChangedEvent(); - - doneCurrent(); + attached_renderer_->Destroy(); } -void ManagedDisplayWidget::initializeGL() +void ManagedDisplayWidget::SetColorTransform(const ColorTransform &transform) { + color_transform_ = transform; + SetupColorProcessor(); - connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &ManagedDisplayWidget::ContextCleanup, Qt::DirectConnection); + ColorProcessorChangedEvent(); +} - static_cast(attached_renderer_)->Init(context()); +void ManagedDisplayWidget::OnInit() +{ + if (RenderManager::instance()->backend() == RenderManager::kOpenGL) { + QOpenGLContext* context = static_cast(inner_widget_)->context(); + static_cast(attached_renderer_)->Init(context); + static_cast(attached_renderer_)->PostInit(); + } } void ManagedDisplayWidget::EnableDefaultContextMenu() @@ -208,6 +234,20 @@ void ManagedDisplayWidget::ColorProcessorChangedEvent() update(); } +void ManagedDisplayWidget::makeCurrent() +{ + if (RenderManager::instance()->backend() == RenderManager::kOpenGL) { + static_cast(inner_widget_)->makeCurrent(); + } +} + +void ManagedDisplayWidget::doneCurrent() +{ + if (RenderManager::instance()->backend() == RenderManager::kOpenGL) { + static_cast(inner_widget_)->doneCurrent(); + } +} + Menu* ManagedDisplayWidget::GetDisplayMenu(QMenu* parent, bool auto_connect) { QStringList displays = color_manager()->ListAvailableDisplays(); @@ -277,35 +317,25 @@ Menu* ManagedDisplayWidget::GetLookMenu(QMenu* parent, bool auto_connect) void ManagedDisplayWidget::SetupColorProcessor() { - if (!context()) { - return; - } - color_service_ = nullptr; if (color_manager_) { // (Re)create color processor - try { - color_service_ = ColorProcessor::Create(color_manager_, color_manager_->GetReferenceColorSpace(), color_transform_); - } catch (OCIO::Exception& e) { - QMessageBox::critical(this, tr("OpenColorIO Error"), tr("Failed to set color configuration: %1").arg(e.what()), QMessageBox::Ok); - } - } else { color_service_ = nullptr; } - emit ColorProcessorChanged(std::static_pointer_cast(color_service_)); + emit ColorProcessorChanged(color_service_); } OLIVE_NAMESPACE_EXIT diff --git a/app/widget/manageddisplay/manageddisplay.h b/app/widget/manageddisplay/manageddisplay.h index ec524faeb..ae73dc30e 100644 --- a/app/widget/manageddisplay/manageddisplay.h +++ b/app/widget/manageddisplay/manageddisplay.h @@ -29,7 +29,49 @@ OLIVE_NAMESPACE_ENTER -class ManagedDisplayWidget : public QOpenGLWidget +class ManagedDisplayWidgetOpenGL : public QOpenGLWidget +{ + Q_OBJECT +public: + ManagedDisplayWidgetOpenGL(QWidget* parent = nullptr) : + QOpenGLWidget(parent) + { + } + +signals: + void OnInit(); + + void OnPaint(); + + void OnDestroy(); + +protected: + virtual void initializeGL() override + { + connect(context(), &QOpenGLContext::aboutToBeDestroyed, + this, &ManagedDisplayWidgetOpenGL::OnDestroy); + + emit OnInit(); + } + + virtual void paintGL() override + { + emit OnPaint(); + } + +private slots: + void DestroyListener() + { + makeCurrent(); + + emit OnDestroy(); + + doneCurrent(); + } + +}; + +class ManagedDisplayWidget : public QWidget { Q_OBJECT public: @@ -94,17 +136,14 @@ signals: */ void ColorManagerChanged(ColorManager* color_manager); + void frameSwapped(); + protected: /** * @brief Provides access to the color processor (nullptr if none is set) */ ColorProcessorPtr color_service(); - /** - * @brief Override when setting up OpenGL context - */ - virtual void initializeGL() override; - /** * @brief Enables a context menu that allows simple access to the DVL pipeline */ @@ -117,6 +156,31 @@ protected: */ virtual void ColorProcessorChangedEvent(); + Renderer* renderer() const + { + return attached_renderer_; + } + + void makeCurrent(); + + void doneCurrent(); + +protected slots: + /** + * @brief Called whenever the internal rendering context has been created + */ + virtual void OnInit(); + + /** + * @brief Called while the internal rendering context is being rendered + */ + virtual void OnPaint() = 0; + + /** + * @brief Called just before the internal rendering context is destroyed + */ + virtual void OnDestroy(); + private: /** * @brief Call this if this user has selected a different display/view/look to recreate the processor @@ -128,6 +192,11 @@ private: */ void ClearOCIOLutTexture(); + /** + * @brief Main drawing surface abstraction + */ + QWidget* inner_widget_; + /** * @brief Renderer abstraction */ @@ -154,11 +223,6 @@ private slots: */ void ColorConfigChanged(); - /** - * @brief Cleans up resources if context is about to be destroyed - */ - void ContextCleanup(); - /** * @brief The default context menu shown */ diff --git a/app/widget/nodetableview/nodetableview.cpp b/app/widget/nodetableview/nodetableview.cpp index 7696ace3b..722ff0a96 100644 --- a/app/widget/nodetableview/nodetableview.cpp +++ b/app/widget/nodetableview/nodetableview.cpp @@ -145,10 +145,7 @@ void NodeTableView::SetTime(const rational &time) case NodeParam::kTexture: { // NodeTableTraverser puts video params in here - VideoParams p = value.data().value(); - int channel_count = PixelFormat::ChannelCount(p.format()); - - for (int k=0;ksetItemWidget(sub_item, 2 + k, new QCheckBox()); } break; diff --git a/app/widget/scope/histogram/histogram.cpp b/app/widget/scope/histogram/histogram.cpp index b88c3996d..44f6a95e8 100644 --- a/app/widget/scope/histogram/histogram.cpp +++ b/app/widget/scope/histogram/histogram.cpp @@ -22,6 +22,7 @@ #include #include +#include #include "common/qtutils.h" #include "node/node.h" @@ -35,74 +36,33 @@ HistogramScope::HistogramScope(QWidget* parent) : HistogramScope::~HistogramScope() { - CleanUp(); - - if (context()) { - disconnect(context(), &QOpenGLContext::aboutToBeDestroyed, this, - &HistogramScope::CleanUp); - } + OnDestroy(); } -void HistogramScope::initializeGL() +void HistogramScope::OnInit() { - ScopeBase::initializeGL(); + ScopeBase::OnInit(); - pipeline_secondary_ = CreateSecondaryShader(); - - connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, - &HistogramScope::CleanUp, Qt::DirectConnection); + ShaderCode secondary_code(Node::ReadFileAsString(":/shaders/rgbhistogram_secondary.frag"), + Node::ReadFileAsString(":/shaders/rgbhistogram.vert")); + pipeline_secondary_ = renderer()->CreateNativeShader(secondary_code); } -void HistogramScope::AssertAdditionalTextures() +void HistogramScope::OnDestroy() { - if (!texture_row_sums_.IsCreated() - || texture_row_sums_.width() != width() - || texture_row_sums_.height() != height()) { - texture_row_sums_.Destroy(); - texture_row_sums_.Create(context(), VideoParams(width(), - height(), managed_tex().format())); - } + ScopeBase::OnDestroy(); + + pipeline_secondary_.clear(); + texture_row_sums_ = nullptr; } -void HistogramScope::CleanUp() +ShaderCode HistogramScope::GenerateShaderCode() { - makeCurrent(); - - pipeline_secondary_ = nullptr; - texture_row_sums_.Destroy(); - - doneCurrent(); + return ShaderCode(Node::ReadFileAsString(":/shaders/rgbhistogram.frag"), + Node::ReadFileAsString(":/shaders/default.vert")); } -QVariant HistogramScope::CreateShader() -{ - OpenGLShaderPtr pipeline = OpenGLShader::Create(); - - pipeline->create(); - pipeline->addShaderFromSourceCode(QOpenGLShader::Vertex, - OpenGLShader::CodeDefaultVertex()); - pipeline->addShaderFromSourceCode(QOpenGLShader::Fragment, - Node::ReadFileAsString(":/shaders/rgbhistogram.frag")); - pipeline->link(); - - return pipeline; -} - -OpenGLShaderPtr HistogramScope::CreateSecondaryShader() -{ - OpenGLShaderPtr shader = OpenGLShader::Create(); - - shader->create(); - shader->addShaderFromSourceCode(QOpenGLShader::Vertex, - Node::ReadFileAsString(":/shaders/rgbhistogram.vert")); - shader->addShaderFromSourceCode(QOpenGLShader::Fragment, - Node::ReadFileAsString(":/shaders/rgbhistogram_secondary.frag")); - shader->link(); - - return shader; -} - -void HistogramScope::DrawScope() +void HistogramScope::DrawScope(Renderer::TexturePtr managed_tex, QVariant pipeline) { float histogram_scale = 0.80f; // This value is eyeballed for usefulness. Until we have a geometry @@ -111,40 +71,21 @@ void HistogramScope::DrawScope() float histogram_base = 2.5f; float histogram_power = 1.0f / histogram_base; - pipeline()->bind(); - pipeline()->setUniformValue("ove_resolution", managed_tex().width(), - managed_tex().height()); - pipeline()->setUniformValue("ove_viewport", width(), height()); - pipeline()->setUniformValue("histogram_scale", histogram_scale); - pipeline()->release(); + Renderer::ShaderUniformMap value_map; - AssertAdditionalTextures(); + value_map.insert(QStringLiteral("viewport"), {QVector2D(width(), height()), NodeParam::kVec2}); + value_map.insert(QStringLiteral("histogram_scale"), {histogram_scale, NodeParam::kFloat}); + value_map.insert(QStringLiteral("histogram_power"), {histogram_power, NodeParam::kFloat}); - framebuffer().Attach(&texture_row_sums_, true); - framebuffer().Bind(); + if (!texture_row_sums_ + || texture_row_sums_->width() != this->width() + || texture_row_sums_->height() != this->height()) { + texture_row_sums_ = renderer()->CreateTexture(VideoParams(width(), height(), managed_tex->format())); + } - managed_tex().Bind(); + renderer()->Blit(managed_tex.get(), pipeline, value_map, texture_row_sums_.get()); - OpenGLRenderFunctions::Blit(pipeline()); - - managed_tex().Release(); - - framebuffer().Release(); - framebuffer().Detach(); - - pipeline_secondary_->bind(); - pipeline_secondary_->setUniformValue("ove_resolution", - texture_row_sums_.width(), texture_row_sums_.height()); - pipeline_secondary_->setUniformValue("ove_viewport", width(), height()); - pipeline_secondary_->setUniformValue("histogram_scale", histogram_scale); - pipeline_secondary_->setUniformValue("histogram_power", histogram_power); - pipeline_secondary_->release(); - - texture_row_sums_.Bind(); - - OpenGLRenderFunctions::Blit(pipeline_secondary_); - - texture_row_sums_.Release(); + renderer()->Blit(texture_row_sums_.get(), pipeline_secondary_, value_map); // Draw line overlays QPainter p(this); @@ -172,28 +113,28 @@ void HistogramScope::DrawScope() float histogram_dim_x = ceil((width() - 1.0) * histogram_scale); float histogram_dim_y = ceil((height() - 1.0) * histogram_scale); float histogram_start_dim_x = - ((width() - 1.0) - histogram_dim_x) / 2.0f; + ((width() - 1.0) - histogram_dim_x) / 2.0f; float histogram_start_dim_y = - ((height() - 1.0) - histogram_dim_y) / 2.0f; + ((height() - 1.0) - histogram_dim_y) / 2.0f; float histogram_end_dim_x = (width() - 1.0) - histogram_start_dim_x; // for (int i=0; i <= histogram_steps; i++) { for(std::vector::iterator it = histogram_increments.begin(); - it != histogram_increments.end(); it++) { + it != histogram_increments.end(); it++) { histogram_lines[it - histogram_increments.begin()].setLine( - histogram_start_dim_x, - (histogram_dim_y * pow(1.0 - *it, histogram_base)) + - histogram_start_dim_y, - histogram_end_dim_x, - (histogram_dim_y * pow(1.0 - *it, histogram_base)) + - histogram_start_dim_y); - label = QString::number( - *it * 100, 'f', 1) + "%"; - font_x_offset = QFontMetricsWidth(font_metrics, label) + 4; + histogram_start_dim_x, + (histogram_dim_y * pow(1.0 - *it, histogram_base)) + + histogram_start_dim_y, + histogram_end_dim_x, + (histogram_dim_y * pow(1.0 - *it, histogram_base)) + + histogram_start_dim_y); + label = QString::number( + *it * 100, 'f', 1) + "%"; + font_x_offset = QFontMetricsWidth(font_metrics, label) + 4; - p.drawText( - histogram_start_dim_x - font_x_offset, - (histogram_dim_y * pow(1.0 - *it, histogram_base)) + + p.drawText( + histogram_start_dim_x - font_x_offset, + (histogram_dim_y * pow(1.0 - *it, histogram_base)) + histogram_start_dim_y + font_y_offset, label); } p.drawLines(histogram_lines); diff --git a/app/widget/scope/histogram/histogram.h b/app/widget/scope/histogram/histogram.h index 8de6e4da4..d0ab83958 100644 --- a/app/widget/scope/histogram/histogram.h +++ b/app/widget/scope/histogram/histogram.h @@ -33,22 +33,21 @@ public: virtual ~HistogramScope() override; -protected: - virtual void initializeGL() override; +protected slots: + virtual void OnInit() override; - virtual QVariant CreateShader() override; + virtual void OnDestroy() override; + +protected: + virtual ShaderCode GenerateShaderCode() override; QVariant CreateSecondaryShader(); - void AssertAdditionalTextures(); - - virtual void DrawScope() override; + virtual void DrawScope(Renderer::TexturePtr managed_tex, QVariant pipeline) override; private: QVariant pipeline_secondary_; - QVariant texture_row_sums_; + Renderer::TexturePtr texture_row_sums_; -private slots: - void CleanUp(); }; OLIVE_NAMESPACE_EXIT diff --git a/app/widget/scope/scopebase/scopebase.cpp b/app/widget/scope/scopebase/scopebase.cpp index 38aa84e13..60aff3da6 100644 --- a/app/widget/scope/scopebase/scopebase.cpp +++ b/app/widget/scope/scopebase/scopebase.cpp @@ -20,8 +20,6 @@ #include "scopebase.h" -#include "render/backend/opengl/openglrenderfunctions.h" - OLIVE_NAMESPACE_ENTER ScopeBase::ScopeBase(QWidget* parent) : @@ -33,11 +31,7 @@ ScopeBase::ScopeBase(QWidget* parent) : ScopeBase::~ScopeBase() { - CleanUp(); - - if (context()) { - disconnect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &ScopeBase::CleanUp); - } + OnDestroy(); } void ScopeBase::SetBuffer(Frame *frame) @@ -54,18 +48,9 @@ void ScopeBase::showEvent(QShowEvent* e) UploadTextureFromBuffer(); } -QVariant ScopeBase::CreateShader() +void ScopeBase::DrawScope(Renderer::TexturePtr managed_tex, QVariant pipeline) { - return OpenGLShader::CreateDefault(); -} - -void ScopeBase::DrawScope() -{ - managed_tex().Bind(); - - OpenGLRenderFunctions::Blit(pipeline()); - - managed_tex().Release(); + renderer()->Blit(managed_tex.get(), pipeline, Renderer::ShaderUniformMap()); } void ScopeBase::UploadTextureFromBuffer() @@ -77,17 +62,18 @@ void ScopeBase::UploadTextureFromBuffer() if (buffer_) { makeCurrent(); - if (!texture_.IsCreated() - || texture_.width() != buffer_->width() - || texture_.height() != buffer_->height() - || texture_.format() != buffer_->format()) { - texture_.Destroy(); - managed_tex_.Destroy(); + if (!texture_ + || texture_->width() != buffer_->width() + || texture_->height() != buffer_->height() + || texture_->format() != buffer_->format()) { + texture_ = nullptr; + managed_tex_ = nullptr; - texture_.Create(context(), buffer_); - managed_tex_.Create(context(), buffer_->video_params()); + texture_ = renderer()->CreateTexture(buffer_->video_params(), + buffer_->data(), buffer_->linesize_pixels()); + managed_tex_ = renderer()->CreateTexture(buffer_->video_params()); } else { - texture_.Upload(buffer_); + texture_->Upload(buffer_->data(), buffer_->linesize_pixels()); } doneCurrent(); @@ -96,58 +82,36 @@ void ScopeBase::UploadTextureFromBuffer() update(); } -void ScopeBase::CleanUp() +void ScopeBase::OnInit() { - makeCurrent(); - - pipeline_ = nullptr; - texture_.Destroy(); - managed_tex_.Destroy(); - framebuffer_.Destroy(); - - doneCurrent(); -} - -void ScopeBase::initializeGL() -{ - ManagedDisplayWidget::initializeGL(); - - pipeline_ = CreateShader(); - - framebuffer_.Create(context()); - - connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &ScopeBase::CleanUp, Qt::DirectConnection); + ManagedDisplayWidget::OnInit(); UploadTextureFromBuffer(); + + pipeline_ = renderer()->CreateNativeShader(GenerateShaderCode()); } -void ScopeBase::paintGL() +void ScopeBase::OnPaint() { - QOpenGLFunctions* f = context()->functions(); + // Clear display surface + renderer()->ClearDestination(); - f->glClearColor(0, 0, 0, 0); - f->glClear(GL_COLOR_BUFFER_BIT); - - if (buffer_ && pipeline() && texture_.IsCreated()) { + if (buffer_) { // Convert reference frame to display space - framebuffer_.Attach(&managed_tex_); - framebuffer_.Bind(); + renderer()->BlitColorManaged(color_service(), texture_.get(), managed_tex_.get()); - texture_.Bind(); - - f->glViewport(0, 0, texture_.width(), texture_.height()); - - color_service()->ProcessOpenGL(); - - texture_.Release(); - - framebuffer_.Release(); - framebuffer_.Detach(); - - f->glViewport(0, 0, width(), height()); - - DrawScope(); + renderer()->SetViewport(width(), height()); + DrawScope(managed_tex_, pipeline_); } } +void ScopeBase::OnDestroy() +{ + ManagedDisplayWidget::OnDestroy(); + + managed_tex_ = nullptr; + texture_ = nullptr; + pipeline_.clear(); +} + OLIVE_NAMESPACE_EXIT diff --git a/app/widget/scope/scopebase/scopebase.h b/app/widget/scope/scopebase/scopebase.h index dc48c20f3..8cde613f8 100644 --- a/app/widget/scope/scopebase/scopebase.h +++ b/app/widget/scope/scopebase/scopebase.h @@ -37,41 +37,36 @@ public: public slots: void SetBuffer(Frame* frame); +protected slots: + virtual void OnInit() override; + + virtual void OnPaint() override; + + virtual void OnDestroy() override; + protected: - virtual void initializeGL() override; - - virtual void paintGL() override; - virtual void showEvent(QShowEvent* e) override; - virtual QVariant CreateShader(); + virtual ShaderCode GenerateShaderCode() = 0; - virtual void DrawScope(); - - QVariant pipeline() - { - return pipeline_; - } - - QVariant managed_tex() - { - return managed_tex_; - } + /** + * @brief Draw function + * + * Override this if your sub-class scope needs extra drawing. + */ + virtual void DrawScope(Renderer::TexturePtr managed_tex, QVariant pipeline); private: void UploadTextureFromBuffer(); QVariant pipeline_; - QVariant texture_; + Renderer::TexturePtr texture_; - QVariant managed_tex_; + Renderer::TexturePtr managed_tex_; Frame* buffer_; -private slots: - void CleanUp(); - }; OLIVE_NAMESPACE_EXIT diff --git a/app/widget/scope/waveform/waveform.cpp b/app/widget/scope/waveform/waveform.cpp index 16e8b260b..f1da05fce 100644 --- a/app/widget/scope/waveform/waveform.cpp +++ b/app/widget/scope/waveform/waveform.cpp @@ -24,10 +24,11 @@ #include #include #include +#include +#include #include "common/qtutils.h" #include "node/node.h" -#include "render/backend/opengl/openglrenderfunctions.h" OLIVE_NAMESPACE_ENTER @@ -36,49 +37,46 @@ WaveformScope::WaveformScope(QWidget* parent) : { } -QVariant WaveformScope::CreateShader() +WaveformScope::~WaveformScope() { - OpenGLShaderPtr pipeline = OpenGLShader::Create(); - - pipeline->create(); - pipeline->addShaderFromSourceCode(QOpenGLShader::Vertex, - Node::ReadFileAsString(":/shaders/rgbwaveform.vert")); - pipeline->addShaderFromSourceCode(QOpenGLShader::Fragment, - Node::ReadFileAsString(":/shaders/rgbwaveform.frag")); - pipeline->link(); - - return pipeline; + OnDestroy(); } -void WaveformScope::DrawScope() +ShaderCode WaveformScope::GenerateShaderCode() +{ + return ShaderCode(Node::ReadFileAsString(":/shaders/rgbwaveform.frag"), + Node::ReadFileAsString(":/shaders/rgbwaveform.vert")); +} + +void WaveformScope::DrawScope(Renderer::TexturePtr managed_tex, QVariant pipeline) { float waveform_scale = 0.80f; // Draw waveform through shader - pipeline()->bind(); - pipeline()->setUniformValue("ove_resolution", managed_tex().width(), managed_tex().height()); - pipeline()->setUniformValue("ove_viewport", width(), height()); - GLfloat luma[3] = {0.0, 0.0, 0.0}; - color_manager()->GetDefaultLumaCoefs(luma); - pipeline()->setUniformValue("luma_coeffs", luma[0], luma[1], luma[2]); + Renderer::ShaderUniformMap value_map; + + // Set viewport size + value_map.insert(QStringLiteral("viewport"), + {QVector2D(width(), height()), NodeParam::kVec2}); + + // Set luma coefficients + float luma_coeffs[3] = {0.0f, 0.0f, 0.0f}; + color_manager()->GetDefaultLumaCoefs(luma_coeffs); + value_map.insert(QStringLiteral("luma_coeffs"), + {QVector3D(luma_coeffs[0], luma_coeffs[1], luma_coeffs[2]), NodeParam::kVec3}); + // Scale of the waveform relative to the viewport surface. - pipeline()->setUniformValue("waveform_scale", waveform_scale); + value_map.insert(QStringLiteral("waveform_scale"), {waveform_scale, NodeParam::kFloat}); - pipeline()->release(); - - managed_tex().Bind(); - - OpenGLRenderFunctions::Blit(pipeline()); - - managed_tex().Release(); + renderer()->Blit(managed_tex.get(), pipeline, value_map); float waveform_dim_x = ceil((width() - 1.0) * waveform_scale); float waveform_dim_y = ceil((height() - 1.0) * waveform_scale); float waveform_start_dim_x = - ((width() - 1.0) - waveform_dim_x) / 2.0f; + ((width() - 1.0) - waveform_dim_x) / 2.0f; float waveform_start_dim_y = - ((height() - 1.0) - waveform_dim_y) / 2.0f; + ((height() - 1.0) - waveform_dim_y) / 2.0f; float waveform_end_dim_x = (width() - 1.0) - waveform_start_dim_x; // Draw line overlays @@ -100,18 +98,19 @@ void WaveformScope::DrawScope() for (int i=0; i <= ire_steps; i++) { ire_lines[i].setLine( - waveform_start_dim_x, - (waveform_dim_y * (i * ire_increment)) + waveform_start_dim_y, - waveform_end_dim_x, - (waveform_dim_y * (i * ire_increment)) + waveform_start_dim_y); - label = QString::number(1.0 - (i * ire_increment), 'f', 1); - font_x_offset = QFontMetricsWidth(font_metrics, label) + 4; + waveform_start_dim_x, + (waveform_dim_y * (i * ire_increment)) + waveform_start_dim_y, + waveform_end_dim_x, + (waveform_dim_y * (i * ire_increment)) + waveform_start_dim_y); + label = QString::number(1.0 - (i * ire_increment), 'f', 1); + font_x_offset = QFontMetricsWidth(font_metrics, label) + 4; - p.drawText( - waveform_start_dim_x - font_x_offset, - (waveform_dim_y * (i * ire_increment)) + waveform_start_dim_y + font_y_offset, - label); + p.drawText( + waveform_start_dim_x - font_x_offset, + (waveform_dim_y * (i * ire_increment)) + waveform_start_dim_y + font_y_offset, + label); } + p.drawLines(ire_lines); } diff --git a/app/widget/scope/waveform/waveform.h b/app/widget/scope/waveform/waveform.h index 4aeebc105..2640f9cf3 100644 --- a/app/widget/scope/waveform/waveform.h +++ b/app/widget/scope/waveform/waveform.h @@ -31,10 +31,12 @@ class WaveformScope : public ScopeBase public: WaveformScope(QWidget* parent = nullptr); -protected: - virtual QVariant CreateShader() override; + virtual ~WaveformScope() override; - virtual void DrawScope() override; +protected: + virtual ShaderCode GenerateShaderCode() override; + + virtual void DrawScope(Renderer::TexturePtr managed_tex, QVariant pipeline) override; }; diff --git a/app/widget/standardcombos/pixelformatcombobox.h b/app/widget/standardcombos/pixelformatcombobox.h index 48638a14b..88ad74f29 100644 --- a/app/widget/standardcombos/pixelformatcombobox.h +++ b/app/widget/standardcombos/pixelformatcombobox.h @@ -31,15 +31,14 @@ class PixelFormatComboBox : public QComboBox { Q_OBJECT public: - PixelFormatComboBox(bool alpha_only, bool float_only, QWidget* parent = nullptr) : + PixelFormatComboBox(bool float_only, QWidget* parent = nullptr) : QComboBox(parent) { // Set up preview formats for (int i=0;i(i); - if ((!alpha_only || PixelFormat::FormatHasAlphaChannel(pix_fmt)) - && (!float_only || PixelFormat::FormatIsFloat(pix_fmt))) { + if (!float_only || PixelFormat::FormatIsFloat(pix_fmt)) { this->addItem(PixelFormat::GetName(pix_fmt), pix_fmt); } } diff --git a/app/widget/viewer/viewerdisplay.cpp b/app/widget/viewer/viewerdisplay.cpp index 983f422e1..f0dec4726 100644 --- a/app/widget/viewer/viewerdisplay.cpp +++ b/app/widget/viewer/viewerdisplay.cpp @@ -32,8 +32,6 @@ #include "common/define.h" #include "common/functiontimer.h" #include "gizmotraverser.h" -#include "render/backend/opengl/openglrenderfunctions.h" -#include "render/backend/opengl/openglshader.h" #include "render/pixelformat.h" #include "core.h" @@ -56,7 +54,7 @@ ViewerDisplayWidget::ViewerDisplayWidget(QWidget *parent) : ViewerDisplayWidget::~ViewerDisplayWidget() { - ContextCleanup(); + OnDestroy(); } void ViewerDisplayWidget::SetMatrixTranslate(const QMatrix4x4 &mat) @@ -104,13 +102,13 @@ void ViewerDisplayWidget::SetImage(FramePtr in_buffer) if (last_loaded_buffer_) { makeCurrent(); - if (!texture_.IsCreated() - || texture_.width() != in_buffer->width() - || texture_.height() != in_buffer->height() - || texture_.format() != in_buffer->format()) { - texture_.Create(context(), in_buffer->video_params(), in_buffer->data(), in_buffer->linesize_pixels()); + if (!texture_ + || texture_->width() != in_buffer->width() + || texture_->height() != in_buffer->height() + || texture_->format() != in_buffer->format()) { + texture_ = renderer()->CreateTexture(in_buffer->video_params(), in_buffer->data(), in_buffer->linesize_pixels()); } else { - texture_.Upload(in_buffer); + texture_->Upload(in_buffer->data(), in_buffer->linesize_bytes()); } doneCurrent(); @@ -205,7 +203,7 @@ void ViewerDisplayWidget::mousePressEvent(QMouseEvent *event) emit DragStarted(); } - QOpenGLWidget::mousePressEvent(event); + ManagedDisplayWidget::mousePressEvent(event); } } @@ -252,7 +250,7 @@ void ViewerDisplayWidget::mouseMoveEvent(QMouseEvent *event) } else { // Default behavior - QOpenGLWidget::mouseMoveEvent(event); + ManagedDisplayWidget::mouseMoveEvent(event); } } @@ -275,49 +273,33 @@ void ViewerDisplayWidget::mouseReleaseEvent(QMouseEvent *event) } else { // Default behavior - QOpenGLWidget::mouseReleaseEvent(event); + ManagedDisplayWidget::mouseReleaseEvent(event); } } -QMatrix4x4 ViewerDisplayWidget::GetMatrixTranslate() +void ViewerDisplayWidget::OnInit() { - return translate_matrix_; + ManagedDisplayWidget::OnInit(); } -void ViewerDisplayWidget::initializeGL() +void ViewerDisplayWidget::OnPaint() { - ManagedDisplayWidget::initializeGL(); - - connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &ViewerDisplayWidget::ContextCleanup, Qt::DirectConnection); -} - -void ViewerDisplayWidget::paintGL() -{ - // Get functions attached to this context (they will already be initialized) - QOpenGLFunctions* f = context()->functions(); - // Clear background to empty - f->glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - f->glClear(GL_COLOR_BUFFER_BIT); + renderer()->ClearDestination(); // We only draw if we have a pipeline if (last_loaded_buffer_ && color_service()) { + if (deinterlace_) { + qDebug() << "FIXME: Deinterlacing is currently broken, we're working on this..."; + //color_service()->pipeline()->setUniformValue("ove_resolution", texture_.width(), texture_.height()); + //color_service()->pipeline()->setUniformValue("ove_deinterlace", deinterlace_); + } + // Bind retrieved texture - f->glBindTexture(GL_TEXTURE_2D, texture_.texture()); - - // Set some parameters - color_service()->pipeline()->bind(); - color_service()->pipeline()->setUniformValue("ove_resolution", texture_.width(), texture_.height()); - color_service()->pipeline()->setUniformValue("ove_deinterlace", deinterlace_); - color_service()->pipeline()->release(); - - // Blit using the color service - color_service()->ProcessOpenGL(true, GetCompleteMatrixFlippedYTranslation()); - - // Release retrieved texture - f->glBindTexture(GL_TEXTURE_2D, 0); + renderer()->SetViewport(width(), height()); + renderer()->BlitColorManaged(color_service(), texture_.get()); } @@ -371,6 +353,18 @@ void ViewerDisplayWidget::paintGL() } } +void ViewerDisplayWidget::OnDestroy() +{ + ManagedDisplayWidget::OnDestroy(); + + texture_ = nullptr; +} + +QMatrix4x4 ViewerDisplayWidget::GetMatrixTranslate() +{ + return translate_matrix_; +} + QPointF ViewerDisplayWidget::GetTexturePosition(const QPoint &screen_pos) { return GetTexturePosition(screen_pos.x(), screen_pos.y()); @@ -426,13 +420,4 @@ QTransform ViewerDisplayWidget::GenerateWorldTransform() return world; } -void ViewerDisplayWidget::ContextCleanup() -{ - makeCurrent(); - - texture_.Destroy(); - - doneCurrent(); -} - OLIVE_NAMESPACE_EXIT diff --git a/app/widget/viewer/viewerdisplay.h b/app/widget/viewer/viewerdisplay.h index 354d622cf..d61d16c20 100644 --- a/app/widget/viewer/viewerdisplay.h +++ b/app/widget/viewer/viewerdisplay.h @@ -178,19 +178,22 @@ protected: */ virtual void mouseReleaseEvent(QMouseEvent* event) override; +protected: /** * @brief Initialize function to set up the OpenGL context upon its construction * * Currently primarily used to regenerate the pipeline shader used for drawing. */ - virtual void initializeGL() override; + virtual void OnInit() override; /** * @brief Paint function to display the texture (received in SetTexture()) on screen. * * Simple OpenGL drawing function for painting the texture on screen. Standardized around OpenGL ES 3.2 Core. */ - virtual void paintGL() override; + virtual void OnPaint() override; + + virtual void OnDestroy() override; private: QPointF GetTexturePosition(const QPoint& screen_pos); @@ -208,7 +211,7 @@ private: /** * @brief Internal reference to the OpenGL texture to draw. Set in SetTexture() and used in paintGL(). */ - QVariant texture_; + Renderer::TexturePtr texture_; /** * @brief Translation only matrix (defaults to identity). @@ -247,12 +250,6 @@ private: bool deinterlace_; -private slots: - /** - * @brief Slot to connect just before the OpenGL context is destroyed to clean up resources - */ - void ContextCleanup(); - }; OLIVE_NAMESPACE_EXIT