From ab31df369dd784a0daa1c0686bd45318a362fc13 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 26 May 2021 23:05:54 +1000 Subject: [PATCH 1/3] packaging: improved nsis installer - Enabled Hi-DPI - Enabled Unicode --- .github/workflows/ci.yml | 6 +++--- app/packaging/windows/nsis/olive.nsi | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dd2190d82..6164c80c1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -257,9 +257,9 @@ jobs: #$DOWNLOAD_TOOL http://web.archive.org/web/20210226132532/http://download.microsoft.com/download/3/2/2/3224B87F-CFA0-4E70-BDA3-3DE650EFEBA5/vcredist_x64.exe cp $(cygpath $GITHUB_WORKSPACE)/app/packaging/windows/nsis/* . cp $(cygpath $GITHUB_WORKSPACE)/LICENSE . - $DOWNLOAD_TOOL https://nsis.sourceforge.io/mediawiki/images/c/c7/ShellExecAsUser.zip - 7z x ShellExecAsUser.zip ShellExecAsUser.dll - makensis -V4 -DX64 "-XOutFile $PKGNAME.exe" "-X!AddPluginDir $(pwd -W)" olive.nsi + $DOWNLOAD_TOOL https://nsis.sourceforge.io/mediawiki/images/6/68/ShellExecAsUser_amd64-Unicode.7z + 7z e ShellExecAsUser_amd64-Unicode.7z Plugins/x86-unicode/ShellExecAsUser.dll + makensis -V4 -DX64 "-XOutFile $PKGNAME.exe" "-X!AddPluginDir /x86-unicode $(pwd -W)" olive.nsi # Create Portable ZIP echo -n > olive-editor/portable diff --git a/app/packaging/windows/nsis/olive.nsi b/app/packaging/windows/nsis/olive.nsi index 550ecdf4b..11e67f3c7 100644 --- a/app/packaging/windows/nsis/olive.nsi +++ b/app/packaging/windows/nsis/olive.nsi @@ -1,4 +1,4 @@ -!include "MUI.nsh" +!include "MUI2.nsh" !define MUI_ICON "install icon.ico" !define MUI_UNICON "uninstall icon.ico" @@ -12,6 +12,8 @@ SetCompressor lzma Name ${APP_NAME} +ManifestDPIAware true +Unicode true !ifdef X64 InstallDir "$PROGRAMFILES64\${APP_NAME}" From 3592982b16be6274dfee9d57f334eb59c1fdf14d Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 29 May 2021 20:02:16 +1000 Subject: [PATCH 2/3] timeline: fixed dodgy code on gap insert command Also added test for this command to help ensure correct behavior. --- app/widget/timelinewidget/timelineundo.h | 30 +++++-- tests/timeline/timeline-tests.cpp | 108 +++++++++++++++++++++++ 2 files changed, 130 insertions(+), 8 deletions(-) diff --git a/app/widget/timelinewidget/timelineundo.h b/app/widget/timelinewidget/timelineundo.h index 8c472a658..3e38eb34d 100644 --- a/app/widget/timelinewidget/timelineundo.h +++ b/app/widget/timelinewidget/timelineundo.h @@ -463,7 +463,6 @@ public: Q_ASSERT(i == 0 || time > times_.at(i-1)); QVector splits(blocks_.size()); - commands_.resize(blocks_.size()); for (int j=0;jredo(); splits.replace(j, split_command->new_block()); - commands_.replace(j, split_command); + commands_.append(split_command); } else { splits.replace(j, nullptr); } @@ -2045,8 +2044,8 @@ public: } foreach (auto add_gap, gaps_added_) { - add_gap.gap->setParent(add_gap.before->parent()); - add_gap.before->track()->InsertBlockAfter(add_gap.gap, add_gap.before); + add_gap.gap->setParent(add_gap.track->parent()); + add_gap.track->InsertBlockAfter(add_gap.gap, add_gap.before); } foreach (Track* track, working_tracks_) { @@ -2121,6 +2120,7 @@ private: QVector blocks_to_split; QVector blocks_to_append_gap_to; + QVector tracks_to_append_gap_to; foreach (Track* track, working_tracks_) { foreach (Block* b, track->Blocks()) { @@ -2129,11 +2129,24 @@ private: gaps_to_extend_.append(b); break; } else if (dynamic_cast(b) && b->out() >= point_) { - if (b->out() > point_) { + bool append_gap = true; + + if (b->in() == point_) { + // The only reason we should be here is if this block is at the start of the track, + // in which case no split needs to occur + b = nullptr; + } else if (b->out() > point_) { + // Block must be split as well as having a gap appended to it blocks_to_split.append(b); + } else if (!b->next()) { + // At the end of a track, no gap needs to be added at all + append_gap = false; } - blocks_to_append_gap_to.append(b); + if (append_gap) { + tracks_to_append_gap_to.append(track); + blocks_to_append_gap_to.append(b); + } break; } } @@ -2143,11 +2156,11 @@ private: split_command_ = new BlockSplitPreservingLinksCommand(blocks_to_split, {point_}); } - foreach (Block* block, blocks_to_append_gap_to) { + for (int i=0; iset_length_and_media_out(length_); gap->setParent(&memory_manager_); - gaps_added_.append({gap, block}); + gaps_added_.append({gap, blocks_to_append_gap_to.at(i), tracks_to_append_gap_to.at(i)}); } } @@ -2166,6 +2179,7 @@ private: struct AddGap { GapBlock* gap; Block* before; + Track* track; }; QVector gaps_added_; diff --git a/tests/timeline/timeline-tests.cpp b/tests/timeline/timeline-tests.cpp index 7c68284f2..529168e30 100644 --- a/tests/timeline/timeline-tests.cpp +++ b/tests/timeline/timeline-tests.cpp @@ -488,4 +488,112 @@ OLIVE_ADD_TEST(ReplaceBlockWithGap_ClipsAndTransitions) OLIVE_TEST_END; } +OLIVE_ADD_TEST(InsertGaps_SingleTrack) +{ + TIMELINE_TEST_START; + + sequence.add_default_nodes(); + + TrackList *list = sequence.track_list(Track::kVideo); + Track *track = list->GetTracks().first(); + + ClipBlock *a = new ClipBlock(); + a->setParent(&project); + track->AppendBlock(a); + + ClipBlock *b = new ClipBlock(); + b->setParent(&project); + track->AppendBlock(b); + + ClipBlock *c = new ClipBlock(); + c->setParent(&project); + track->AppendBlock(c); + + OLIVE_ASSERT(track->Blocks().size() == 3); + OLIVE_ASSERT(track->Blocks().at(0) == a); + OLIVE_ASSERT(track->Blocks().at(1) == b); + OLIVE_ASSERT(track->Blocks().at(2) == c); + + { + // Insert gap at the start of the track, all blocks should be unsplit and shifted to the right + TrackListInsertGaps command(list, 0, 2); + command.redo(); + + OLIVE_ASSERT(track->Blocks().size() == 4); + OLIVE_ASSERT(dynamic_cast(track->Blocks().at(0))); + OLIVE_ASSERT(track->Blocks().at(0)->length() == 2); + OLIVE_ASSERT(track->Blocks().at(1) == a); + OLIVE_ASSERT(track->Blocks().at(2) == b); + OLIVE_ASSERT(track->Blocks().at(3) == c); + + command.undo(); + + OLIVE_ASSERT(track->Blocks().size() == 3); + OLIVE_ASSERT(track->Blocks().at(0) == a); + OLIVE_ASSERT(track->Blocks().at(1) == b); + OLIVE_ASSERT(track->Blocks().at(2) == c); + } + + { + // Insert gap in the middle of block A, block A should be halved with a copy at 2 and the gap at 1 + TrackListInsertGaps command(list, rational(1, 2), 2); + command.redo(); + + OLIVE_ASSERT(track->Blocks().size() == 5); + OLIVE_ASSERT(track->Blocks().at(0) == a); + OLIVE_ASSERT(track->Blocks().at(0)->length() == rational(1, 2)); + OLIVE_ASSERT(dynamic_cast(track->Blocks().at(1))); + OLIVE_ASSERT(dynamic_cast(track->Blocks().at(2))); + OLIVE_ASSERT(track->Blocks().at(3) == b); + OLIVE_ASSERT(track->Blocks().at(4) == c); + + command.undo(); + + OLIVE_ASSERT(track->Blocks().size() == 3); + OLIVE_ASSERT(track->Blocks().at(0) == a); + OLIVE_ASSERT(track->Blocks().at(0)->length() = 1); + OLIVE_ASSERT(track->Blocks().at(1) == b); + OLIVE_ASSERT(track->Blocks().at(2) == c); + } + + { + // Insert gap between block A and B, blocks should be unsplit with a gap at 1 + TrackListInsertGaps command(list, 1, 2); + command.redo(); + + OLIVE_ASSERT(track->Blocks().size() == 4); + OLIVE_ASSERT(track->Blocks().at(0) == a); + OLIVE_ASSERT(dynamic_cast(track->Blocks().at(1))); + OLIVE_ASSERT(track->Blocks().at(2) == b); + OLIVE_ASSERT(track->Blocks().at(3) == c); + + command.undo(); + + OLIVE_ASSERT(track->Blocks().size() == 3); + OLIVE_ASSERT(track->Blocks().at(0) == a); + OLIVE_ASSERT(track->Blocks().at(1) == b); + OLIVE_ASSERT(track->Blocks().at(2) == c); + } + + { + // Insert gap at end, nothing should be added + TrackListInsertGaps command(list, 3, 2); + command.redo(); + + OLIVE_ASSERT(track->Blocks().size() == 3); + OLIVE_ASSERT(track->Blocks().at(0) == a); + OLIVE_ASSERT(track->Blocks().at(1) == b); + OLIVE_ASSERT(track->Blocks().at(2) == c); + + command.undo(); + + OLIVE_ASSERT(track->Blocks().size() == 3); + OLIVE_ASSERT(track->Blocks().at(0) == a); + OLIVE_ASSERT(track->Blocks().at(1) == b); + OLIVE_ASSERT(track->Blocks().at(2) == c); + } + + OLIVE_TEST_END; +} + } From ae3393f03141ec0f179523670950e2483b8d5c33 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 29 May 2021 20:02:45 +1000 Subject: [PATCH 3/3] opengl: use shared contexts and downgrade to ES 2.0 (BIG CHANGE) If you run into new OpenGL issues, this is likely the commit that caused it. I've tested this on as many systems as I have access to (including AMD, Apple, Intel, and NVIDIA GPUs with a mixture of Windows, Linux, and macOS) and it appears to work fine, however we all know how unpredictable GPUs can be. This should help increase GPU performance and compatibility. Squashed commit of the following: commit fa3c0ac7c469aebe2941a803736bdc06cdd5a2d8 Author: itsmattkc Date: Thu May 27 13:17:44 2021 +1000 viewer: updated pixel sampling for shared contexts commit 9334261da235f09cb67b193c5bb6169abd7a95f7 Author: itsmattkc Date: Thu May 27 13:12:39 2021 +1000 pixelsampler: show color preview box commit dacd9be6971c5c0312ae45c8db349d47910a80dd Author: itsmattkc Date: Thu May 27 12:43:32 2021 +1000 renderer: interlace on GPU commit 074cea06cb87e9fdc4e8ea6b09587be6df296057 Author: itsmattkc Date: Thu May 27 12:43:14 2021 +1000 renderer: added texture clear option commit 5d4f86b4cf1b1dcd3fb8e8c854ffa7f6fb8ca346 Author: itsmattkc Date: Thu May 27 12:42:12 2021 +1000 renderer: updated scopes for shared textures commit 6fad96358d189874cf45f7887b7e13cc2b1603c4 Author: itsmattkc Date: Thu May 27 11:42:05 2021 +1000 opengl: fixed edge case commit 1a19ffb53703ac6f30ed210f6d8eb5fc4b7b0f2d Author: itsmattkc Date: Thu May 27 10:56:13 2021 +1000 opengl: fix deinterlace shader commit 085961aa39cca90ebf51b919a78ef07a4882f0e1 Author: itsmattkc Date: Thu May 27 10:08:40 2021 +1000 opengl: downgrade to ES 2.0 With half-float/float/3D extensions, this should be doable commit ea1ab45cbcf5e017dfd0d8f28b231ff3255dfe92 Author: itsmattkc Date: Thu May 27 10:06:59 2021 +1000 renderer: fixed mutex deadlock commit ecc2f08b16f03421870c5b61625ecd26f2f96582 Author: itsmattkc Date: Wed May 26 21:28:13 2021 +1000 opengl: fixed shared contexts commit 261c689ea8089463155cf11b9ab15aa3f879f439 Merge: bfd4338a 91c88b1c Author: itsmattkc Date: Wed May 26 19:52:00 2021 +1000 Merge branch 'master' into shared-contexts commit bfd4338aaa3b09b6fbb7717c637face8d5490721 Author: itsmattkc Date: Wed May 26 18:52:38 2021 +1000 viewer: remove debug line commit 45868d8a396181bf81c24b92a1ac5ce9ffed3426 Merge: 642f8e9d d0bd5e21 Author: itsmattkc Date: Wed May 26 17:21:48 2021 +1000 Merge branch 'master' into shared-contexts commit 642f8e9df7d7d1a9ed4202d428f4d968ff668552 Merge: 861e5b89 4de0a727 Author: itsmattkc Date: Wed May 26 17:01:38 2021 +1000 Merge branch 'master' of https://github.com/olive-editor/olive into shared-contexts commit 861e5b89a38c281897dc44b20e3587d39c4f808c Merge: 472598a7 ef31c3fb Author: itsmattkc Date: Wed May 26 14:28:21 2021 +1000 Merge branch 'master' into shared-contexts commit 472598a740a095aa43a4aad5680dd5abcf453a4c Author: itsmattkc Date: Fri May 14 16:29:35 2021 +1000 attempt at sharing contexts --- app/main.cpp | 4 +- app/panel/scope/scope.cpp | 2 +- app/panel/scope/scope.h | 2 +- app/panel/viewer/viewerbase.cpp | 2 +- app/render/opengl/openglrenderer.cpp | 202 +++++++++++++++------- app/render/opengl/openglrenderer.h | 11 +- app/render/previewautocacher.cpp | 10 +- app/render/previewautocacher.h | 2 +- app/render/renderer.cpp | 62 +++++-- app/render/renderer.h | 13 +- app/render/rendererthreadwrapper.cpp | 20 ++- app/render/rendererthreadwrapper.h | 6 +- app/render/rendermanager.cpp | 8 +- app/render/rendermanager.h | 4 +- app/render/renderprocessor.cpp | 39 +++-- app/render/renderprocessor.h | 4 +- app/render/texture.h | 5 + app/shaders/alphaover.frag | 18 +- app/shaders/blur.frag | 10 +- app/shaders/crop.frag | 11 +- app/shaders/crossdissolve.frag | 10 +- app/shaders/default.frag | 9 +- app/shaders/default.vert | 8 +- app/shaders/deinterlace.frag | 11 +- app/shaders/diptoblack.frag | 16 +- app/shaders/interlace.frag | 15 ++ app/shaders/mosaic.frag | 9 +- app/shaders/polygon.frag | 8 +- app/shaders/rgbhistogram.frag | 12 +- app/shaders/rgbhistogram.vert | 6 +- app/shaders/rgbhistogram_secondary.frag | 10 +- app/shaders/rgbwaveform.frag | 10 +- app/shaders/rgbwaveform.vert | 6 +- app/shaders/solid.frag | 4 +- app/shaders/stroke.frag | 12 +- app/widget/colorwheel/colorpreviewbox.cpp | 13 +- app/widget/pixelsampler/pixelsampler.cpp | 10 +- app/widget/pixelsampler/pixelsampler.h | 3 + app/widget/scope/scopebase/scopebase.cpp | 49 ++---- app/widget/scope/scopebase/scopebase.h | 6 +- app/widget/viewer/viewer.cpp | 56 ++---- app/widget/viewer/viewer.h | 7 +- app/widget/viewer/viewerdisplay.cpp | 131 +++++++++----- app/widget/viewer/viewerdisplay.h | 42 +++-- app/widget/viewer/viewerqueue.h | 2 +- 45 files changed, 538 insertions(+), 362 deletions(-) create mode 100644 app/shaders/interlace.frag 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 {