diff --git a/app/main.cpp b/app/main.cpp index 38bdc6bf6..14dd9bbf1 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -162,7 +162,7 @@ int main(int argc, char *argv[]) // // https://bugreports.qt.io/browse/QTBUG-46140 QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL); - format.setVersion(3, 2); + format.setVersion(2, 0); format.setProfile(QSurfaceFormat::CoreProfile); format.setOption(QSurfaceFormat::DeprecatedFunctions); @@ -172,6 +172,8 @@ int main(int argc, char *argv[]) // Enable application automatically using higher resolution images from icons QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); + QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); + // Create application instance std::unique_ptr a; diff --git a/app/panel/scope/scope.cpp b/app/panel/scope/scope.cpp index 78237c8bb..bd2209b8c 100644 --- a/app/panel/scope/scope.cpp +++ b/app/panel/scope/scope.cpp @@ -84,7 +84,7 @@ QString ScopePanel::TypeToName(ScopePanel::Type t) return QString(); } -void ScopePanel::SetReferenceBuffer(Frame *frame) +void ScopePanel::SetReferenceBuffer(TexturePtr frame) { histogram_->SetBuffer(frame); waveform_view_->SetBuffer(frame); diff --git a/app/panel/scope/scope.h b/app/panel/scope/scope.h index 953e429d1..cd1e70dfe 100644 --- a/app/panel/scope/scope.h +++ b/app/panel/scope/scope.h @@ -50,7 +50,7 @@ public: static QString TypeToName(Type t); public slots: - void SetReferenceBuffer(Frame* frame); + void SetReferenceBuffer(TexturePtr frame); void SetColorManager(ColorManager* manager); diff --git a/app/panel/viewer/viewerbase.cpp b/app/panel/viewer/viewerbase.cpp index 28d38f7fd..ed1e456b1 100644 --- a/app/panel/viewer/viewerbase.cpp +++ b/app/panel/viewer/viewerbase.cpp @@ -108,7 +108,7 @@ void ViewerPanelBase::CreateScopePanel(ScopePanel::Type type) p->SetType(type); // Connect viewer widget texture drawing to scope panel - connect(vw, &ViewerWidget::LoadedBuffer, p, &ScopePanel::SetReferenceBuffer); + connect(vw, &ViewerWidget::TextureChanged, p, &ScopePanel::SetReferenceBuffer); connect(vw, &ViewerWidget::ColorManagerChanged, p, &ScopePanel::SetColorManager); p->SetColorManager(vw->color_manager()); diff --git a/app/render/opengl/openglrenderer.cpp b/app/render/opengl/openglrenderer.cpp index 992c660f7..6d9e8d640 100644 --- a/app/render/opengl/openglrenderer.cpp +++ b/app/render/opengl/openglrenderer.cpp @@ -76,6 +76,11 @@ private: #define PRINT_GL_ERRORS ErrorPrinter __e(__FUNCTION__, functions_) +#define GL_PREAMBLE \ + QMutexLocker __l(&global_opengl_mutex); + +QMutex global_opengl_mutex; + OpenGLRenderer::OpenGLRenderer(QObject* parent) : Renderer(parent), cache_timer_(this), @@ -104,6 +109,8 @@ void OpenGLRenderer::Init(QOpenGLContext *existing_ctx) bool OpenGLRenderer::Init() { + QMutexLocker locker(&global_opengl_mutex); + if (context_) { qCritical() << "Can't initialize already initialized OpenGLRenderer"; return false; @@ -112,6 +119,7 @@ bool OpenGLRenderer::Init() surface_.create(); context_ = new QOpenGLContext(this); + context_->setShareContext(QOpenGLContext::globalShareContext()); if (!context_->create()) { qCritical() << "Failed to create OpenGL context"; return false; @@ -132,6 +140,8 @@ void OpenGLRenderer::PostDestroy() void OpenGLRenderer::PostInit() { + GL_PREAMBLE; + // Make context current on that surface if (context_->parent() == this && !context_->makeCurrent(&surface_)) { qCritical() << "Failed to makeCurrent() on offscreen surface in thread" << thread(); @@ -152,6 +162,8 @@ void OpenGLRenderer::PostInit() void OpenGLRenderer::DestroyInternal() { if (context_) { + GL_PREAMBLE; + // Delete framebuffer functions_->glDeleteFramebuffers(1, &framebuffer_); framebuffer_ = 0; @@ -171,55 +183,32 @@ void OpenGLRenderer::DestroyInternal() cache_timer_.stop(); } -void OpenGLRenderer::ClearDestination(double r, double g, double b, double a) +void OpenGLRenderer::ClearDestination(Texture *texture, double r, double g, double b, double a) { - functions_->glClearColor(r, g, b, a); - functions_->glClear(GL_COLOR_BUFFER_BIT); + GL_PREAMBLE; + + if (texture) { + AttachTextureAsDestination(texture); + } + + ClearDestinationInternal(r, g, b, a); + + if (texture) { + DetachTextureAsDestination(); + } } QVariant OpenGLRenderer::CreateNativeTexture2D(int width, int height, VideoParams::Format format, int channel_count, const void *data, int linesize) { - GLuint texture = GetCachedTexture(width, height, 1, format, channel_count); + GL_PREAMBLE; - // If no texture in cache, generate new texture - bool new_tex = (texture == 0); - if (new_tex) { - functions_->glGenTextures(1, &texture); - texture_params_.insert(texture, {width, height, 1, format, channel_count}); - } - - if (new_tex || data) { - functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, linesize); - - GLint current_tex; - functions_->glGetIntegerv(GL_TEXTURE_BINDING_2D, ¤t_tex); - - functions_->glBindTexture(GL_TEXTURE_2D, texture); - - { - PRINT_GL_ERRORS; - if (new_tex) { - functions_->glTexImage2D(GL_TEXTURE_2D, 0, GetInternalFormat(format, channel_count), - width, height, 0, GetPixelFormat(channel_count), - GetPixelType(format), data); - } else { - functions_->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, - width, height, - GetPixelFormat(channel_count), GetPixelType(format), - data); - } - } - - functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - - functions_->glBindTexture(GL_TEXTURE_2D, current_tex); - } - - return texture; + return CreateNativeTexture2DInternal(width, height, format, channel_count, data, linesize); } QVariant OpenGLRenderer::CreateNativeTexture3D(int width, int height, int depth, VideoParams::Format format, int channel_count, const void *data, int linesize) { + GL_PREAMBLE; + GLuint texture = GetCachedTexture(width, height, depth, format, channel_count); // If no texture in cache, generate new texture @@ -287,6 +276,8 @@ void OpenGLRenderer::DestroyNativeTexture(QVariant texture) QVariant OpenGLRenderer::CreateNativeShader(ShaderCode code) { + GL_PREAMBLE; + PRINT_GL_ERRORS; QOpenGLShaderProgram* program = new QOpenGLShaderProgram(context_); @@ -294,17 +285,8 @@ QVariant OpenGLRenderer::CreateNativeShader(ShaderCode code) QString vert_code = code.vert_code(); QString frag_code = code.frag_code(); - QString shader_preamble; - if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) { - shader_preamble = QStringLiteral("#version 300 es\n" - "\n" - "precision highp int;\n" - "precision highp float;\n" - "\n"); - } else { - shader_preamble = QStringLiteral("#version 150\n" - "\n"); - } + QString shader_preamble = QStringLiteral("#version 110\n" + "\n"); vert_code.prepend(shader_preamble); frag_code.prepend(shader_preamble); @@ -333,12 +315,14 @@ error: void OpenGLRenderer::DestroyNativeShader(QVariant shader) { + GL_PREAMBLE; + delete Node::ValueToPtr(shader); } void OpenGLRenderer::UploadToTexture(Texture *texture, const void *data, int linesize) { - PRINT_GL_ERRORS; + GL_PREAMBLE; GLuint t = texture->id().value(); const VideoParams& p = texture->params(); @@ -354,16 +338,20 @@ void OpenGLRenderer::UploadToTexture(Texture *texture, const void *data, int lin functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, linesize); - if (texture->type() == Texture::k2D) { - functions_->glTexSubImage2D(tex_type, 0, 0, 0, - p.effective_width(), p.effective_height(), - GetPixelFormat(p.channel_count()), GetPixelType(p.format()), - data); - } else { - context_->extraFunctions()->glTexSubImage3D(tex_type, 0, 0, 0, 0, - p.effective_width(), p.effective_height(), p.effective_depth(), - GetPixelFormat(p.channel_count()), GetPixelType(p.format()), - data); + { + PRINT_GL_ERRORS; + + if (texture->type() == Texture::k2D) { + functions_->glTexSubImage2D(tex_type, 0, 0, 0, + p.effective_width(), p.effective_height(), + GetPixelFormat(p.channel_count()), GetPixelType(p.format()), + data); + } else { + context_->extraFunctions()->glTexSubImage3D(tex_type, 0, 0, 0, 0, + p.effective_width(), p.effective_height(), p.effective_depth(), + GetPixelFormat(p.channel_count()), GetPixelType(p.format()), + data); + } } functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); @@ -373,6 +361,8 @@ void OpenGLRenderer::UploadToTexture(Texture *texture, const void *data, int lin void OpenGLRenderer::DownloadFromTexture(Texture* texture, void *data, int linesize) { + GL_PREAMBLE; + const VideoParams& p = texture->params(); GLint current_tex; @@ -388,7 +378,7 @@ void OpenGLRenderer::DownloadFromTexture(Texture* texture, void *data, int lines 0, p.effective_width(), p.effective_height(), - (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) ? GL_RGBA : GetPixelFormat(p.channel_count()), + GetPixelFormat(p.channel_count()), GetPixelType(p.format()), data); } @@ -400,6 +390,33 @@ void OpenGLRenderer::DownloadFromTexture(Texture* texture, void *data, int lines functions_->glBindTexture(GL_TEXTURE_2D, current_tex); } +void OpenGLRenderer::Flush() +{ + GL_PREAMBLE; + + functions_->glFinish(); +} + +Color OpenGLRenderer::GetPixelFromTexture(Texture *texture, const QPointF &pt) +{ + AttachTextureAsDestination(texture); + + QByteArray data(VideoParams::GetBytesPerPixel(texture->format(), texture->channel_count()), Qt::Uninitialized); + + functions_->glReadPixels(pt.x(), pt.y(), 1, 1, GetPixelFormat(texture->channel_count()), GetPixelType(texture->format()), data.data()); + + Color c = Color::fromData(data.data(), texture->format(), texture->channel_count()); + + if (texture->channel_count() == VideoParams::kRGBChannelCount) { + // No alpha channel, set to 1.0 + c.set_alpha(1.0); + } + + DetachTextureAsDestination(); + + return c; +} + struct TextureToBind { TexturePtr texture; Texture::Interpolation interpolation; @@ -407,6 +424,8 @@ struct TextureToBind { void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, VideoParams destination_params, bool clear_destination) { + GL_PREAMBLE; + // If this node is iterative, we'll pick up which input here QString iterative_name; GLuint iterative_input = 0; @@ -581,11 +600,11 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video TexturePtr output_tex, input_tex; if (real_iteration_count > 1) { // Create one texture to bounce off - output_tex = CreateTexture(destination_params); + output_tex = CreateTextureFromNativeHandle(CreateNativeTexture2DInternal(destination_params), destination_params); if (real_iteration_count > 2) { // Create a second texture bounce off - input_tex = CreateTexture(destination_params); + input_tex = CreateTextureFromNativeHandle(CreateNativeTexture2DInternal(destination_params), destination_params); } } @@ -606,7 +625,7 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video // Clear the destination if the caller requested it if (clear_destination) { - ClearDestination(); + ClearDestinationInternal(); } } else { // Always draw to output_tex, which gets swapped with input_tex every iteration @@ -771,6 +790,58 @@ void OpenGLRenderer::PrepareInputTexture(GLenum target, Texture::Interpolation i functions_->glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } +void OpenGLRenderer::ClearDestinationInternal(double r, double g, double b, double a) +{ + functions_->glClearColor(r, g, b, a); + functions_->glClear(GL_COLOR_BUFFER_BIT); +} + +QVariant OpenGLRenderer::CreateNativeTexture2DInternal(int width, int height, VideoParams::Format format, int channel_count, const void *data, int linesize) +{ + GLuint texture = GetCachedTexture(width, height, 1, format, channel_count); + + // If no texture in cache, generate new texture + bool new_tex = (texture == 0); + if (new_tex) { + functions_->glGenTextures(1, &texture); + texture_params_.insert(texture, {width, height, 1, format, channel_count}); + } + + if (new_tex || data) { + functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, linesize); + + GLint current_tex; + functions_->glGetIntegerv(GL_TEXTURE_BINDING_2D, ¤t_tex); + + functions_->glBindTexture(GL_TEXTURE_2D, texture); + + { + PRINT_GL_ERRORS; + if (new_tex) { + functions_->glTexImage2D(GL_TEXTURE_2D, 0, GetInternalFormat(format, channel_count), + width, height, 0, GetPixelFormat(channel_count), + GetPixelType(format), data); + } else { + functions_->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, + width, height, + GetPixelFormat(channel_count), GetPixelType(format), + data); + } + } + + functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + + functions_->glBindTexture(GL_TEXTURE_2D, current_tex); + } + + return texture; +} + +QVariant OpenGLRenderer::CreateNativeTexture2DInternal(const VideoParams ¶ms, const void *data, int linesize) +{ + return CreateNativeTexture2DInternal(params.effective_width(), params.effective_height(), params.format(), params.channel_count(), data, linesize); +} + GLuint OpenGLRenderer::GetCachedTexture(int width, int height, int depth, VideoParams::Format format, int channel_count) { TextureCacheKey input_key = {width, height, depth, format, channel_count}; @@ -795,6 +866,7 @@ void OpenGLRenderer::GarbageCollectTextureCache() qint64 max_age = QDateTime::currentMSecsSinceEpoch() - kTextureCacheMaxSize; for (auto it=texture_cache_.begin(); it!=texture_cache_.end(); ) { if (it->age < max_age) { + GL_PREAMBLE; GLuint t = it->texture; texture_params_.remove(t); functions_->glDeleteTextures(1, &t); diff --git a/app/render/opengl/openglrenderer.h b/app/render/opengl/openglrenderer.h index 340a9eeee..6b1906cac 100644 --- a/app/render/opengl/openglrenderer.h +++ b/app/render/opengl/openglrenderer.h @@ -52,7 +52,7 @@ public slots: virtual void DestroyInternal() override; - virtual void ClearDestination(double r = 0.0, double g = 0.0, double b = 0.0, double a = 0.0) override; + virtual void ClearDestination(olive::Texture *texture = nullptr, double r = 0.0, double g = 0.0, double b = 0.0, double a = 0.0) override; virtual QVariant CreateNativeTexture2D(int width, int height, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0) override; virtual QVariant CreateNativeTexture3D(int width, int height, int depth, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0) override; @@ -67,6 +67,10 @@ public slots: virtual void DownloadFromTexture(olive::Texture* texture, void* data, int linesize) override; + virtual void Flush() override; + + virtual Color GetPixelFromTexture(olive::Texture *texture, const QPointF &pt) override; + protected slots: virtual void Blit(QVariant shader, olive::ShaderJob job, @@ -87,6 +91,11 @@ private: void PrepareInputTexture(GLenum target, Texture::Interpolation interp); + void ClearDestinationInternal(double r = 0.0, double g = 0.0, double b = 0.0, double a = 0.0); + + QVariant CreateNativeTexture2DInternal(int width, int height, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0); + QVariant CreateNativeTexture2DInternal(const VideoParams ¶ms, const void* data = nullptr, int linesize = 0); + GLuint GetCachedTexture(int width, int height, int depth, VideoParams::Format format, int channel_count); QTimer cache_timer_; diff --git a/app/render/previewautocacher.cpp b/app/render/previewautocacher.cpp index 240c97b8a..0c785d4db 100644 --- a/app/render/previewautocacher.cpp +++ b/app/render/previewautocacher.cpp @@ -530,7 +530,8 @@ void PreviewAutoCacher::TryRender() } else { watcher = RenderFrame(hash, single_frame_render_->property("time").value(), - single_frame_render_->property("prioritize").toBool()); + single_frame_render_->property("prioritize").toBool(), + paused_); video_immediate_passthroughs_[watcher].append(single_frame_render_); } @@ -539,7 +540,7 @@ void PreviewAutoCacher::TryRender() } } -RenderTicketWatcher* PreviewAutoCacher::RenderFrame(const QByteArray &hash, const rational& time, bool prioritize) +RenderTicketWatcher* PreviewAutoCacher::RenderFrame(const QByteArray &hash, const rational& time, bool prioritize, bool texture_only) { RenderTicketWatcher* watcher = new RenderTicketWatcher(); watcher->setProperty("hash", hash); @@ -550,7 +551,8 @@ RenderTicketWatcher* PreviewAutoCacher::RenderFrame(const QByteArray &hash, cons time, RenderMode::kOffline, viewer_node_->video_frame_cache(), - prioritize)); + prioritize, + texture_only)); return watcher; } @@ -585,7 +587,7 @@ void PreviewAutoCacher::RequeueFrames() // We want this hash, if we're not already rendering, start render now if (!render_task && !video_download_tasks_.key(hash)) { // Don't render any hash more than once - RenderFrame(hash, t, false); + RenderFrame(hash, t, false, false); } } else if (render_task) { // Cancel this frame unless it's already started diff --git a/app/render/previewautocacher.h b/app/render/previewautocacher.h index 63b0e76f3..6d0bd4f44 100644 --- a/app/render/previewautocacher.h +++ b/app/render/previewautocacher.h @@ -84,7 +84,7 @@ private: void TryRender(); - RenderTicketWatcher *RenderFrame(const QByteArray& hash, const rational &time, bool prioritize); + RenderTicketWatcher *RenderFrame(const QByteArray& hash, const rational &time, bool prioritize, bool texture_only); /** * @brief Process all changes to internal NodeGraph copy diff --git a/app/render/renderer.cpp b/app/render/renderer.cpp index bf2d448c1..3cc41aa2e 100644 --- a/app/render/renderer.cpp +++ b/app/render/renderer.cpp @@ -20,6 +20,8 @@ #include "renderer.h" +#include + #include "common/ocioutils.h" namespace olive { @@ -42,11 +44,7 @@ TexturePtr Renderer::CreateTexture(const VideoParams ¶ms, Texture::Type type params.channel_count(), data, linesize); } - if (v.isNull()) { - return nullptr; - } - - return std::make_shared(this, v, params, type); + return CreateTextureFromNativeHandle(v, params, type); } TexturePtr Renderer::CreateTexture(const VideoParams ¶ms, const void *data, int linesize) @@ -64,13 +62,47 @@ void Renderer::BlitColorManaged(ColorProcessorPtr color_processor, TexturePtr so BlitColorManagedInternal(color_processor, source, source_is_premultiplied, nullptr, params, clear_destination, matrix, crop_matrix); } +TexturePtr Renderer::InterlaceTexture(TexturePtr top, TexturePtr bottom, const VideoParams ¶ms) +{ + color_cache_mutex_.lock(); + if (interlace_texture_.isNull()) { + interlace_texture_ = CreateNativeShader(ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/interlace.frag")))); + } + color_cache_mutex_.unlock(); + + ShaderJob job; + job.InsertValue(QStringLiteral("top_tex_in"), NodeValue(NodeValue::kTexture, QVariant::fromValue(top))); + job.InsertValue(QStringLiteral("bottom_tex_in"), NodeValue(NodeValue::kTexture, QVariant::fromValue(bottom))); + job.InsertValue(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, QVector2D(params.effective_width(), params.effective_height()))); + + TexturePtr output = CreateTexture(params); + + BlitToTexture(interlace_texture_, job, output.get()); + + return output; +} + void Renderer::Destroy() { color_cache_.clear(); + if (!interlace_texture_.isNull()) { + DestroyNativeShader(interlace_texture_); + interlace_texture_.clear(); + } + DestroyInternal(); } +TexturePtr Renderer::CreateTextureFromNativeHandle(const QVariant &v, const VideoParams ¶ms, Texture::Type type) +{ + if (v.isNull()) { + return nullptr; + } + + return std::make_shared(this, v, params, type); +} + bool Renderer::GetColorContext(ColorProcessorPtr color_processor, Renderer::ColorContext *ctx) { QMutexLocker locker(&color_cache_mutex_); @@ -103,15 +135,9 @@ bool Renderer::GetColorContext(ColorProcessorPtr color_processor, Renderer::Colo "#define ALPHA_UNASSOC 1\n" "#define ALPHA_ASSOC 2\n" "\n" - "// Macros so OCIO's shaders work on this GLSL version\n" - "#define texture2D texture\n" - "#define texture3D texture\n" - "\n" "// Main texture coordinate\n" - "in vec2 ove_texcoord;\n" - "\n" - "// Texture output\n" - "out vec4 fragColor;\n")); + "varying vec2 ove_texcoord;\n" + "\n")); shader_frag.append(shader_desc->getShaderText()); shader_frag.append(QStringLiteral("\n" "// Alpha association functions\n" @@ -128,13 +154,13 @@ bool Renderer::GetColorContext(ColorProcessorPtr color_processor, Renderer::Colo "}\n" "\n" "void main() {\n" - " vec2 cropped_coord = (vec4(ove_texcoord-vec2(0.5, 0.5), 0.0, 1.0)*inverse(ove_cropmatrix)).xy + vec2(0.5, 0.5);\n" + " vec2 cropped_coord = (vec4(ove_texcoord-vec2(0.5, 0.5), 0.0, 1.0)*ove_cropmatrix).xy + vec2(0.5, 0.5);\n" " if (cropped_coord.x < 0.0 || cropped_coord.x >= 1.0 || cropped_coord.y < 0.0 || cropped_coord.y >= 1.0) {\n" - " fragColor = vec4(0.0);\n" + " gl_FragColor = vec4(0.0);\n" " return;\n" " }\n" " \n" - " vec4 col = texture(ove_maintex, cropped_coord);\n" + " vec4 col = texture2D(ove_maintex, cropped_coord);\n" "\n" " // If alpha is associated, de-associate now\n" " if (ove_maintex_alpha == ALPHA_ASSOC) {\n" @@ -151,7 +177,7 @@ bool Renderer::GetColorContext(ColorProcessorPtr color_processor, Renderer::Colo " col = assoc(col);\n" " }\n" "\n" - " fragColor = col;\n" + " gl_FragColor = col;\n" "}\n").arg(ocio_func_name)); // Try to compile shader @@ -244,7 +270,7 @@ void Renderer::BlitColorManagedInternal(ColorProcessorPtr color_processor, Textu job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(source))); job.InsertValue(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, matrix)); - job.InsertValue(QStringLiteral("ove_cropmatrix"), NodeValue(NodeValue::kMatrix, crop_matrix)); + job.InsertValue(QStringLiteral("ove_cropmatrix"), NodeValue(NodeValue::kMatrix, crop_matrix.inverted())); AlphaAssociated associated; if (source->channel_count() == VideoParams::kRGBAChannelCount) { diff --git a/app/render/renderer.h b/app/render/renderer.h index a6fcc252b..628212c13 100644 --- a/app/render/renderer.h +++ b/app/render/renderer.h @@ -66,6 +66,8 @@ public: void BlitColorManaged(ColorProcessorPtr color_processor, TexturePtr source, bool source_is_premultiplied, Texture* destination, bool clear_destination = true, const QMatrix4x4& matrix = QMatrix4x4(), const QMatrix4x4 &crop_matrix = QMatrix4x4()); void BlitColorManaged(ColorProcessorPtr color_processor, TexturePtr source, bool source_is_premultiplied, VideoParams params, bool clear_destination = true, const QMatrix4x4& matrix = QMatrix4x4(), const QMatrix4x4 &crop_matrix = QMatrix4x4()); + TexturePtr InterlaceTexture(TexturePtr top, TexturePtr bottom, const VideoParams ¶ms); + void Destroy(); virtual void PostDestroy() = 0; @@ -75,7 +77,7 @@ public slots: virtual void DestroyInternal() = 0; - virtual void ClearDestination(double r = 0.0, double g = 0.0, double b = 0.0, double a = 0.0) = 0; + virtual void ClearDestination(olive::Texture *texture = nullptr, double r = 0.0, double g = 0.0, double b = 0.0, double a = 0.0) = 0; virtual QVariant CreateNativeTexture2D(int width, int height, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0) = 0; virtual QVariant CreateNativeTexture3D(int width, int height, int depth, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0) = 0; @@ -90,6 +92,10 @@ public slots: virtual void DownloadFromTexture(olive::Texture* texture, void* data, int linesize) = 0; + virtual void Flush() = 0; + + virtual Color GetPixelFromTexture(olive::Texture *texture, const QPointF &pt) = 0; + protected slots: virtual void Blit(QVariant shader, olive::ShaderJob job, @@ -97,6 +103,9 @@ protected slots: olive::VideoParams destination_params, bool clear_destination) = 0; +protected: + TexturePtr CreateTextureFromNativeHandle(const QVariant &v, const VideoParams ¶ms, Texture::Type type = Texture::k2D); + private: struct ColorContext { struct LUT { @@ -128,6 +137,8 @@ private: QMutex color_cache_mutex_; + QVariant interlace_texture_; + }; } diff --git a/app/render/rendererthreadwrapper.cpp b/app/render/rendererthreadwrapper.cpp index 3708fd610..5566f9c59 100644 --- a/app/render/rendererthreadwrapper.cpp +++ b/app/render/rendererthreadwrapper.cpp @@ -69,9 +69,10 @@ void RendererThreadWrapper::DestroyInternal() } } -void RendererThreadWrapper::ClearDestination(double r, double g, double b, double a) +void RendererThreadWrapper::ClearDestination(Texture *texture, double r, double g, double b, double a) { QMetaObject::invokeMethod(inner_, "ClearDestination", Qt::BlockingQueuedConnection, + OLIVE_NS_ARG(Texture*, texture), Q_ARG(double, r), Q_ARG(double, g), Q_ARG(double, b), @@ -150,6 +151,23 @@ void RendererThreadWrapper::DownloadFromTexture(Texture *texture, void *data, in Q_ARG(int, linesize)); } +void RendererThreadWrapper::Flush() +{ + QMetaObject::invokeMethod(inner_, "Flush", Qt::BlockingQueuedConnection); +} + +Color RendererThreadWrapper::GetPixelFromTexture(Texture *texture, const QPointF &pt) +{ + Color c; + + QMetaObject::invokeMethod(inner_, "GetPixelFromTexture", Qt::BlockingQueuedConnection, + OLIVE_NS_RETURN_ARG(Color, c), + OLIVE_NS_ARG(Texture*, texture), + Q_ARG(QPointF, pt)); + + return c; +} + void RendererThreadWrapper::Blit(QVariant shader, ShaderJob job, Texture *destination, VideoParams destination_params, bool clear_destination) { QMetaObject::invokeMethod(inner_, "Blit", Qt::BlockingQueuedConnection, diff --git a/app/render/rendererthreadwrapper.h b/app/render/rendererthreadwrapper.h index d77a3f547..694a630b7 100644 --- a/app/render/rendererthreadwrapper.h +++ b/app/render/rendererthreadwrapper.h @@ -48,7 +48,7 @@ public slots: virtual void DestroyInternal() override; - virtual void ClearDestination(double r = 0.0, double g = 0.0, double b = 0.0, double a = 0.0) override; + virtual void ClearDestination(olive::Texture *texture = nullptr, double r = 0.0, double g = 0.0, double b = 0.0, double a = 0.0) override; virtual QVariant CreateNativeTexture2D(int width, int height, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0) override; virtual QVariant CreateNativeTexture3D(int width, int height, int depth, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0) override; @@ -63,6 +63,10 @@ public slots: virtual void DownloadFromTexture(olive::Texture* texture, void* data, int linesize) override; + virtual void Flush() override; + + virtual Color GetPixelFromTexture(olive::Texture *texture, const QPointF &pt) override; + protected slots: virtual void Blit(QVariant shader, olive::ShaderJob job, diff --git a/app/render/rendermanager.cpp b/app/render/rendermanager.cpp index c72212a27..8f3df3815 100644 --- a/app/render/rendermanager.cpp +++ b/app/render/rendermanager.cpp @@ -126,7 +126,7 @@ QByteArray RenderManager::Hash(const Node *n, const QString& output, const Video RenderTicketPtr RenderManager::RenderFrame(ViewerOutput *viewer, ColorManager* color_manager, const rational& time, RenderMode::Mode mode, - FrameHashCache* cache, bool prioritize) + FrameHashCache* cache, bool prioritize, bool texture_only) { return RenderFrame(viewer, color_manager, @@ -139,7 +139,8 @@ RenderTicketPtr RenderManager::RenderFrame(ViewerOutput *viewer, ColorManager* c VideoParams::kFormatInvalid, nullptr, cache, - prioritize); + prioritize, + texture_only); } RenderTicketPtr RenderManager::RenderFrame(ViewerOutput *viewer, ColorManager* color_manager, @@ -148,7 +149,7 @@ RenderTicketPtr RenderManager::RenderFrame(ViewerOutput *viewer, ColorManager* c const QSize& force_size, const QMatrix4x4& force_matrix, VideoParams::Format force_format, ColorProcessorPtr force_color_output, - FrameHashCache* cache, bool prioritize) + FrameHashCache* cache, bool prioritize, bool texture_only) { // Create ticket RenderTicketPtr ticket = std::make_shared(); @@ -164,6 +165,7 @@ RenderTicketPtr RenderManager::RenderFrame(ViewerOutput *viewer, ColorManager* c ticket->setProperty("coloroutput", QVariant::fromValue(force_color_output)); ticket->setProperty("vparam", QVariant::fromValue(video_params)); ticket->setProperty("aparam", QVariant::fromValue(audio_params)); + ticket->setProperty("textureonly", texture_only); if (cache) { ticket->setProperty("cache", cache->GetCacheDirectory()); diff --git a/app/render/rendermanager.h b/app/render/rendermanager.h index 4069a8c07..34f8532d9 100644 --- a/app/render/rendermanager.h +++ b/app/render/rendermanager.h @@ -86,14 +86,14 @@ public: */ RenderTicketPtr RenderFrame(ViewerOutput *viewer, ColorManager* color_manager, const rational& time, RenderMode::Mode mode, - FrameHashCache* cache = nullptr, bool prioritize = false); + FrameHashCache* cache = nullptr, bool prioritize = false, bool texture_only = false); RenderTicketPtr RenderFrame(ViewerOutput* viewer, ColorManager* color_manager, const rational& time, RenderMode::Mode mode, const VideoParams& video_params, const AudioParams& audio_params, const QSize& force_size, const QMatrix4x4& force_matrix, VideoParams::Format force_format, ColorProcessorPtr force_color_output, - FrameHashCache* cache = nullptr, bool prioritize = false); + FrameHashCache* cache = nullptr, bool prioritize = false, bool texture_only = false); /** * @brief Asynchronously generate a chunk of audio diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index 1ca1a7da8..9bb00ff3a 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -40,7 +40,7 @@ RenderProcessor::RenderProcessor(RenderTicketPtr ticket, Renderer *render_ctx, S { } -FramePtr RenderProcessor::GenerateFrame(const rational& time, const rational& frame_length) +TexturePtr RenderProcessor::GenerateTexture(const rational &time, const rational &frame_length) { ViewerOutput* viewer = Node::ValueToPtr(ticket_->property("viewer")); @@ -51,8 +51,11 @@ FramePtr RenderProcessor::GenerateFrame(const rational& time, const rational& fr TimeRange(time, time + frame_length)); } - TexturePtr texture = table.Get(NodeValue::kTexture).value(); + return table.Get(NodeValue::kTexture).value(); +} +FramePtr RenderProcessor::GenerateFrame(TexturePtr texture, const rational& time) +{ // Set up output frame parameters VideoParams frame_params = GetCacheVideoParams(); @@ -128,25 +131,35 @@ void RenderProcessor::Run() frame_length /= 2; } - FramePtr frame = GenerateFrame(time, frame_length); + TexturePtr texture = GenerateTexture(time, frame_length); if (GetCacheVideoParams().interlacing() != VideoParams::kInterlaceNone) { // Get next between frame and interlace it - FramePtr next_frame = GenerateFrame(time + frame_length, frame_length); + TexturePtr top = texture; + TexturePtr bottom = GenerateTexture(time + frame_length, frame_length); - FramePtr top, bottom; - if (GetCacheVideoParams().interlacing() == VideoParams::kInterlacedTopFirst) { - top = frame; - bottom = next_frame; - } else { - top = next_frame; - bottom = frame; + if (GetCacheVideoParams().interlacing() == VideoParams::kInterlacedBottomFirst) { + std::swap(top, bottom); } - frame = Frame::Interlace(top, bottom); + texture = render_ctx_->InterlaceTexture(top, bottom, GetCacheVideoParams()); } - ticket_->Finish(QVariant::fromValue(frame)); + if (ticket_->property("textureonly").toBool()) { + // Return GPU texture + if (!texture) { + texture = render_ctx_->CreateTexture(GetCacheVideoParams()); + } + + render_ctx_->Flush(); + + ticket_->Finish(QVariant::fromValue(texture)); + } else { + // Convert to CPU frame + FramePtr frame = GenerateFrame(texture, time); + + ticket_->Finish(QVariant::fromValue(frame)); + } break; } case RenderManager::kTypeAudio: diff --git a/app/render/renderprocessor.h b/app/render/renderprocessor.h index 8e91ec774..c3dd9e91a 100644 --- a/app/render/renderprocessor.h +++ b/app/render/renderprocessor.h @@ -62,7 +62,9 @@ protected: private: RenderProcessor(RenderTicketPtr ticket, Renderer* render_ctx, StillImageCache* still_image_cache, DecoderCache* decoder_cache, ShaderCache* shader_cache, QVariant default_shader); - FramePtr GenerateFrame(const rational &time, const rational &frame_length); + TexturePtr GenerateTexture(const rational& time, const rational& frame_length); + + FramePtr GenerateFrame(TexturePtr texture, const rational &time); void Run(); diff --git a/app/render/texture.h b/app/render/texture.h index 10c8257c7..0d73c2be9 100644 --- a/app/render/texture.h +++ b/app/render/texture.h @@ -120,6 +120,11 @@ public: return type_; } + Renderer* renderer() const + { + return renderer_; + } + private: Renderer* renderer_; diff --git a/app/shaders/alphaover.frag b/app/shaders/alphaover.frag index 3411ad3d3..58c4be721 100644 --- a/app/shaders/alphaover.frag +++ b/app/shaders/alphaover.frag @@ -3,31 +3,29 @@ uniform sampler2D blend_in; uniform bool base_in_enabled; uniform bool blend_in_enabled; -in vec2 ove_texcoord; - -out vec4 fragColor; +varying vec2 ove_texcoord; void main(void) { - vec4 base_col = texture(base_in, ove_texcoord); - vec4 blend_col = texture(blend_in, ove_texcoord); + vec4 base_col = texture2D(base_in, ove_texcoord); + vec4 blend_col = texture2D(blend_in, ove_texcoord); if (!base_in_enabled && !blend_in_enabled) { - fragColor = vec4(0.0); + gl_FragColor = vec4(0.0); return; } if (!base_in_enabled) { - fragColor = blend_col; + gl_FragColor = blend_col; return; } if (!blend_in_enabled) { - fragColor = base_col; - return; + gl_FragColor = base_col; + return; } base_col *= 1.0 - blend_col.a; base_col += blend_col; - fragColor = base_col; + gl_FragColor = base_col; } diff --git a/app/shaders/blur.frag b/app/shaders/blur.frag index dcb0bf253..fb60660f0 100644 --- a/app/shaders/blur.frag +++ b/app/shaders/blur.frag @@ -8,9 +8,7 @@ uniform vec2 resolution_in; uniform int ove_iteration; -in vec2 ove_texcoord; - -out vec4 fragColor; +varying vec2 ove_texcoord; // Gaussian function uses PI #define M_PI 3.1415926535897932384626433832795 @@ -65,7 +63,7 @@ void main(void) { int mode = determine_mode(); if (mode == MODE_NONE) { - fragColor = texture(tex_in, ove_texcoord); + gl_FragColor = texture2D(tex_in, ove_texcoord); return; } @@ -117,9 +115,9 @@ void main(void) { && pixel_coord.x < 1.0 && pixel_coord.y >= 0.0 && pixel_coord.y < 1.0)) { - composite += texture(tex_in, pixel_coord) * weight; + composite += texture2D(tex_in, pixel_coord) * weight; } } - fragColor = composite; + gl_FragColor = composite; } diff --git a/app/shaders/crop.frag b/app/shaders/crop.frag index 22248e0ef..a76b1219d 100644 --- a/app/shaders/crop.frag +++ b/app/shaders/crop.frag @@ -8,10 +8,7 @@ uniform float feather_in; uniform vec2 resolution_in; // Input texture coordinate -in vec2 ove_texcoord; - -// Output color -out vec4 fragColor; +varying vec2 ove_texcoord; void main() { float multiplier = 1.0; @@ -47,9 +44,9 @@ void main() { } if (multiplier > 0.0) { - vec4 color = texture(tex_in, ove_texcoord) * multiplier; - fragColor = color; + vec4 color = texture2D(tex_in, ove_texcoord) * multiplier; + gl_FragColor = color; } else { - fragColor = vec4(0.0); + gl_FragColor = vec4(0.0); } } diff --git a/app/shaders/crossdissolve.frag b/app/shaders/crossdissolve.frag index 66152486b..7ddfa9055 100644 --- a/app/shaders/crossdissolve.frag +++ b/app/shaders/crossdissolve.frag @@ -10,9 +10,7 @@ uniform int curve_in; uniform float ove_tprog_all; -in vec2 ove_texcoord; - -out vec4 fragColor; +varying vec2 ove_texcoord; float TransformCurve(float linear) { if (curve_in == EXPONENTIAL_CURVE) { @@ -28,12 +26,12 @@ void main(void) { vec4 composite = vec4(0.0); if (out_block_in_enabled) { - composite += texture(out_block_in, ove_texcoord) * TransformCurve(1.0 - ove_tprog_all); + composite += texture2D(out_block_in, ove_texcoord) * TransformCurve(1.0 - ove_tprog_all); } if (in_block_in_enabled) { - composite += texture(in_block_in, ove_texcoord) * TransformCurve(ove_tprog_all); + composite += texture2D(in_block_in, ove_texcoord) * TransformCurve(ove_tprog_all); } - fragColor = composite; + gl_FragColor = composite; } diff --git a/app/shaders/default.frag b/app/shaders/default.frag index 2a6a46b9e..1182bd241 100644 --- a/app/shaders/default.frag +++ b/app/shaders/default.frag @@ -2,12 +2,9 @@ uniform sampler2D ove_maintex; // Input texture coordinate -in vec2 ove_texcoord; - -// Output color -out vec4 fragColor; +varying vec2 ove_texcoord; void main() { - vec4 color = texture(ove_maintex, ove_texcoord); - fragColor = color; + vec4 color = texture2D(ove_maintex, ove_texcoord); + gl_FragColor = color; } diff --git a/app/shaders/default.vert b/app/shaders/default.vert index eda14fb70..0b3105a9b 100644 --- a/app/shaders/default.vert +++ b/app/shaders/default.vert @@ -1,11 +1,11 @@ uniform mat4 ove_mvpmat; -in vec4 a_position; -in vec2 a_texcoord; +attribute vec4 a_position; +attribute vec2 a_texcoord; -out vec2 ove_texcoord; +varying vec2 ove_texcoord; void main() { gl_Position = ove_mvpmat * a_position; ove_texcoord = a_texcoord; -} \ No newline at end of file +} diff --git a/app/shaders/deinterlace.frag b/app/shaders/deinterlace.frag index 45104a145..d4f7b4c66 100644 --- a/app/shaders/deinterlace.frag +++ b/app/shaders/deinterlace.frag @@ -2,9 +2,12 @@ uniform sampler2D ove_maintex; uniform vec2 resolution_in; -in vec2 ove_texcoord; +varying vec2 ove_texcoord; -out vec4 fragColor; +float round(float x) +{ + return floor(x + 0.5); +} void main() { vec2 using_texcoord = ove_texcoord; @@ -15,6 +18,6 @@ void main() { float half_vert = round(resolution_in.y / 2.0); using_texcoord.y = (round(using_texcoord.y * half_vert) + 0.25) / half_vert; - vec4 color = texture(ove_maintex, using_texcoord); - fragColor = color; + vec4 color = texture2D(ove_maintex, using_texcoord); + gl_FragColor = color; } diff --git a/app/shaders/diptoblack.frag b/app/shaders/diptoblack.frag index 1238a7b85..cc3a8d68b 100644 --- a/app/shaders/diptoblack.frag +++ b/app/shaders/diptoblack.frag @@ -8,21 +8,19 @@ uniform float ove_tprog_all; uniform float ove_tprog_out; uniform float ove_tprog_in; -in vec2 ove_texcoord; - -out vec4 fragColor; +varying vec2 ove_texcoord; void main(void) { if (out_block_in_enabled && in_block_in_enabled) { - vec4 out_block_col = mix(texture(out_block_in, ove_texcoord), color_in, ove_tprog_out); - vec4 in_block_col = mix(texture(in_block_in, ove_texcoord), color_in, 1.0 - ove_tprog_in); + vec4 out_block_col = mix(texture2D(out_block_in, ove_texcoord), color_in, ove_tprog_out); + vec4 in_block_col = mix(texture2D(in_block_in, ove_texcoord), color_in, 1.0 - ove_tprog_in); - fragColor = out_block_col + in_block_col; + gl_FragColor = out_block_col + in_block_col; } else if (out_block_in_enabled) { - fragColor = mix(texture(out_block_in, ove_texcoord), color_in, ove_tprog_all); + gl_FragColor = mix(texture2D(out_block_in, ove_texcoord), color_in, ove_tprog_all); } else if (in_block_in_enabled) { - fragColor = mix(texture(in_block_in, ove_texcoord), color_in, 1.0 - ove_tprog_all); + gl_FragColor = mix(texture2D(in_block_in, ove_texcoord), color_in, 1.0 - ove_tprog_all); } else { - fragColor = vec4(0.0); + gl_FragColor = vec4(0.0); } } diff --git a/app/shaders/interlace.frag b/app/shaders/interlace.frag new file mode 100644 index 000000000..15cf182b9 --- /dev/null +++ b/app/shaders/interlace.frag @@ -0,0 +1,15 @@ +uniform sampler2D top_tex_in; +uniform sampler2D bottom_tex_in; +uniform vec2 resolution_in; + +varying vec2 ove_texcoord; + +void main() { + float y_pixel = floor(ove_texcoord.y * resolution_in.y); + + if (mod(y_pixel, 2.0) == 0.0) { + gl_FragColor = texture2D(top_tex_in, ove_texcoord); + } else { + gl_FragColor = texture2D(bottom_tex_in, ove_texcoord); + } +} diff --git a/app/shaders/mosaic.frag b/app/shaders/mosaic.frag index 6169d9d62..a56f7c191 100644 --- a/app/shaders/mosaic.frag +++ b/app/shaders/mosaic.frag @@ -5,10 +5,7 @@ uniform float horiz_in; uniform float vert_in; // Input texture coordinate -in vec2 ove_texcoord; - -// Output color -out vec4 fragColor; +varying vec2 ove_texcoord; void main() { float x; @@ -26,6 +23,6 @@ void main() { y = ove_texcoord.y; } - vec4 color = texture(tex_in, vec2(x, y)); - fragColor = color; + vec4 color = texture2D(tex_in, vec2(x, y)); + gl_FragColor = color; } diff --git a/app/shaders/polygon.frag b/app/shaders/polygon.frag index b5fa0fed1..209918247 100644 --- a/app/shaders/polygon.frag +++ b/app/shaders/polygon.frag @@ -4,9 +4,7 @@ uniform vec4 color_in; uniform vec2 resolution_in; -in vec2 ove_texcoord; - -out vec4 fragColor; +varying vec2 ove_texcoord; /* int pnpoly(int npol, float *xp, float *yp, float x, float y) { @@ -35,8 +33,8 @@ bool pnpoly(vec2 p) { void main(void) { if (points_in_count > 0 && pnpoly(ove_texcoord * resolution_in)) { - fragColor = color_in; + gl_FragColor = color_in; } else { - fragColor = vec4(0.0, 0.0, 0.0, 0.0); + gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); } } diff --git a/app/shaders/rgbhistogram.frag b/app/shaders/rgbhistogram.frag index 25471dc6e..8fdfe53a6 100644 --- a/app/shaders/rgbhistogram.frag +++ b/app/shaders/rgbhistogram.frag @@ -2,9 +2,7 @@ uniform sampler2D ove_maintex; uniform vec2 viewport; uniform float histogram_scale; -in vec2 ove_texcoord; - -out vec4 fragColor; +varying vec2 ove_texcoord; void main(void) { float histogram_width = ceil(histogram_scale * viewport.y); @@ -13,9 +11,9 @@ void main(void) { vec3 sum = vec3(0.0); float ratio = 0.0; - for (int i = 0; i < histogram_width; i++) { - ratio = float(i) / float(histogram_width - 1); - cur_col = texture( + for (int i = 0; float(i) < histogram_width; i++) { + ratio = float(i) / float(histogram_width - 1.0); + cur_col = texture2D( ove_maintex, vec2(ove_texcoord.y, ratio) ).rgb; @@ -29,5 +27,5 @@ void main(void) { ); } - fragColor = vec4(sum, 1.0); + gl_FragColor = vec4(sum, 1.0); } diff --git a/app/shaders/rgbhistogram.vert b/app/shaders/rgbhistogram.vert index 44100df79..6c3283f07 100644 --- a/app/shaders/rgbhistogram.vert +++ b/app/shaders/rgbhistogram.vert @@ -1,9 +1,9 @@ uniform float histogram_scale; -in vec4 a_position; -in vec2 a_texcoord; +attribute vec4 a_position; +attribute vec2 a_texcoord; -out vec2 ove_texcoord; +varying vec2 ove_texcoord; mat4 scale_mat4(vec3 scale) { return mat4( diff --git a/app/shaders/rgbhistogram_secondary.frag b/app/shaders/rgbhistogram_secondary.frag index 742584502..1a6dc17b8 100644 --- a/app/shaders/rgbhistogram_secondary.frag +++ b/app/shaders/rgbhistogram_secondary.frag @@ -4,9 +4,7 @@ uniform vec2 viewport; uniform float histogram_scale; uniform float histogram_power; -in vec2 ove_texcoord; - -out vec4 fragColor; +varying vec2 ove_texcoord; void main(void) { vec3 col = vec3(0.0); @@ -17,9 +15,9 @@ void main(void) { vec3 total_pixels = vec3(ceil(viewport.x * viewport.y * histogram_scale)); - for (int i = 0; i < histogram_height; i++) { + for (int i = 0; float(i) < histogram_height; i++) { ratio = float(i) / float(histogram_height - 1.0); - sum += texture( + sum += texture2D( ove_maintex, vec2(ove_texcoord.x, ratio) ).rgb; @@ -28,5 +26,5 @@ void main(void) { histogram_ratio = pow(sum / total_pixels, vec3(histogram_power)); col = step(vec3(ove_texcoord.y), histogram_ratio); - fragColor = vec4(col, 1.0); + gl_FragColor = vec4(col, 1.0); } diff --git a/app/shaders/rgbwaveform.frag b/app/shaders/rgbwaveform.frag index a3242ec76..c43905055 100644 --- a/app/shaders/rgbwaveform.frag +++ b/app/shaders/rgbwaveform.frag @@ -5,9 +5,7 @@ uniform vec3 luma_coeffs; uniform float waveform_scale; -in vec2 ove_texcoord; - -out vec4 fragColor; +varying vec2 ove_texcoord; void main(void) { float waveform_height = ceil(waveform_scale * viewport.y); @@ -17,9 +15,9 @@ void main(void) { vec4 cur_col = vec4(0.0); float ratio = 0.0; - for (int i = 0; i < waveform_height; i++) { + for (int i = 0; float(i) < waveform_height; i++) { ratio = float(i) / float(waveform_height - 1.0); - cur_col.rgb = texture( + cur_col.rgb = texture2D( ove_maintex, vec2(ove_texcoord.x, ratio) ).rgb; @@ -35,5 +33,5 @@ void main(void) { } col.rgb += vec3(col.w); - fragColor = vec4(col.rgb, 1.0); + gl_FragColor = vec4(col.rgb, 1.0); } diff --git a/app/shaders/rgbwaveform.vert b/app/shaders/rgbwaveform.vert index 33980f284..af1eec88b 100644 --- a/app/shaders/rgbwaveform.vert +++ b/app/shaders/rgbwaveform.vert @@ -1,9 +1,9 @@ uniform float waveform_scale; -in vec4 a_position; -in vec2 a_texcoord; +attribute vec4 a_position; +attribute vec2 a_texcoord; -out vec2 ove_texcoord; +varying vec2 ove_texcoord; mat4 scale_mat4(vec3 scale) { return mat4( diff --git a/app/shaders/solid.frag b/app/shaders/solid.frag index 2be4254a9..159841e79 100644 --- a/app/shaders/solid.frag +++ b/app/shaders/solid.frag @@ -1,7 +1,5 @@ uniform vec4 color_in; -out vec4 fragColor; - void main(void) { - fragColor = color_in; + gl_FragColor = color_in; } diff --git a/app/shaders/stroke.frag b/app/shaders/stroke.frag index 147b1db55..ce6fef99f 100644 --- a/app/shaders/stroke.frag +++ b/app/shaders/stroke.frag @@ -9,12 +9,10 @@ uniform vec2 resolution_in; // Standard inputs uniform int ove_iteration; -in vec2 ove_texcoord; - -out vec4 fragColor; +varying vec2 ove_texcoord; void main(void) { - vec4 pixel_here = texture(tex_in, ove_texcoord); + vec4 pixel_here = texture2D(tex_in, ove_texcoord); // Detect no-op situations if (radius_in == 0.0 @@ -22,7 +20,7 @@ void main(void) { || (inner_in && pixel_here.a == 0.0) || (!inner_in && pixel_here.a == 1.0)) { // No-op, do nothing - fragColor = pixel_here; + gl_FragColor = pixel_here; return; } @@ -39,7 +37,7 @@ void main(void) { if (abs(length(vec2(i, j))) < radius) { // Get pixel here - float alpha = texture(tex_in, ove_texcoord + vec2(x_coord, y_coord)).a; + float alpha = texture2D(tex_in, ove_texcoord + vec2(x_coord, y_coord)).a; if (inner_in) { alpha = 1.0 - alpha; @@ -76,5 +74,5 @@ void main(void) { stroke_col = stroke_col * (1.0 - pixel_here.a) + pixel_here; } - fragColor = stroke_col; + gl_FragColor = stroke_col; } diff --git a/app/widget/colorwheel/colorpreviewbox.cpp b/app/widget/colorwheel/colorpreviewbox.cpp index 550dfae1e..c2b0c3f89 100644 --- a/app/widget/colorwheel/colorpreviewbox.cpp +++ b/app/widget/colorwheel/colorpreviewbox.cpp @@ -60,10 +60,19 @@ void ColorPreviewBox::paintEvent(QPaintEvent *e) QPainter p(this); + QRect draw_rect = rect().adjusted(0, 0, -1, -1); + p.setPen(Qt::black); + + if (color_.alpha() < 1.0) { + // Draw black background so the background isn't the window color + p.setBrush(Qt::black); + p.drawRect(draw_rect); + } + + // Draw with color over the top p.setBrush(c); - - p.drawRect(rect().adjusted(0, 0, -1, -1)); + p.drawRect(draw_rect); } } diff --git a/app/widget/pixelsampler/pixelsampler.cpp b/app/widget/pixelsampler/pixelsampler.cpp index bdbbe2125..7011466f3 100644 --- a/app/widget/pixelsampler/pixelsampler.cpp +++ b/app/widget/pixelsampler/pixelsampler.cpp @@ -27,7 +27,13 @@ namespace olive { PixelSamplerWidget::PixelSamplerWidget(QWidget *parent) : QGroupBox(parent) { - QVBoxLayout* layout = new QVBoxLayout(this); + QHBoxLayout* layout = new QHBoxLayout(this); + + box_ = new ColorPreviewBox(); + QFontMetrics fm = fontMetrics(); + int box_sz = fm.height() * 2; + box_->setFixedSize(box_sz, box_sz); + layout->addWidget(box_); label_ = new QLabel(); layout->addWidget(label_); @@ -45,6 +51,8 @@ void PixelSamplerWidget::SetValues(const Color &color) void PixelSamplerWidget::UpdateLabelInternal() { + box_->SetColor(color_); + label_->setText(tr("" "R: %1
" "G: %2
" diff --git a/app/widget/pixelsampler/pixelsampler.h b/app/widget/pixelsampler/pixelsampler.h index ad8b399fe..bcbe60e23 100644 --- a/app/widget/pixelsampler/pixelsampler.h +++ b/app/widget/pixelsampler/pixelsampler.h @@ -26,6 +26,7 @@ #include #include "render/color.h" +#include "widget/colorwheel/colorpreviewbox.h" namespace olive { @@ -43,6 +44,8 @@ private: Color color_; + ColorPreviewBox *box_; + QLabel* label_; }; diff --git a/app/widget/scope/scopebase/scopebase.cpp b/app/widget/scope/scopebase/scopebase.cpp index 7540795e9..2e6875872 100644 --- a/app/widget/scope/scopebase/scopebase.cpp +++ b/app/widget/scope/scopebase/scopebase.cpp @@ -28,23 +28,22 @@ namespace olive { ScopeBase::ScopeBase(QWidget* parent) : super(parent), - buffer_(nullptr) + texture_(nullptr), + managed_tex_up_to_date_(false) { EnableDefaultContextMenu(); } -void ScopeBase::SetBuffer(Frame *frame) +void ScopeBase::SetBuffer(TexturePtr frame) { - buffer_ = frame; - - UploadTextureFromBuffer(); + texture_ = frame; + managed_tex_up_to_date_ = false; + update(); } void ScopeBase::showEvent(QShowEvent* e) { super::showEvent(e); - - UploadTextureFromBuffer(); } void ScopeBase::DrawScope(TexturePtr managed_tex, QVariant pipeline) @@ -58,32 +57,6 @@ void ScopeBase::DrawScope(TexturePtr managed_tex, QVariant pipeline) VideoParams::kInternalChannelCount)); } -void ScopeBase::UploadTextureFromBuffer() -{ - if (!isVisible()) { - return; - } - - if (buffer_) { - makeCurrent(); - - if (!texture_ || texture_->params() != buffer_->video_params()) { - texture_ = nullptr; - managed_tex_ = nullptr; - - texture_ = renderer()->CreateTexture(buffer_->video_params(), - buffer_->data(), buffer_->linesize_pixels()); - managed_tex_ = renderer()->CreateTexture(buffer_->video_params()); - } else { - texture_->Upload(buffer_->data(), buffer_->linesize_pixels()); - } - - doneCurrent(); - } - - update(); -} - void ScopeBase::OnInit() { super::OnInit(); @@ -96,13 +69,13 @@ void ScopeBase::OnPaint() // Clear display surface renderer()->ClearDestination(); - if (buffer_) { + if (texture_) { // Convert reference frame to display space - if (!texture_ || !managed_tex_) { - UploadTextureFromBuffer(); - makeCurrent(); // UploadTextureFromBuffer calls "doneCurrent", so we re-call "makeCurrent" + if (!managed_tex_ || !managed_tex_up_to_date_ + || managed_tex_->params() != texture_->params()) { + managed_tex_ = renderer()->CreateTexture(texture_->params()); + renderer()->BlitColorManaged(color_service(), texture_, true, managed_tex_.get()); } - renderer()->BlitColorManaged(color_service(), texture_, true, managed_tex_.get()); DrawScope(managed_tex_, pipeline_); } diff --git a/app/widget/scope/scopebase/scopebase.h b/app/widget/scope/scopebase/scopebase.h index 072633151..a8891a384 100644 --- a/app/widget/scope/scopebase/scopebase.h +++ b/app/widget/scope/scopebase/scopebase.h @@ -35,7 +35,7 @@ public: MANAGEDDISPLAYWIDGET_DEFAULT_DESTRUCTOR(ScopeBase) public slots: - void SetBuffer(Frame* frame); + void SetBuffer(TexturePtr frame); protected slots: virtual void OnInit() override; @@ -57,15 +57,13 @@ protected: virtual void DrawScope(TexturePtr managed_tex, QVariant pipeline); private: - void UploadTextureFromBuffer(); - QVariant pipeline_; TexturePtr texture_; TexturePtr managed_tex_; - Frame* buffer_; + bool managed_tex_up_to_date_; }; diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 7e8c31d97..b5e8be846 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -56,8 +56,6 @@ ViewerWidget::ViewerWidget(QWidget *parent) : color_menu_enabled_(true), time_changed_from_timer_(false), prequeuing_(false), - last_loaded_buffer_(nullptr), - last_loaded_buffer_is_empty_(false), active_queue_jobs_(0), cache_time_(rational::NaN) { @@ -83,6 +81,7 @@ ViewerWidget::ViewerWidget(QWidget *parent) : connect(display_widget_, &ViewerDisplayWidget::DragEntered, this, &ViewerWidget::DragEntered); connect(display_widget_, &ViewerDisplayWidget::Dropped, this, &ViewerWidget::Dropped); connect(display_widget_, &ViewerDisplayWidget::VisibilityChanged, this, &ViewerWidget::Pause); + connect(display_widget_, &ViewerDisplayWidget::TextureChanged, this, &ViewerWidget::TextureChanged); connect(sizer_, &ViewerSizer::RequestScale, display_widget_, &ViewerDisplayWidget::SetMatrixZoom); connect(sizer_, &ViewerSizer::RequestTranslate, display_widget_, &ViewerDisplayWidget::SetMatrixTranslate); connect(display_widget_, &ViewerDisplayWidget::HandDragMoved, sizer_, &ViewerSizer::HandDragMove); @@ -249,7 +248,7 @@ void ViewerWidget::DisconnectNodeEvent(ViewerOutput *n) disconnect(n->audio_playback_cache(), &AudioPlaybackCache::Validated, this, &ViewerWidget::AudioCacheValidated); disconnect(n, &ViewerOutput::TextureInputChanged, this, &ViewerWidget::UpdateStack); - SetDisplayImage(nullptr); + SetDisplayImage(QVariant()); ruler()->SetPlaybackCache(nullptr); @@ -351,7 +350,7 @@ void ViewerWidget::SetFullScreen(QScreen *screen) vw->display_widget()->SetDeinterlacing(vw->display_widget()->IsDeinterlacing()); } - vw->display_widget()->SetImage(last_loaded_buffer_); + vw->display_widget()->SetImage(QVariant::fromValue(display_widget()->GetCurrentTexture())); windows_.insert(screen, vw); } @@ -420,33 +419,10 @@ bool ViewerWidget::ShouldForceWaveform() const void ViewerWidget::SetEmptyImage() { - FramePtr frame = nullptr; + display_widget()->SetBlank(); - if (GetConnectedNode()) { - frame = last_loaded_buffer_; - - if (!frame) { - frame = Frame::Create(); - } - - if (frame->video_params() != GetConnectedNode()->GetVideoParams()) { - frame->destroy(); - frame->set_video_params(GetConnectedNode()->GetVideoParams()); - } - - if (!frame->is_allocated()) { - frame->allocate(); - } - - if (!last_loaded_buffer_is_empty_) { - memset(frame->data(), 0, frame->allocated_size()); - } - } - - SetDisplayImage(frame); - - if (frame) { - last_loaded_buffer_is_empty_ = true; + foreach (ViewerWindow *vw, windows_) { + vw->display_widget()->SetBlank(); } } @@ -554,6 +530,7 @@ void ViewerWidget::UpdateTextureFromNode() } else { // Not playing, run a task to get the frame either from the cache or the renderer RenderTicketWatcher* watcher = new RenderTicketWatcher(); + watcher->setProperty("time", QVariant::fromValue(time)); connect(watcher, &RenderTicketWatcher::Finished, this, &ViewerWidget::RendererGeneratedFrame); nonqueue_watchers_.append(watcher); watcher->SetTicket(GetFrame(time, true)); @@ -723,7 +700,7 @@ bool ViewerWidget::ViewerMightBeAStill() return GetConnectedNode() && GetConnectedNode()->GetConnectedTextureOutput().IsValid() && GetConnectedNode()->GetVideoLength().isNull(); } -void ViewerWidget::SetDisplayImage(FramePtr frame, bool main_only) +void ViewerWidget::SetDisplayImage(QVariant frame, bool main_only) { display_widget_->SetImage(frame); @@ -732,10 +709,6 @@ void ViewerWidget::SetDisplayImage(FramePtr frame, bool main_only) vw->display_widget()->SetImage(frame); } } - - last_loaded_buffer_ = frame; - last_loaded_buffer_is_empty_ = false; - emit LoadedBuffer(frame.get()); } void ViewerWidget::RequestNextFrameForQueue(bool prioritize, bool increment) @@ -749,6 +722,7 @@ void ViewerWidget::RequestNextFrameForQueue(bool prioritize, bool increment) } RenderTicketWatcher* watcher = new RenderTicketWatcher(); + watcher->setProperty("time", QVariant::fromValue(next_time)); connect(watcher, &RenderTicketWatcher::Finished, this, &ViewerWidget::RendererGeneratedFrameForQueue); watcher->SetTicket(GetFrame(next_time, prioritize)); active_queue_jobs_++; @@ -886,8 +860,6 @@ void ViewerWidget::RendererGeneratedFrame() RenderTicketWatcher* ticket = static_cast(sender()); if (ticket->HasResult()) { - FramePtr frame = ticket->Get().value(); - if (nonqueue_watchers_.contains(ticket)) { while (!nonqueue_watchers_.isEmpty()) { if (nonqueue_watchers_.takeFirst() == ticket) { @@ -895,7 +867,7 @@ void ViewerWidget::RendererGeneratedFrame() } } - SetDisplayImage(frame); + SetDisplayImage(ticket->Get()); } } @@ -907,14 +879,16 @@ void ViewerWidget::RendererGeneratedFrameForQueue() RenderTicketWatcher* watcher = static_cast(sender()); if (watcher->HasResult()) { - FramePtr frame = watcher->Get().value(); + QVariant frame = watcher->Get(); // Ignore this signal if we've paused now if (IsPlaying() || prequeuing_) { - playback_queue_.AppendTimewise({frame->timestamp(), frame}, playback_speed_); + rational ts = watcher->property("time").value(); + + playback_queue_.AppendTimewise({ts, frame}, playback_speed_); foreach (ViewerWindow* window, windows_) { - window->queue()->AppendTimewise({frame->timestamp(), frame}, playback_speed_); + window->queue()->AppendTimewise({ts, frame}, playback_speed_); } if (prequeuing_ && int(playback_queue_.size()) == prequeue_length_) { diff --git a/app/widget/viewer/viewer.h b/app/widget/viewer/viewer.h index 41b956816..8b0f93a1a 100644 --- a/app/widget/viewer/viewer.h +++ b/app/widget/viewer/viewer.h @@ -128,7 +128,7 @@ signals: /** * @brief Signal emitted when a new frame is loaded */ - void LoadedBuffer(Frame* load_buffer); + void TextureChanged(TexturePtr t); /** * @brief Request a scope panel @@ -185,7 +185,7 @@ private: bool ViewerMightBeAStill(); - void SetDisplayImage(FramePtr frame, bool main_only = false); + void SetDisplayImage(QVariant frame, bool main_only = false); void RequestNextFrameForQueue(bool prioritize = false, bool increment = true); @@ -251,9 +251,6 @@ private: QTimer audio_restart_timer_; - FramePtr last_loaded_buffer_; - bool last_loaded_buffer_is_empty_; - int active_queue_jobs_; rational cache_time_; diff --git a/app/widget/viewer/viewerdisplay.cpp b/app/widget/viewer/viewerdisplay.cpp index f959e7edc..46ab98c55 100644 --- a/app/widget/viewer/viewerdisplay.cpp +++ b/app/widget/viewer/viewerdisplay.cpp @@ -45,13 +45,12 @@ ViewerDisplayWidget::ViewerDisplayWidget(QWidget *parent) : signal_cursor_color_(false), gizmos_(nullptr), gizmo_click_(false), - last_loaded_buffer_(nullptr), hand_dragging_(false), deinterlace_(false), show_fps_(false), frames_skipped_(0), show_widget_background_(false), - texture_equal_to_frame_(false) + push_mode_(kPushNull) { connect(Core::instance(), &Core::ToolChanged, this, &ViewerDisplayWidget::UpdateCursor); @@ -100,17 +99,26 @@ void ViewerDisplayWidget::SetSignalCursorColorEnabled(bool e) inner_widget()->setMouseTracking(e); } -void ViewerDisplayWidget::SetImage(FramePtr in_buffer) +void ViewerDisplayWidget::SetImage(const QVariant &buffer) { - if (last_loaded_buffer_ != in_buffer) { - last_loaded_buffer_ = in_buffer; + load_frame_ = buffer; - texture_equal_to_frame_ = false; + if (load_frame_.isNull()) { + push_mode_ = kPushNull; + } else { + push_mode_ = kPushFrame; } update(); } +void ViewerDisplayWidget::SetBlank() +{ + push_mode_ = kPushBlank; + + update(); +} + void ViewerDisplayWidget::SetDeinterlacing(bool e) { deinterlace_ = e; @@ -318,51 +326,73 @@ void ViewerDisplayWidget::OnPaint() { // Clear background to empty QColor bg_color = show_widget_background_ ? palette().window().color() : Qt::black; - renderer()->ClearDestination(bg_color.redF(), bg_color.greenF(), bg_color.blueF()); + renderer()->ClearDestination(nullptr, bg_color.redF(), bg_color.greenF(), bg_color.blueF()); // We only draw if we have a pipeline - if (last_loaded_buffer_ && color_service()) { - if (!texture_ - || texture_->width() != last_loaded_buffer_->width() - || texture_->height() != last_loaded_buffer_->height() - || texture_->format() != last_loaded_buffer_->format() - || texture_->channel_count() != last_loaded_buffer_->channel_count()) { - texture_ = renderer()->CreateTexture(last_loaded_buffer_->video_params(), last_loaded_buffer_->data(), last_loaded_buffer_->linesize_pixels()); - } else if (!texture_equal_to_frame_) { - texture_->Upload(last_loaded_buffer_->data(), last_loaded_buffer_->linesize_pixels()); - } - texture_equal_to_frame_ = true; - - TexturePtr texture_to_draw = texture_; - - if (deinterlace_) { - if (deinterlace_shader_.isNull()) { - deinterlace_shader_ = renderer()->CreateNativeShader(ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/deinterlace.frag")))); - } - - if (!deinterlace_texture_ - || deinterlace_texture_->params() != texture_to_draw->params()) { - // (Re)create texture - deinterlace_texture_ = renderer()->CreateTexture(texture_to_draw->params()); - } - - ShaderJob job; - job.InsertValue(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, QVector2D(texture_to_draw->width(), texture_to_draw->height()))); - job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(texture_to_draw))); - - renderer()->BlitToTexture(deinterlace_shader_, job, deinterlace_texture_.get()); - - texture_to_draw = deinterlace_texture_; - } - + if (push_mode_ != kPushNull) { // Draw texture through color transform int device_width = width() * devicePixelRatioF(); int device_height = height() * devicePixelRatioF(); VideoParams::Format device_format = static_cast(Config::Current()["OfflinePixelFormat"].toInt()); VideoParams device_params(device_width, device_height, device_format, VideoParams::kInternalChannelCount); - renderer()->BlitColorManaged(color_service(), texture_to_draw, true, device_params, false, - combined_matrix_flipped_, crop_matrix_); + if (push_mode_ == kPushBlank) { + if (blank_shader_.isNull()) { + blank_shader_ = renderer()->CreateNativeShader(ShaderCode()); + } + + ShaderJob job; + job.InsertValue(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, combined_matrix_flipped_)); + job.InsertValue(QStringLiteral("ove_cropmatrix"), NodeValue(NodeValue::kMatrix, crop_matrix_)); + + renderer()->Blit(blank_shader_, job, device_params, false); + } else if (color_service()) { + if (FramePtr frame = load_frame_.value()) { + // This is a CPU frame, upload it now + if (!texture_ + || texture_->renderer() != renderer() // Some implementations don't like it if we upload to a texture created in another (albeit shared) context + || texture_->width() != frame->width() + || texture_->height() != frame->height() + || texture_->format() != frame->format() + || texture_->channel_count() != frame->channel_count()) { + texture_ = renderer()->CreateTexture(frame->video_params(), frame->data(), frame->linesize_pixels()); + } else { + texture_->Upload(frame->data(), frame->linesize_pixels()); + } + } else if (TexturePtr texture = load_frame_.value()) { + // This is a GPU texture, switch to it directly + texture_ = texture; + } + + emit TextureChanged(texture_); + + push_mode_ = kPushUnnecessary; + + TexturePtr texture_to_draw = texture_; + + if (deinterlace_) { + if (deinterlace_shader_.isNull()) { + deinterlace_shader_ = renderer()->CreateNativeShader(ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/deinterlace.frag")))); + } + + if (!deinterlace_texture_ + || deinterlace_texture_->params() != texture_to_draw->params()) { + // (Re)create texture + deinterlace_texture_ = renderer()->CreateTexture(texture_to_draw->params()); + } + + ShaderJob job; + job.InsertValue(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, QVector2D(texture_to_draw->width(), texture_to_draw->height()))); + job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(texture_to_draw))); + + renderer()->BlitToTexture(deinterlace_shader_, job, deinterlace_texture_.get()); + + texture_to_draw = deinterlace_texture_; + } + + renderer()->BlitColorManaged(color_service(), texture_to_draw, true, device_params, false, + combined_matrix_flipped_, crop_matrix_); + } } // Draw gizmos if we have any @@ -452,12 +482,20 @@ void ViewerDisplayWidget::OnPaint() void ViewerDisplayWidget::OnDestroy() { + renderer()->DestroyNativeShader(deinterlace_shader_); deinterlace_shader_.clear(); + renderer()->DestroyNativeShader(blank_shader_); + blank_shader_.clear(); super::OnDestroy(); texture_ = nullptr; deinterlace_texture_ = nullptr; + if (load_frame_.isNull()) { + push_mode_ = kPushNull; + } else { + push_mode_ = kPushFrame; + } } QPointF ViewerDisplayWidget::GetTexturePosition(const QPoint &screen_pos) @@ -542,12 +580,11 @@ void ViewerDisplayWidget::EmitColorAtCursor(QMouseEvent *e) if (signal_cursor_color_) { Color reference, display; - if (last_loaded_buffer_) { + if (texture_) { QPointF pixel_pos = GenerateGizmoTransform().inverted().map(e->pos()); + pixel_pos /= texture_->params().divider(); - pixel_pos /= last_loaded_buffer_->video_params().divider(); - - reference = last_loaded_buffer_->get_pixel(qRound(pixel_pos.x()), qRound(pixel_pos.y())); + reference = renderer()->GetPixelFromTexture(texture_.get(), pixel_pos); display = color_service()->ConvertColor(reference); } diff --git a/app/widget/viewer/viewerdisplay.h b/app/widget/viewer/viewerdisplay.h index fca062853..4ebf91c0d 100644 --- a/app/widget/viewer/viewerdisplay.h +++ b/app/widget/viewer/viewerdisplay.h @@ -102,6 +102,11 @@ public: fps_timer_update_count_++; } + TexturePtr GetCurrentTexture() const + { + return texture_; + } + public slots: /** * @brief Set the transformation matrix to draw with @@ -126,13 +131,9 @@ public slots: */ void SetSignalCursorColorEnabled(bool e); - /** - * @brief Overrides the image with the load buffer of another ViewerGLWidget - * - * If there are multiple ViewerGLWidgets showing the same thing, this is faster than decoding the image from file - * each time. - */ - void SetImage(FramePtr in_buffer); + void SetImage(const QVariant &buffer); + + void SetBlank(); /** * @brief Changes the pointer type if the tool is changed to the hand tool. Otherwise resets the pointer to it's @@ -181,6 +182,8 @@ signals: void VisibilityChanged(bool visible); + void TextureChanged(TexturePtr texture); + protected: /** * @brief Override the mouse press event for the DragStarted() signal and gizmos @@ -249,6 +252,11 @@ private: */ QVariant deinterlace_shader_; + /** + * @brief Blank shader + */ + QVariant blank_shader_; + /** * @brief Translation only matrix (defaults to identity). */ @@ -283,8 +291,6 @@ private: rational time_; - FramePtr last_loaded_buffer_; - /** * @brief Position of mouse to calculate delta from. */ @@ -304,7 +310,23 @@ private: bool show_widget_background_; - bool texture_equal_to_frame_; + QVariant load_frame_; + + enum PushMode { + /// New frame to push to internal texture + kPushFrame, + + /// Internal texture reference is up to date, keep showing it + kPushUnnecessary, + + /// Draw blank/black screen + kPushBlank, + + /// Draw nothing (not even a black frame) + kPushNull, + }; + + PushMode push_mode_; private slots: void EmitColorAtCursor(QMouseEvent* e); diff --git a/app/widget/viewer/viewerqueue.h b/app/widget/viewer/viewerqueue.h index c247290cf..d3aa65843 100644 --- a/app/widget/viewer/viewerqueue.h +++ b/app/widget/viewer/viewerqueue.h @@ -29,7 +29,7 @@ namespace olive { struct ViewerPlaybackFrame { rational timestamp; - FramePtr frame; + QVariant frame; }; class ViewerQueue : public std::list {