From c7de93cc5deacd9241d07025e207c536f5592657 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 24 Apr 2020 15:14:20 +1000 Subject: [PATCH 01/10] viewer/opengl: minor code improvements --- .../backend/opengl/openglrenderfunctions.cpp | 36 +++++++++---------- app/render/backend/opengl/openglshader.cpp | 9 ++--- app/widget/viewer/viewerglwidget.cpp | 4 +-- 3 files changed, 23 insertions(+), 26 deletions(-) diff --git a/app/render/backend/opengl/openglrenderfunctions.cpp b/app/render/backend/opengl/openglrenderfunctions.cpp index 2e6458b10..2a3b538a9 100644 --- a/app/render/backend/opengl/openglrenderfunctions.cpp +++ b/app/render/backend/opengl/openglrenderfunctions.cpp @@ -37,23 +37,23 @@ const GLfloat blit_vertices[] = { }; const GLfloat blit_texcoords[] = { - 0.0, 0.0, - 1.0, 0.0, - 1.0, 1.0, + 0.0f, 0.0f, + 1.0f, 0.0f, + 1.0f, 1.0f, - 0.0, 0.0, - 0.0, 1.0, - 1.0, 1.0 + 0.0f, 0.0f, + 0.0f, 1.0f, + 1.0f, 1.0f }; const GLfloat flipped_blit_texcoords[] = { - 0.0, 1.0, - 1.0, 1.0, - 1.0, 0.0, + 0.0f, 1.0f, + 1.0f, 1.0f, + 1.0f, 0.0f, - 0.0, 1.0, - 0.0, 0.0, - 1.0, 0.0 + 0.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 0.0f }; /** @@ -65,7 +65,8 @@ const GLfloat flipped_blit_texcoords[] = { * * Currently active QOpenGLFunctions object (use context()->functions() if unsure). */ -void OpenGLRenderFunctions::PrepareToDraw(QOpenGLFunctions* f) { +void OpenGLRenderFunctions::PrepareToDraw(QOpenGLFunctions* f) +{ f->glGenerateMipmap(GL_TEXTURE_2D); f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); @@ -141,7 +142,6 @@ void OpenGLRenderFunctions::Blit(OpenGLShaderPtr pipeline, bool flipped, QMatrix void OpenGLRenderFunctions::Blit(OpenGLShader *pipeline, bool flipped, QMatrix4x4 matrix) { - // FIXME: is currentContext() reliable here? QOpenGLFunctions* func = QOpenGLContext::currentContext()->functions(); PrepareToDraw(func); @@ -153,13 +153,13 @@ void OpenGLRenderFunctions::Blit(OpenGLShader *pipeline, bool flipped, QMatrix4x QOpenGLBuffer m_vbo; m_vbo.create(); m_vbo.bind(); - m_vbo.allocate(blit_vertices, 18 * static_cast(sizeof(GLfloat))); + m_vbo.allocate(blit_vertices, 18 * sizeof(GLfloat)); m_vbo.release(); QOpenGLBuffer m_vbo2; m_vbo2.create(); m_vbo2.bind(); - m_vbo2.allocate(flipped ? flipped_blit_texcoords : blit_texcoords, 12 * static_cast(sizeof(GLfloat))); + m_vbo2.allocate(flipped ? flipped_blit_texcoords : blit_texcoords, 12 * sizeof(GLfloat)); m_vbo2.release(); pipeline->bind(); @@ -167,13 +167,13 @@ void OpenGLRenderFunctions::Blit(OpenGLShader *pipeline, bool flipped, QMatrix4x pipeline->setUniformValue("ove_mvpmat", matrix); pipeline->setUniformValue("ove_maintex", 0); - GLuint vertex_location = static_cast(pipeline->attributeLocation("a_position")); + int vertex_location = pipeline->attributeLocation("a_position"); m_vbo.bind(); func->glEnableVertexAttribArray(vertex_location); func->glVertexAttribPointer(vertex_location, 3, GL_FLOAT, GL_FALSE, 0, nullptr); m_vbo.release(); - GLuint tex_location = static_cast(pipeline->attributeLocation("a_texcoord")); + int tex_location = pipeline->attributeLocation("a_texcoord"); m_vbo2.bind(); func->glEnableVertexAttribArray(tex_location); func->glVertexAttribPointer(tex_location, 2, GL_FLOAT, GL_FALSE, 0, nullptr); diff --git a/app/render/backend/opengl/openglshader.cpp b/app/render/backend/opengl/openglshader.cpp index b5aa8dc8b..21ff57752 100644 --- a/app/render/backend/opengl/openglshader.cpp +++ b/app/render/backend/opengl/openglshader.cpp @@ -61,8 +61,8 @@ OpenGLShaderPtr OpenGLShader::CreateOCIO(QOpenGLContext* ctx, shaderDesc.setLut3DEdgeLen(OCIO_LUT3D_EDGE_SIZE); // Compute LUT - GLfloat* ocio_lut_data = new GLfloat[OCIO_NUM_3D_ENTRIES]; - processor->getGpuLut3D(ocio_lut_data, shaderDesc); + std::vector ocio_lut_data(OCIO_NUM_3D_ENTRIES); + processor->getGpuLut3D(&ocio_lut_data[0], shaderDesc); // Create LUT texture xf->glGenTextures(1, &lut_texture); @@ -81,10 +81,7 @@ OpenGLShaderPtr OpenGLShader::CreateOCIO(QOpenGLContext* ctx, // Allocate storage for texture xf->glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB16F, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, - 0, GL_RGB, GL_FLOAT, ocio_lut_data); - - // Delete local copy - delete [] ocio_lut_data; + 0, GL_RGB, GL_FLOAT, &ocio_lut_data[0]); // Create OCIO shader code QString shader_text; diff --git a/app/widget/viewer/viewerglwidget.cpp b/app/widget/viewer/viewerglwidget.cpp index a4cbe935f..5edd99ec1 100644 --- a/app/widget/viewer/viewerglwidget.cpp +++ b/app/widget/viewer/viewerglwidget.cpp @@ -266,7 +266,7 @@ void ViewerGLWidget::paintGL() framebuffer_.Attach(&managed_texture_); framebuffer_.Bind(); - context()->functions()->glViewport(0, 0, managed_texture_.width(), managed_texture_.height()); + f->glViewport(0, 0, managed_texture_.width(), managed_texture_.height()); } @@ -289,7 +289,7 @@ void ViewerGLWidget::paintGL() // Bind retrieved texture managed_texture_.Bind(); - context()->functions()->glViewport(0, 0, width(), height()); + f->glViewport(0, 0, width(), height()); OpenGLRenderFunctions::Blit(managed_copy_pipeline_); From acef99eb17cf35c975acb9509dcc1120aed2a2e4 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 25 Apr 2020 14:46:30 +1000 Subject: [PATCH 02/10] renamed viewerglwidget to viewerdisplaywidget Initially this class was named to provide the possibility of supporting other APIs, however this abstraction has been done with a base class instead. --- app/widget/viewer/CMakeLists.txt | 4 +- app/widget/viewer/footageviewer.cpp | 2 +- app/widget/viewer/viewer.cpp | 40 +++++++++---------- app/widget/viewer/viewer.h | 12 +++--- .../{viewerglwidget.cpp => viewerdisplay.cpp} | 40 +++++++++---------- .../{viewerglwidget.h => viewerdisplay.h} | 8 ++-- app/widget/viewer/viewerwindow.cpp | 4 +- app/widget/viewer/viewerwindow.h | 6 +-- 8 files changed, 58 insertions(+), 58 deletions(-) rename app/widget/viewer/{viewerglwidget.cpp => viewerdisplay.cpp} (88%) rename app/widget/viewer/{viewerglwidget.h => viewerdisplay.h} (97%) diff --git a/app/widget/viewer/CMakeLists.txt b/app/widget/viewer/CMakeLists.txt index 5b457a802..e96b9e639 100644 --- a/app/widget/viewer/CMakeLists.txt +++ b/app/widget/viewer/CMakeLists.txt @@ -24,8 +24,8 @@ set(OLIVE_SOURCES widget/viewer/pixelsamplerwidget.cpp widget/viewer/viewer.h widget/viewer/viewer.cpp - widget/viewer/viewerglwidget.h - widget/viewer/viewerglwidget.cpp + widget/viewer/viewerdisplay.h + widget/viewer/viewerdisplay.cpp widget/viewer/viewersafemargininfo.h widget/viewer/viewersizer.h widget/viewer/viewersizer.cpp diff --git a/app/widget/viewer/footageviewer.cpp b/app/widget/viewer/footageviewer.cpp index 08f1c4b71..3ee2569b0 100644 --- a/app/widget/viewer/footageviewer.cpp +++ b/app/widget/viewer/footageviewer.cpp @@ -35,7 +35,7 @@ FootageViewerWidget::FootageViewerWidget(QWidget *parent) : audio_node_ = new AudioInput(); viewer_node_ = new ViewerOutput(); - connect(main_gl_widget(), &ViewerGLWidget::DragStarted, this, &FootageViewerWidget::StartFootageDrag); + connect(main_gl_widget(), &ViewerDisplayWidget::DragStarted, this, &FootageViewerWidget::StartFootageDrag); controls_->SetAudioVideoDragButtonsVisible(true); connect(controls_, &PlaybackControls::VideoPressed, this, &FootageViewerWidget::StartVideoDrag); diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index e56ef59ac..f25e1084b 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -62,15 +62,15 @@ ViewerWidget::ViewerWidget(QWidget *parent) : sizer_ = new ViewerSizer(); stack_->addWidget(sizer_); - ViewerGLWidget* main_widget = new ViewerGLWidget(); - connect(main_widget, &ViewerGLWidget::customContextMenuRequested, this, &ViewerWidget::ShowContextMenu); - connect(main_widget, &ViewerGLWidget::CursorColor, this, &ViewerWidget::CursorColor); - connect(main_widget, &ViewerGLWidget::LoadedBuffer, this, &ViewerWidget::LoadedBuffer); - connect(main_widget, &ViewerGLWidget::LoadedTexture, this, &ViewerWidget::LoadedTexture); - connect(main_widget, &ViewerGLWidget::DrewManagedTexture, this, &ViewerWidget::DrewManagedTexture); - connect(main_widget, &ViewerGLWidget::ColorProcessorChanged, this, &ViewerWidget::ColorProcessorChanged); - connect(main_widget, &ViewerGLWidget::ColorManagerChanged, this, &ViewerWidget::ColorManagerChanged); - connect(sizer_, &ViewerSizer::RequestMatrix, main_widget, &ViewerGLWidget::SetMatrix); + ViewerDisplayWidget* main_widget = new ViewerDisplayWidget(); + connect(main_widget, &ViewerDisplayWidget::customContextMenuRequested, this, &ViewerWidget::ShowContextMenu); + connect(main_widget, &ViewerDisplayWidget::CursorColor, this, &ViewerWidget::CursorColor); + connect(main_widget, &ViewerDisplayWidget::LoadedBuffer, this, &ViewerWidget::LoadedBuffer); + connect(main_widget, &ViewerDisplayWidget::LoadedTexture, this, &ViewerWidget::LoadedTexture); + connect(main_widget, &ViewerDisplayWidget::DrewManagedTexture, this, &ViewerWidget::DrewManagedTexture); + connect(main_widget, &ViewerDisplayWidget::ColorProcessorChanged, this, &ViewerWidget::ColorProcessorChanged); + connect(main_widget, &ViewerDisplayWidget::ColorManagerChanged, this, &ViewerWidget::ColorManagerChanged); + connect(sizer_, &ViewerSizer::RequestMatrix, main_widget, &ViewerDisplayWidget::SetMatrix); sizer_->SetWidget(main_widget); gl_widgets_.append(main_widget); @@ -168,7 +168,7 @@ void ViewerWidget::ConnectNodeInternal(ViewerOutput *n) using_manager = nullptr; } - foreach (ViewerGLWidget* glw, gl_widgets_) { + foreach (ViewerDisplayWidget* glw, gl_widgets_) { glw->ConnectColorManager(using_manager); } @@ -200,7 +200,7 @@ void ViewerWidget::DisconnectNodeInternal(ViewerOutput *n) // Effectively disables the viewer and clears the state SizeChangedSlot(0, 0); - foreach (ViewerGLWidget* glw, gl_widgets_) { + foreach (ViewerDisplayWidget* glw, gl_widgets_) { glw->DisconnectColorManager(); } @@ -234,12 +234,12 @@ void ViewerWidget::resizeEvent(QResizeEvent *event) UpdateMinimumScale(); } -const QList &ViewerWidget::gl_widgets() const +const QList &ViewerWidget::gl_widgets() const { return gl_widgets_; } -ViewerGLWidget *ViewerWidget::main_gl_widget() const +ViewerDisplayWidget *ViewerWidget::main_gl_widget() const { return gl_widgets_.first(); } @@ -280,7 +280,7 @@ void ViewerWidget::SetOverrideSize(int width, int height) void ViewerWidget::SetMatrix(const QMatrix4x4 &mat) { - foreach (ViewerGLWidget* glw, gl_widgets_) { + foreach (ViewerDisplayWidget* glw, gl_widgets_) { glw->SetMatrix(mat); } } @@ -309,7 +309,7 @@ void ViewerWidget::SetFullScreen(QScreen *screen) vw->gl_widget()->ConnectColorManager(main_gl_widget()->color_manager()); main_gl_widget()->ConnectSibling(vw->gl_widget()); connect(vw, &ViewerWindow::destroyed, this, &ViewerWidget::WindowAboutToClose); - connect(vw->gl_widget(), &ViewerGLWidget::customContextMenuRequested, this, &ViewerWidget::ShowContextMenu); + connect(vw->gl_widget(), &ViewerDisplayWidget::customContextMenuRequested, this, &ViewerWidget::ShowContextMenu); if (GetConnectedNode()) { vw->SetResolution(GetConnectedNode()->video_params().width(), GetConnectedNode()->video_params().height()); @@ -373,7 +373,7 @@ void ViewerWidget::PlayInternal(int speed, bool in_to_out_only) controls_->ShowPauseButton(); if (stack_->currentWidget() == sizer_) { - connect(main_gl_widget(), &ViewerGLWidget::frameSwapped, this, &ViewerWidget::PlaybackTimerUpdate); + connect(main_gl_widget(), &ViewerDisplayWidget::frameSwapped, this, &ViewerWidget::PlaybackTimerUpdate); } else { connect(AudioManager::instance(), &AudioManager::OutputNotified, this, &ViewerWidget::PlaybackTimerUpdate); } @@ -427,7 +427,7 @@ void ViewerWidget::UpdateMinimumScale() } } -void ViewerWidget::SetColorTransform(const ColorTransform &transform, ViewerGLWidget *sender) +void ViewerWidget::SetColorTransform(const ColorTransform &transform, ViewerDisplayWidget *sender) { sender->SetColorTransform(transform); } @@ -543,7 +543,7 @@ void ViewerWidget::ShowContextMenu(const QPoint &pos) { Menu menu(static_cast(sender())); - context_menu_widget_ = static_cast(sender()); + context_menu_widget_ = static_cast(sender()); // Color options if (context_menu_widget_->color_manager() && color_menu_enabled_) { @@ -683,7 +683,7 @@ void ViewerWidget::Pause() controls_->ShowPlayButton(); if (stack_->currentWidget() == sizer_) { - disconnect(main_gl_widget(), &ViewerGLWidget::frameSwapped, this, &ViewerWidget::PlaybackTimerUpdate); + disconnect(main_gl_widget(), &ViewerDisplayWidget::frameSwapped, this, &ViewerWidget::PlaybackTimerUpdate); } else { disconnect(AudioManager::instance(), &AudioManager::OutputNotified, this, &ViewerWidget::PlaybackTimerUpdate); } @@ -736,7 +736,7 @@ void ViewerWidget::SetColorTransform(const ColorTransform &transform) void ViewerWidget::SetSignalCursorColorEnabled(bool e) { - foreach (ViewerGLWidget* glw, gl_widgets_) { + foreach (ViewerDisplayWidget* glw, gl_widgets_) { glw->SetSignalCursorColorEnabled(e); } } diff --git a/app/widget/viewer/viewer.h b/app/widget/viewer/viewer.h index c1fbf6481..ec638238a 100644 --- a/app/widget/viewer/viewer.h +++ b/app/widget/viewer/viewer.h @@ -35,7 +35,7 @@ #include "render/backend/opengl/openglbackend.h" #include "render/backend/opengl/opengltexture.h" #include "render/backend/audio/audiobackend.h" -#include "viewerglwidget.h" +#include "viewerdisplay.h" #include "viewersizer.h" #include "viewerwindow.h" #include "widget/playbackcontrols/playbackcontrols.h" @@ -167,8 +167,8 @@ protected: PlaybackControls* controls_; - const QList &gl_widgets() const; - ViewerGLWidget* main_gl_widget() const; + const QList &gl_widgets() const; + ViewerDisplayWidget* main_gl_widget() const; private: void UpdateTimeInternal(int64_t i); @@ -183,7 +183,7 @@ private: void UpdateMinimumScale(); - void SetColorTransform(const ColorTransform& transform, ViewerGLWidget* sender); + void SetColorTransform(const ColorTransform& transform, ViewerDisplayWidget* sender); QStackedWidget* stack_; @@ -212,9 +212,9 @@ private: QList windows_; - QList gl_widgets_; + QList gl_widgets_; - ViewerGLWidget* context_menu_widget_; + ViewerDisplayWidget* context_menu_widget_; private slots: void PlaybackTimerUpdate(); diff --git a/app/widget/viewer/viewerglwidget.cpp b/app/widget/viewer/viewerdisplay.cpp similarity index 88% rename from app/widget/viewer/viewerglwidget.cpp rename to app/widget/viewer/viewerdisplay.cpp index 5edd99ec1..640539a61 100644 --- a/app/widget/viewer/viewerglwidget.cpp +++ b/app/widget/viewer/viewerdisplay.cpp @@ -18,7 +18,7 @@ ***/ -#include "viewerglwidget.h" +#include "viewerdisplay.h" #include #include @@ -37,10 +37,10 @@ OLIVE_NAMESPACE_ENTER #ifdef Q_OS_LINUX -bool ViewerGLWidget::nouveau_check_done_ = false; +bool ViewerDisplayWidget::nouveau_check_done_ = false; #endif -ViewerGLWidget::ViewerGLWidget(QWidget *parent) : +ViewerDisplayWidget::ViewerDisplayWidget(QWidget *parent) : ManagedDisplayWidget(parent), managed_copy_pipeline_(nullptr), has_image_(false), @@ -49,18 +49,18 @@ ViewerGLWidget::ViewerGLWidget(QWidget *parent) : { } -ViewerGLWidget::~ViewerGLWidget() +ViewerDisplayWidget::~ViewerDisplayWidget() { ContextCleanup(); } -void ViewerGLWidget::SetMatrix(const QMatrix4x4 &mat) +void ViewerDisplayWidget::SetMatrix(const QMatrix4x4 &mat) { matrix_ = mat; update(); } -void ViewerGLWidget::SetImage(const QString &fn) +void ViewerDisplayWidget::SetImage(const QString &fn) { has_image_ = false; @@ -119,13 +119,13 @@ void ViewerGLWidget::SetImage(const QString &fn) } } -void ViewerGLWidget::SetSignalCursorColorEnabled(bool e) +void ViewerDisplayWidget::SetSignalCursorColorEnabled(bool e) { signal_cursor_color_ = e; setMouseTracking(e); } -void ViewerGLWidget::SetImageFromLoadBuffer(Frame *in_buffer) +void ViewerDisplayWidget::SetImageFromLoadBuffer(Frame *in_buffer) { has_image_ = in_buffer; @@ -147,7 +147,7 @@ void ViewerGLWidget::SetImageFromLoadBuffer(Frame *in_buffer) update(); } -void ViewerGLWidget::SetEmitDrewManagedTextureEnabled(bool e) +void ViewerDisplayWidget::SetEmitDrewManagedTextureEnabled(bool e) { enable_display_referred_signal_ = e; @@ -159,32 +159,32 @@ void ViewerGLWidget::SetEmitDrewManagedTextureEnabled(bool e) } } -void ViewerGLWidget::ConnectSibling(ViewerGLWidget *sibling) +void ViewerDisplayWidget::ConnectSibling(ViewerDisplayWidget *sibling) { - connect(this, &ViewerGLWidget::LoadedBuffer, sibling, &ViewerGLWidget::SetImageFromLoadBuffer, Qt::QueuedConnection); + connect(this, &ViewerDisplayWidget::LoadedBuffer, sibling, &ViewerDisplayWidget::SetImageFromLoadBuffer, Qt::QueuedConnection); sibling->SetImageFromLoadBuffer(&load_buffer_); } -const ViewerSafeMarginInfo &ViewerGLWidget::GetSafeMargin() const +const ViewerSafeMarginInfo &ViewerDisplayWidget::GetSafeMargin() const { return safe_margin_; } -void ViewerGLWidget::SetSafeMargins(const ViewerSafeMarginInfo &safe_margin) +void ViewerDisplayWidget::SetSafeMargins(const ViewerSafeMarginInfo &safe_margin) { safe_margin_ = safe_margin; update(); } -void ViewerGLWidget::mousePressEvent(QMouseEvent *event) +void ViewerDisplayWidget::mousePressEvent(QMouseEvent *event) { QOpenGLWidget::mousePressEvent(event); emit DragStarted(); } -void ViewerGLWidget::mouseMoveEvent(QMouseEvent *event) +void ViewerDisplayWidget::mouseMoveEvent(QMouseEvent *event) { QOpenGLWidget::mouseMoveEvent(event); @@ -209,11 +209,11 @@ void ViewerGLWidget::mouseMoveEvent(QMouseEvent *event) } } -void ViewerGLWidget::initializeGL() +void ViewerDisplayWidget::initializeGL() { ManagedDisplayWidget::initializeGL(); - connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &ViewerGLWidget::ContextCleanup, Qt::DirectConnection); + connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &ViewerDisplayWidget::ContextCleanup, Qt::DirectConnection); #ifdef Q_OS_LINUX if (!nouveau_check_done_) { @@ -231,7 +231,7 @@ void ViewerGLWidget::initializeGL() #endif } -void ViewerGLWidget::paintGL() +void ViewerDisplayWidget::paintGL() { // Get functions attached to this context (they will already be initialized) QOpenGLFunctions* f = context()->functions(); @@ -333,7 +333,7 @@ void ViewerGLWidget::paintGL() } #ifdef Q_OS_LINUX -void ViewerGLWidget::ShowNouveauWarning() +void ViewerDisplayWidget::ShowNouveauWarning() { QMessageBox::warning(this, tr("Driver Warning"), @@ -344,7 +344,7 @@ void ViewerGLWidget::ShowNouveauWarning() } #endif -void ViewerGLWidget::ContextCleanup() +void ViewerDisplayWidget::ContextCleanup() { makeCurrent(); diff --git a/app/widget/viewer/viewerglwidget.h b/app/widget/viewer/viewerdisplay.h similarity index 97% rename from app/widget/viewer/viewerglwidget.h rename to app/widget/viewer/viewerdisplay.h index 496456405..6289433a4 100644 --- a/app/widget/viewer/viewerglwidget.h +++ b/app/widget/viewer/viewerdisplay.h @@ -49,7 +49,7 @@ OLIVE_NAMESPACE_ENTER * the same texture object, use SetTexture() since it will nearly always be faster to just set it than to check *and* * set it. */ -class ViewerGLWidget : public ManagedDisplayWidget +class ViewerDisplayWidget : public ManagedDisplayWidget { Q_OBJECT public: @@ -60,9 +60,9 @@ public: * * QWidget parent. */ - ViewerGLWidget(QWidget* parent = nullptr); + ViewerDisplayWidget(QWidget* parent = nullptr); - virtual ~ViewerGLWidget() override; + virtual ~ViewerDisplayWidget() override; /** * @brief Set an image to load and display on screen @@ -71,7 +71,7 @@ public: const QMatrix4x4& GetMatrix(); - void ConnectSibling(ViewerGLWidget* sibling); + void ConnectSibling(ViewerDisplayWidget* sibling); const ViewerSafeMarginInfo& GetSafeMargin() const; void SetSafeMargins(const ViewerSafeMarginInfo& safe_margin); diff --git a/app/widget/viewer/viewerwindow.cpp b/app/widget/viewer/viewerwindow.cpp index f05961e4a..3f92f997d 100644 --- a/app/widget/viewer/viewerwindow.cpp +++ b/app/widget/viewer/viewerwindow.cpp @@ -32,11 +32,11 @@ ViewerWindow::ViewerWindow(QWidget *parent) : layout->setMargin(0); layout->setSpacing(0); - gl_widget_ = new ViewerGLWidget(); + gl_widget_ = new ViewerDisplayWidget(); layout->addWidget(gl_widget_); } -ViewerGLWidget *ViewerWindow::gl_widget() const +ViewerDisplayWidget *ViewerWindow::gl_widget() const { return gl_widget_; } diff --git a/app/widget/viewer/viewerwindow.h b/app/widget/viewer/viewerwindow.h index 19c1676a4..8baa30c9c 100644 --- a/app/widget/viewer/viewerwindow.h +++ b/app/widget/viewer/viewerwindow.h @@ -23,7 +23,7 @@ #include -#include "viewerglwidget.h" +#include "viewerdisplay.h" OLIVE_NAMESPACE_ENTER @@ -32,7 +32,7 @@ class ViewerWindow : public QWidget public: ViewerWindow(QWidget* parent = nullptr); - ViewerGLWidget* gl_widget() const; + ViewerDisplayWidget* gl_widget() const; /** * @brief Used to adjust resulting picture to be the right aspect ratio @@ -45,7 +45,7 @@ protected: virtual void closeEvent(QCloseEvent* e) override; private: - ViewerGLWidget* gl_widget_; + ViewerDisplayWidget* gl_widget_; }; From 17999eab7a71eea93110a7c546b5da9835d489bc Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 25 Apr 2020 15:18:53 +1000 Subject: [PATCH 03/10] timelineview: removed "end item" This was written as a hack to force our QGraphicsScenes to larger sizes. Turns out it was completely unnecessary and can be done better. --- app/widget/timelinewidget/view/CMakeLists.txt | 2 - .../timelinewidget/view/timelineview.cpp | 30 ++++++++- app/widget/timelinewidget/view/timelineview.h | 10 ++- .../timelinewidget/view/timelineviewbase.cpp | 57 +++++------------ .../timelinewidget/view/timelineviewbase.h | 7 ++- .../view/timelineviewenditem.cpp | 61 ------------------- .../timelinewidget/view/timelineviewenditem.h | 53 ---------------- 7 files changed, 52 insertions(+), 168 deletions(-) delete mode 100644 app/widget/timelinewidget/view/timelineviewenditem.cpp delete mode 100644 app/widget/timelinewidget/view/timelineviewenditem.h diff --git a/app/widget/timelinewidget/view/CMakeLists.txt b/app/widget/timelinewidget/view/CMakeLists.txt index f1aa3a9a1..5bbce0175 100644 --- a/app/widget/timelinewidget/view/CMakeLists.txt +++ b/app/widget/timelinewidget/view/CMakeLists.txt @@ -28,8 +28,6 @@ set(OLIVE_SOURCES widget/timelinewidget/view/timelineviewbase.cpp widget/timelinewidget/view/timelineviewblockitem.h widget/timelinewidget/view/timelineviewblockitem.cpp - widget/timelinewidget/view/timelineviewenditem.h - widget/timelinewidget/view/timelineviewenditem.cpp widget/timelinewidget/view/timelineviewghostitem.h widget/timelinewidget/view/timelineviewghostitem.cpp PARENT_SCOPE diff --git a/app/widget/timelinewidget/view/timelineview.cpp b/app/widget/timelinewidget/view/timelineview.cpp index 8c57c2469..0970cb2bf 100644 --- a/app/widget/timelinewidget/view/timelineview.cpp +++ b/app/widget/timelinewidget/view/timelineview.cpp @@ -230,6 +230,17 @@ void TimelineView::ToolChangedEvent(Tool::Item tool) } } +void TimelineView::SceneRectUpdateEvent(QRectF &rect) +{ + if (alignment() & Qt::AlignTop) { + rect.setTop(0); + rect.setBottom(GetHeightOfAllTracks() + height() / 2); + } else if (alignment() & Qt::AlignBottom) { + rect.setBottom(0); + rect.setTop(GetHeightOfAllTracks() - height() / 2); + } +} + Timeline::TrackType TimelineView::ConnectedTrackType() { if (connected_track_list_) { @@ -279,7 +290,20 @@ TimelineViewMouseEvent TimelineView::CreateMouseEvent(const QPoint& pos, Qt::Key return timeline_event; } -int TimelineView::GetTrackY(int track_index) +int TimelineView::GetHeightOfAllTracks() const +{ + if (connected_track_list_) { + if (alignment() & Qt::AlignTop) { + return GetTrackY(connected_track_list_->TrackCount()); + } else { + return GetTrackY(connected_track_list_->TrackCount() - 1); + } + } else { + return 0; + } +} + +int TimelineView::GetTrackY(int track_index) const { int y = 0; @@ -301,7 +325,7 @@ int TimelineView::GetTrackY(int track_index) return y; } -int TimelineView::GetTrackHeight(int track_index) +int TimelineView::GetTrackHeight(int track_index) const { if (!connected_track_list_ || track_index >= connected_track_list_->TrackCount()) { return TrackOutput::GetDefaultTrackHeight(); @@ -310,7 +334,7 @@ int TimelineView::GetTrackHeight(int track_index) return connected_track_list_->TrackAt(track_index)->GetTrackHeight(); } -QPoint TimelineView::GetScrollCoordinates() +QPoint TimelineView::GetScrollCoordinates() const { return QPoint(horizontalScrollBar()->value(), verticalScrollBar()->value()); } diff --git a/app/widget/timelinewidget/view/timelineview.h b/app/widget/timelinewidget/view/timelineview.h index bb1e7e06e..ecf66c2b7 100644 --- a/app/widget/timelinewidget/view/timelineview.h +++ b/app/widget/timelinewidget/view/timelineview.h @@ -53,10 +53,10 @@ public: void DeselectAll(); - int GetTrackY(int track_index); - int GetTrackHeight(int track_index); + int GetTrackY(int track_index) const; + int GetTrackHeight(int track_index) const; - QPoint GetScrollCoordinates(); + QPoint GetScrollCoordinates() const; void SetScrollCoordinates(const QPoint& pt); void ConnectTrackList(TrackList* list); @@ -91,6 +91,8 @@ protected: virtual void ToolChangedEvent(Tool::Item tool) override; + virtual void SceneRectUpdateEvent(QRectF& rect) override; + private: Timeline::TrackType ConnectedTrackType(); Stream::Type TrackTypeToStreamType(Timeline::TrackType track_type); @@ -100,6 +102,8 @@ private: TimelineViewMouseEvent CreateMouseEvent(const QPoint &pos, Qt::KeyboardModifiers modifiers); + int GetHeightOfAllTracks() const; + int SceneToTrack(double y); void UserSetTime(const int64_t& time); diff --git a/app/widget/timelinewidget/view/timelineviewbase.cpp b/app/widget/timelinewidget/view/timelineviewbase.cpp index f10108831..31f53b586 100644 --- a/app/widget/timelinewidget/view/timelineviewbase.cpp +++ b/app/widget/timelinewidget/view/timelineviewbase.cpp @@ -44,10 +44,6 @@ TimelineViewBase::TimelineViewBase(QWidget *parent) : { setScene(&scene_); - // Create end item - end_item_ = new TimelineViewEndItem(); - scene_.addItem(end_item_); - // Set default scale SetScale(1.0); @@ -223,54 +219,28 @@ qreal TimelineViewBase::GetPlayheadX() void TimelineViewBase::SetEndTime(const rational &length) { - end_item_->SetEndTime(length); + end_time_ = length; + + UpdateSceneRect(); } void TimelineViewBase::UpdateSceneRect() { QRectF bounding_rect = scene_.itemsBoundingRect(); - if (limit_y_axis_) { - // Make a gap of half the viewport height - if (alignment() & Qt::AlignBottom) { - bounding_rect.setTop(bounding_rect.top() - height()/2); - } else { - bounding_rect.setBottom(bounding_rect.bottom() + height()/2); - } - - // Ensure the scene height is always AT LEAST the height of the view - // The scrollbar appears to have a 1px margin on the top and bottom, hence the -2 - int minimum_height = height() - horizontalScrollBar()->height() - 2; - - if (alignment() & Qt::AlignBottom) { - // Ensure the scene left and bottom are always 0 - bounding_rect.setBottomLeft(QPointF(0, 0)); - - if (bounding_rect.top() > minimum_height) { - bounding_rect.setTop(-minimum_height); - } - } else { - // Ensure the scene left and top are always 0 - bounding_rect.setTopLeft(QPointF(0, 0)); - - if (bounding_rect.height() < minimum_height) { - bounding_rect.setHeight(minimum_height); - } - } - } else { - // We'll still limit the X to 0 since that behavior is desired by all TimelineViewBase derivatives - bounding_rect.setLeft(0); - } + // There's no need for a timeline to ever go below 0 on the X scale + bounding_rect.setLeft(0); // Ensure the scene is always the full length of the timeline with a gap at the end to work with - end_item_->SetEndPadding(width()/2); + bounding_rect.setRight(TimeToScene(end_time_) + width() / 2); + + // Any further rect processing from derivatives can be done here + SceneRectUpdateEvent(bounding_rect); // If the scene is already this rect, do nothing - if (scene_.sceneRect() == bounding_rect) { - return; + if (scene_.sceneRect() != bounding_rect) { + scene_.setSceneRect(bounding_rect); } - - scene_.setSceneRect(bounding_rect); } void TimelineViewBase::PageScrollToPlayhead() @@ -299,9 +269,10 @@ void TimelineViewBase::ScaleChangedEvent(const double &scale) { TimelineScaledObject::ScaleChangedEvent(scale); - end_item_->SetScale(scale); + // Update scene rect + UpdateSceneRect(); - // Force redraw for playhead + // Force redraw for playhead if the above function didn't do it viewport()->update(); } diff --git a/app/widget/timelinewidget/view/timelineviewbase.h b/app/widget/timelinewidget/view/timelineviewbase.h index ccd1d0b68..c64718bb6 100644 --- a/app/widget/timelinewidget/view/timelineviewbase.h +++ b/app/widget/timelinewidget/view/timelineviewbase.h @@ -25,7 +25,6 @@ #include "core.h" #include "timelineplayhead.h" -#include "timelineviewenditem.h" #include "widget/timelinewidget/timelinescaledobject.h" OLIVE_NAMESPACE_ENTER @@ -57,6 +56,8 @@ protected: virtual void ScaleChangedEvent(const double& scale) override; + virtual void SceneRectUpdateEvent(QRectF&){} + bool HandleZoomFromScroll(QWheelEvent* event); bool WheelEventIsAZoomEvent(QWheelEvent* event); @@ -95,14 +96,14 @@ private: bool dragging_hand_; DragMode pre_hand_drag_mode_; - TimelineViewEndItem* end_item_; - QGraphicsScene scene_; bool limit_y_axis_; DragMode default_drag_mode_; + rational end_time_; + private slots: /** * @brief Slot called whenever the view resizes or the scene contents change to enforce minimum scene sizes diff --git a/app/widget/timelinewidget/view/timelineviewenditem.cpp b/app/widget/timelinewidget/view/timelineviewenditem.cpp deleted file mode 100644 index 767a2a008..000000000 --- a/app/widget/timelinewidget/view/timelineviewenditem.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#include "timelineviewenditem.h" - -OLIVE_NAMESPACE_ENTER - -TimelineViewEndItem::TimelineViewEndItem(QGraphicsItem *parent) : - TimelineViewRect(parent), - end_padding_(0) -{ -} - -void TimelineViewEndItem::SetEndTime(const rational &time) -{ - end_time_ = time; - - UpdateRect(); -} - -void TimelineViewEndItem::SetEndPadding(int padding) -{ - end_padding_ = padding; - - UpdateRect(); -} - -void TimelineViewEndItem::UpdateRect() -{ - // Doesn't need to be more than one pixel - setRect(0, 0, 1, 1); - - setPos(TimeToScene(end_time_) + end_padding_, 0); -} - -void TimelineViewEndItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) -{ - // Item is invisible, this is a no-op - Q_UNUSED(painter) - Q_UNUSED(option) - Q_UNUSED(widget) -} - -OLIVE_NAMESPACE_EXIT diff --git a/app/widget/timelinewidget/view/timelineviewenditem.h b/app/widget/timelinewidget/view/timelineviewenditem.h deleted file mode 100644 index e3f758af4..000000000 --- a/app/widget/timelinewidget/view/timelineviewenditem.h +++ /dev/null @@ -1,53 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#ifndef TIMELINEVIEWENDITEM_H -#define TIMELINEVIEWENDITEM_H - -#include "timelineviewrect.h" - -OLIVE_NAMESPACE_ENTER - -/** - * @brief An item placed at the end point of the Timeline to ensure the correct scene size - */ -class TimelineViewEndItem : public TimelineViewRect -{ -public: - TimelineViewEndItem(QGraphicsItem* parent = nullptr); - - void SetEndTime(const rational& time); - - void SetEndPadding(int padding); - - virtual void UpdateRect() override; - -protected: - virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; - -private: - rational end_time_; - - int end_padding_; -}; - -OLIVE_NAMESPACE_EXIT - -#endif // TIMELINEVIEWENDITEM_H From 53ae25a7cbf55c357a3b652331e3971d892b92d5 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 26 Apr 2020 04:16:15 +1000 Subject: [PATCH 04/10] nodes: optimize node graph changes by only updating from inputs that changed A huge optimization that ensures only the parts of a node graph that have changed get pushed to the renderer. For thread-safety, the node graph is copied elsewhere so that users can make changes asynchronously and the graph can update when its threads are ready. Up until now, if an input value changed, every node's values would be re-copied, or worse, if a connection was changed, the entire graph would be recopied. This has been negligible in testing since we've been largely testing with small graphs, but for massive projects, it's important that this be as optimized as possible. --- app/dialog/export/export.cpp | 2 +- app/node/block/block.cpp | 6 +- app/node/block/block.h | 2 +- app/node/block/clip/clip.cpp | 10 +- app/node/block/clip/clip.h | 2 +- app/node/input.cpp | 52 +++- app/node/input.h | 12 +- app/node/input/media/media.cpp | 4 +- app/node/inputarray.cpp | 6 +- app/node/inputarray.h | 6 +- app/node/node.cpp | 193 +++++---------- app/node/node.h | 29 +-- app/node/output/track/track.cpp | 30 ++- app/node/output/track/track.h | 4 +- app/node/output/track/tracklist.cpp | 8 +- app/node/output/viewer/viewer.cpp | 27 +- app/node/output/viewer/viewer.h | 17 +- app/render/backend/audio/audiobackend.cpp | 14 +- app/render/backend/audio/audiobackend.h | 4 - app/render/backend/audiorenderbackend.cpp | 20 +- app/render/backend/audiorenderbackend.h | 6 - app/render/backend/exporter.cpp | 6 +- app/render/backend/opengl/openglbackend.cpp | 9 - app/render/backend/opengl/openglbackend.h | 4 - app/render/backend/renderbackend.cpp | 230 ++++++++++-------- app/render/backend/renderbackend.h | 26 +- app/render/backend/videorenderbackend.cpp | 2 - app/render/backend/videorenderworker.cpp | 5 +- app/widget/keyframeview/keyframeviewbase.cpp | 4 +- .../nodeparamviewwidgetbridge.cpp | 6 +- .../nodeparamview/nodeparamviewwidgetbridge.h | 2 +- app/widget/timelinewidget/undo/undo.cpp | 6 +- app/widget/viewer/viewer.cpp | 26 +- app/widget/viewer/viewer.h | 2 +- 34 files changed, 360 insertions(+), 422 deletions(-) diff --git a/app/dialog/export/export.cpp b/app/dialog/export/export.cpp index e079b9296..81b7f845b 100644 --- a/app/dialog/export/export.cpp +++ b/app/dialog/export/export.cpp @@ -229,7 +229,7 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) : // Update renderer // FIXME: This is going to be VERY slow since it will need to hash every single frame. It would be better to have a // the renderer save the map as some sort of file that this can load. - preview_viewer_->video_renderer()->InvalidateCache(TimeRange(0, viewer_node_->Length())); + preview_viewer_->video_renderer()->InvalidateCache(TimeRange(0, viewer_node_->Length()), nullptr); progress_timer_.setInterval(1000); connect(&progress_timer_, &QTimer::timeout, this, &ExportDialog::UpdateTimeLabels); diff --git a/app/node/block/block.cpp b/app/node/block/block.cpp index ad314a946..ccb3f0140 100644 --- a/app/node/block/block.cpp +++ b/app/node/block/block.cpp @@ -40,7 +40,7 @@ Block::Block() : length_input_->SetConnectable(false); length_input_->set_is_keyframable(false); AddInput(length_input_); - connect(length_input_, SIGNAL(ValueChanged(const rational&, const rational&)), this, SLOT(LengthInputChanged())); + connect(length_input_, &NodeInput::ValueChanged, this, &Block::LengthInputChanged); media_in_input_ = new NodeInput("media_in_in", NodeParam::kRational); media_in_input_->SetConnectable(false); @@ -343,14 +343,14 @@ NodeInput *Block::speed_input() const return speed_input_; } -void Block::InvalidateCache(const rational &start_range, const rational &end_range, NodeInput *from) +void Block::InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput *source) { // We ignore length changes since they don't have an effect on our frames if (from == length_input_) { return; } - Node::InvalidateCache(start_range, end_range, from); + Node::InvalidateCache(range, from, source); } OLIVE_NAMESPACE_EXIT diff --git a/app/node/block/block.h b/app/node/block/block.h index a0feb41a9..26df9d13a 100644 --- a/app/node/block/block.h +++ b/app/node/block/block.h @@ -97,7 +97,7 @@ public: NodeInput* media_in_input() const; NodeInput* speed_input() const; - virtual void InvalidateCache(const rational& start_range, const rational& end_range, NodeInput* from = nullptr) override; + virtual void InvalidateCache(const TimeRange& range, NodeInput* from, NodeInput* source) override; public slots: diff --git a/app/node/block/clip/clip.cpp b/app/node/block/clip/clip.cpp index ebd286171..8db5efdd5 100644 --- a/app/node/block/clip/clip.cpp +++ b/app/node/block/clip/clip.cpp @@ -59,12 +59,12 @@ NodeInput *ClipBlock::texture_input() const return texture_input_; } -void ClipBlock::InvalidateCache(const rational &start_range, const rational &end_range, NodeInput *from) +void ClipBlock::InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput *source) { // If signal is from texture input, transform all times from media time to sequence time if (from == texture_input_) { - rational start = MediaToSequenceTime(start_range); - rational end = MediaToSequenceTime(end_range); + rational start = MediaToSequenceTime(range.in()); + rational end = MediaToSequenceTime(range.out()); // Ensure range actually covers this clip's area if (!(end < in() || start > out())) { @@ -73,12 +73,12 @@ void ClipBlock::InvalidateCache(const rational &start_range, const rational &end start = qMax(start, in()); end = qMin(end, out()); - Node::InvalidateCache(start, end, from); + Node::InvalidateCache(TimeRange(start, end), from, source); } } else { // Otherwise, pass signal along normally - Node::InvalidateCache(start_range, end_range, from); + Node::InvalidateCache(range, from, source); } } diff --git a/app/node/block/clip/clip.h b/app/node/block/clip/clip.h index 6dc996b08..56f81c927 100644 --- a/app/node/block/clip/clip.h +++ b/app/node/block/clip/clip.h @@ -44,7 +44,7 @@ public: NodeInput* texture_input() const; - virtual void InvalidateCache(const rational &start_range, const rational &end_range, NodeInput *from = nullptr) override; + virtual void InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput* source) override; virtual TimeRange InputTimeAdjustment(NodeInput* input, const TimeRange& input_time) const override; diff --git a/app/node/input.cpp b/app/node/input.cpp index cdea0d1ed..e025f5ed7 100644 --- a/app/node/input.cpp +++ b/app/node/input.cpp @@ -60,7 +60,7 @@ NodeInput::NodeInput(const QString &id, const NodeParam::DataType &type) : Init(type); } -bool NodeInput::IsArray() +bool NodeInput::IsArray() const { return false; } @@ -273,7 +273,6 @@ void NodeInput::SaveConnections(QXmlStreamWriter *writer) const writer->writeEndElement(); // connections } - const NodeParam::DataType &NodeInput::data_type() const { return data_type_; @@ -363,6 +362,45 @@ QVariant NodeInput::StringToValue(const DataType& data_type, const QString &stri } } +void NodeInput::GetDependencies(QList &list, bool traverse, bool exclusive_only) const +{ + if (IsConnected() + && (get_connected_output()->edges().size() == 1 || !exclusive_only)) { + Node* connected = get_connected_node(); + + if (!list.contains(connected)) { + list.append(connected); + + if (traverse) { + QList connected_inputs = connected->GetInputsIncludingArrays(); + + foreach (NodeInput* i, connected_inputs) { + i->GetDependencies(list, traverse, exclusive_only); + } + } + } + } +} + +QList NodeInput::GetDependencies(bool traverse, bool exclusive_only) const +{ + QList list; + + GetDependencies(list, traverse, exclusive_only); + + return list; +} + +QList NodeInput::GetExclusiveDependencies() const +{ + return GetDependencies(true, true); +} + +QList NodeInput::GetImmediateDependencies() const +{ + return GetDependencies(false, false); +} + QVariant NodeInput::StringToValue(const QString &string, QList& footage_connections) { if (data_type_ == NodeParam::kFootage) { @@ -757,7 +795,7 @@ void NodeInput::KeyframeBezierInChanged() start = keyframe_tracks_.at(key->track()).at(keyframe_index - 1)->time(); } - emit ValueChanged(start, end); + emit ValueChanged(TimeRange(start, end)); } void NodeInput::KeyframeBezierOutChanged() @@ -772,7 +810,7 @@ void NodeInput::KeyframeBezierOutChanged() end = keyframe_tracks_.at(key->track()).at(keyframe_index + 1)->time(); } - emit ValueChanged(start, end); + emit ValueChanged(TimeRange(start, end)); } int NodeInput::FindIndexOfKeyframeFromRawPtr(NodeKeyframe *raw_ptr) const @@ -855,7 +893,7 @@ TimeRange NodeInput::get_range_around_index(int index, int track) const void NodeInput::emit_time_range(const TimeRange &range) { - emit ValueChanged(range.in(), range.out()); + emit ValueChanged(range); } void NodeInput::emit_range_affected_by_keyframe(NodeKeyframe *key) @@ -915,7 +953,7 @@ void NodeInput::set_standard_value(const QVariant &value, int track) if (is_using_standard_value(track)) { // If this standard value is being used, we need to send a value changed signal - emit ValueChanged(RATIONAL_MIN, RATIONAL_MAX); + emit ValueChanged(TimeRange(RATIONAL_MIN, RATIONAL_MAX)); } } @@ -964,7 +1002,7 @@ void NodeInput::CopyValues(NodeInput *source, NodeInput *dest, bool include_conn } } - emit dest->ValueChanged(RATIONAL_MIN, RATIONAL_MAX); + emit dest->ValueChanged(TimeRange(RATIONAL_MIN, RATIONAL_MAX)); } void NodeInput::set_property(const QString &key, const QVariant &value) diff --git a/app/node/input.h b/app/node/input.h index 6d1910623..c672907ee 100644 --- a/app/node/input.h +++ b/app/node/input.h @@ -49,7 +49,7 @@ public: NodeInput(const QString &id, const DataType& type, const QVariant& default_value); NodeInput(const QString &id, const DataType& type); - virtual bool IsArray(); + virtual bool IsArray() const; /** * @brief Returns kInput @@ -270,8 +270,16 @@ public: static QVariant StringToValue(const DataType &data_type, const QString &string); + void GetDependencies(QList& list, bool traverse, bool exclusive_only) const; + + QList GetDependencies(bool traverse = true, bool exclusive_only = false) const; + + QList GetExclusiveDependencies() const; + + QList GetImmediateDependencies() const; + signals: - void ValueChanged(const rational& start, const rational& end); + void ValueChanged(const TimeRange& range); void KeyframeEnableChanged(bool); diff --git a/app/node/input/media/media.cpp b/app/node/input/media/media.cpp index a69207ab6..ff371da53 100644 --- a/app/node/input/media/media.cpp +++ b/app/node/input/media/media.cpp @@ -31,7 +31,7 @@ MediaInput::MediaInput() : footage_input_ = new NodeInput("footage_in", NodeInput::kFootage); footage_input_->SetConnectable(false); footage_input_->set_is_keyframable(false); - connect(footage_input_, SIGNAL(ValueChanged(const rational&, const rational&)), this, SLOT(FootageChanged())); + connect(footage_input_, &NodeInput::ValueChanged, this, &MediaInput::FootageChanged); AddInput(footage_input_); } @@ -96,7 +96,7 @@ void MediaInput::FootageChanged() void MediaInput::FootageParametersChanged() { - InvalidateCache(0, RATIONAL_MAX, footage_input_); + InvalidateCache(TimeRange(0, RATIONAL_MAX), footage_input_, footage_input_); } OLIVE_NAMESPACE_EXIT diff --git a/app/node/inputarray.cpp b/app/node/inputarray.cpp index 4cea15ac2..42789b7d6 100644 --- a/app/node/inputarray.cpp +++ b/app/node/inputarray.cpp @@ -33,7 +33,7 @@ NodeInputArray::NodeInputArray(const QString &id, const DataType &type, const QV { } -bool NodeInputArray::IsArray() +bool NodeInputArray::IsArray() const { return true; } @@ -79,8 +79,8 @@ void NodeInputArray::SetSize(int size) sub_params_.replace(i, new_param); connect(new_param, &NodeInput::ValueChanged, this, &NodeInput::ValueChanged); - connect(new_param, &NodeInput::EdgeAdded, this, &NodeInput::EdgeAdded); - connect(new_param, &NodeInput::EdgeRemoved, this, &NodeInput::EdgeRemoved); + connect(new_param, &NodeInput::EdgeAdded, this, &NodeInputArray::SubParamEdgeAdded); + connect(new_param, &NodeInput::EdgeRemoved, this, &NodeInputArray::SubParamEdgeRemoved); } } diff --git a/app/node/inputarray.h b/app/node/inputarray.h index d43300e22..8a935f73e 100644 --- a/app/node/inputarray.h +++ b/app/node/inputarray.h @@ -31,7 +31,7 @@ class NodeInputArray : public NodeInput public: NodeInputArray(const QString &id, const DataType& type, const QVariant& default_value = 0); - virtual bool IsArray() override; + virtual bool IsArray() const override; int GetSize() const; @@ -54,6 +54,10 @@ public: signals: void SizeChanged(int size); + void SubParamEdgeAdded(NodeEdgePtr edge); + + void SubParamEdgeRemoved(NodeEdgePtr edge); + protected: virtual void LoadInternal(QXmlStreamReader* reader, XMLNodeData& xml_node_data, const QAtomicInt *cancelled) override; diff --git a/app/node/node.cpp b/app/node/node.cpp index ae681ecf1..ffde76468 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -75,7 +75,13 @@ void Node::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, const QAto continue; } - NodeParam* param = GetParameterWithID(param_id); + NodeParam* param; + + if (reader->name() == QStringLiteral("input")) { + param = GetInputWithID(param_id); + } else { + param = GetOutputWithID(param_id); + } if (!param) { qDebug() << "No parameter in" << id() << "with parameter" << param_id; @@ -153,21 +159,21 @@ NodeValueTable Node::Value(NodeValueDatabase &value) const return value.Merge(); } -void Node::InvalidateCache(const rational &start_range, const rational &end_range, NodeInput *from) +void Node::InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput *source) { Q_UNUSED(from) - SendInvalidateCache(start_range, end_range); + SendInvalidateCache(range, source); } -void Node::InvalidateVisible(NodeInput *from) +void Node::InvalidateVisible(NodeInput *from, NodeInput* source) { Q_UNUSED(from) foreach (NodeParam* param, params_) { if (param->type() == NodeParam::kOutput) { foreach (NodeEdgePtr edge, param->edges()) { - edge->input()->parentNode()->InvalidateVisible(edge->input()); + edge->input()->parentNode()->InvalidateVisible(edge->input(), source); } } } @@ -185,7 +191,7 @@ TimeRange Node::OutputTimeAdjustment(NodeInput *, const TimeRange &input_time) c return input_time; } -void Node::SendInvalidateCache(const rational &start_range, const rational &end_range) +void Node::SendInvalidateCache(const TimeRange &range, NodeInput *source) { // Loop through all parameters (there should be no children that are not NodeParams) foreach (NodeParam* param, params_) { @@ -199,25 +205,7 @@ void Node::SendInvalidateCache(const rational &start_range, const rational &end_ Node* connected_node = connected_input->parentNode(); // Send clear cache signal to the Node - connected_node->InvalidateCache(start_range, end_range, connected_input); - } - } - } -} - -void Node::DependentEdgeChanged(NodeInput *from) -{ - Q_UNUSED(from) - - foreach (NodeParam* p, params_) { - if (p->type() == NodeParam::kOutput && p->IsConnected()) { - NodeOutput* out = static_cast(p); - - foreach (NodeEdgePtr edge, out->edges()) { - NodeInput* connected_input = edge->input(); - Node* connected_node = connected_input->parentNode(); - - connected_node->DependentEdgeChanged(connected_input); + connected_node->InvalidateCache(range, connected_input, source); } } } @@ -300,57 +288,6 @@ void Node::CopyInputs(Node *source, Node *destination, bool include_connections) } } -void DuplicateConnectionsBetweenListsInternal(const QList &source, const QList &destination, NodeInput* source_input, NodeInput* dest_input) -{ - if (source_input->IsConnected()) { - // Get this input's connected outputs - NodeOutput* source_output = source_input->get_connected_output(); - Node* source_output_node = source_output->parentNode(); - - // Find equivalent in destination list - Node* dest_output_node = destination.at(source.indexOf(source_output_node)); - - Q_ASSERT(dest_output_node->id() == source_output_node->id()); - - NodeOutput* dest_output = static_cast(dest_output_node->GetParameterWithID(source_output->id())); - - NodeParam::ConnectEdge(dest_output, dest_input); - } - - // If inputs are arrays, duplicate their connections too - if (source_input->IsArray()) { - NodeInputArray* source_array = static_cast(source_input); - NodeInputArray* dest_array = static_cast(dest_input); - - for (int i=0;iGetSize();i++) { - DuplicateConnectionsBetweenListsInternal(source, destination, source_array->At(i), dest_array->At(i)); - } - } -} - -void Node::DuplicateConnectionsBetweenLists(const QList &source, const QList &destination) -{ - Q_ASSERT(source.size() == destination.size()); - - for (int i=0;iid() == dest_input_node->id()); - - for (int j=0;jparams_.size();j++) { - NodeParam* source_param = source_input_node->params_.at(j); - - if (source_param->type() == NodeInput::kInput) { - NodeInput* source_input = static_cast(source_param); - NodeInput* dest_input = static_cast(dest_input_node->params_.at(j)); - - DuplicateConnectionsBetweenListsInternal(source, destination, source_input, dest_input); - } - } - } -} - bool Node::CanBeDeleted() const { return can_be_deleted_; @@ -381,29 +318,6 @@ int Node::IndexOfParameter(NodeParam *param) const return params_.indexOf(param); } -void Node::TraverseInputInternal(QList& list, NodeInput* input, bool traverse, bool exclusive_only) { - if (input->IsConnected() - && (input->get_connected_output()->edges().size() == 1 || !exclusive_only)) { - Node* connected = input->get_connected_node(); - - if (!list.contains(connected)) { - list.append(connected); - - if (traverse) { - GetDependenciesInternal(connected, list, traverse, exclusive_only); - } - } - } - - if (input->IsArray()) { - NodeInputArray* input_array = static_cast(input); - - for (int i=0;iGetSize();i++) { - TraverseInputInternal(list, input_array->At(i), traverse, exclusive_only); - } - } -} - /** * @brief Recursively collects dependencies of Node `n` and appends them to QList `list` * @@ -412,41 +326,30 @@ void Node::TraverseInputInternal(QList& list, NodeInput* input, bool trav * TRUE to recursively traverse each node for a complete dependency graph. FALSE to return only the immediate * dependencies. */ -void Node::GetDependenciesInternal(const Node* n, QList& list, bool traverse, bool exclusive_only) { - foreach (NodeParam* p, n->parameters()) { - if (p->type() == NodeParam::kInput) { - NodeInput* input = static_cast(p); +QList Node::GetDependenciesInternal(bool traverse, bool exclusive_only) const { + QList inputs = GetInputsIncludingArrays(); + QList list; - TraverseInputInternal(list, input, traverse, exclusive_only); - } + foreach (NodeInput* i, inputs) { + i->GetDependencies(list, traverse, exclusive_only); } + + return list; } QList Node::GetDependencies() const { - QList node_list; - - GetDependenciesInternal(this, node_list, true, false); - - return node_list; + return GetDependenciesInternal(true, false); } QList Node::GetExclusiveDependencies() const { - QList node_list; - - GetDependenciesInternal(this, node_list, true, true); - - return node_list; + return GetDependenciesInternal(true, true); } QList Node::GetImmediateDependencies() const { - QList node_list; - - GetDependenciesInternal(this, node_list, false, false); - - return node_list; + return GetDependenciesInternal(false, false); } Node::Capabilities Node::GetCapabilities(const NodeValueDatabase &) const @@ -479,7 +382,7 @@ NodeInput *Node::ShaderIterativeInput() const return nullptr; } -NodeInput* Node::ProcessesSamplesFrom(const NodeValueDatabase &value) const +NodeInput* Node::ProcessesSamplesFrom(const NodeValueDatabase &) const { return nullptr; } @@ -488,11 +391,25 @@ void Node::ProcessSamples(const NodeValueDatabase &, const AudioRenderingParams& { } -NodeParam *Node::GetParameterWithID(const QString &id) const +NodeInput *Node::GetInputWithID(const QString &id) const { - foreach (NodeParam* param, params_) { - if (param->id() == id) { - return param; + QList inputs = GetInputsIncludingArrays(); + + foreach (NodeInput* i, inputs) { + if (i->id() == id) { + return i; + } + } + + return nullptr; +} + +NodeOutput *Node::GetOutputWithID(const QString &id) const +{ + foreach (NodeParam* p, params_) { + if (p->type() == NodeParam::kOutput + && p->id() == id) { + return static_cast(p); } } @@ -665,25 +582,41 @@ void Node::ConnectInput(NodeInput *input) connect(input, &NodeInput::ValueChanged, this, &Node::InputChanged); connect(input, &NodeInput::EdgeAdded, this, &Node::InputConnectionChanged); connect(input, &NodeInput::EdgeRemoved, this, &Node::InputConnectionChanged); + + if (input->IsArray()) { + NodeInputArray* array = static_cast(input); + + connect(array, &NodeInputArray::SubParamEdgeAdded, this, &Node::InputConnectionChanged); + connect(array, &NodeInputArray::SubParamEdgeRemoved, this, &Node::InputConnectionChanged); + connect(array, &NodeInputArray::SubParamEdgeAdded, this, &Node::EdgeAdded); + connect(array, &NodeInputArray::SubParamEdgeRemoved, this, &Node::EdgeRemoved); + } } void Node::DisconnectInput(NodeInput *input) { + if (input->IsArray()) { + NodeInputArray* array = static_cast(input); + + disconnect(array, &NodeInputArray::SubParamEdgeAdded, this, &Node::InputConnectionChanged); + disconnect(array, &NodeInputArray::SubParamEdgeRemoved, this, &Node::InputConnectionChanged); + disconnect(array, &NodeInputArray::SubParamEdgeAdded, this, &Node::EdgeAdded); + disconnect(array, &NodeInputArray::SubParamEdgeRemoved, this, &Node::EdgeRemoved); + } + disconnect(input, &NodeInput::ValueChanged, this, &Node::InputChanged); disconnect(input, &NodeInput::EdgeAdded, this, &Node::InputConnectionChanged); disconnect(input, &NodeInput::EdgeRemoved, this, &Node::InputConnectionChanged); } -void Node::InputChanged(rational start, rational end) +void Node::InputChanged(const TimeRange& range) { - InvalidateCache(start, end, static_cast(sender())); + InvalidateCache(range, static_cast(sender()), static_cast(sender())); } void Node::InputConnectionChanged(NodeEdgePtr edge) { - DependentEdgeChanged(edge->input()); - - InvalidateCache(RATIONAL_MIN, RATIONAL_MAX, static_cast(sender())); + InvalidateCache(TimeRange(RATIONAL_MIN, RATIONAL_MAX), edge->input(), edge->input()); } OLIVE_NAMESPACE_EXIT diff --git a/app/node/node.h b/app/node/node.h index 0ec7e06eb..16b33c8c1 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -29,7 +29,6 @@ #include "codec/samplebuffer.h" #include "common/rational.h" #include "common/xmlutils.h" -#include "node/dependency.h" #include "node/input.h" #include "node/inputarray.h" #include "node/output.h" @@ -196,9 +195,14 @@ public: virtual void ProcessSamples(const NodeValueDatabase &values, const AudioRenderingParams& params, const SampleBufferPtr input, SampleBufferPtr output, int index) const; /** - * @brief Returns the parameter with the specified ID (or nullptr if it doesn't exist) + * @brief Returns the input with the specified ID (or nullptr if it doesn't exist) */ - NodeParam* GetParameterWithID(const QString& id) const; + NodeInput* GetInputWithID(const QString& id) const; + + /** + * @brief Returns the output with the specified ID (or nullptr if it doesn't exist) + */ + NodeOutput* GetOutputWithID(const QString& id) const; /** * @brief Returns whether this Node outputs data to the Node `n` in any way @@ -261,12 +265,12 @@ public: * the DAG. Even if the time needs to be transformed somehow (e.g. converting media time to sequence time), you can * call this function with transformed time and relay the signal that way. */ - virtual void InvalidateCache(const rational& start_range, const rational& end_range, NodeInput* from = nullptr); + virtual void InvalidateCache(const TimeRange& range, NodeInput* from, NodeInput* source); /** * @brief Signal through node graph to only invalidate frames that are currently visible on a ViewerWidget */ - virtual void InvalidateVisible(NodeInput *from); + virtual void InvalidateVisible(NodeInput *from, NodeInput* source); /** * @brief Adjusts time that should be sent to nodes connected to certain inputs. @@ -288,11 +292,6 @@ public: */ static void CopyInputs(Node* source, Node* destination, bool include_connections = true); - /** - * @brief For a list of copies nodes, this function will duplicate all the connections in the source list to the destination list - */ - static void DuplicateConnectionsBetweenLists(const QList& source, const QList &destination); - /** * @brief Return whether this Node can be deleted or not */ @@ -358,9 +357,7 @@ protected: void ClearCachedValuesInParameters(const rational& start_range, const rational& end_range); - void SendInvalidateCache(const rational& start_range, const rational& end_range); - - virtual void DependentEdgeChanged(NodeInput* from); + void SendInvalidateCache(const TimeRange &range, NodeInput *source); virtual void LoadInternal(QXmlStreamReader* reader, XMLNodeData& xml_node_data); @@ -403,9 +400,7 @@ private: void DisconnectInput(NodeInput* input); - static void TraverseInputInternal(QList& list, NodeInput* input, bool traverse, bool exclusive_only); - - static void GetDependenciesInternal(const Node* n, QList& list, bool traverse, bool exclusive_only); + QList GetDependenciesInternal(bool traverse, bool exclusive_only) const; QList params_; @@ -425,7 +420,7 @@ private: QPointF position_; private slots: - void InputChanged(rational start, rational end); + void InputChanged(const TimeRange &range); void InputConnectionChanged(NodeEdgePtr edge); diff --git a/app/node/output/track/track.cpp b/app/node/output/track/track.cpp index 3e8df213d..aeb6be27c 100644 --- a/app/node/output/track/track.cpp +++ b/app/node/output/track/track.cpp @@ -38,8 +38,8 @@ TrackOutput::TrackOutput() : block_input_ = new NodeInputArray("block_in", NodeParam::kAny); block_input_->set_is_keyframable(false); AddInput(block_input_); - connect(block_input_, &NodeInputArray::EdgeAdded, this, &TrackOutput::BlockConnected); - connect(block_input_, &NodeInputArray::EdgeRemoved, this, &TrackOutput::BlockDisconnected); + connect(block_input_, &NodeInputArray::SubParamEdgeAdded, this, &TrackOutput::BlockConnected); + connect(block_input_, &NodeInputArray::SubParamEdgeRemoved, this, &TrackOutput::BlockDisconnected); connect(block_input_, &NodeInputArray::SizeChanged, this, &TrackOutput::BlockListSizeChanged); muted_input_ = new NodeInput("muted_in", NodeParam::kBoolean); @@ -236,10 +236,10 @@ const QVector &TrackOutput::Blocks() const return block_cache_; } -void TrackOutput::InvalidateCache(const rational &start_range, const rational &end_range, NodeInput *from) +void TrackOutput::InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput *source) { if (block_invalidate_cache_stack_ == 0) { - Node::InvalidateCache(qMax(start_range, rational(0)), qMin(end_range, track_length()), from); + Node::InvalidateCache(TimeRange(qMax(range.in(), rational(0)), qMin(range.out(), track_length())), from, source); } } @@ -276,7 +276,7 @@ void TrackOutput::InsertBlockAtIndex(Block *block, int index) UnblockInvalidateCache(); - InvalidateCache(block->in(), track_length()); + InvalidateCache(TimeRange(block->in(), track_length()), block_input_, block_input_); } void TrackOutput::AppendBlock(Block *block) @@ -291,7 +291,7 @@ void TrackOutput::AppendBlock(Block *block) UnblockInvalidateCache(); // Invalidate area that block was added to - InvalidateCache(block->in(), track_length()); + InvalidateCache(TimeRange(block->in(), track_length()), block_input_, block_input_); } void TrackOutput::BlockInvalidateCache() @@ -316,7 +316,7 @@ void TrackOutput::RippleRemoveBlock(Block *block) UnblockInvalidateCache(); - InvalidateCache(remove_in, track_length()); + InvalidateCache(TimeRange(remove_in, track_length()), block_input_, block_input_); } void TrackOutput::ReplaceBlock(Block *old, Block *replace) @@ -334,9 +334,9 @@ void TrackOutput::ReplaceBlock(Block *old, Block *replace) UnblockInvalidateCache(); if (old->length() == replace->length()) { - InvalidateCache(replace->in(), replace->out()); + InvalidateCache(TimeRange(replace->in(), replace->out()), block_input_, block_input_); } else { - InvalidateCache(replace->in(), RATIONAL_MAX); + InvalidateCache(TimeRange(replace->in(), RATIONAL_MAX), block_input_, block_input_); } } @@ -407,6 +407,11 @@ bool TrackOutput::IsLocked() const return locked_; } +NodeInputArray *TrackOutput::block_input() const +{ + return block_input_; +} + void TrackOutput::SetTrackName(const QString &name) { track_name_ = name; @@ -415,7 +420,7 @@ void TrackOutput::SetTrackName(const QString &name) void TrackOutput::SetMuted(bool e) { muted_input_->set_standard_value(e); - InvalidateCache(0, track_length()); + InvalidateCache(TimeRange(0, track_length()), block_input_, block_input_); } void TrackOutput::SetLocked(bool e) @@ -462,8 +467,9 @@ void TrackOutput::UpdateInOutFrom(int index) track_length_ = new_track_length; emit TrackLengthChanged(); - InvalidateCache(qMin(old_track_length, new_track_length), - qMax(old_track_length, new_track_length)); + InvalidateCache(TimeRange(qMin(old_track_length, new_track_length), qMax(old_track_length, new_track_length)), + block_input_, + block_input_); } } diff --git a/app/node/output/track/track.h b/app/node/output/track/track.h index 9ff9df009..11eec0f7b 100644 --- a/app/node/output/track/track.h +++ b/app/node/output/track/track.h @@ -84,7 +84,7 @@ public: const QVector& Blocks() const; - virtual void InvalidateCache(const rational& start_range, const rational& end_range, NodeInput* from = nullptr) override; + virtual void InvalidateCache(const TimeRange& range, NodeInput* from, NodeInput *source) override; /** * @brief Adds Block `block` at the very beginning of the Sequence before all other clips @@ -150,6 +150,8 @@ public: bool IsLocked() const; + NodeInputArray* block_input() const; + public slots: void SetTrackName(const QString& name); diff --git a/app/node/output/track/tracklist.cpp b/app/node/output/track/tracklist.cpp index 05f9a6e6f..b3246f257 100644 --- a/app/node/output/track/tracklist.cpp +++ b/app/node/output/track/tracklist.cpp @@ -31,8 +31,8 @@ TrackList::TrackList(ViewerOutput *parent, const Timeline::TrackType &type, Node track_input_(track_input), type_(type) { - connect(track_input, &NodeInputArray::EdgeAdded, this, &TrackList::TrackConnected); - connect(track_input, &NodeInputArray::EdgeRemoved, this, &TrackList::TrackDisconnected); + connect(track_input, &NodeInputArray::SubParamEdgeAdded, this, &TrackList::TrackConnected); + connect(track_input, &NodeInputArray::SubParamEdgeRemoved, this, &TrackList::TrackDisconnected); connect(track_input, &NodeInputArray::SizeChanged, this, &TrackList::TrackListSizeChanged); } @@ -120,8 +120,8 @@ TrackOutput* TrackList::AddTrack() Node* blend = NodeFactory::CreateFromID(QStringLiteral("org.olivevideoeditor.Olive.alphaoverblend")); GetParentGraph()->AddNode(blend); - NodeParam::ConnectEdge(track->output(), static_cast(blend->GetParameterWithID("blend_in"))); - NodeParam::ConnectEdge(last_track->output(), static_cast(blend->GetParameterWithID("base_in"))); + NodeParam::ConnectEdge(track->output(), static_cast(blend->GetInputWithID("blend_in"))); + NodeParam::ConnectEdge(last_track->output(), static_cast(blend->GetInputWithID("base_in"))); NodeParam::ConnectEdge(blend->output(), edge->input()); break; } diff --git a/app/node/output/viewer/viewer.cpp b/app/node/output/viewer/viewer.cpp index d8d472371..665f287f6 100644 --- a/app/node/output/viewer/viewer.cpp +++ b/app/node/output/viewer/viewer.cpp @@ -92,26 +92,24 @@ NodeInput *ViewerOutput::samples_input() const return samples_input_; } -void ViewerOutput::InvalidateCache(const rational &start_range, const rational &end_range, NodeInput *from) +void ViewerOutput::InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput *source) { - Node::InvalidateCache(start_range, end_range, from); - if (from == texture_input()) { - emit VideoChangedBetween(TimeRange(start_range, end_range)); + emit VideoChangedBetween(range, source); } else if (from == samples_input()) { - emit AudioChangedBetween(TimeRange(start_range, end_range)); + emit AudioChangedBetween(range, source); } - SendInvalidateCache(start_range, end_range); + Node::InvalidateCache(range, from, source); } -void ViewerOutput::InvalidateVisible(NodeInput* from) +void ViewerOutput::InvalidateVisible(NodeInput* from, NodeInput *source) { if (from == texture_input()) { - emit VisibleInvalidated(); + emit VisibleInvalidated(source); } - Node::InvalidateVisible(from); + Node::InvalidateVisible(from, source); } const VideoParams &ViewerOutput::video_params() const @@ -164,17 +162,6 @@ const QUuid &ViewerOutput::uuid() const return uuid_; } -void ViewerOutput::DependentEdgeChanged(NodeInput *from) -{ - if (from == texture_input_) { - emit VideoGraphChanged(); - } else if (from == samples_input_) { - emit AudioGraphChanged(); - } - - Node::DependentEdgeChanged(from); -} - void ViewerOutput::UpdateTrackCache() { track_cache_.clear(); diff --git a/app/node/output/viewer/viewer.h b/app/node/output/viewer/viewer.h index 95afbce70..57101b67a 100644 --- a/app/node/output/viewer/viewer.h +++ b/app/node/output/viewer/viewer.h @@ -55,8 +55,8 @@ public: NodeInput* texture_input() const; NodeInput* samples_input() const; - virtual void InvalidateCache(const rational &start_range, const rational &end_range, NodeInput *from = nullptr) override; - virtual void InvalidateVisible(NodeInput *from) override; + virtual void InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput* source) override; + virtual void InvalidateVisible(NodeInput *from, NodeInput* source) override; const VideoParams& video_params() const; const AudioParams& audio_params() const; @@ -79,21 +79,14 @@ public: const QString& media_name() const; void set_media_name(const QString& name); -protected: - virtual void DependentEdgeChanged(NodeInput* from) override; - signals: void TimebaseChanged(const rational&); - void VideoChangedBetween(const TimeRange& range); + void VideoChangedBetween(const TimeRange& range, NodeInput* source); - void AudioChangedBetween(const TimeRange& range); + void AudioChangedBetween(const TimeRange& range, NodeInput* source); - void VisibleInvalidated(); - - void VideoGraphChanged(); - - void AudioGraphChanged(); + void VisibleInvalidated(NodeInput* source); void LengthChanged(const rational& length); diff --git a/app/render/backend/audio/audiobackend.cpp b/app/render/backend/audio/audiobackend.cpp index 07c023187..6b6f81ab2 100644 --- a/app/render/backend/audio/audiobackend.cpp +++ b/app/render/backend/audio/audiobackend.cpp @@ -39,7 +39,7 @@ bool AudioBackend::InitInternal() // Initiate one thread per CPU core for (int i=0;iSetParameters(params()); processors_.append(processor); } @@ -51,18 +51,6 @@ void AudioBackend::CloseInternal() { } -bool AudioBackend::CompileInternal() -{ - // This backend doesn't compile anything yet - return AudioRenderBackend::CompileInternal(); -} - -void AudioBackend::DecompileInternal() -{ - // This backend doesn't compile anything yet - AudioRenderBackend::DecompileInternal(); -} - void AudioBackend::ConnectWorkerToThis(RenderWorker *worker) { AudioRenderBackend::ConnectWorkerToThis(worker); diff --git a/app/render/backend/audio/audiobackend.h b/app/render/backend/audio/audiobackend.h index 81b423c68..1eaa2941d 100644 --- a/app/render/backend/audio/audiobackend.h +++ b/app/render/backend/audio/audiobackend.h @@ -40,10 +40,6 @@ protected: virtual void CloseInternal() override; - virtual bool CompileInternal() override; - - virtual void DecompileInternal() override; - virtual void ConnectWorkerToThis(RenderWorker* worker) override; private slots: diff --git a/app/render/backend/audiorenderbackend.cpp b/app/render/backend/audiorenderbackend.cpp index d5226945a..b4df5f4ba 100644 --- a/app/render/backend/audiorenderbackend.cpp +++ b/app/render/backend/audiorenderbackend.cpp @@ -58,31 +58,15 @@ void AudioRenderBackend::SetParameters(const AudioRenderingParams ¶ms) void AudioRenderBackend::ConnectViewer(ViewerOutput *node) { connect(node, &ViewerOutput::AudioChangedBetween, this, &AudioRenderBackend::InvalidateCache); - connect(node, &ViewerOutput::AudioGraphChanged, this, &AudioRenderBackend::QueueRecompile); connect(node, &ViewerOutput::LengthChanged, this, &AudioRenderBackend::TruncateCache); } void AudioRenderBackend::DisconnectViewer(ViewerOutput *node) { disconnect(node, &ViewerOutput::AudioChangedBetween, this, &AudioRenderBackend::InvalidateCache); - disconnect(node, &ViewerOutput::AudioGraphChanged, this, &AudioRenderBackend::QueueRecompile); disconnect(node, &ViewerOutput::LengthChanged, this, &AudioRenderBackend::TruncateCache); } -bool AudioRenderBackend::CompileInternal() -{ - for (int i=0;ihas_conformed_version(params)) { // Index JUST finished, requeue this time - InvalidateCache(range); + InvalidateCache(range, nullptr); } else { @@ -215,7 +199,7 @@ void AudioRenderBackend::ConformUpdated(Stream *stream, AudioRenderingParams par // Send invalidate cache signal ic_from_conform_ = true; - InvalidateCache(copy.affected_range); + InvalidateCache(copy.affected_range, nullptr); ic_from_conform_ = false; } diff --git a/app/render/backend/audiorenderbackend.h b/app/render/backend/audiorenderbackend.h index f2420079d..77a774915 100644 --- a/app/render/backend/audiorenderbackend.h +++ b/app/render/backend/audiorenderbackend.h @@ -54,10 +54,6 @@ protected: virtual void DisconnectViewer(ViewerOutput* node) override; - virtual bool CompileInternal() override; - - virtual void DecompileInternal() override; - /** * @brief Internal function for generating the cache ID */ @@ -73,8 +69,6 @@ protected: virtual void InvalidateCacheInternal(const rational &start_range, const rational &end_range) override; - QHash copy_map_; - private: struct ConformWaitInfo { StreamPtr stream; diff --git a/app/render/backend/exporter.cpp b/app/render/backend/exporter.cpp index 5b92a663f..aa8af3165 100644 --- a/app/render/backend/exporter.cpp +++ b/app/render/backend/exporter.cpp @@ -291,14 +291,14 @@ void Exporter::EncoderOpenedSuccessfully() video_backend_->SetOperatingMode(VideoRenderWorker::kHashOnly); connect(video_backend_, &VideoRenderBackend::QueueComplete, this, &Exporter::VideoHashesComplete); - video_backend_->InvalidateCache(export_range_); + video_backend_->InvalidateCache(export_range_, nullptr); } if (!audio_done_) { // We set the audio backend to render the full sequence to the disk connect(audio_backend_, &AudioRenderBackend::AudioComplete, this, &Exporter::AudioRendered); - audio_backend_->InvalidateCache(export_range_); + audio_backend_->InvalidateCache(export_range_, nullptr); } } @@ -344,7 +344,7 @@ void Exporter::VideoHashesComplete() } foreach (const TimeRange& range, ranges) { - video_backend_->InvalidateCache(range); + video_backend_->InvalidateCache(range, nullptr); } } diff --git a/app/render/backend/opengl/openglbackend.cpp b/app/render/backend/opengl/openglbackend.cpp index 2db0cefa6..e1144dc5d 100644 --- a/app/render/backend/opengl/openglbackend.cpp +++ b/app/render/backend/opengl/openglbackend.cpp @@ -89,15 +89,6 @@ void OpenGLBackend::CloseInternal() VideoRenderBackend::CloseInternal(); } -bool OpenGLBackend::CompileInternal() -{ - return true; -} - -void OpenGLBackend::DecompileInternal() -{ -} - void OpenGLBackend::ParamsChangedEvent() { // If we're initiated, we need to recreate the texture. Otherwise this backend isn't active so it doesn't matter. diff --git a/app/render/backend/opengl/openglbackend.h b/app/render/backend/opengl/openglbackend.h index 59fc34465..62945ffa1 100644 --- a/app/render/backend/opengl/openglbackend.h +++ b/app/render/backend/opengl/openglbackend.h @@ -43,10 +43,6 @@ protected: virtual void CloseInternal() override; - virtual bool CompileInternal() override; - - virtual void DecompileInternal() override; - virtual void ParamsChangedEvent() override; private: diff --git a/app/render/backend/renderbackend.cpp b/app/render/backend/renderbackend.cpp index 0cfb0eae4..310a85431 100644 --- a/app/render/backend/renderbackend.cpp +++ b/app/render/backend/renderbackend.cpp @@ -31,12 +31,9 @@ OLIVE_NAMESPACE_ENTER RenderBackend::RenderBackend(QObject *parent) : QObject(parent), - compiled_(false), started_(false), viewer_node_(nullptr), - copied_viewer_node_(nullptr), - recompile_queued_(false), - input_update_queued_(false) + copied_viewer_node_(nullptr) { // FIXME: Don't create in CLI mode cancel_dialog_ = new RenderCancelDialog(Core::instance()->main_window()); @@ -84,7 +81,7 @@ void RenderBackend::Close() CancelQueue(); - Decompile(); + SetViewerNode(nullptr); CloseInternal(); @@ -114,23 +111,30 @@ const QString &RenderBackend::GetError() const void RenderBackend::SetViewerNode(ViewerOutput *viewer_node) { - if (viewer_node_ != nullptr) { + if (viewer_node_) { CancelQueue(); DisconnectViewer(viewer_node_); - Decompile(); + copied_graph_.Clear(); + copied_viewer_node_ = nullptr; + node_copy_map_.clear(); } viewer_node_ = viewer_node; - if (viewer_node_ != nullptr) { + if (viewer_node_) { ConnectViewer(viewer_node_); RegenerateCacheID(); - } - InvalidateCache(TimeRange(0, RATIONAL_MAX)); + copied_viewer_node_ = static_cast(viewer_node_->copy()); + copied_graph_.AddNode(copied_viewer_node_); + node_copy_map_.insert(viewer_node_, copied_viewer_node_); + + InvalidateCache(TimeRange(0, RATIONAL_MAX), + static_cast(viewer_node_->GetInputWithID(GetDependentInput()->id()))); + } } bool RenderBackend::IsInitiated() @@ -138,58 +142,6 @@ bool RenderBackend::IsInitiated() return started_; } -bool RenderBackend::Compile() -{ - if (compiled_) { - return true; - } - - // Get dependencies of viewer node - source_node_list_.append(viewer_node_); - source_node_list_.append(viewer_node_->GetDependencies()); - - // Copy all dependencies into graph - foreach (Node* n, source_node_list_) { - Node* copy = n->copy(); - - Node::CopyInputs(n, copy, false); - - copied_graph_.AddNode(copy); - } - - // We just copied the inputs, so if an input update is queued, it's unnecessary - input_update_queued_ = false; - - // We know that the first node will be the viewer node since we appended that first in the copy - copied_viewer_node_ = static_cast(copied_graph_.nodes().first()); - - // Copy connections - Node::DuplicateConnectionsBetweenLists(source_node_list_, copied_graph_.nodes()); - - compiled_ = CompileInternal(); - - if (!compiled_) { - Decompile(); - } - - return compiled_; -} - -void RenderBackend::Decompile() -{ - if (!compiled_) { - return; - } - - DecompileInternal(); - - copied_graph_.Clear(); - copied_viewer_node_ = nullptr; - source_node_list_.clear(); - - compiled_ = false; -} - void RenderBackend::RegenerateCacheID() { QCryptographicHash hash(QCryptographicHash::Sha1); @@ -261,34 +213,19 @@ void RenderBackend::CacheNext() return; } - if (!Init() - || !ViewerIsConnected() - || !CanRender()) { + if (!ViewerIsConnected() + || !CanRender() + || !Init()) { return; } - if ((input_update_queued_ || recompile_queued_) && !AllProcessorsAreAvailable()) { - return; - } - - if (recompile_queued_) { - Decompile(); - recompile_queued_ = false; - } - - if (!compiled_ && !Compile()) { - return; - } - - if (input_update_queued_) { - for (int i=0;iget_connected_node(); @@ -356,12 +293,8 @@ void RenderBackend::CancelQueue() cancel_dialog_->RunIfWorkersAreBusy(); } -void RenderBackend::InvalidateCache(const TimeRange &range) +void RenderBackend::InvalidateCache(const TimeRange &range, NodeInput *from) { - if (!CanRender()) { - return; - } - // Adjust range to min/max values rational start_range_adj = qMax(rational(0), range.in()); rational end_range_adj = qMin(GetSequenceLength(), range.out()); @@ -371,15 +304,18 @@ void RenderBackend::InvalidateCache(const TimeRange &range) << "and" << end_range_adj.toDouble(); - // Queue value update - QueueValueUpdate(); + if (from) { + // Queue value update + qDebug() << " from" << from->parentNode()->id() << "::" << from->id(); + QueueValueUpdate(from); + } InvalidateCacheInternal(start_range_adj, end_range_adj); } bool RenderBackend::ViewerIsConnected() const { - return viewer_node_ != nullptr; + return viewer_node_; } const QString &RenderBackend::cache_id() const @@ -387,9 +323,23 @@ const QString &RenderBackend::cache_id() const return cache_id_; } -void RenderBackend::QueueValueUpdate() +void RenderBackend::QueueValueUpdate(NodeInput* from) { - input_update_queued_ = true; + if (!input_update_queued_.isEmpty()) { + // Remove any inputs that are dependents of this input since they may have been removed since + // it was queued + QList deps = from->GetDependencies(); + + for (int i=0;iparentNode())) { + // We don't need to queue this value since this input supersedes it + input_update_queued_.removeAt(i); + i--; + } + } + } + + input_update_queued_.append(from); } bool RenderBackend::WorkerIsBusy(RenderWorker *worker) const @@ -402,6 +352,87 @@ void RenderBackend::SetWorkerBusyState(RenderWorker *worker, bool busy) processor_busy_state_.replace(processors_.indexOf(worker), busy); } +void RenderBackend::CopyNodeInputValue(NodeInput *input) +{ + // Find our copy of this parameter + Node* our_copy_node = node_copy_map_.value(input->parentNode()); + NodeInput* our_copy = our_copy_node->GetInputWithID(input->id()); + + // Copy the standard/keyframe values between these two inputs + NodeInput::CopyValues(input, + our_copy, + false); + + // Handle connections + if (input->IsConnected() || our_copy->IsConnected()) { + // If one of the inputs is connected, it's likely this change came from connecting or + // disconnecting whatever was connected to it + + { + // We start by removing all old dependencies from the map + QList old_deps = our_copy->GetExclusiveDependencies(); + + foreach (Node* i, old_deps) { + Node* n = node_copy_map_.take(node_copy_map_.key(i)); + copied_graph_.TakeNode(n); + delete n; + } + } + + // Then we copy all node dependencies and connections (if there are any) + CopyNodeMakeConnection(input, our_copy); + } + + // Call on sub-elements too + if (input->IsArray()) { + foreach (NodeInput* i, static_cast(input)->sub_params()) { + CopyNodeInputValue(i); + } + } +} + +Node* RenderBackend::CopyNodeConnections(Node* src_node) +{ + // Check if this node is already in the map + Node* dst_node = node_copy_map_.value(src_node); + + // If not, create it now + if (!dst_node) { + dst_node = src_node->copy(); + copied_graph_.AddNode(dst_node); + node_copy_map_.insert(src_node, dst_node); + } + + // Make sure its values are copied + Node::CopyInputs(src_node, dst_node, false); + + // Copy all connections + QList src_node_inputs = src_node->GetInputsIncludingArrays(); + QList dst_node_inputs = dst_node->GetInputsIncludingArrays(); + + for (int i=0;iid() << "from" << src_input->parentNode()->id(); + + if (src_input->IsConnected()) { + Node* dst_node = CopyNodeConnections(src_input->get_connected_node()); + + NodeOutput* corresponding_output = dst_node->GetOutputWithID(src_input->get_connected_output()->id()); + + NodeParam::ConnectEdge(corresponding_output, + dst_input); + } +} + bool RenderBackend::AllProcessorsAreAvailable() const { foreach (bool busy, processor_busy_state_) { @@ -455,11 +486,6 @@ void RenderBackend::InitWorkers() processor_busy_state_.fill(false); } -void RenderBackend::QueueRecompile() -{ - recompile_queued_ = true; -} - void RenderBackend::FootageUnavailable(StreamPtr stream, Decoder::RetrieveState state, const TimeRange &range, const rational &stream_time) { if (state == Decoder::kFailedToOpen){ @@ -484,7 +510,7 @@ void RenderBackend::FootageUnavailable(StreamPtr stream, Decoder::RetrieveState || (stream->type() == Stream::kAudio && std::static_pointer_cast(stream)->index_done())) { // Index JUST finished, requeue this time - InvalidateCache(range); + InvalidateCache(range, nullptr); } else { @@ -529,7 +555,7 @@ void RenderBackend::IndexUpdated(Stream* stream) } if (footage_ready) { - InvalidateCache(info.affected_range); + InvalidateCache(info.affected_range, nullptr); footage_wait_info_.removeAt(i); i--; } diff --git a/app/render/backend/renderbackend.h b/app/render/backend/renderbackend.h index 7b1b120fb..1e8db284a 100644 --- a/app/render/backend/renderbackend.h +++ b/app/render/backend/renderbackend.h @@ -52,11 +52,7 @@ public: void CancelQueue(); public slots: - void InvalidateCache(const TimeRange &range); - - bool Compile(); - - void Decompile(); + void InvalidateCache(const TimeRange &range, NodeInput *from); signals: void QueueComplete(); @@ -68,10 +64,6 @@ protected: virtual void CloseInternal(); - virtual bool CompileInternal() = 0; - - virtual void DecompileInternal() = 0; - virtual bool CanRender(); virtual TimeRange PopNextFrameFromQueue(); @@ -111,7 +103,7 @@ protected: const QString& cache_id() const; - void QueueValueUpdate(); + void QueueValueUpdate(NodeInput *from); bool AllProcessorsAreAvailable() const; bool WorkerIsBusy(RenderWorker* worker) const; @@ -121,18 +113,17 @@ protected: QVector processors_; - bool compiled_; - QHash render_job_info_; - QList source_node_list_; + QHash node_copy_map_; NodeGraph copied_graph_; -protected slots: - void QueueRecompile(); - private: + void CopyNodeInputValue(NodeInput* input); + Node *CopyNodeConnections(Node *src_node); + void CopyNodeMakeConnection(NodeInput *src_input, NodeInput *dst_input); + /** * @brief Internal list of RenderProcessThreads */ @@ -160,8 +151,7 @@ private: QString cache_id_; - bool recompile_queued_; - bool input_update_queued_; + QList input_update_queued_; QVector processor_busy_state_; diff --git a/app/render/backend/videorenderbackend.cpp b/app/render/backend/videorenderbackend.cpp index 7e13d1af7..7452b8ab7 100644 --- a/app/render/backend/videorenderbackend.cpp +++ b/app/render/backend/videorenderbackend.cpp @@ -49,14 +49,12 @@ VideoRenderBackend::VideoRenderBackend(QObject *parent) : void VideoRenderBackend::ConnectViewer(ViewerOutput *node) { connect(node, &ViewerOutput::VideoChangedBetween, this, &VideoRenderBackend::InvalidateCache); - connect(node, &ViewerOutput::VideoGraphChanged, this, &VideoRenderBackend::QueueRecompile); connect(node, &ViewerOutput::LengthChanged, this, &VideoRenderBackend::TruncateFrameCacheLength); } void VideoRenderBackend::DisconnectViewer(ViewerOutput *node) { disconnect(node, &ViewerOutput::VideoChangedBetween, this, &VideoRenderBackend::InvalidateCache); - disconnect(node, &ViewerOutput::VideoGraphChanged, this, &VideoRenderBackend::QueueRecompile); disconnect(node, &ViewerOutput::LengthChanged, this, &VideoRenderBackend::TruncateFrameCacheLength); frame_cache_.Clear(); diff --git a/app/render/backend/videorenderworker.cpp b/app/render/backend/videorenderworker.cpp index ec1a4ad47..5dabdc5cb 100644 --- a/app/render/backend/videorenderworker.cpp +++ b/app/render/backend/videorenderworker.cpp @@ -261,7 +261,10 @@ void VideoRenderWorker::SetFrameGenerationParams(int width, int height, const QM bool VideoRenderWorker::InitInternal() { - ResizeDownloadBuffer(); + if (video_params_.is_valid()) { + ResizeDownloadBuffer(); + } + return true; } diff --git a/app/widget/keyframeview/keyframeviewbase.cpp b/app/widget/keyframeview/keyframeviewbase.cpp index 300fbf4b3..2baac4e4c 100644 --- a/app/widget/keyframeview/keyframeviewbase.cpp +++ b/app/widget/keyframeview/keyframeviewbase.cpp @@ -174,7 +174,7 @@ void KeyframeViewBase::mouseMoveEvent(QMouseEvent *event) // frame gets rendered in this time input_parent->blockSignals(false); - input_parent->parentNode()->InvalidateVisible(input_parent); + input_parent->parentNode()->InvalidateVisible(input_parent, input_parent); } } } @@ -377,7 +377,7 @@ void KeyframeViewBase::ProcessBezierDrag(QPointF mouse_diff_scaled, bool include input_parent->blockSignals(false); - input_parent->parentNode()->InvalidateVisible(input_parent); + input_parent->parentNode()->InvalidateVisible(input_parent, input_parent); } } diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp index bc90a50e0..aaf1fefba 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp @@ -255,7 +255,7 @@ void NodeParamViewWidgetBridge::ProcessSlider(SliderBase *slider, const QVariant input_->blockSignals(false); - input_->parentNode()->InvalidateVisible(input_); + input_->parentNode()->InvalidateVisible(input_, input_); } else { if (dragging_) { @@ -513,9 +513,9 @@ rational NodeParamViewWidgetBridge::GetCurrentTimeAsNodeTime() const return GetAdjustedTime(GetTimeTarget(), input_->parentNode(), time_, NodeParam::kInput); } -void NodeParamViewWidgetBridge::InputValueChanged(const rational &start, const rational &end) +void NodeParamViewWidgetBridge::InputValueChanged(const TimeRange &range) { - if (!dragging_ && start <= time_ && end >= time_) { + if (!dragging_ && range.in() <= time_ && range.out() >= time_) { // We'll need to update the widgets because the values have changed on our current time UpdateWidgetValues(); } diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.h b/app/widget/nodeparamview/nodeparamviewwidgetbridge.h index 51b4c6ebc..d1543c73e 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.h +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.h @@ -68,7 +68,7 @@ private: private slots: void WidgetCallback(); - void InputValueChanged(const rational& start, const rational& end); + void InputValueChanged(const TimeRange& range); void PropertyChanged(const QString& key, const QVariant& value); diff --git a/app/widget/timelinewidget/undo/undo.cpp b/app/widget/timelinewidget/undo/undo.cpp index 8dd71617a..8ba10b15e 100644 --- a/app/widget/timelinewidget/undo/undo.cpp +++ b/app/widget/timelinewidget/undo/undo.cpp @@ -274,7 +274,9 @@ void TrackRippleRemoveAreaCommand::redo_internal() track_->UnblockInvalidateCache(); - track_->InvalidateCache(in_, insert_ ? out_ : RATIONAL_MAX); + track_->InvalidateCache(TimeRange(in_, insert_ ? out_ : RATIONAL_MAX), + track_->block_input(), + track_->block_input()); } void TrackRippleRemoveAreaCommand::undo_internal() @@ -328,7 +330,7 @@ void TrackRippleRemoveAreaCommand::undo_internal() track_->UnblockInvalidateCache(); - track_->InvalidateCache(in_, insert_ ? out_ : RATIONAL_MAX); + track_->InvalidateCache(TimeRange(in_, insert_ ? out_ : RATIONAL_MAX), track_->block_input(), track_->block_input()); } TrackPlaceBlockCommand::TrackPlaceBlockCommand(TrackList *timeline, int track, Block *block, rational in, QUndoCommand *parent) : diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index f25e1084b..4107ceab1 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -152,8 +152,8 @@ void ViewerWidget::ConnectNodeInternal(ViewerOutput *n) connect(n, &ViewerOutput::LengthChanged, this, &ViewerWidget::LengthChangedSlot); connect(n, &ViewerOutput::VideoParamsChanged, this, &ViewerWidget::UpdateRendererParameters); connect(n, &ViewerOutput::VisibleInvalidated, this, &ViewerWidget::InvalidateVisible); - connect(n, &ViewerOutput::VideoGraphChanged, this, &ViewerWidget::UpdateStack); - connect(n, &ViewerOutput::AudioGraphChanged, this, &ViewerWidget::UpdateStack); + connect(n, &ViewerOutput::VideoChangedBetween, this, &ViewerWidget::UpdateStack); + connect(n, &ViewerOutput::AudioChangedBetween, this, &ViewerWidget::UpdateStack); SizeChangedSlot(n->video_params().width(), n->video_params().height()); LengthChangedSlot(n->Length()); @@ -194,8 +194,8 @@ void ViewerWidget::DisconnectNodeInternal(ViewerOutput *n) disconnect(n, &ViewerOutput::LengthChanged, this, &ViewerWidget::LengthChangedSlot); disconnect(n, &ViewerOutput::VideoParamsChanged, this, &ViewerWidget::UpdateRendererParameters); disconnect(n, &ViewerOutput::VisibleInvalidated, this, &ViewerWidget::InvalidateVisible); - disconnect(n, &ViewerOutput::VideoGraphChanged, this, &ViewerWidget::UpdateStack); - disconnect(n, &ViewerOutput::AudioGraphChanged, this, &ViewerWidget::UpdateStack); + disconnect(n, &ViewerOutput::VideoChangedBetween, this, &ViewerWidget::UpdateStack); + disconnect(n, &ViewerOutput::AudioChangedBetween, this, &ViewerWidget::UpdateStack); // Effectively disables the viewer and clears the state SizeChangedSlot(0, 0); @@ -434,10 +434,14 @@ void ViewerWidget::SetColorTransform(const ColorTransform &transform, ViewerDisp void ViewerWidget::UpdateStack() { - if (!GetConnectedNode() || GetConnectedNode()->texture_input()->IsConnected()) { - stack_->setCurrentWidget(sizer_); - } else { + if (GetConnectedNode() + && !GetConnectedNode()->texture_input()->IsConnected() + && GetConnectedNode()->samples_input()->IsConnected()) { + // If we have a node AND video is disconnected AND audio is connected, show waveform view stack_->setCurrentWidget(waveform_view_); + } else { + // Otherwise show regular display + stack_->setCurrentWidget(sizer_); } } @@ -527,7 +531,7 @@ void ViewerWidget::UpdateRendererParameters() if (video_renderer_->params() != vparam) { video_renderer_->SetParameters(vparam); - video_renderer_->InvalidateCache(TimeRange(0, GetConnectedNode()->Length())); + video_renderer_->InvalidateCache(TimeRange(0, GetConnectedNode()->Length()), nullptr); } AudioRenderingParams aparam(GetConnectedNode()->audio_params(), @@ -535,7 +539,7 @@ void ViewerWidget::UpdateRendererParameters() if (audio_renderer_->params() != aparam) { audio_renderer_->SetParameters(aparam); - audio_renderer_->InvalidateCache(TimeRange(0, GetConnectedNode()->Length())); + audio_renderer_->InvalidateCache(TimeRange(0, GetConnectedNode()->Length()), nullptr); } } @@ -869,9 +873,9 @@ void ViewerWidget::SetZoomFromMenu(QAction *action) sizer_->SetZoom(action->data().toInt()); } -void ViewerWidget::InvalidateVisible() +void ViewerWidget::InvalidateVisible(NodeInput* source) { - video_renderer_->InvalidateCache(TimeRange(GetTime(), GetTime())); + video_renderer_->InvalidateCache(TimeRange(GetTime(), GetTime()), source); } OLIVE_NAMESPACE_EXIT diff --git a/app/widget/viewer/viewer.h b/app/widget/viewer/viewer.h index ec638238a..8cf6bb24c 100644 --- a/app/widget/viewer/viewer.h +++ b/app/widget/viewer/viewer.h @@ -233,7 +233,7 @@ private slots: void SetZoomFromMenu(QAction* action); - void InvalidateVisible(); + void InvalidateVisible(NodeInput *source); void UpdateStack(); From 9c4999cfae8e9f2357850086d88dd7e7ede1a8a6 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 27 Apr 2020 00:04:07 +1000 Subject: [PATCH 05/10] curvepanel: reconfigured to be a spawnable child of NodeParamView --- app/panel/curve/curve.cpp | 14 +++ app/panel/curve/curve.h | 2 + app/panel/panelmanager.cpp | 5 +- app/panel/param/param.cpp | 83 ++++++++++++++++- app/panel/param/param.h | 17 +++- app/panel/timebased/timebased.cpp | 2 +- app/panel/timebased/timebased.h | 2 +- app/panel/viewer/viewerbase.cpp | 3 - app/widget/curvewidget/curvewidget.cpp | 5 ++ app/widget/curvewidget/curvewidget.h | 1 + app/widget/nodeparamview/nodeparamview.cpp | 6 +- app/widget/nodeparamview/nodeparamview.h | 6 +- .../nodeparamview/nodeparamviewitem.cpp | 20 +++-- app/widget/nodeparamview/nodeparamviewitem.h | 8 +- app/window/mainwindow/mainwindow.cpp | 88 ++++++++++--------- app/window/mainwindow/mainwindow.h | 11 ++- 16 files changed, 208 insertions(+), 65 deletions(-) diff --git a/app/panel/curve/curve.cpp b/app/panel/curve/curve.cpp index d67972a7e..233b637f5 100644 --- a/app/panel/curve/curve.cpp +++ b/app/panel/curve/curve.cpp @@ -32,9 +32,16 @@ CurvePanel::CurvePanel(QWidget *parent) : Retranslate(); } +NodeInput *CurvePanel::GetInput() const +{ + return static_cast(GetTimeBasedWidget())->GetInput(); +} + void CurvePanel::SetInput(NodeInput *input) { static_cast(GetTimeBasedWidget())->SetInput(input); + + Retranslate(); } void CurvePanel::SetTimeTarget(Node *target) @@ -59,6 +66,13 @@ void CurvePanel::Retranslate() TimeBasedPanel::Retranslate(); SetTitle(tr("Curve Editor")); + + NodeInput* connected_input = static_cast(GetTimeBasedWidget())->GetInput(); + if (connected_input) { + SetSubtitle(connected_input->name()); + } else { + SetSubtitle(QString()); + } } OLIVE_NAMESPACE_EXIT diff --git a/app/panel/curve/curve.h b/app/panel/curve/curve.h index 36a3ba564..761b00a0f 100644 --- a/app/panel/curve/curve.h +++ b/app/panel/curve/curve.h @@ -32,6 +32,8 @@ class CurvePanel : public TimeBasedPanel public: CurvePanel(QWidget* parent); + NodeInput* GetInput() const; + public slots: void SetInput(NodeInput* input); diff --git a/app/panel/panelmanager.cpp b/app/panel/panelmanager.cpp index a76d65093..f85e6824f 100644 --- a/app/panel/panelmanager.cpp +++ b/app/panel/panelmanager.cpp @@ -144,7 +144,10 @@ void PanelManager::FocusChanged(QWidget *old, QWidget *now) void PanelManager::SetPanelsLocked(bool locked) { foreach (PanelWidget* panel, focus_history_) { - panel->SetMovementLocked(locked); + // Only affect panels actually in our layout + if (!panel->isFloating()) { + panel->SetMovementLocked(locked); + } } locked_ = locked; diff --git a/app/panel/param/param.cpp b/app/panel/param/param.cpp index e714f8d05..bdbb8e8e0 100644 --- a/app/panel/param/param.cpp +++ b/app/panel/param/param.cpp @@ -20,15 +20,19 @@ #include "param.h" +#include "window/mainwindow/mainwindow.h" + OLIVE_NAMESPACE_ENTER ParamPanel::ParamPanel(QWidget* parent) : TimeBasedPanel(QStringLiteral("ParamPanel"), parent) { NodeParamView* view = new NodeParamView(); - connect(view, &NodeParamView::SelectedInputChanged, this, &ParamPanel::SelectedInputChanged); + connect(view, &NodeParamView::InputDoubleClicked, this, &ParamPanel::CreateCurvePanel); connect(view, &NodeParamView::TimeTargetChanged, this, &ParamPanel::TimeTargetChanged); connect(view, &NodeParamView::RequestSelectNode, this, &ParamPanel::RequestSelectNode); + connect(view, &NodeParamView::OpenedNode, this, &ParamPanel::OpeningNode); + connect(view, &NodeParamView::ClosedNode, this, &ParamPanel::ClosingNode); SetTimeBasedWidget(view); Retranslate(); @@ -41,6 +45,20 @@ void ParamPanel::SetNodes(QList nodes) Retranslate(); } +void ParamPanel::SetTimestamp(const int64_t ×tamp) +{ + TimeBasedPanel::SetTimestamp(timestamp); + + // Ensure all CurvePanels are updated with this time too + QHash::const_iterator i; + + for (i=open_curve_panels_.begin(); i!=open_curve_panels_.end(); i++) { + if (i.value() && i.value() != sender()) { + i.value()->SetTimestamp(timestamp); + } + } +} + void ParamPanel::Retranslate() { SetTitle(tr("Parameter Editor")); @@ -56,4 +74,67 @@ void ParamPanel::Retranslate() } } +void ParamPanel::CreateCurvePanel(NodeInput *input) +{ + if (!input->is_keyframable()) { + return; + } + + CurvePanel* panel = open_curve_panels_.value(input); + + if (panel) { + panel->raise(); + return; + } + + NodeParamView* view = static_cast(GetTimeBasedWidget()); + + panel = Core::instance()->main_window()->AppendCurvePanel(); + + panel->SetInput(input); + panel->SetTimebase(view->timebase()); + panel->SetTimestamp(view->GetTimestamp()); + + connect(view, &NodeParamView::TimebaseChanged, panel, &CurvePanel::SetTimebase); + connect(view, &NodeParamView::TimeChanged, panel, &CurvePanel::SetTimestamp); + connect(panel, &CurvePanel::TimeChanged, view, &NodeParamView::SetTimestamp); + connect(panel, &CurvePanel::TimeChanged, view, &NodeParamView::TimeChanged); + connect(panel, &CurvePanel::CloseRequested, this, &ParamPanel::ClosingCurvePanel); + + open_curve_panels_.insert(input, panel); +} + +void ParamPanel::OpeningNode(Node *n) +{ + QList inputs = n->GetInputsIncludingArrays(); + + foreach (NodeInput* i, inputs) { + if (open_curve_panels_.contains(i)) { + // We had a CurvePanel open for this input that was closed in ClosingNode(), re-open it + CreateCurvePanel(i); + } + } +} + +void ParamPanel::ClosingNode(Node *n) +{ + QList inputs = n->GetInputsIncludingArrays(); + + foreach (NodeInput* i, inputs) { + CurvePanel* panel = open_curve_panels_.value(i); + + // Close the panel (this also destroys it), but keep a reference in the hash + if (panel) { + panel->close(); + open_curve_panels_.insert(i, nullptr); + } + } +} + +void ParamPanel::ClosingCurvePanel() +{ + CurvePanel* panel = static_cast(sender()); + open_curve_panels_.remove(panel->GetInput()); +} + OLIVE_NAMESPACE_EXIT diff --git a/app/panel/param/param.h b/app/panel/param/param.h index 7d78aab64..0ad5523c4 100644 --- a/app/panel/param/param.h +++ b/app/panel/param/param.h @@ -21,6 +21,7 @@ #ifndef PARAM_H #define PARAM_H +#include "panel/curve/curve.h" #include "panel/timebased/timebased.h" #include "widget/nodeparamview/nodeparamview.h" @@ -35,9 +36,9 @@ public: public slots: void SetNodes(QList nodes); -signals: - void SelectedInputChanged(NodeInput* input); + virtual void SetTimestamp(const int64_t& timestamp) override; +signals: void TimeTargetChanged(Node* node); void RequestSelectNode(const QList& target); @@ -45,6 +46,18 @@ signals: protected: virtual void Retranslate() override; +private slots: + void CreateCurvePanel(NodeInput* input); + + void OpeningNode(Node* n); + + void ClosingNode(Node* n); + + void ClosingCurvePanel(); + +private: + QHash open_curve_panels_; + }; OLIVE_NAMESPACE_EXIT diff --git a/app/panel/timebased/timebased.cpp b/app/panel/timebased/timebased.cpp index 4da1e95c0..37a0959fa 100644 --- a/app/panel/timebased/timebased.cpp +++ b/app/panel/timebased/timebased.cpp @@ -68,7 +68,7 @@ void TimeBasedPanel::SetTimebase(const rational &timebase) widget_->SetTimebase(timebase); } -void TimeBasedPanel::SetTime(const int64_t ×tamp) +void TimeBasedPanel::SetTimestamp(const int64_t ×tamp) { widget_->SetTimestamp(timestamp); } diff --git a/app/panel/timebased/timebased.h b/app/panel/timebased/timebased.h index 9c68567b3..90fb83723 100644 --- a/app/panel/timebased/timebased.h +++ b/app/panel/timebased/timebased.h @@ -89,7 +89,7 @@ public: public slots: void SetTimebase(const rational& timebase); - void SetTime(const int64_t& timestamp); + virtual void SetTimestamp(const int64_t& timestamp); signals: void TimeChanged(const int64_t& time); diff --git a/app/panel/viewer/viewerbase.cpp b/app/panel/viewer/viewerbase.cpp index da1726540..a8d4d55b8 100644 --- a/app/panel/viewer/viewerbase.cpp +++ b/app/panel/viewer/viewerbase.cpp @@ -98,9 +98,6 @@ void ViewerPanelBase::CreateScopePanel(ScopePanel::Type type) p->SetType(type); - // We treat our scope panels as kind of children, and destroy them if we're ever destroyed - connect(this, &ViewerPanelBase::destroyed, p, &ScopePanel::deleteLater); - // If the scope closes, reduce the count (we do this because if no scopes are open, we can optimize the viewer slightly) connect(p, &ScopePanel::CloseRequested, this, &ViewerPanelBase::ScopePanelClosed); diff --git a/app/widget/curvewidget/curvewidget.cpp b/app/widget/curvewidget/curvewidget.cpp index a16e0a726..71c742619 100644 --- a/app/widget/curvewidget/curvewidget.cpp +++ b/app/widget/curvewidget/curvewidget.cpp @@ -108,6 +108,11 @@ CurveWidget::~CurveWidget() view_->Clear(); } +NodeInput *CurveWidget::GetInput() const +{ + return input_; +} + void CurveWidget::SetInput(NodeInput *input) { if (bridge_) { diff --git a/app/widget/curvewidget/curvewidget.h b/app/widget/curvewidget/curvewidget.h index 551825028..b0276bb7f 100644 --- a/app/widget/curvewidget/curvewidget.h +++ b/app/widget/curvewidget/curvewidget.h @@ -41,6 +41,7 @@ public: virtual ~CurveWidget() override; + NodeInput* GetInput() const; void SetInput(NodeInput* input); const double& GetVerticalScale(); diff --git a/app/widget/nodeparamview/nodeparamview.cpp b/app/widget/nodeparamview/nodeparamview.cpp index 6b57f2c14..b9d1e7366 100644 --- a/app/widget/nodeparamview/nodeparamview.cpp +++ b/app/widget/nodeparamview/nodeparamview.cpp @@ -126,6 +126,8 @@ void NodeParamView::SetNodes(QList nodes) // If we already have item widgets, delete them all now foreach (NodeParamViewItem* item, items_) { + emit ClosedNode(item->GetNode()); + delete item; } items_.clear(); @@ -149,12 +151,14 @@ void NodeParamView::SetNodes(QList nodes) connect(item, &NodeParamViewItem::KeyframeAdded, keyframe_view_, &KeyframeView::AddKeyframe); connect(item, &NodeParamViewItem::KeyframeRemoved, keyframe_view_, &KeyframeView::RemoveKeyframe); connect(item, &NodeParamViewItem::RequestSetTime, this, &NodeParamView::ItemRequestedTimeChanged); - connect(item, &NodeParamViewItem::InputClicked, this, &NodeParamView::SelectedInputChanged); + connect(item, &NodeParamViewItem::InputDoubleClicked, this, &NodeParamView::InputDoubleClicked); connect(item, &NodeParamViewItem::RequestSelectNode, this, &NodeParamView::RequestSelectNode); items_.append(item); QTimer::singleShot(1, item, &NodeParamViewItem::SignalAllKeyframes); + + emit OpenedNode(node); } ViewerOutput* viewer = nodes_.first()->FindOutputNode(); diff --git a/app/widget/nodeparamview/nodeparamview.h b/app/widget/nodeparamview/nodeparamview.h index 5188469fe..097fcd4ae 100644 --- a/app/widget/nodeparamview/nodeparamview.h +++ b/app/widget/nodeparamview/nodeparamview.h @@ -41,12 +41,16 @@ public: const QList& nodes(); signals: - void SelectedInputChanged(NodeInput* input); + void InputDoubleClicked(NodeInput* input); void TimeTargetChanged(Node* target); void RequestSelectNode(const QList& target); + void OpenedNode(Node* n); + + void ClosedNode(Node* n); + protected: virtual void resizeEvent(QResizeEvent *event) override; diff --git a/app/widget/nodeparamview/nodeparamviewitem.cpp b/app/widget/nodeparamview/nodeparamviewitem.cpp index b464e3135..6d7a2f0ee 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.cpp +++ b/app/widget/nodeparamview/nodeparamviewitem.cpp @@ -46,7 +46,6 @@ NodeParamViewItem::NodeParamViewItem(Node *node, QWidget *parent) : QHBoxLayout* title_bar_layout = new QHBoxLayout(title_bar_); title_bar_collapse_btn_ = new CollapseButton(); - connect(title_bar_collapse_btn_, &QPushButton::toggled, this, &NodeParamViewItemBody::setVisible); title_bar_layout->addWidget(title_bar_collapse_btn_); title_bar_lbl_ = new QLabel(title_bar_); @@ -66,11 +65,12 @@ NodeParamViewItem::NodeParamViewItem(Node *node, QWidget *parent) : } body_ = new NodeParamViewItemBody(inputs); - connect(body_, &NodeParamViewItemBody::InputClicked, this, &NodeParamViewItem::InputClicked); + connect(body_, &NodeParamViewItemBody::InputDoubleClicked, this, &NodeParamViewItem::InputDoubleClicked); connect(body_, &NodeParamViewItemBody::RequestSelectNode, this, &NodeParamViewItem::RequestSelectNode); connect(body_, &NodeParamViewItemBody::RequestSetTime, this, &NodeParamViewItem::RequestSetTime); connect(body_, &NodeParamViewItemBody::KeyframeAdded, this, &NodeParamViewItem::KeyframeAdded); connect(body_, &NodeParamViewItemBody::KeyframeRemoved, this, &NodeParamViewItem::KeyframeRemoved); + connect(title_bar_collapse_btn_, &QPushButton::toggled, body_, &NodeParamViewItemBody::setVisible); main_layout->addWidget(body_); Retranslate(); @@ -88,6 +88,11 @@ void NodeParamViewItem::SetTime(const rational &time) body_->SetTime(time_); } +Node *NodeParamViewItem::GetNode() const +{ + return node_; +} + void NodeParamViewItem::SignalAllKeyframes() { body_->SignalAllKeyframes(); @@ -142,7 +147,7 @@ NodeParamViewItemBody::NodeParamViewItemBody(const QVector &inputs, // Add descriptor label ui_objects.main_label = new ClickableLabel(); - connect(ui_objects.main_label, &ClickableLabel::MouseClicked, this, &NodeParamViewItemBody::LabelClicked); + connect(ui_objects.main_label, &ClickableLabel::MouseDoubleClicked, this, &NodeParamViewItemBody::LabelDoubleClicked); if (input->IsArray()) { QHBoxLayout* array_label_layout = new QHBoxLayout(); @@ -164,7 +169,7 @@ NodeParamViewItemBody::NodeParamViewItemBody(const QVector &inputs, connect(sub_body, &NodeParamViewItemBody::KeyframeAdded, this, &NodeParamViewItemBody::KeyframeAdded); connect(sub_body, &NodeParamViewItemBody::KeyframeRemoved, this, &NodeParamViewItemBody::KeyframeRemoved); connect(sub_body, &NodeParamViewItemBody::RequestSetTime, this, &NodeParamViewItemBody::RequestSetTime); - connect(sub_body, &NodeParamViewItemBody::InputClicked, this, &NodeParamViewItemBody::InputClicked); + connect(sub_body, &NodeParamViewItemBody::InputDoubleClicked, this, &NodeParamViewItemBody::InputDoubleClicked); connect(sub_body, &NodeParamViewItemBody::RequestSelectNode, this, &NodeParamViewItemBody::RequestSelectNode); } else { content_layout->addWidget(ui_objects.main_label, row_count, 0); @@ -330,12 +335,13 @@ void NodeParamViewItemBody::InputAddedKeyframe(NodeKeyframePtr key) InputAddedKeyframeInternal(input, key); } -void NodeParamViewItemBody::LabelClicked() +void NodeParamViewItemBody::LabelDoubleClicked() { QMap::const_iterator iterator; + for (iterator=input_ui_map_.begin(); iterator!=input_ui_map_.end(); iterator++) { - if (iterator.value().connected_label == sender()) { - emit InputClicked(iterator.key()); + if (iterator.value().main_label == sender()) { + emit InputDoubleClicked(iterator.key()); return; } } diff --git a/app/widget/nodeparamview/nodeparamviewitem.h b/app/widget/nodeparamview/nodeparamviewitem.h index 5963391ba..5209d68ed 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.h +++ b/app/widget/nodeparamview/nodeparamviewitem.h @@ -64,7 +64,7 @@ signals: void RequestSetTime(const rational& time); - void InputClicked(NodeInput* input); + void InputDoubleClicked(NodeInput* input); void RequestSelectNode(const QList& node); @@ -93,7 +93,7 @@ private slots: void InputAddedKeyframe(NodeKeyframePtr key); - void LabelClicked(); + void LabelDoubleClicked(); void ConnectionClicked(); @@ -109,6 +109,8 @@ public: void SetTime(const rational& time); + Node* GetNode() const; + public slots: void SignalAllKeyframes(); @@ -119,7 +121,7 @@ signals: void RequestSetTime(const rational& time); - void InputClicked(NodeInput* input); + void InputDoubleClicked(NodeInput* input); void RequestSelectNode(const QList& node); diff --git a/app/window/mainwindow/mainwindow.cpp b/app/window/mainwindow/mainwindow.cpp index 1a83578e5..4c192af99 100644 --- a/app/window/mainwindow/mainwindow.cpp +++ b/app/window/mainwindow/mainwindow.cpp @@ -73,27 +73,17 @@ MainWindow::MainWindow(QWidget *parent) : AppendProjectPanel(); tool_panel_ = PanelManager::instance()->CreatePanel(this); task_man_panel_ = PanelManager::instance()->CreatePanel(this); - curve_panel_ = PanelManager::instance()->CreatePanel(this); AppendTimelinePanel(); audio_monitor_panel_ = PanelManager::instance()->CreatePanel(this); // Make connections to sequence viewer connect(node_panel_, &NodePanel::SelectionChanged, param_panel_, &ParamPanel::SetNodes); - connect(param_panel_, &ParamPanel::SelectedInputChanged, curve_panel_, &CurvePanel::SetInput); - connect(param_panel_, &ParamPanel::TimebaseChanged, curve_panel_, &CurvePanel::SetTimebase); - connect(param_panel_, &ParamPanel::TimeTargetChanged, curve_panel_, &CurvePanel::SetTimeTarget); connect(param_panel_, &ParamPanel::RequestSelectNode, node_panel_, &NodePanel::Select); - connect(sequence_viewer_panel_, &SequenceViewerPanel::TimeChanged, param_panel_, &ParamPanel::SetTime); - connect(sequence_viewer_panel_, &SequenceViewerPanel::TimeChanged, curve_panel_, &CurvePanel::SetTime); - connect(param_panel_, &ParamPanel::TimeChanged, sequence_viewer_panel_, &SequenceViewerPanel::SetTime); - connect(param_panel_, &ParamPanel::TimeChanged, curve_panel_, &CurvePanel::SetTime); - connect(curve_panel_, &CurvePanel::TimeChanged, sequence_viewer_panel_, &SequenceViewerPanel::SetTime); - connect(curve_panel_, &CurvePanel::TimeChanged, param_panel_, &ParamPanel::SetTime); - + connect(sequence_viewer_panel_, &SequenceViewerPanel::TimeChanged, param_panel_, &ParamPanel::SetTimestamp); + connect(param_panel_, &ParamPanel::TimeChanged, sequence_viewer_panel_, &SequenceViewerPanel::SetTimestamp); connect(PanelManager::instance(), &PanelManager::FocusedPanelChanged, this, &MainWindow::FocusedPanelChanged); sequence_viewer_panel_->ConnectTimeBasedPanel(param_panel_); - sequence_viewer_panel_->ConnectTimeBasedPanel(curve_panel_); footage_viewer_panel_->ConnectPixelSamplerPanel(pixel_sampler_panel_); sequence_viewer_panel_->ConnectPixelSamplerPanel(pixel_sampler_panel_); @@ -239,24 +229,35 @@ void MainWindow::FolderOpen(Project* p, Item *i, bool floating) // If the panel is closed, just destroy it panel->SetSignalInsteadOfClose(true); - connect(panel, &ProjectPanel::CloseRequested, this, &MainWindow::FolderCloseRequested); + panel->setProperty("parent_list", reinterpret_cast(&folder_panels_)); + connect(panel, &ProjectPanel::CloseRequested, this, &MainWindow::FloatingPanelCloseRequested); folder_panels_.append(panel); } ScopePanel *MainWindow::AppendScopePanel() { - ScopePanel* panel = PanelManager::instance()->CreatePanel(this); + return AppendFloatingPanelInternal(scope_panels_); +} - SetUniquePanelID(panel, scope_panels_); +CurvePanel *MainWindow::AppendCurvePanel() +{ + CurvePanel* p = AppendFloatingPanelInternal(curve_panels_); - panel->setFloating(true); - panel->show(); + sequence_viewer_panel_->ConnectTimeBasedPanel(p); - panel->SetSignalInsteadOfClose(true); - connect(panel, &ScopePanel::CloseRequested, this, &MainWindow::ScopeCloseRequested); + return p; - return panel; + /*connect(panel, &TimelinePanel::TimeChanged, curve_panel_, &CurvePanel::SetTime); + connect(curve_panel_, &CurvePanel::TimeChanged, panel, &TimelinePanel::SetTime); + connect(param_panel_, &ParamPanel::SelectedInputChanged, curve_panel_, &CurvePanel::SetInput); + connect(param_panel_, &ParamPanel::TimebaseChanged, curve_panel_, &CurvePanel::SetTimebase); + connect(param_panel_, &ParamPanel::TimeTargetChanged, curve_panel_, &CurvePanel::SetTimeTarget); + connect(param_panel_, &ParamPanel::TimeChanged, curve_panel_, &CurvePanel::SetTime); + connect(curve_panel_, &CurvePanel::TimeChanged, sequence_viewer_panel_, &SequenceViewerPanel::SetTime); + connect(curve_panel_, &CurvePanel::TimeChanged, param_panel_, &ParamPanel::SetTime); + sequence_viewer_panel_->ConnectTimeBasedPanel(curve_panel_); + connect(sequence_viewer_panel_, &SequenceViewerPanel::TimeChanged, curve_panel_, &CurvePanel::SetTime);*/ } void MainWindow::SetFullscreen(bool fullscreen) @@ -413,19 +414,14 @@ void MainWindow::ProjectCloseRequested() Core::instance()->CloseProject(Core::instance()->GetSharedPtrFromProject(p), true); } -void MainWindow::FolderCloseRequested() +void MainWindow::FloatingPanelCloseRequested() { - ProjectPanel* panel = static_cast(sender()); + PanelWidget* panel = static_cast(sender()); - folder_panels_.removeOne(panel); - panel->deleteLater(); -} + quintptr list_ptr = panel->property("parent_list").value(); + QList* list = reinterpret_cast< QList* >(list_ptr); -void MainWindow::ScopeCloseRequested() -{ - ScopePanel* panel = static_cast(sender()); - - scope_panels_.removeOne(panel); + list->removeOne(panel); panel->deleteLater(); } @@ -481,13 +477,11 @@ TimelinePanel* MainWindow::AppendTimelinePanel() TimelinePanel* panel = AppendPanelInternal(timeline_panels_); connect(panel, &PanelWidget::CloseRequested, this, &MainWindow::TimelineCloseRequested); - connect(panel, &TimelinePanel::TimeChanged, param_panel_, &ParamPanel::SetTime); - connect(panel, &TimelinePanel::TimeChanged, sequence_viewer_panel_, &SequenceViewerPanel::SetTime); - connect(panel, &TimelinePanel::TimeChanged, curve_panel_, &CurvePanel::SetTime); + connect(panel, &TimelinePanel::TimeChanged, param_panel_, &ParamPanel::SetTimestamp); + connect(panel, &TimelinePanel::TimeChanged, sequence_viewer_panel_, &SequenceViewerPanel::SetTimestamp); connect(panel, &TimelinePanel::SelectionChanged, node_panel_, &NodePanel::SelectWithDependencies); - connect(param_panel_, &ParamPanel::TimeChanged, panel, &TimelinePanel::SetTime); - connect(curve_panel_, &CurvePanel::TimeChanged, panel, &TimelinePanel::SetTime); - connect(sequence_viewer_panel_, &SequenceViewerPanel::TimeChanged, panel, &TimelinePanel::SetTime); + connect(param_panel_, &ParamPanel::TimeChanged, panel, &TimelinePanel::SetTimestamp); + connect(sequence_viewer_panel_, &SequenceViewerPanel::TimeChanged, panel, &TimelinePanel::SetTimestamp); connect(sequence_viewer_panel_->video_renderer(), &VideoRenderBackend::CachedTimeReady, panel->ruler(), &TimeRuler::CacheTimeReady); connect(sequence_viewer_panel_->video_renderer(), &VideoRenderBackend::RangeInvalidated, panel->ruler(), &TimeRuler::CacheInvalidatedRange); @@ -588,10 +582,6 @@ void MainWindow::SetDefaultLayout() timeline_panels_.first()->show(); addDockWidget(Qt::BottomDockWidgetArea, timeline_panels_.first()); - curve_panel_->hide(); - curve_panel_->setFloating(true); - addDockWidget(Qt::BottomDockWidgetArea, curve_panel_); - task_man_panel_->hide(); task_man_panel_->setFloating(true); addDockWidget(Qt::BottomDockWidgetArea, task_man_panel_); @@ -642,4 +632,22 @@ void MainWindow::SetUniquePanelID(T *panel, const QList &list) panel->setObjectName(panel->objectName().append(QString::number(list.size()))); } +template +T *MainWindow::AppendFloatingPanelInternal(QList &list) +{ + T* panel = PanelManager::instance()->CreatePanel(this); + + SetUniquePanelID(panel, list); + + panel->setFloating(true); + panel->show(); + + panel->SetSignalInsteadOfClose(true); + connect(panel, &PanelWidget::CloseRequested, this, &MainWindow::FloatingPanelCloseRequested); + + panel->setProperty("parent_list", reinterpret_cast(&list)); + + return panel; +} + OLIVE_NAMESPACE_EXIT diff --git a/app/window/mainwindow/mainwindow.h b/app/window/mainwindow/mainwindow.h index 4f2d52c6e..cbf2ccc26 100644 --- a/app/window/mainwindow/mainwindow.h +++ b/app/window/mainwindow/mainwindow.h @@ -68,6 +68,8 @@ public: ScopePanel* AppendScopePanel(); + CurvePanel* AppendCurvePanel(); + #ifdef Q_OS_WINDOWS void SetTaskbarButtonState(TBPFLAG flags); @@ -100,6 +102,9 @@ private: template T* AppendPanelInternal(QList& list); + template + T* AppendFloatingPanelInternal(QList& list); + template void SetUniquePanelID(T* panel, const QList& list); @@ -122,7 +127,7 @@ private: QList timeline_panels_; AudioMonitorPanel* audio_monitor_panel_; TaskManagerPanel* task_man_panel_; - CurvePanel* curve_panel_; + QList curve_panels_; PixelSamplerPanel* pixel_sampler_panel_; QList scope_panels_; @@ -141,9 +146,7 @@ private slots: void ProjectCloseRequested(); - void FolderCloseRequested(); - - void ScopeCloseRequested(); + void FloatingPanelCloseRequested(); void LoadLayoutInternal(QXmlStreamReader* reader, XMLNodeData *xml_data); From 981860b60b509484ec7e9f64a1035196644bb906 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 27 Apr 2020 00:55:40 +1000 Subject: [PATCH 06/10] fixed audio regression introduced with optimized node copy --- app/render/backend/audiorenderworker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/render/backend/audiorenderworker.cpp b/app/render/backend/audiorenderworker.cpp index b60417c37..c8d53c8b1 100644 --- a/app/render/backend/audiorenderworker.cpp +++ b/app/render/backend/audiorenderworker.cpp @@ -100,7 +100,7 @@ NodeValueTable AudioRenderWorker::RenderBlock(const TrackOutput *track, const Ti { // Save waveform to file - Block* src_block = static_cast(copy_map_->value(b)); + Block* src_block = static_cast(copy_map_->key(b)); QDir local_appdata_dir(Config::Current()["DiskCachePath"].toString()); QDir waveform_loc = local_appdata_dir.filePath(QStringLiteral("waveform")); waveform_loc.mkpath("."); From f61321d68bcab3d863f1202e1d33be1adff5514b Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 27 Apr 2020 01:01:12 +1000 Subject: [PATCH 07/10] nodes/timeline: correctly detect when tracks are removed from the graph Fixes several segfaults that could result from this occurring. --- app/node/output/track/track.cpp | 2 + app/node/output/track/track.h | 5 ++ app/node/output/track/tracklist.cpp | 69 +++++++++++-------- app/node/output/track/tracklist.h | 13 ++-- app/node/output/viewer/viewer.cpp | 8 +-- app/widget/timelinewidget/timelinewidget.cpp | 25 +++++-- app/widget/timelinewidget/timelinewidget.h | 1 + .../timelinewidget/trackview/trackview.cpp | 4 +- .../trackview/trackviewsplitter.cpp | 2 +- app/widget/timelinewidget/undo/undo.cpp | 8 +-- .../timelinewidget/view/timelineview.cpp | 18 ++--- 11 files changed, 92 insertions(+), 63 deletions(-) diff --git a/app/node/output/track/track.cpp b/app/node/output/track/track.cpp index aeb6be27c..ab77c7690 100644 --- a/app/node/output/track/track.cpp +++ b/app/node/output/track/track.cpp @@ -128,6 +128,8 @@ const int &TrackOutput::Index() void TrackOutput::SetIndex(const int &index) { index_ = index; + + emit IndexChanged(index); } Block *TrackOutput::BlockContainingTime(const rational &time) const diff --git a/app/node/output/track/track.h b/app/node/output/track/track.h index 11eec0f7b..6bc342256 100644 --- a/app/node/output/track/track.h +++ b/app/node/output/track/track.h @@ -185,6 +185,11 @@ signals: */ void MutedChanged(bool e); + /** + * @brief Signal emitted when the index has changed + */ + void IndexChanged(int i); + protected: private: diff --git a/app/node/output/track/tracklist.cpp b/app/node/output/track/tracklist.cpp index b3246f257..646a8309a 100644 --- a/app/node/output/track/tracklist.cpp +++ b/app/node/output/track/tracklist.cpp @@ -33,7 +33,6 @@ TrackList::TrackList(ViewerOutput *parent, const Timeline::TrackType &type, Node { connect(track_input, &NodeInputArray::SubParamEdgeAdded, this, &TrackList::TrackConnected); connect(track_input, &NodeInputArray::SubParamEdgeRemoved, this, &TrackList::TrackDisconnected); - connect(track_input, &NodeInputArray::SizeChanged, this, &TrackList::TrackListSizeChanged); } const Timeline::TrackType &TrackList::type() const @@ -51,38 +50,22 @@ void TrackList::TrackRemovedBlock(Block *block) emit BlockRemoved(block); } -void TrackList::TrackListSizeChanged(int size) -{ - int old_size = track_cache_.size(); - - track_cache_.resize(size); - - // Fill new slots with nullptr - for (int i=old_size;i &TrackList::Tracks() const +const QVector &TrackList::GetTracks() const { return track_cache_; } -TrackOutput *TrackList::TrackAt(int index) const +TrackOutput *TrackList::GetTrackAt(int index) const { - if (index < 0 || index >= track_cache_.size()) { - return nullptr; - } - return track_cache_.at(index); } -const rational &TrackList::TrackLength() const +const rational &TrackList::GetTotalLength() const { return total_length_; } -int TrackList::TrackCount() const +int TrackList::GetTrackCount() const { return track_cache_.size(); } @@ -98,7 +81,7 @@ TrackOutput* TrackList::AddTrack() NodeParam::ConnectEdge(track->output(), track_input_->At(track_input_->GetSize() - 1)); - // FIXME: Test code only + // Auto-merge with previous track if (track_input_->GetSize() > 1) { TrackOutput* last_track = nullptr; @@ -142,7 +125,6 @@ TrackOutput* TrackList::AddTrack() } } } - // End test code return track; } @@ -173,14 +155,38 @@ void TrackList::TrackConnected(NodeEdgePtr edge) if (connected_node->IsTrack()) { TrackOutput* connected_track = static_cast(connected_node); - track_cache_.replace(track_index, connected_track); + { + // Find "real" index + TrackOutput* next = nullptr; + for (int i=track_index+1; iGetSize(); i++) { + Node* that_track = track_input_->At(i)->get_connected_node(); + + if (that_track->IsTrack()) { + next = static_cast(that_track); + break; + } + } + + int track_index; + + if (next) { + // Insert track before "next" + track_index = track_cache_.indexOf(next); + track_cache_.insert(track_index, connected_track); + } else { + // No "next", this track must come at the end + track_index = track_cache_.size(); + track_cache_.append(connected_track); + } + + connected_track->SetIndex(track_index); + } connect(connected_track, &TrackOutput::BlockAdded, this, &TrackList::TrackAddedBlock); connect(connected_track, &TrackOutput::BlockRemoved, this, &TrackList::TrackRemovedBlock); connect(connected_track, &TrackOutput::TrackLengthChanged, this, &TrackList::UpdateTotalLength); connect(connected_track, &TrackOutput::TrackHeightChanged, this, &TrackList::TrackHeightChangedSlot); - connected_track->SetIndex(track_index); connected_track->set_track_type(type_); emit TrackListChanged(); @@ -201,10 +207,17 @@ void TrackList::TrackDisconnected(NodeEdgePtr edge) Q_ASSERT(track_index >= 0); Node* connected_node = edge->output()->parentNode(); - TrackOutput* track = connected_node->IsTrack() ? static_cast(connected_node) : nullptr; - if (track) { - track_cache_.replace(track_index, nullptr); + if (connected_node->IsTrack()) { + TrackOutput* track = static_cast(connected_node); + + int index_of_track = track_cache_.indexOf(track); + track_cache_.removeAt(index_of_track); + + // Update indices for all subsequent tracks + for (int i=index_of_track; iSetIndex(i); + } // Traverse through Tracks uncaching and disconnecting them emit TrackRemoved(track); diff --git a/app/node/output/track/tracklist.h b/app/node/output/track/tracklist.h index 283394909..44c380cc9 100644 --- a/app/node/output/track/tracklist.h +++ b/app/node/output/track/tracklist.h @@ -38,17 +38,17 @@ public: const Timeline::TrackType& type() const; - const QVector& Tracks() const; + const QVector& GetTracks() const; - TrackOutput* TrackAt(int index) const; + TrackOutput* GetTrackAt(int index) const; TrackOutput *AddTrack(); void RemoveTrack(); - const rational& TrackLength() const; + const rational& GetTotalLength() const; - int TrackCount() const; + int GetTrackCount() const; NodeGraph* GetParentGraph() const; @@ -100,11 +100,6 @@ private slots: */ void TrackRemovedBlock(Block* block); - /** - * @brief Slot for when the count of tracks in the track input changes - */ - void TrackListSizeChanged(int size); - /** * @brief Slot for when any of the track's length changes so we can update the length of the tracklist */ diff --git a/app/node/output/viewer/viewer.cpp b/app/node/output/viewer/viewer.cpp index 665f287f6..e06c5443d 100644 --- a/app/node/output/viewer/viewer.cpp +++ b/app/node/output/viewer/viewer.cpp @@ -167,10 +167,8 @@ void ViewerOutput::UpdateTrackCache() track_cache_.clear(); foreach (TrackList* list, track_lists_) { - foreach (TrackOutput* track, list->Tracks()) { - if (track) { - track_cache_.append(track); - } + foreach (TrackOutput* track, list->GetTracks()) { + track_cache_.append(track); } } } @@ -193,7 +191,7 @@ void ViewerOutput::UpdateLength(const rational &length) rational new_length = 0; foreach (TrackList* list, track_lists_) { - new_length = qMax(new_length, list->TrackLength()); + new_length = qMax(new_length, list->GetTotalLength()); } if (new_length != timeline_length_) { diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index 0ce7604d8..41a1da410 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -241,7 +241,7 @@ void TimelineWidget::ConnectNodeInternal(ViewerOutput *n) view->ConnectTrackList(track_list); // Defer to the track to make all the block UI items necessary - foreach (TrackOutput* track, n->track_list(track_type)->Tracks()) { + foreach (TrackOutput* track, n->track_list(track_type)->GetTracks()) { AddTrack(track, track_type); } } @@ -881,7 +881,7 @@ void TimelineWidget::InsertGapsAt(const rational &earliest_point, const rational TrackOutput *TimelineWidget::GetTrackFromReference(const TrackReference &ref) { - return GetConnectedNode()->track_list(ref.type())->TrackAt(ref.index()); + return GetConnectedNode()->track_list(ref.type())->GetTrackAt(ref.index()); } int TimelineWidget::GetTrackY(const TrackReference &ref) @@ -1017,9 +1017,7 @@ void TimelineWidget::AddBlock(Block *block, TrackReference track) void TimelineWidget::RemoveBlock(Block *block) { - delete block_items_[block]; - - block_items_.remove(block); + delete block_items_.take(block); } void TimelineWidget::AddTrack(TrackOutput *track, Timeline::TrackType type) @@ -1027,15 +1025,32 @@ void TimelineWidget::AddTrack(TrackOutput *track, Timeline::TrackType type) foreach (Block* b, track->Blocks()) { AddBlock(b, TrackReference(type, track->Index())); } + + connect(track, &TrackOutput::IndexChanged, this, &TimelineWidget::TrackIndexChanged); } void TimelineWidget::RemoveTrack(TrackOutput *track) { + disconnect(track, &TrackOutput::IndexChanged, this, &TimelineWidget::TrackIndexChanged); + foreach (Block* b, track->Blocks()) { RemoveBlock(b); } } +void TimelineWidget::TrackIndexChanged() +{ + TrackOutput* track = static_cast(sender()); + TrackReference ref(track->track_type(), track->Index()); + + foreach (Block* b, track->Blocks()) { + TimelineViewBlockItem* item = block_items_.value(b); + + item->SetYCoords(GetTrackY(ref), GetTrackHeight(ref)); + item->SetTrack(ref); + } +} + void TimelineWidget::ViewSelectionChanged() { if (rubberband_.isVisible()) { diff --git a/app/widget/timelinewidget/timelinewidget.h b/app/widget/timelinewidget/timelinewidget.h index a00027602..6f7b6a41c 100644 --- a/app/widget/timelinewidget/timelinewidget.h +++ b/app/widget/timelinewidget/timelinewidget.h @@ -486,6 +486,7 @@ private slots: void AddTrack(TrackOutput* track, Timeline::TrackType type); void RemoveTrack(TrackOutput* track); + void TrackIndexChanged(); void ViewSelectionChanged(); diff --git a/app/widget/timelinewidget/trackview/trackview.cpp b/app/widget/timelinewidget/trackview/trackview.cpp index 1d0721ea1..06d34f136 100644 --- a/app/widget/timelinewidget/trackview/trackview.cpp +++ b/app/widget/timelinewidget/trackview/trackview.cpp @@ -81,7 +81,7 @@ void TrackView::ConnectTrackList(TrackList *list) list_ = list; if (list_ != nullptr) { - foreach (TrackOutput* track, list_->Tracks()) { + foreach (TrackOutput* track, list_->GetTracks()) { TrackViewItem* item = new TrackViewItem(track); items_.append(item); splitter_->Insert(track->Index(), track->GetTrackHeight(), item); @@ -120,7 +120,7 @@ void TrackView::ScrollbarRangeChanged(int, int max) void TrackView::TrackHeightChanged(int index, int height) { - list_->TrackAt(index)->SetTrackHeight(height); + list_->GetTrackAt(index)->SetTrackHeight(height); } void TrackView::InsertTrack(TrackOutput *track) diff --git a/app/widget/timelinewidget/trackview/trackviewsplitter.cpp b/app/widget/timelinewidget/trackview/trackviewsplitter.cpp index b8895876e..108d70039 100644 --- a/app/widget/timelinewidget/trackview/trackviewsplitter.cpp +++ b/app/widget/timelinewidget/trackview/trackviewsplitter.cpp @@ -141,7 +141,7 @@ void TrackViewSplitter::Remove(int index) QList sz = sizes(); if (alignment_ == Qt::AlignBottom) { - index = count() - index; + index = count() - 1 - index; } sz.removeAt(index); diff --git a/app/widget/timelinewidget/undo/undo.cpp b/app/widget/timelinewidget/undo/undo.cpp index 8ba10b15e..992a69a89 100644 --- a/app/widget/timelinewidget/undo/undo.cpp +++ b/app/widget/timelinewidget/undo/undo.cpp @@ -347,13 +347,13 @@ void TrackPlaceBlockCommand::redo_internal() added_track_count_ = 0; // Get track (or make it if necessary) - while (track_index_ >= timeline_->Tracks().size()) { + while (track_index_ >= timeline_->GetTracks().size()) { timeline_->AddTrack(); added_track_count_++; } - track_ = timeline_->TrackAt(track_index_); + track_ = timeline_->GetTrackAt(track_index_); append_ = (in_ >= track_->track_length()); @@ -610,7 +610,7 @@ void TrackCleanGapsCommand::redo_internal() GapBlock* on_gap = nullptr; QList consecutive_gaps; - TrackOutput* track = track_list_->TrackAt(track_index_); + TrackOutput* track = track_list_->GetTrackAt(track_index_); foreach (Block* b, track->Blocks()) { if (b) { @@ -657,7 +657,7 @@ void TrackCleanGapsCommand::redo_internal() void TrackCleanGapsCommand::undo_internal() { - TrackOutput* track = track_list_->TrackAt(track_index_); + TrackOutput* track = track_list_->GetTrackAt(track_index_); // Restored removed end gaps foreach (GapBlock* gap, removed_end_gaps_) { diff --git a/app/widget/timelinewidget/view/timelineview.cpp b/app/widget/timelinewidget/view/timelineview.cpp index 0970cb2bf..04dc87864 100644 --- a/app/widget/timelinewidget/view/timelineview.cpp +++ b/app/widget/timelinewidget/view/timelineview.cpp @@ -189,11 +189,7 @@ void TimelineView::drawBackground(QPainter *painter, const QRectF &rect) int line_y = 0; - foreach (TrackOutput* track, connected_track_list_->Tracks()) { - if (!track) { - continue; - } - + foreach (TrackOutput* track, connected_track_list_->GetTracks()) { line_y += track->GetTrackHeight(); // One px gap between tracks @@ -294,9 +290,9 @@ int TimelineView::GetHeightOfAllTracks() const { if (connected_track_list_) { if (alignment() & Qt::AlignTop) { - return GetTrackY(connected_track_list_->TrackCount()); + return GetTrackY(connected_track_list_->GetTrackCount()); } else { - return GetTrackY(connected_track_list_->TrackCount() - 1); + return GetTrackY(connected_track_list_->GetTrackCount() - 1); } } else { return 0; @@ -305,6 +301,10 @@ int TimelineView::GetHeightOfAllTracks() const int TimelineView::GetTrackY(int track_index) const { + if (!connected_track_list_ || !connected_track_list_->GetTrackCount()) { + return 0; + } + int y = 0; if (alignment() & Qt::AlignBottom) { @@ -327,11 +327,11 @@ int TimelineView::GetTrackY(int track_index) const int TimelineView::GetTrackHeight(int track_index) const { - if (!connected_track_list_ || track_index >= connected_track_list_->TrackCount()) { + if (!connected_track_list_ || track_index >= connected_track_list_->GetTrackCount()) { return TrackOutput::GetDefaultTrackHeight(); } - return connected_track_list_->TrackAt(track_index)->GetTrackHeight(); + return connected_track_list_->GetTrackAt(track_index)->GetTrackHeight(); } QPoint TimelineView::GetScrollCoordinates() const From ac3f49a845245666be4313a0411f205d13302d0d Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 27 Apr 2020 04:11:24 +1000 Subject: [PATCH 08/10] nodes: moved matrix input on "video input" elsewhere Rather than plugging a matrix into the video input node, the matrix is now multiplied by the video input using a math node. This is probably more sensible from a user perspective. This also means the renderer is tolerant of texture sizes that are not equal to the sequence size, however most nodes will downsample the texture to the sequence size (and if not, it will be downsampled once it is cached). Textures will still *always* be in reference space and the sequence's format. This seems like the best compromise between backend and frontend congruity. --- app/node/CMakeLists.txt | 2 +- app/node/factory.cpp | 6 +- app/node/factory.h | 2 +- .../{distort => generator}/CMakeLists.txt | 2 +- .../matrix}/CMakeLists.txt | 4 +- .../matrix/matrix.cpp} | 30 +-- .../transform.h => generator/matrix/matrix.h} | 8 +- app/node/input/media/audio/audio.cpp | 4 - app/node/input/media/audio/audio.h | 3 +- app/node/input/media/media.cpp | 2 +- app/node/input/media/video/video.cpp | 33 --- app/node/input/media/video/video.h | 15 +- app/node/math/math/math.cpp | 194 ++++++++------- app/node/math/math/math.h | 16 +- app/node/output/track/track.cpp | 228 +++++++++--------- app/node/output/track/track.h | 9 +- app/node/output/track/tracklist.cpp | 2 +- app/render/backend/opengl/openglproxy.cpp | 4 +- app/render/backend/renderbackend.cpp | 2 +- app/shaders/{videoinput.vert => matrix.vert} | 8 +- app/shaders/shaders.qrc | 3 +- app/shaders/videoinput.frag | 11 - .../nodeparamviewconnectedlabel.cpp | 2 + app/widget/timelinewidget/tool/import.cpp | 19 +- app/widget/timelinewidget/undo/undo.cpp | 50 ++-- .../view/timelineviewblockitem.cpp | 6 +- 26 files changed, 307 insertions(+), 358 deletions(-) rename app/node/{distort => generator}/CMakeLists.txt (96%) rename app/node/{distort/transform => generator/matrix}/CMakeLists.txt (90%) rename app/node/{distort/transform/transform.cpp => generator/matrix/matrix.cpp} (81%) rename app/node/{distort/transform/transform.h => generator/matrix/matrix.h} (92%) rename app/shaders/{videoinput.vert => matrix.vert} (79%) delete mode 100644 app/shaders/videoinput.frag diff --git a/app/node/CMakeLists.txt b/app/node/CMakeLists.txt index 89780c8dc..965982241 100644 --- a/app/node/CMakeLists.txt +++ b/app/node/CMakeLists.txt @@ -16,7 +16,7 @@ add_subdirectory(audio) add_subdirectory(block) -add_subdirectory(distort) +add_subdirectory(generator) add_subdirectory(input) add_subdirectory(math) add_subdirectory(output) diff --git a/app/node/factory.cpp b/app/node/factory.cpp index 2e2f79652..09791c6a5 100644 --- a/app/node/factory.cpp +++ b/app/node/factory.cpp @@ -25,7 +25,7 @@ #include "block/clip/clip.h" #include "block/gap/gap.h" #include "block/transition/externaltransition.h" -#include "distort/transform/transform.h" +#include "generator/matrix/matrix.h" #include "input/media/video/video.h" #include "input/media/audio/audio.h" #include "input/time/timeinput.h" @@ -141,8 +141,8 @@ Node *NodeFactory::CreateInternal(const NodeFactory::InternalID &id) return new ClipBlock(); case kGapBlock: return new GapBlock(); - case kTransformDistort: - return new TransformDistort(); + case kMatrixGenerator: + return new MatrixGenerator(); case kVideoInput: return new VideoInput(); case kAudioInput: diff --git a/app/node/factory.h b/app/node/factory.h index 9cd683781..c6329de7e 100644 --- a/app/node/factory.h +++ b/app/node/factory.h @@ -36,7 +36,7 @@ public: kClipBlock, kGapBlock, kAudioInput, - kTransformDistort, + kMatrixGenerator, kVideoInput, kTrackOutput, kAudioVolume, diff --git a/app/node/distort/CMakeLists.txt b/app/node/generator/CMakeLists.txt similarity index 96% rename from app/node/distort/CMakeLists.txt rename to app/node/generator/CMakeLists.txt index f43e36675..9e8d25261 100644 --- a/app/node/distort/CMakeLists.txt +++ b/app/node/generator/CMakeLists.txt @@ -14,7 +14,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -add_subdirectory(transform) +add_subdirectory(matrix) set(OLIVE_SOURCES ${OLIVE_SOURCES} diff --git a/app/node/distort/transform/CMakeLists.txt b/app/node/generator/matrix/CMakeLists.txt similarity index 90% rename from app/node/distort/transform/CMakeLists.txt rename to app/node/generator/matrix/CMakeLists.txt index bfa0cfc5a..b986f2aca 100644 --- a/app/node/distort/transform/CMakeLists.txt +++ b/app/node/generator/matrix/CMakeLists.txt @@ -16,7 +16,7 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} - node/distort/transform/transform.h - node/distort/transform/transform.cpp + node/generator/matrix/matrix.h + node/generator/matrix/matrix.cpp PARENT_SCOPE ) diff --git a/app/node/distort/transform/transform.cpp b/app/node/generator/matrix/matrix.cpp similarity index 81% rename from app/node/distort/transform/transform.cpp rename to app/node/generator/matrix/matrix.cpp index 649aa065e..c967869ed 100644 --- a/app/node/distort/transform/transform.cpp +++ b/app/node/generator/matrix/matrix.cpp @@ -18,14 +18,14 @@ ***/ -#include "transform.h" +#include "matrix.h" #include #include OLIVE_NAMESPACE_ENTER -TransformDistort::TransformDistort() +MatrixGenerator::MatrixGenerator() { position_input_ = new NodeInput("pos_in", NodeParam::kVec2); AddInput(position_input_); @@ -42,39 +42,39 @@ TransformDistort::TransformDistort() uniform_scale_input_ = new NodeInput("uniform_scale_in", NodeParam::kBoolean, true); uniform_scale_input_->set_is_keyframable(false); uniform_scale_input_->SetConnectable(false); - connect(uniform_scale_input_, &NodeInput::ValueChanged, this, &TransformDistort::UniformScaleChanged); + connect(uniform_scale_input_, &NodeInput::ValueChanged, this, &MatrixGenerator::UniformScaleChanged); AddInput(uniform_scale_input_); anchor_input_ = new NodeInput("anchor_in", NodeParam::kVec2); AddInput(anchor_input_); } -Node *TransformDistort::copy() const +Node *MatrixGenerator::copy() const { - return new TransformDistort(); + return new MatrixGenerator(); } -QString TransformDistort::Name() const +QString MatrixGenerator::Name() const { - return tr("Transform"); + return tr("Orthographic Matrix"); } -QString TransformDistort::id() const +QString MatrixGenerator::id() const { return QStringLiteral("org.olivevideoeditor.Olive.transform"); } -QString TransformDistort::Category() const +QString MatrixGenerator::Category() const { - return tr("Distort"); + return tr("Generator"); } -QString TransformDistort::Description() const +QString MatrixGenerator::Description() const { - return tr("Apply transformations to position, rotation, and scale."); + return tr("Generate an orthographic matrix using position, rotation, and scale."); } -void TransformDistort::Retranslate() +void MatrixGenerator::Retranslate() { position_input_->set_name(tr("Position")); rotation_input_->set_name(tr("Rotation")); @@ -83,7 +83,7 @@ void TransformDistort::Retranslate() anchor_input_->set_name(tr("Anchor Point")); } -NodeValueTable TransformDistort::Value(NodeValueDatabase &value) const +NodeValueTable MatrixGenerator::Value(NodeValueDatabase &value) const { QMatrix4x4 mat; @@ -110,7 +110,7 @@ NodeValueTable TransformDistort::Value(NodeValueDatabase &value) const return output; } -void TransformDistort::UniformScaleChanged() +void MatrixGenerator::UniformScaleChanged() { scale_input_->set_property("disabley", uniform_scale_input_->get_standard_value().toBool()); } diff --git a/app/node/distort/transform/transform.h b/app/node/generator/matrix/matrix.h similarity index 92% rename from app/node/distort/transform/transform.h rename to app/node/generator/matrix/matrix.h index 6e3acd6a4..53c5c5356 100644 --- a/app/node/distort/transform/transform.h +++ b/app/node/generator/matrix/matrix.h @@ -18,18 +18,18 @@ ***/ -#ifndef TRANSFORMDISTORT_H -#define TRANSFORMDISTORT_H +#ifndef MATRIXGENERATOR_H +#define MATRIXGENERATOR_H #include "node/node.h" OLIVE_NAMESPACE_ENTER -class TransformDistort : public Node +class MatrixGenerator : public Node { Q_OBJECT public: - TransformDistort(); + MatrixGenerator(); virtual Node* copy() const override; diff --git a/app/node/input/media/audio/audio.cpp b/app/node/input/media/audio/audio.cpp index c51b021d5..0dadd4b60 100644 --- a/app/node/input/media/audio/audio.cpp +++ b/app/node/input/media/audio/audio.cpp @@ -22,10 +22,6 @@ OLIVE_NAMESPACE_ENTER -AudioInput::AudioInput() -{ -} - Node *AudioInput::copy() const { return new AudioInput(); diff --git a/app/node/input/media/audio/audio.h b/app/node/input/media/audio/audio.h index 2bc6db0ef..b28500c53 100644 --- a/app/node/input/media/audio/audio.h +++ b/app/node/input/media/audio/audio.h @@ -28,7 +28,7 @@ OLIVE_NAMESPACE_ENTER class AudioInput : public MediaInput { public: - AudioInput(); + AudioInput() = default; virtual Node* copy() const override; @@ -36,7 +36,6 @@ public: virtual QString id() const override; virtual QString Description() const override; -private: }; OLIVE_NAMESPACE_EXIT diff --git a/app/node/input/media/media.cpp b/app/node/input/media/media.cpp index ff371da53..b7bad0efc 100644 --- a/app/node/input/media/media.cpp +++ b/app/node/input/media/media.cpp @@ -67,7 +67,7 @@ NodeValueTable MediaInput::Value(NodeValueDatabase &value) const } // Push buffer to the top of the stack - NodeValue buffer = value[footage_input_].GetWithMeta(NodeParam::kSamples); + NodeValue buffer = value[footage_input_].GetWithMeta(NodeParam::kBuffer); if (buffer.type() != NodeParam::kNone) { table.Push(buffer); } diff --git a/app/node/input/media/video/video.cpp b/app/node/input/media/video/video.cpp index 2820cb292..e6caab973 100644 --- a/app/node/input/media/video/video.cpp +++ b/app/node/input/media/video/video.cpp @@ -31,12 +31,6 @@ OLIVE_NAMESPACE_ENTER -VideoInput::VideoInput() -{ - matrix_input_ = new NodeInput("matrix_in", NodeInput::kMatrix); - AddInput(matrix_input_); -} - Node *VideoInput::copy() const { return new VideoInput(); @@ -57,31 +51,4 @@ QString VideoInput::Description() const return tr("Import a video footage stream."); } -NodeInput *VideoInput::matrix_input() const -{ - return matrix_input_; -} - -Node::Capabilities VideoInput::GetCapabilities(const NodeValueDatabase &) const -{ - return kShader; -} - -QString VideoInput::ShaderVertexCode(const NodeValueDatabase&) const -{ - return ReadFileAsString(":/shaders/videoinput.vert"); -} - -QString VideoInput::ShaderFragmentCode(const NodeValueDatabase&) const -{ - return ReadFileAsString(":/shaders/videoinput.frag"); -} - -void VideoInput::Retranslate() -{ - MediaInput::Retranslate(); - - matrix_input_->set_name(tr("Transform")); -} - OLIVE_NAMESPACE_EXIT diff --git a/app/node/input/media/video/video.h b/app/node/input/media/video/video.h index fdeb109fe..23a5698c8 100644 --- a/app/node/input/media/video/video.h +++ b/app/node/input/media/video/video.h @@ -31,7 +31,7 @@ OLIVE_NAMESPACE_ENTER class VideoInput : public MediaInput { public: - VideoInput(); + VideoInput() = default; virtual Node* copy() const override; @@ -39,19 +39,6 @@ public: virtual QString id() const override; virtual QString Description() const override; - NodeInput* matrix_input() const; - - virtual Capabilities GetCapabilities(const NodeValueDatabase&) const override; - virtual QString ShaderVertexCode(const NodeValueDatabase&) const override; - virtual QString ShaderFragmentCode(const NodeValueDatabase&) const override; - - virtual void Retranslate() override; - -protected: - -private: - NodeInput* matrix_input_; - }; OLIVE_NAMESPACE_EXIT diff --git a/app/node/math/math/math.cpp b/app/node/math/math/math.cpp index c90764eed..93e802481 100644 --- a/app/node/math/math/math.cpp +++ b/app/node/math/math/math.cpp @@ -131,9 +131,9 @@ QString MathNode::ShaderFragmentCode(const NodeValueDatabase &input) const // Override the operation for this operation since we multiply texture COORDS by the matrix rather than NodeParam* tex_in = (type_a == NodeParam::kTexture) ? param_a_in_ : param_b_in_; - NodeParam* mat_in = (type_a == NodeParam::kTexture) ? param_b_in_ : param_a_in_; - operation = QStringLiteral("texture(%1, (vec4(ove_texcoord, 0.0, 1.0) * %2).xy)").arg(tex_in->id(), mat_in->id()); + // No-op frag shader (can we return QString() instead?) + operation = QStringLiteral("texture(%1, ove_texcoord)").arg(tex_in->id()); } else { switch (GetOperation()) { @@ -176,6 +176,25 @@ QString MathNode::ShaderFragmentCode(const NodeValueDatabase &input) const operation); } +QString MathNode::ShaderVertexCode(const NodeValueDatabase &input) const +{ + PairingCalculator calc(input[param_a_in_], input[param_b_in_]); + + if (calc.GetMostLikelyPairing() == kPairTextureMatrix && GetOperation() == kOpMultiply) { + + NodeParam::DataType type_a = calc.GetMostLikelyValueA().type(); + + // Override the operation for this operation since we multiply texture COORDS by the matrix rather than + NodeParam* tex_in = (type_a == NodeParam::kTexture) ? param_a_in_ : param_b_in_; + NodeParam* mat_in = (type_a == NodeParam::kTexture) ? param_b_in_ : param_a_in_; + + return ReadFileAsString(":/shaders/matrix.vert").arg(mat_in->id(), tex_in->id()); + + } + + return QString(); +} + NodeValue MathNode::InputValueFromTable(NodeInput *input, NodeValueDatabase &db, bool take) const { if (input == param_a_in_ || input == param_b_in_) { @@ -382,6 +401,11 @@ MathNode::Operation MathNode::GetOperation() const return static_cast(method_in_->get_standard_value().toInt()); } +void MathNode::SetOperation(MathNode::Operation o) +{ + method_in_->set_standard_value(o); +} + QString MathNode::GetShaderUniformType(const NodeParam::DataType &type) { switch (type) { @@ -405,6 +429,81 @@ QString MathNode::GetShaderVariableCall(const QString &input_id, const NodeParam return input_id; } +QVector4D MathNode::RetrieveVector(const NodeValue &val) +{ + // QVariant doesn't know that QVector*D can convert themselves so we do it here + switch (val.type()) { + case NodeParam::kVec2: + return val.data().value(); + case NodeParam::kVec3: + return val.data().value(); + case NodeParam::kVec4: + default: + return val.data().value(); + } +} + +void MathNode::PushVector(NodeValueTable *output, NodeParam::DataType type, const QVector4D &vec) +{ + switch (type) { + case NodeParam::kVec2: + output->Push(type, QVector2D(vec)); + break; + case NodeParam::kVec3: + output->Push(type, QVector3D(vec)); + break; + case NodeParam::kVec4: + output->Push(type, vec); + break; + default: + break; + } +} + +float MathNode::RetrieveNumber(const NodeValue &val) +{ + if (val.type() == NodeParam::kRational) { + return val.data().value().toDouble(); + } else { + return val.data().toFloat(); + } +} + +MathNode::PairingCalculator::PairingCalculator(const NodeValueTable &table_a, const NodeValueTable &table_b) +{ + QVector pair_likelihood_a = GetPairLikelihood(table_a); + QVector pair_likelihood_b = GetPairLikelihood(table_b); + + int weight_a = qMax(0, table_b.Count() - table_a.Count()); + int weight_b = qMax(0, table_a.Count() - table_b.Count()); + + QVector likelihoods(kPairCount); + + for (int i=0;i -1) { + if (most_likely_pairing_ == kPairNone + || likelihoods.at(i) > likelihoods.at(most_likely_pairing_)) { + most_likely_pairing_ = static_cast(i); + } + } + } + + if (most_likely_pairing_ != kPairNone) { + most_likely_value_a_ = table_a.At(pair_likelihood_a.at(most_likely_pairing_)); + most_likely_value_b_ = table_b.At(pair_likelihood_b.at(most_likely_pairing_)); + } +} + QVector MathNode::PairingCalculator::GetPairLikelihood(const NodeValueTable &table) { // FIXME: When we introduce a manual override, placing it here would be the least problematic @@ -448,88 +547,6 @@ QVector MathNode::PairingCalculator::GetPairLikelihood(const NodeValueTable return likelihood; } -MathNode::Pairing MathNode::PairingCalculator::GetMostLikelyPairingInternal(const QVector &a, - const QVector &b, - const int& weight_a, - const int& weight_b) -{ - QVector likelihoods(kPairCount); - - for (int i=0;i -1) { - if (pairing == kPairNone - || likelihoods.at(i) > likelihoods.at(pairing)) { - pairing = static_cast(i); - } - } - } - - return pairing; -} - -QVector4D MathNode::RetrieveVector(const NodeValue &val) -{ - // QVariant doesn't know that QVector*D can convert themselves so we do it here - switch (val.type()) { - case NodeParam::kVec2: - return val.data().value(); - case NodeParam::kVec3: - return val.data().value(); - case NodeParam::kVec4: - default: - return val.data().value(); - } -} - -void MathNode::PushVector(NodeValueTable *output, NodeParam::DataType type, const QVector4D &vec) -{ - switch (type) { - case NodeParam::kVec2: - output->Push(type, QVector2D(vec)); - break; - case NodeParam::kVec3: - output->Push(type, QVector3D(vec)); - break; - case NodeParam::kVec4: - output->Push(type, vec); - break; - default: - break; - } -} - -float MathNode::RetrieveNumber(const NodeValue &val) -{ - if (val.type() == NodeParam::kRational) { - return val.data().value().toDouble(); - } else { - return val.data().toFloat(); - } -} - -MathNode::PairingCalculator::PairingCalculator(const NodeValueTable &table_a, const NodeValueTable &table_b) : - table_a_(table_a), - table_b_(table_b) -{ - pair_likelihood_a_ = GetPairLikelihood(table_a_); - pair_likelihood_b_ = GetPairLikelihood(table_b_); - - most_likely_pairing_ = GetMostLikelyPairingInternal(pair_likelihood_a_, - pair_likelihood_b_, - qMax(0, table_b_.Count() - table_a_.Count()), - qMax(0, table_a_.Count() - table_b_.Count())); -} - bool MathNode::PairingCalculator::FoundMostLikelyPairing() const { return (most_likely_pairing_ > kPairNone && most_likely_pairing_ < kPairCount); @@ -542,17 +559,12 @@ MathNode::Pairing MathNode::PairingCalculator::GetMostLikelyPairing() const const NodeValue &MathNode::PairingCalculator::GetMostLikelyValueA() const { - return GetMostLikelyValue(table_a_, pair_likelihood_a_); + return most_likely_value_a_; } const NodeValue &MathNode::PairingCalculator::GetMostLikelyValueB() const { - return GetMostLikelyValue(table_b_, pair_likelihood_b_); -} - -const NodeValue& MathNode::PairingCalculator::GetMostLikelyValue(const NodeValueTable &table, const QVector &likelihood) const -{ - return table.At(likelihood.at(most_likely_pairing_)); + return most_likely_value_b_; } template diff --git a/app/node/math/math/math.h b/app/node/math/math/math.h index 306dfc487..1909c4bd5 100644 --- a/app/node/math/math/math.h +++ b/app/node/math/math/math.h @@ -42,6 +42,7 @@ public: virtual Capabilities GetCapabilities(const NodeValueDatabase&) const override; virtual QString ShaderID(const NodeValueDatabase&) const override; virtual QString ShaderFragmentCode(const NodeValueDatabase&) const override; + virtual QString ShaderVertexCode(const NodeValueDatabase&input) const override; virtual NodeValue InputValueFromTable(NodeInput* input, NodeValueDatabase &db, bool take) const override; @@ -53,7 +54,6 @@ public: NodeInput* param_a_in() const; NodeInput* param_b_in() const; -private: enum Operation { kOpAdd, kOpSubtract, @@ -63,7 +63,9 @@ private: }; Operation GetOperation() const; + void SetOperation(Operation o); +private: enum Pairing { kPairNone = -1, @@ -96,21 +98,13 @@ private: const NodeValue& GetMostLikelyValueB() const; private: - static Pairing GetMostLikelyPairingInternal(const QVector &a, const QVector &b, const int &weight_a, const int &weight_b); - static QVector GetPairLikelihood(const NodeValueTable& table); - const NodeValue &GetMostLikelyValue(const NodeValueTable& table, const QVector& likelihood) const; - Pairing most_likely_pairing_; - const NodeValueTable& table_a_; + NodeValue most_likely_value_a_; - const NodeValueTable& table_b_; - - QVector pair_likelihood_a_; - - QVector pair_likelihood_b_; + NodeValue most_likely_value_b_; }; diff --git a/app/node/output/track/track.cpp b/app/node/output/track/track.cpp index ab77c7690..24497f684 100644 --- a/app/node/output/track/track.cpp +++ b/app/node/output/track/track.cpp @@ -40,7 +40,6 @@ TrackOutput::TrackOutput() : AddInput(block_input_); connect(block_input_, &NodeInputArray::SubParamEdgeAdded, this, &TrackOutput::BlockConnected); connect(block_input_, &NodeInputArray::SubParamEdgeRemoved, this, &TrackOutput::BlockDisconnected); - connect(block_input_, &NodeInputArray::SizeChanged, this, &TrackOutput::BlockListSizeChanged); muted_input_ = new NodeInput("muted_in", NodeParam::kBoolean); muted_input_->set_is_keyframable(false); @@ -233,7 +232,7 @@ QList TrackOutput::BlocksAtTimeRange(const TimeRange &range) const return list; } -const QVector &TrackOutput::Blocks() const +const QList &TrackOutput::Blocks() const { return block_cache_; } @@ -265,16 +264,25 @@ void TrackOutput::InsertBlockAfter(Block *block, Block *before) void TrackOutput::PrependBlock(Block *block) { - InsertBlockAtIndex(block, 0); + BlockInvalidateCache(); + + block_input_->Prepend(); + NodeParam::ConnectEdge(block->output(), block_input_->First()); + + UnblockInvalidateCache(); + + // Everything has shifted at this point + InvalidateCache(TimeRange(0, track_length()), block_input_, block_input_); } void TrackOutput::InsertBlockAtIndex(Block *block, int index) { BlockInvalidateCache(); - block_input_->InsertAt(index); + int insert_index = GetInputIndexFromCacheIndex(index); + block_input_->InsertAt(insert_index); NodeParam::ConnectEdge(block->output(), - block_input_->At(index)); + block_input_->At(insert_index)); UnblockInvalidateCache(); @@ -285,10 +293,8 @@ void TrackOutput::AppendBlock(Block *block) { BlockInvalidateCache(); - int last_index = block_input_->GetSize(); block_input_->Append(); - NodeParam::ConnectEdge(block->output(), - block_input_->At(last_index)); + NodeParam::ConnectEdge(block->output(), block_input_->Last()); UnblockInvalidateCache(); @@ -312,9 +318,7 @@ void TrackOutput::RippleRemoveBlock(Block *block) rational remove_in = block->in(); - int index_of_block_to_remove = block_cache_.indexOf(block); - - block_input_->RemoveAt(index_of_block_to_remove); + block_input_->RemoveAt(GetInputIndexFromCacheIndex(block)); UnblockInvalidateCache(); @@ -325,7 +329,7 @@ void TrackOutput::ReplaceBlock(Block *old, Block *replace) { BlockInvalidateCache(); - int index_of_old_block = block_cache_.indexOf(old); + int index_of_old_block = GetInputIndexFromCacheIndex(old); NodeParam::DisconnectEdge(old->output(), block_input_->At(index_of_old_block)); @@ -432,108 +436,107 @@ void TrackOutput::SetLocked(bool e) void TrackOutput::UpdateInOutFrom(int index) { - Q_ASSERT(index >= 0); - Q_ASSERT(index < block_cache_.size()); - - rational new_track_length; - // Find block just before this one to find the last out point - for (int i=index-1;i>=0;i--) { + rational last_out = (index == 0) ? 0 : block_cache_.at(index - 1)->out(); + + // Iterate through all blocks updating their in/outs + for (int i=index; iout(); - break; - } - } + b->set_in(last_out); - for (int i=index;ilength(); - if (b) { - // Set in - b->set_in(new_track_length); + b->set_out(last_out); - // Set out - new_track_length += b->length(); - b->set_out(new_track_length); - - emit b->Refreshed(); - } + emit b->Refreshed(); } // Update track length - if (new_track_length != track_length_) { + if (last_out != track_length_) { rational old_track_length = track_length_; - track_length_ = new_track_length; + track_length_ = last_out; emit TrackLengthChanged(); - InvalidateCache(TimeRange(qMin(old_track_length, new_track_length), qMax(old_track_length, new_track_length)), + InvalidateCache(TimeRange(qMin(old_track_length, last_out), qMax(old_track_length, last_out)), block_input_, block_input_); } } -void TrackOutput::UpdatePreviousAndNextOfIndex(int index) +int TrackOutput::GetInputIndexFromCacheIndex(int cache_index) { - Block* ref = block_cache_.at(index); + return GetInputIndexFromCacheIndex(block_cache_.at(cache_index)); +} - Block* previous = nullptr; - Block* next = nullptr; - - // Find previous - for (int i=index-1;i>=0;i--) { - previous = block_cache_.at(i); - - if (previous) { - break; +int TrackOutput::GetInputIndexFromCacheIndex(Block *block) +{ + for (int i=0; iGetSize(); i++) { + if (block_input_->At(i)->get_connected_node() == block) { + return i; } } - // Find next - for (int i=index+1;iset_previous(previous); - ref->set_next(next); - - if (previous) - previous->set_next(ref); - - if (next) - next->set_previous(ref); - } else { - // Link previous and next together - if (previous) - previous->set_next(next); - - if (next) - next->set_previous(previous); - } + return -1; } void TrackOutput::BlockConnected(NodeEdgePtr edge) { - int block_index = block_input_->IndexOfSubParameter(edge->input()); - - Q_ASSERT(block_index >= 0); - + // Determine what node was just connected Node* connected_node = edge->output()->parentNode(); - Block* connected_block = connected_node->IsBlock() ? static_cast(connected_node) : nullptr; - block_cache_.replace(block_index, connected_block); - UpdatePreviousAndNextOfIndex(block_index); - UpdateInOutFrom(block_index); - if (connected_block) { - connect(connected_block, SIGNAL(LengthChanged(const rational&)), this, SLOT(BlockLengthChanged())); + // If this node is a block, we can do something with it + if (connected_node->IsBlock()) { + Block* connected_block = static_cast(connected_node); + + // See where this input falls in our internal "block cache" + Block* next = nullptr; + for (int i=block_input_->IndexOfSubParameter(edge->input())+1; iGetSize(); i++) { + Node* that_node = block_input_->At(i)->get_connected_node(); + + // If we find a block, this is the block that will follow the one just connected + if (that_node && that_node->IsBlock()) { + next = static_cast(that_node); + break; + } + } + + int real_block_index; + + // Either insert or append depending on if we found a "next" block + if (next) { + // Insert block before this next block + real_block_index = block_cache_.indexOf(next); + block_cache_.insert(real_block_index, connected_block); + + // Update values with next + next->set_previous(connected_block); + connected_block->set_next(next); + } else { + // No "next", this block must come at the end + real_block_index = block_cache_.size(); + block_cache_.append(connected_block); + + // Update next value + connected_block->set_next(nullptr); + } + + // For all blocks after the block we inserted (including it), update the "previous" and "next" + // fields as well as the in/out values + if (real_block_index == 0) { + connected_block->set_previous(nullptr); + } else { + Block* prev = block_cache_.at(real_block_index - 1); + + connected_block->set_previous(prev); + prev->set_next(connected_block); + } + + UpdateInOutFrom(real_block_index); + + // Make connections to this block + connect(connected_block, &Block::LengthChanged, this, &TrackOutput::BlockLengthChanged); emit BlockAdded(connected_block); } @@ -541,46 +544,43 @@ void TrackOutput::BlockConnected(NodeEdgePtr edge) void TrackOutput::BlockDisconnected(NodeEdgePtr edge) { - int block_index = block_input_->IndexOfSubParameter(edge->input()); - - Q_ASSERT(block_index >= 0); - - block_cache_.replace(block_index, nullptr); - UpdatePreviousAndNextOfIndex(block_index); - UpdateInOutFrom(block_index); - + // See what kind of node was just connected Node* connected_node = edge->output()->parentNode(); - Block* connected_block = connected_node->IsBlock() ? static_cast(connected_node) : nullptr; - if (connected_block) { - disconnect(connected_block, SIGNAL(LengthChanged(const rational&)), this, SLOT(BlockLengthChanged())); - // Update previous and next references + // If this was a block, we would have put it in our block cache in BlockConnected() + if (connected_node->IsBlock()) { + Block* connected_block = static_cast(connected_node); + + // Determine what index this block was in our cache and remove it + int index_of_block = block_cache_.indexOf(connected_block); + block_cache_.removeAt(index_of_block); + + // If there were blocks following this one, update their ins/outs + if (index_of_block < block_cache_.size()) { + UpdateInOutFrom(index_of_block); + } + + // Join the previous and next blocks together + if (connected_block->previous()) { + connected_block->previous()->set_next(connected_block->next()); + } + + if (connected_block->next()) { + connected_block->next()->set_previous(connected_block->previous()); + } + + disconnect(connected_block, &Block::LengthChanged, this, &TrackOutput::BlockLengthChanged); + emit BlockRemoved(connected_block); } } -void TrackOutput::BlockListSizeChanged(int size) -{ - int old_size = block_cache_.size(); - - block_cache_.resize(size); - - // Fill new slots with nullptr - for (int i=old_size;i(sender()); - int index = block_cache_.indexOf(b); - - Q_ASSERT(index >= 0); - - UpdateInOutFrom(index); + UpdateInOutFrom(block_cache_.indexOf(b)); } void TrackOutput::MutedInputValueChanged() diff --git a/app/node/output/track/track.h b/app/node/output/track/track.h index 6bc342256..89f58903a 100644 --- a/app/node/output/track/track.h +++ b/app/node/output/track/track.h @@ -82,7 +82,7 @@ public: Block* BlockAtTime(const rational& time) const; QList BlocksAtTimeRange(const TimeRange& range) const; - const QVector& Blocks() const; + const QList &Blocks() const; virtual void InvalidateCache(const TimeRange& range, NodeInput* from, NodeInput *source) override; @@ -195,9 +195,10 @@ protected: private: void UpdateInOutFrom(int index); - void UpdatePreviousAndNextOfIndex(int index); + int GetInputIndexFromCacheIndex(int cache_index); + int GetInputIndexFromCacheIndex(Block* block); - QVector block_cache_; + QList block_cache_; NodeInputArray* block_input_; @@ -222,8 +223,6 @@ private slots: void BlockDisconnected(NodeEdgePtr edge); - void BlockListSizeChanged(int size); - void BlockLengthChanged(); void MutedInputValueChanged(); diff --git a/app/node/output/track/tracklist.cpp b/app/node/output/track/tracklist.cpp index 646a8309a..743c1b50b 100644 --- a/app/node/output/track/tracklist.cpp +++ b/app/node/output/track/tracklist.cpp @@ -161,7 +161,7 @@ void TrackList::TrackConnected(NodeEdgePtr edge) for (int i=track_index+1; iGetSize(); i++) { Node* that_track = track_input_->At(i)->get_connected_node(); - if (that_track->IsTrack()) { + if (that_track && that_track->IsTrack()) { next = static_cast(that_track); break; } diff --git a/app/render/backend/opengl/openglproxy.cpp b/app/render/backend/opengl/openglproxy.cpp index 663096514..d1f4f7ddd 100644 --- a/app/render/backend/opengl/openglproxy.cpp +++ b/app/render/backend/opengl/openglproxy.cpp @@ -212,8 +212,8 @@ void OpenGLProxy::RunNodeAccelerated(const Node *node, const TimeRange &range, N if (!shader) { // Since we have shader code, compile it now - QString frag_code = node->ShaderFragmentCode(input_params); QString vert_code = node->ShaderVertexCode(input_params); + QString frag_code = node->ShaderFragmentCode(input_params); if (frag_code.isEmpty()) { frag_code = OpenGLShader::CodeDefaultFragment(); @@ -258,7 +258,7 @@ void OpenGLProxy::RunNodeAccelerated(const Node *node, const TimeRange &range, N NodeInput* input = static_cast(param); // Get value from database at this input - NodeValue meta_value = node->InputValueFromTable(input, input_params, true); + NodeValue meta_value = node->InputValueFromTable(input, input_params, false); const QVariant& value = meta_value.data(); NodeParam::DataType data_type; diff --git a/app/render/backend/renderbackend.cpp b/app/render/backend/renderbackend.cpp index 310a85431..38d07ad35 100644 --- a/app/render/backend/renderbackend.cpp +++ b/app/render/backend/renderbackend.cpp @@ -421,7 +421,7 @@ Node* RenderBackend::CopyNodeConnections(Node* src_node) void RenderBackend::CopyNodeMakeConnection(NodeInput* src_input, NodeInput* dst_input) { - qDebug() << "Copying input" << src_input->id() << "from" << src_input->parentNode()->id(); + //qDebug() << "Copying input" << src_input->id() << "from" << src_input->parentNode()->id(); if (src_input->IsConnected()) { Node* dst_node = CopyNodeConnections(src_input->get_connected_node()); diff --git a/app/shaders/videoinput.vert b/app/shaders/matrix.vert similarity index 79% rename from app/shaders/videoinput.vert rename to app/shaders/matrix.vert index 59efb9515..dcfc4741f 100644 --- a/app/shaders/videoinput.vert +++ b/app/shaders/matrix.vert @@ -1,8 +1,8 @@ #version 150 -uniform mat4 matrix_in; +uniform mat4 %1; -uniform vec2 footage_in_resolution; +uniform vec2 %2_resolution; uniform vec2 ove_resolution; in vec4 a_position; @@ -27,10 +27,10 @@ void main() { transform *= scale_mat4(vec3(1.0 / ove_resolution, 1.0)); // Multiply by received matrix - transform *= matrix_in; + transform *= %1; // Scale back out to footage size - transform *= scale_mat4(vec3(footage_in_resolution, 1.0)); + transform *= scale_mat4(vec3(%2_resolution, 1.0)); gl_Position = transform * a_position; ove_texcoord = a_texcoord; diff --git a/app/shaders/shaders.qrc b/app/shaders/shaders.qrc index ab4e87535..7e6f4876c 100644 --- a/app/shaders/shaders.qrc +++ b/app/shaders/shaders.qrc @@ -17,7 +17,6 @@ solid.xml stroke.frag stroke.xml - videoinput.frag - videoinput.vert + matrix.vert diff --git a/app/shaders/videoinput.frag b/app/shaders/videoinput.frag deleted file mode 100644 index 01eb57b2e..000000000 --- a/app/shaders/videoinput.frag +++ /dev/null @@ -1,11 +0,0 @@ -#version 150 - -uniform sampler2D footage_in; - -in vec2 ove_texcoord; - -out vec4 fragColor; - -void main(void) { - fragColor = texture(footage_in, ove_texcoord); -} diff --git a/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp b/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp index 9c48bc888..65ed2504d 100644 --- a/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp +++ b/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp @@ -42,6 +42,8 @@ NodeParamViewConnectedLabel::NodeParamViewConnectedLabel(NodeInput *input, QWidg connect(connected_to_lbl_, &ClickableLabel::MouseClicked, this, &NodeParamViewConnectedLabel::ConnectionClicked); layout->addWidget(connected_to_lbl_); + layout->addStretch(); + // Set up "link" font QFont link_font = connected_to_lbl_->font(); link_font.setUnderline(true); diff --git a/app/widget/timelinewidget/tool/import.cpp b/app/widget/timelinewidget/tool/import.cpp index 73264b243..fc8662f49 100644 --- a/app/widget/timelinewidget/tool/import.cpp +++ b/app/widget/timelinewidget/tool/import.cpp @@ -30,9 +30,10 @@ #include "core.h" #include "dialog/sequence/sequence.h" #include "node/audio/volume/volume.h" -#include "node/distort/transform/transform.h" +#include "node/generator/matrix/matrix.h" #include "node/input/media/audio/audio.h" #include "node/input/media/video/video.h" +#include "node/math/math/math.h" #include "project/item/sequence/sequence.h" #include "widget/nodeview/nodeviewundo.h" #include "window/mainwindow/mainwindow.h" @@ -397,15 +398,17 @@ void TimelineWidget::ImportTool::DropGhosts(bool insert) VideoInput* video_input = new VideoInput(); video_input->SetFootage(footage_stream); new NodeAddCommand(dst_graph, video_input, command); - new NodeEdgeAddCommand(video_input->output(), clip->texture_input(), command); - TransformDistort* transform = new TransformDistort(); - new NodeAddCommand(dst_graph, transform, command); - new NodeEdgeAddCommand(transform->output(), video_input->matrix_input(), command); + MatrixGenerator* matrix = new MatrixGenerator(); + new NodeAddCommand(dst_graph, matrix, command); - //OpacityNode* opacity = new OpacityNode(); - //NodeParam::ConnectEdge(opacity->texture_output(), clip->texture_input()); - //NodeParam::ConnectEdge(media->texture_output(), opacity->texture_input()); + MathNode* multiply = new MathNode(); + multiply->SetOperation(MathNode::kOpMultiply); + new NodeAddCommand(dst_graph, multiply, command); + + new NodeEdgeAddCommand(video_input->output(), multiply->param_a_in(), command); + new NodeEdgeAddCommand(matrix->output(), multiply->param_b_in(), command); + new NodeEdgeAddCommand(multiply->output(), clip->texture_input(), command); break; } case Stream::kAudio: diff --git a/app/widget/timelinewidget/undo/undo.cpp b/app/widget/timelinewidget/undo/undo.cpp index 992a69a89..6833b6c67 100644 --- a/app/widget/timelinewidget/undo/undo.cpp +++ b/app/widget/timelinewidget/undo/undo.cpp @@ -613,33 +613,31 @@ void TrackCleanGapsCommand::redo_internal() TrackOutput* track = track_list_->GetTrackAt(track_index_); foreach (Block* b, track->Blocks()) { - if (b) { - if (b->type() == Block::kGap) { - if (on_gap) { - consecutive_gaps.append(static_cast(b)); - } else { - on_gap = static_cast(b); - } - } else if (on_gap) { - merged_gaps_.append({on_gap, on_gap->length(), consecutive_gaps}); - - // Remove each gap and add to the length of the merged - // We can block the IC signal because merging gaps won't actually change anything - track->BlockInvalidateCache(); - rational new_gap_length = on_gap->length(); - foreach (GapBlock* gap, consecutive_gaps) { - track->RippleRemoveBlock(gap); - static_cast(track->parent())->TakeNode(gap, &memory_manager_); - - new_gap_length += gap->length(); - } - on_gap->set_length_and_media_out(new_gap_length); - track->UnblockInvalidateCache(); - - // Reset state - on_gap = nullptr; - consecutive_gaps.clear(); + if (b->type() == Block::kGap) { + if (on_gap) { + consecutive_gaps.append(static_cast(b)); + } else { + on_gap = static_cast(b); } + } else if (on_gap) { + merged_gaps_.append({on_gap, on_gap->length(), consecutive_gaps}); + + // Remove each gap and add to the length of the merged + // We can block the IC signal because merging gaps won't actually change anything + track->BlockInvalidateCache(); + rational new_gap_length = on_gap->length(); + foreach (GapBlock* gap, consecutive_gaps) { + track->RippleRemoveBlock(gap); + static_cast(track->parent())->TakeNode(gap, &memory_manager_); + + new_gap_length += gap->length(); + } + on_gap->set_length_and_media_out(new_gap_length); + track->UnblockInvalidateCache(); + + // Reset state + on_gap = nullptr; + consecutive_gaps.clear(); } } diff --git a/app/widget/timelinewidget/view/timelineviewblockitem.cpp b/app/widget/timelinewidget/view/timelineviewblockitem.cpp index 1f022450a..7e182657f 100644 --- a/app/widget/timelinewidget/view/timelineviewblockitem.cpp +++ b/app/widget/timelinewidget/view/timelineviewblockitem.cpp @@ -134,7 +134,11 @@ void TimelineViewBlockItem::paint(QPainter *painter, const QStyleOptionGraphicsI } else { painter->setPen(Qt::lightGray); } - painter->drawText(rect(), static_cast(Qt::AlignLeft | Qt::AlignTop), block_->block_name()); + + int text_top = TrackOutput::GetTrackHeightMinimum() / 2 - painter->fontMetrics().height() / 2; + QRectF text_rect = rect(); + text_rect.adjust(0, text_top, 0, 0); + painter->drawText(text_rect, Qt::AlignLeft | Qt::AlignTop, block_->block_name()); // Linked clips are underlined if (block_->HasLinks()) { From 690bdb658150d71631d1f70b527a26bef6c30bfe Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 27 Apr 2020 14:01:44 +1000 Subject: [PATCH 09/10] timeline: make sure track length is updated if a block is disconnected --- app/node/output/track/track.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/node/output/track/track.cpp b/app/node/output/track/track.cpp index 24497f684..3adc7e6b7 100644 --- a/app/node/output/track/track.cpp +++ b/app/node/output/track/track.cpp @@ -556,9 +556,7 @@ void TrackOutput::BlockDisconnected(NodeEdgePtr edge) block_cache_.removeAt(index_of_block); // If there were blocks following this one, update their ins/outs - if (index_of_block < block_cache_.size()) { - UpdateInOutFrom(index_of_block); - } + UpdateInOutFrom(index_of_block); // Join the previous and next blocks together if (connected_block->previous()) { From 4906bb7d537e39835c11f1c402a547525d078bb3 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Tue, 28 Apr 2020 17:08:52 +1000 Subject: [PATCH 10/10] scopes: don't use shared context OpenGL shared contexts (through Qt at least) seem to be buggy. Scopes have been shifted to a non-shared approach as a result. --- app/main.cpp | 3 - app/panel/panelmanager.h | 4 + app/panel/scope/scope.cpp | 11 +-- app/panel/scope/scope.h | 4 - app/panel/viewer/viewerbase.cpp | 23 +----- app/panel/viewer/viewerbase.h | 6 -- app/render/backend/opengl/openglproxy.cpp | 2 +- app/render/backend/opengl/opengltexture.cpp | 15 ++++ app/render/backend/opengl/opengltexture.h | 3 + .../backend/opengl/opengltexturecache.cpp | 10 +++ .../backend/opengl/opengltexturecache.h | 2 + app/widget/scope/waveform/waveform.cpp | 51 ++++++++++--- app/widget/scope/waveform/waveform.h | 8 +- app/widget/viewer/viewer.cpp | 7 -- app/widget/viewer/viewer.h | 15 ---- app/widget/viewer/viewerdisplay.cpp | 74 ++----------------- app/widget/viewer/viewerdisplay.h | 40 ---------- 17 files changed, 90 insertions(+), 188 deletions(-) diff --git a/app/main.cpp b/app/main.cpp index 859ed0b76..1b4b6e3a7 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -50,9 +50,6 @@ int main(int argc, char *argv[]) { format.setProfile(QSurfaceFormat::CoreProfile); QSurfaceFormat::setDefaultFormat(format); - // Try to share OpenGL contexts - QApplication::setAttribute(Qt::AA_ShareOpenGLContexts); - // Create application instance QApplication a(argc, argv); diff --git a/app/panel/panelmanager.h b/app/panel/panelmanager.h index 7acba73e3..9e773ec92 100644 --- a/app/panel/panelmanager.h +++ b/app/panel/panelmanager.h @@ -177,6 +177,10 @@ T *PanelManager::CreatePanel(QWidget *parent) panel->SetMovementLocked(locked_); + // Sane default for panel geometry + panel->resize(parent->size() / 3); + panel->move(panel->mapFromGlobal(parent->mapToGlobal(parent->pos()))); + // Connect destroy signal so we can remove it from focus history connect(panel, &PanelWidget::destroyed, this, &PanelManager::PanelDestroyed); diff --git a/app/panel/scope/scope.cpp b/app/panel/scope/scope.cpp index 222fcec64..5daa7e9e0 100644 --- a/app/panel/scope/scope.cpp +++ b/app/panel/scope/scope.cpp @@ -84,19 +84,10 @@ QString ScopePanel::TypeToName(ScopePanel::Type t) return QString(); } -void ScopePanel::SetDisplayReferredTexture(OpenGLTexture *texture) -{ - Q_UNUSED(texture) -} - void ScopePanel::SetReferenceBuffer(Frame *frame) { histogram_->SetBuffer(frame); -} - -void ScopePanel::SetReferenceTexture(OpenGLTexture *texture) -{ - waveform_view_->SetTexture(texture); + waveform_view_->SetBuffer(frame); } void ScopePanel::SetColorManager(ColorManager *manager) diff --git a/app/panel/scope/scope.h b/app/panel/scope/scope.h index f44e37aa6..3e771af69 100644 --- a/app/panel/scope/scope.h +++ b/app/panel/scope/scope.h @@ -50,12 +50,8 @@ public: static QString TypeToName(Type t); public slots: - void SetDisplayReferredTexture(OpenGLTexture* texture); - void SetReferenceBuffer(Frame* frame); - void SetReferenceTexture(OpenGLTexture* texture); - void SetColorManager(ColorManager* manager); protected: diff --git a/app/panel/viewer/viewerbase.cpp b/app/panel/viewer/viewerbase.cpp index a8d4d55b8..3cab17f98 100644 --- a/app/panel/viewer/viewerbase.cpp +++ b/app/panel/viewer/viewerbase.cpp @@ -25,8 +25,7 @@ OLIVE_NAMESPACE_ENTER ViewerPanelBase::ViewerPanelBase(const QString& object_name, QWidget *parent) : - TimeBasedPanel(object_name, parent), - scope_panel_count_(0) + TimeBasedPanel(object_name, parent) { } @@ -98,33 +97,13 @@ void ViewerPanelBase::CreateScopePanel(ScopePanel::Type type) p->SetType(type); - // If the scope closes, reduce the count (we do this because if no scopes are open, we can optimize the viewer slightly) - connect(p, &ScopePanel::CloseRequested, this, &ViewerPanelBase::ScopePanelClosed); - // Connect viewer widget texture drawing to scope panel - connect(vw, &ViewerWidget::DrewManagedTexture, p, &ScopePanel::SetDisplayReferredTexture); connect(vw, &ViewerWidget::LoadedBuffer, p, &ScopePanel::SetReferenceBuffer); - connect(vw, &ViewerWidget::LoadedTexture, p, &ScopePanel::SetReferenceTexture); connect(vw, &ViewerWidget::ColorManagerChanged, p, &ScopePanel::SetColorManager); p->SetColorManager(vw->color_manager()); - if (!scope_panel_count_) { - vw->SetEmitDrewManagedTextureEnabled(true); - } - - scope_panel_count_++; - vw->ForceUpdate(); } -void ViewerPanelBase::ScopePanelClosed() -{ - scope_panel_count_--; - - if (!scope_panel_count_) { - static_cast(GetTimeBasedWidget())->SetEmitDrewManagedTextureEnabled(false); - } -} - OLIVE_NAMESPACE_EXIT diff --git a/app/panel/viewer/viewerbase.h b/app/panel/viewer/viewerbase.h index 7d551141d..a0acd2d29 100644 --- a/app/panel/viewer/viewerbase.h +++ b/app/panel/viewer/viewerbase.h @@ -59,12 +59,6 @@ public: protected: void CreateScopePanel(ScopePanel::Type type); -private: - int scope_panel_count_; - -private slots: - void ScopePanelClosed(); - }; OLIVE_NAMESPACE_EXIT diff --git a/app/render/backend/opengl/openglproxy.cpp b/app/render/backend/opengl/openglproxy.cpp index d1f4f7ddd..194ac6dc4 100644 --- a/app/render/backend/opengl/openglproxy.cpp +++ b/app/render/backend/opengl/openglproxy.cpp @@ -132,7 +132,7 @@ void OpenGLProxy::FrameToValue(FramePtr frame, StreamPtr stream, NodeValueTable* VideoRenderingParams footage_params(frame->width(), frame->height(), frame->format()); - footage_tex_ref = texture_cache_.Get(ctx_, footage_params, frame->data(), frame->linesize_pixels()); + footage_tex_ref = texture_cache_.Get(ctx_, footage_params, frame); if (ocio_method == ColorManager::kOCIOFast) { if (!color_processor->IsEnabled()) { diff --git a/app/render/backend/opengl/opengltexture.cpp b/app/render/backend/opengl/opengltexture.cpp index c0c1621ea..d5013c10f 100644 --- a/app/render/backend/opengl/opengltexture.cpp +++ b/app/render/backend/opengl/opengltexture.cpp @@ -74,6 +74,11 @@ void OpenGLTexture::Create(QOpenGLContext *ctx, int width, int height, const Pix } void OpenGLTexture::Create(QOpenGLContext *ctx, FramePtr frame) +{ + Create(ctx, frame.get()); +} + +void OpenGLTexture::Create(QOpenGLContext *ctx, Frame *frame) { Create(ctx, frame->width(), frame->height(), frame->format(), frame->data(), frame->linesize_pixels()); } @@ -120,6 +125,16 @@ const GLuint &OpenGLTexture::texture() const return texture_; } +void OpenGLTexture::Upload(FramePtr frame) +{ + Upload(frame.get()); +} + +void OpenGLTexture::Upload(Frame *frame) +{ + Upload(frame->data(), frame->linesize_pixels()); +} + void OpenGLTexture::Upload(const void *data, int linesize) { if (!IsCreated()) { diff --git a/app/render/backend/opengl/opengltexture.h b/app/render/backend/opengl/opengltexture.h index 941e2aae8..70fa653f7 100644 --- a/app/render/backend/opengl/opengltexture.h +++ b/app/render/backend/opengl/opengltexture.h @@ -44,6 +44,7 @@ public: void Create(QOpenGLContext* ctx, int width, int height, const PixelFormat::Format &format, const void *data, int linesize); void Create(QOpenGLContext* ctx, int width, int height, const PixelFormat::Format &format); void Create(QOpenGLContext* ctx, FramePtr frame); + void Create(QOpenGLContext* ctx, Frame* frame); bool IsCreated() const; @@ -59,6 +60,8 @@ public: const GLuint& texture() const; + void Upload(FramePtr frame); + void Upload(Frame* frame); void Upload(const void *data, int linesize); public slots: diff --git a/app/render/backend/opengl/opengltexturecache.cpp b/app/render/backend/opengl/opengltexturecache.cpp index 95e0638ee..a54994467 100644 --- a/app/render/backend/opengl/opengltexturecache.cpp +++ b/app/render/backend/opengl/opengltexturecache.cpp @@ -29,6 +29,16 @@ OpenGLTextureCache::~OpenGLTextureCache() } } +OpenGLTextureCache::ReferencePtr OpenGLTextureCache::Get(QOpenGLContext *ctx, const VideoRenderingParams ¶ms, FramePtr frame) +{ + return Get(ctx, params, frame.get()); +} + +OpenGLTextureCache::ReferencePtr OpenGLTextureCache::Get(QOpenGLContext *ctx, const VideoRenderingParams ¶ms, Frame *frame) +{ + return Get(ctx, params, frame->data(), frame->linesize_pixels()); +} + OpenGLTextureCache::ReferencePtr OpenGLTextureCache::Get(QOpenGLContext* ctx, const VideoRenderingParams ¶ms, const void *data, int linesize) { OpenGLTexturePtr texture = nullptr; diff --git a/app/render/backend/opengl/opengltexturecache.h b/app/render/backend/opengl/opengltexturecache.h index 7be07bfbb..2baa87a67 100644 --- a/app/render/backend/opengl/opengltexturecache.h +++ b/app/render/backend/opengl/opengltexturecache.h @@ -57,6 +57,8 @@ public: DISABLE_COPY_MOVE(OpenGLTextureCache) + ReferencePtr Get(QOpenGLContext *ctx, const VideoRenderingParams& params, FramePtr frame); + ReferencePtr Get(QOpenGLContext *ctx, const VideoRenderingParams& params, Frame* frame); ReferencePtr Get(QOpenGLContext *ctx, const VideoRenderingParams& params, const void *data, int linesize); ReferencePtr Get(QOpenGLContext *ctx, const VideoRenderingParams& params); diff --git a/app/widget/scope/waveform/waveform.cpp b/app/widget/scope/waveform/waveform.cpp index 8a30dac66..64c71004d 100644 --- a/app/widget/scope/waveform/waveform.cpp +++ b/app/widget/scope/waveform/waveform.cpp @@ -28,27 +28,35 @@ OLIVE_NAMESPACE_ENTER WaveformScope::WaveformScope(QWidget* parent) : ManagedDisplayWidget(parent), - texture_(nullptr) + buffer_(nullptr) { EnableDefaultContextMenu(); } -void WaveformScope::SetTexture(OpenGLTexture *texture) +void WaveformScope::SetBuffer(Frame *frame) { - texture_ = texture; + buffer_ = frame; - update(); + UploadTextureFromBuffer(); } void WaveformScope::initializeGL() { ManagedDisplayWidget::initializeGL(); + makeCurrent(); pipeline_ = OpenGLShader::Create(); pipeline_->create(); pipeline_->addShaderFromSourceCode(QOpenGLShader::Vertex, OpenGLShader::CodeDefaultVertex()); pipeline_->addShaderFromSourceCode(QOpenGLShader::Fragment, Node::ReadFileAsString(":/shaders/rgbwaveform.frag")); pipeline_->link(); + doneCurrent(); + + connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &WaveformScope::CleanUp, Qt::DirectConnection); + + if (buffer_) { + UploadTextureFromBuffer(); + } } void WaveformScope::paintGL() @@ -56,12 +64,12 @@ void WaveformScope::paintGL() context()->functions()->glClearColor(0, 0, 0, 0); context()->functions()->glClear(GL_COLOR_BUFFER_BIT); - if (!pipeline_ || !texture_) { + if (!pipeline_ || !texture_.IsCreated()) { return; } pipeline_->bind(); - pipeline_->setUniformValue("ove_resolution", texture_->width(), texture_->height()); + pipeline_->setUniformValue("ove_resolution", texture_.width(), texture_.height()); pipeline_->setUniformValue("ove_viewport", width(), height()); // The general size of a pixel @@ -69,17 +77,42 @@ void WaveformScope::paintGL() pipeline_->release(); - texture_->Bind(); + texture_.Bind(); OpenGLRenderFunctions::Blit(pipeline_); - texture_->Release(); + texture_.Release(); +} + +void WaveformScope::UploadTextureFromBuffer() +{ + makeCurrent(); + + if (!texture_.IsCreated() + || texture_.width() != buffer_->width() + || texture_.height() != buffer_->height() + || texture_.format() != buffer_->format()) { + texture_.Destroy(); + texture_.Create(context(), buffer_); + } else { + texture_.Upload(buffer_); + } + + doneCurrent(); + + update(); } void WaveformScope::CleanUp() { + qDebug() << "Cleaned up..."; + + makeCurrent(); + pipeline_ = nullptr; - texture_ = nullptr; + texture_.Destroy(); + + doneCurrent(); } OLIVE_NAMESPACE_EXIT diff --git a/app/widget/scope/waveform/waveform.h b/app/widget/scope/waveform/waveform.h index f9f930c5c..efb7a08b4 100644 --- a/app/widget/scope/waveform/waveform.h +++ b/app/widget/scope/waveform/waveform.h @@ -36,7 +36,7 @@ public: WaveformScope(QWidget* parent = nullptr); public slots: - void SetTexture(OpenGLTexture* texture); + void SetBuffer(Frame* frame); protected: virtual void initializeGL() override; @@ -44,9 +44,13 @@ protected: virtual void paintGL() override; private: + void UploadTextureFromBuffer(); + OpenGLShaderPtr pipeline_; - OpenGLTexture* texture_; + OpenGLTexture texture_; + + Frame* buffer_; private slots: void CleanUp(); diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 4107ceab1..d4ebb84f0 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -66,8 +66,6 @@ ViewerWidget::ViewerWidget(QWidget *parent) : connect(main_widget, &ViewerDisplayWidget::customContextMenuRequested, this, &ViewerWidget::ShowContextMenu); connect(main_widget, &ViewerDisplayWidget::CursorColor, this, &ViewerWidget::CursorColor); connect(main_widget, &ViewerDisplayWidget::LoadedBuffer, this, &ViewerWidget::LoadedBuffer); - connect(main_widget, &ViewerDisplayWidget::LoadedTexture, this, &ViewerWidget::LoadedTexture); - connect(main_widget, &ViewerDisplayWidget::DrewManagedTexture, this, &ViewerWidget::DrewManagedTexture); connect(main_widget, &ViewerDisplayWidget::ColorProcessorChanged, this, &ViewerWidget::ColorProcessorChanged); connect(main_widget, &ViewerDisplayWidget::ColorManagerChanged, this, &ViewerWidget::ColorManagerChanged); connect(sizer_, &ViewerSizer::RequestMatrix, main_widget, &ViewerDisplayWidget::SetMatrix); @@ -745,11 +743,6 @@ void ViewerWidget::SetSignalCursorColorEnabled(bool e) } } -void ViewerWidget::SetEmitDrewManagedTextureEnabled(bool e) -{ - main_gl_widget()->SetEmitDrewManagedTextureEnabled(e); -} - void ViewerWidget::TimebaseChangedEvent(const rational &timebase) { TimeBasedWidget::TimebaseChangedEvent(timebase); diff --git a/app/widget/viewer/viewer.h b/app/widget/viewer/viewer.h index 8cf6bb24c..57b6e2970 100644 --- a/app/widget/viewer/viewer.h +++ b/app/widget/viewer/viewer.h @@ -107,11 +107,6 @@ public slots: */ void SetSignalCursorColorEnabled(bool e); - /** - * @brief Wrapper for ViewerGLWidget::SetEmitDrewManagedTextureEnabled() - */ - void SetEmitDrewManagedTextureEnabled(bool e); - signals: /** * @brief Wrapper for ViewerGLWidget::CursorColor() @@ -123,16 +118,6 @@ signals: */ void LoadedBuffer(Frame* load_buffer); - /** - * @brief Wrapper for ViewerGLWidget::LoadedTexture() - */ - void LoadedTexture(OpenGLTexture* texture); - - /** - * @brief Wrapper for ViewerGLWidget::DrewManagedTexture() - */ - void DrewManagedTexture(OpenGLTexture* texture); - /** * @brief Request a scope panel * diff --git a/app/widget/viewer/viewerdisplay.cpp b/app/widget/viewer/viewerdisplay.cpp index 640539a61..779f65237 100644 --- a/app/widget/viewer/viewerdisplay.cpp +++ b/app/widget/viewer/viewerdisplay.cpp @@ -42,10 +42,8 @@ bool ViewerDisplayWidget::nouveau_check_done_ = false; ViewerDisplayWidget::ViewerDisplayWidget(QWidget *parent) : ManagedDisplayWidget(parent), - managed_copy_pipeline_(nullptr), has_image_(false), - signal_cursor_color_(false), - enable_display_referred_signal_(false) + signal_cursor_color_(false) { } @@ -91,14 +89,12 @@ void ViewerDisplayWidget::SetImage(const QString &fn) input->read_image(input->spec().format, load_buffer_.data(), OIIO::AutoStride, load_buffer_.linesize_bytes()); input->close(); - emit LoadedBuffer(&load_buffer_); - - texture_.Upload(load_buffer_.data(), load_buffer_.linesize_pixels()); - - emit LoadedTexture(&texture_); + texture_.Upload(&load_buffer_); doneCurrent(); + emit LoadedBuffer(&load_buffer_); + has_image_ = true; #if OIIO_VERSION < 10903 @@ -138,7 +134,7 @@ void ViewerDisplayWidget::SetImageFromLoadBuffer(Frame *in_buffer) || texture_.format() != in_buffer->format()) { texture_.Create(context(), in_buffer->width(), in_buffer->height(), in_buffer->format(), in_buffer->data(), load_buffer_.linesize_pixels()); } else { - texture_.Upload(in_buffer->data(), load_buffer_.linesize_pixels()); + texture_.Upload(in_buffer); } doneCurrent(); @@ -147,18 +143,6 @@ void ViewerDisplayWidget::SetImageFromLoadBuffer(Frame *in_buffer) update(); } -void ViewerDisplayWidget::SetEmitDrewManagedTextureEnabled(bool e) -{ - enable_display_referred_signal_ = e; - - if (!enable_display_referred_signal_) { - // Destroy the texture now - managed_texture_.Destroy(); - managed_copy_pipeline_ = nullptr; - framebuffer_.Destroy(); - } -} - void ViewerDisplayWidget::ConnectSibling(ViewerDisplayWidget *sibling) { connect(this, &ViewerDisplayWidget::LoadedBuffer, sibling, &ViewerDisplayWidget::SetImageFromLoadBuffer, Qt::QueuedConnection); @@ -243,33 +227,6 @@ void ViewerDisplayWidget::paintGL() // We only draw if we have a pipeline if (has_image_ && color_service() && texture_.IsCreated()) { - // If we're distributing our display-referred final buffer, we'll have to make a copy of it - if (enable_display_referred_signal_) { - - if (!managed_texture_.IsCreated() - || managed_texture_.width() != texture_.width() - || managed_texture_.height() != texture_.height() - || managed_texture_.format() != texture_.format()) { - managed_texture_.Destroy(); - - managed_texture_.Create(context(), texture_.width(), texture_.height(), texture_.format()); - } - - if (!managed_copy_pipeline_) { - managed_copy_pipeline_ = OpenGLShader::CreateDefault(); - } - - if (!framebuffer_.IsCreated()) { - framebuffer_.Create(context()); - } - - framebuffer_.Attach(&managed_texture_); - framebuffer_.Bind(); - - f->glViewport(0, 0, managed_texture_.width(), managed_texture_.height()); - - } - // Bind retrieved texture f->glBindTexture(GL_TEXTURE_2D, texture_.texture()); @@ -279,24 +236,6 @@ void ViewerDisplayWidget::paintGL() // Release retrieved texture f->glBindTexture(GL_TEXTURE_2D, 0); - if (enable_display_referred_signal_) { - - framebuffer_.Release(); - framebuffer_.Detach(); - - emit DrewManagedTexture(&managed_texture_); - - // Bind retrieved texture - managed_texture_.Bind(); - - f->glViewport(0, 0, width(), height()); - - OpenGLRenderFunctions::Blit(managed_copy_pipeline_); - - // Bind retrieved texture - managed_texture_.Release(); - - } } // Draw action/title safe areas @@ -348,10 +287,7 @@ void ViewerDisplayWidget::ContextCleanup() { makeCurrent(); - managed_copy_pipeline_ = nullptr; texture_.Destroy(); - managed_texture_.Destroy(); - framebuffer_.Destroy(); doneCurrent(); } diff --git a/app/widget/viewer/viewerdisplay.h b/app/widget/viewer/viewerdisplay.h index 6289433a4..43e42eed2 100644 --- a/app/widget/viewer/viewerdisplay.h +++ b/app/widget/viewer/viewerdisplay.h @@ -101,15 +101,6 @@ public slots: */ void SetImageFromLoadBuffer(Frame* in_buffer); - /** - * @brief Enables or disables DrewManagedTexture() - * - * To emit a display referred texture, it needs to be copied after the color transform is complete. This naturally - * adds extra GPU cycles that are wasted if there's nothing receiving the signal. Therefore, the signal is disabled - * by default. - */ - void SetEmitDrewManagedTextureEnabled(bool e); - signals: /** * @brief Signal emitted when the user starts dragging from the viewer @@ -130,18 +121,6 @@ signals: */ void LoadedBuffer(Frame* load_buffer); - /** - * @brief Signal emitted when a buffer is loaded into a texture - * - * This texture will be the direct output of the renderer in reference space in GPU VRAM. - */ - void LoadedTexture(OpenGLTexture* texture); - - /** - * @brief Emitted when the a texture has been transformed to display - */ - void DrewManagedTexture(OpenGLTexture* texture); - protected: /** * @brief Override the mouse press event simply to emit the DragStarted() signal @@ -173,23 +152,6 @@ private: */ OpenGLTexture texture_; - /** - * @brief Internal framebuffer used to draw to managed_texture_ - */ - OpenGLFramebuffer framebuffer_; - - /** - * @brief Internal referenceto the OpenGL texture that's been managed - * - * Kept so that scopes can use the display-referred buffer without having to transform again. - */ - OpenGLTexture managed_texture_; - - /** - * @brief Pipeline used to draw to managed_texture_ - */ - OpenGLShaderPtr managed_copy_pipeline_; - /** * @brief Drawing matrix (defaults to identity) */ @@ -210,8 +172,6 @@ private: ViewerSafeMarginInfo safe_margin_; - bool enable_display_referred_signal_; - private slots: /** * @brief Slot to connect just before the OpenGL context is destroyed to clean up resources