From 02f4be6e3c0c34b2b8139eb991bcf7e5055d693c Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Sun, 24 Jul 2022 08:57:09 -0700 Subject: [PATCH 01/22] footage: account for invalid streams before valid streams Fixes #1983 --- app/codec/ffmpeg/ffmpegdecoder.cpp | 2 ++ app/node/output/viewer/viewer.h | 2 +- app/node/project/footage/footage.cpp | 8 +++++++- app/node/project/footage/footage.h | 4 ++++ .../project/footage/footagedescription.cpp | 18 +++++++++++++++--- app/node/project/footage/footagedescription.h | 10 ++++++++-- 6 files changed, 37 insertions(+), 7 deletions(-) diff --git a/app/codec/ffmpeg/ffmpegdecoder.cpp b/app/codec/ffmpeg/ffmpegdecoder.cpp index 2ab14eadb..c2dbe2488 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.cpp +++ b/app/codec/ffmpeg/ffmpegdecoder.cpp @@ -513,6 +513,8 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, CancelAtom *can } + desc.SetStreamCount(fmt_ctx->nb_streams); + } // Free all memory diff --git a/app/node/output/viewer/viewer.h b/app/node/output/viewer/viewer.h index 09b0ac3d5..8d26f4bd3 100644 --- a/app/node/output/viewer/viewer.h +++ b/app/node/output/viewer/viewer.h @@ -126,7 +126,7 @@ public: return InputArraySize(kSubtitleParamsInput); } - int GetTotalStreamCount() const + virtual int GetTotalStreamCount() const { return GetVideoStreamCount() + GetAudioStreamCount() + GetSubtitleStreamCount(); } diff --git a/app/node/project/footage/footage.cpp b/app/node/project/footage/footage.cpp index 7b8870ee2..7c89f3496 100644 --- a/app/node/project/footage/footage.cpp +++ b/app/node/project/footage/footage.cpp @@ -44,7 +44,8 @@ Footage::Footage(const QString &filename) : ViewerOutput(false, false), timestamp_(0), valid_(false), - cancelled_(nullptr) + cancelled_(nullptr), + total_stream_count_(0) { SetCacheTextures(true); @@ -125,6 +126,9 @@ void Footage::Clear() // Clear decoder link decoder_.clear(); + // Clear total stream count + total_stream_count_ = 0; + // Reset ready state valid_ = false; } @@ -497,6 +501,8 @@ void Footage::Reprobe() SetStream(Track::kSubtitle, QVariant::fromValue(footage_info.GetSubtitleStreams().at(i)), i); } + total_stream_count_ = footage_info.GetStreamCount(); + SetValid(); } diff --git a/app/node/project/footage/footage.h b/app/node/project/footage/footage.h index 8397726a2..7eb38f3c5 100644 --- a/app/node/project/footage/footage.h +++ b/app/node/project/footage/footage.h @@ -180,6 +180,8 @@ public: virtual qint64 creation_time() const override; virtual qint64 mod_time() const override; + virtual int GetTotalStreamCount() const override { return total_stream_count_; } + static const QString kFilenameInput; protected: @@ -224,6 +226,8 @@ private: CancelAtom *cancelled_; + int total_stream_count_; + private slots: void CheckFootage(); diff --git a/app/node/project/footage/footagedescription.cpp b/app/node/project/footage/footagedescription.cpp index 610db5c39..742372586 100644 --- a/app/node/project/footage/footagedescription.cpp +++ b/app/node/project/footage/footagedescription.cpp @@ -43,9 +43,11 @@ bool FootageDescription::Load(const QString &filename) // Default to first version of metadata (which wasn't versioned at all) unsigned version = 1; - XMLAttributeLoop((&reader), attr) { - if (attr.name() == QStringLiteral("version")) { - version = attr.value().toUInt(); + { + XMLAttributeLoop((&reader), attr) { + if (attr.name() == QStringLiteral("version")) { + version = attr.value().toUInt(); + } } } @@ -58,6 +60,14 @@ bool FootageDescription::Load(const QString &filename) if (reader.name() == QStringLiteral("decoder")) { decoder_ = reader.readElementText(); } else if (reader.name() == QStringLiteral("streams")) { + { + XMLAttributeLoop((&reader), attr) { + if (attr.name() == QStringLiteral("count")) { + total_stream_count_ = attr.value().toInt(); + } + } + } + while (XMLReadNextStartElement(&reader)) { if (reader.name() == QStringLiteral("video")) { VideoParams vp; @@ -116,6 +126,8 @@ bool FootageDescription::Save(const QString &filename) const writer.writeStartElement(QStringLiteral("streams")); + writer.writeAttribute(QStringLiteral("count"), QString::number(total_stream_count_)); + foreach (const VideoParams& vp, video_streams_) { writer.writeStartElement(QStringLiteral("video")); vp.Save(&writer); diff --git a/app/node/project/footage/footagedescription.h b/app/node/project/footage/footagedescription.h index 930a6e8be..0c55cb044 100644 --- a/app/node/project/footage/footagedescription.h +++ b/app/node/project/footage/footagedescription.h @@ -32,7 +32,8 @@ class FootageDescription { public: FootageDescription(const QString& decoder = QString()) : - decoder_(decoder) + decoder_(decoder), + total_stream_count_(0) { } @@ -118,6 +119,9 @@ public: return StreamIsVideo(index) || StreamIsAudio(index) || StreamIsSubtitle(index); } + int GetStreamCount() const { return total_stream_count_; } + void SetStreamCount(int s) { total_stream_count_ = s; } + bool Load(const QString& filename); bool Save(const QString& filename) const; @@ -138,7 +142,7 @@ public: } private: - static constexpr unsigned kFootageMetaVersion = 2; + static constexpr unsigned kFootageMetaVersion = 3; QString decoder_; @@ -148,6 +152,8 @@ private: QVector subtitle_streams_; + int total_stream_count_; + }; } From 1fa171e507653a8d4439a38b605ea15f7593ff58 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Sun, 24 Jul 2022 08:57:23 -0700 Subject: [PATCH 02/22] exportdialog: rename yuv ranges --- app/dialog/export/exportadvancedvideodialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/dialog/export/exportadvancedvideodialog.cpp b/app/dialog/export/exportadvancedvideodialog.cpp index 57851f424..2d08ceacd 100644 --- a/app/dialog/export/exportadvancedvideodialog.cpp +++ b/app/dialog/export/exportadvancedvideodialog.cpp @@ -35,7 +35,7 @@ ExportAdvancedVideoDialog::ExportAdvancedVideoDialog(const QList &pix_f pixel_layout->addWidget(new QLabel(tr("YUV Color Range:")), row, 0); yuv_color_range_combobox_ = new QComboBox(); - yuv_color_range_combobox_->addItems({tr("MPEG (16-235)"), tr("JPEG (0-255)")}); + yuv_color_range_combobox_->addItems({tr("Limited (16-235)"), tr("Full (0-255)")}); pixel_layout->addWidget(yuv_color_range_combobox_, row, 1); } From 3fb786a868b31332632c7adc9c5e408a72f5cfbc Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Mon, 25 Jul 2022 12:53:08 -0700 Subject: [PATCH 03/22] manageddisplaywidget: add toggle for QOpenGLWindow --- app/widget/manageddisplay/manageddisplay.cpp | 4 ++++ app/widget/manageddisplay/manageddisplay.h | 24 ++++++++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/app/widget/manageddisplay/manageddisplay.cpp b/app/widget/manageddisplay/manageddisplay.cpp index 923d0bdee..62afcb876 100644 --- a/app/widget/manageddisplay/manageddisplay.cpp +++ b/app/widget/manageddisplay/manageddisplay.cpp @@ -62,7 +62,11 @@ ManagedDisplayWidget::ManagedDisplayWidget(QWidget *parent) : attached_renderer_ = new OpenGLRenderer(this); // Create widget wrapper for OpenGL window +#ifdef USE_QOPENGLWINDOW wrapper_ = QWidget::createWindowContainer(static_cast(inner_widget_)); +#else + wrapper_ = inner_widget_; +#endif layout->addWidget(wrapper_); } else { inner_widget_ = nullptr; diff --git a/app/widget/manageddisplay/manageddisplay.h b/app/widget/manageddisplay/manageddisplay.h index 89a952eb0..644ab0711 100644 --- a/app/widget/manageddisplay/manageddisplay.h +++ b/app/widget/manageddisplay/manageddisplay.h @@ -21,9 +21,15 @@ #ifndef MANAGEDDISPLAYOBJECT_H #define MANAGEDDISPLAYOBJECT_H +//#define USE_QOPENGLWINDOW + #include #include +#ifdef USE_QOPENGLWINDOW #include +#else +#include +#endif #include "node/color/colormanager/colormanager.h" #include "render/renderer.h" @@ -31,7 +37,12 @@ namespace olive { -class ManagedDisplayWidgetOpenGL : public QOpenGLWindow +class ManagedDisplayWidgetOpenGL +#ifdef USE_QOPENGLWINDOW + : public QOpenGLWindow +#else + : public QOpenGLWidget +#endif { Q_OBJECT public: @@ -175,7 +186,12 @@ protected: void doneCurrent(); - QWindow* inner_widget() const +#ifdef USE_QOPENGLWINDOW + QWindow* +#else + QWidget* +#endif + inner_widget() const { return inner_widget_; } @@ -225,7 +241,11 @@ private: /** * @brief Main drawing surface abstraction */ +#ifdef USE_QOPENGLWINDOW QWindow* inner_widget_; +#else + QWidget* inner_widget_; +#endif QWidget *wrapper_; /** From 33742a62cfa58b1faea7381f042b56a8ba8e9521 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Mon, 25 Jul 2022 13:08:28 -0700 Subject: [PATCH 04/22] oiiodecoder: set correct stream count Fixes #1985 --- app/codec/footagemeta.h | 10 ---------- app/codec/oiio/oiiodecoder.cpp | 5 ++++- app/node/project/footage/footagedescription.h | 2 +- 3 files changed, 5 insertions(+), 12 deletions(-) delete mode 100644 app/codec/footagemeta.h diff --git a/app/codec/footagemeta.h b/app/codec/footagemeta.h deleted file mode 100644 index 4d23025f1..000000000 --- a/app/codec/footagemeta.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef FOOTAGEMETA_H -#define FOOTAGEMETA_H - -struct FootageData { - struct StreamData { - - }; -}; - -#endif // FOOTAGEMETA_H diff --git a/app/codec/oiio/oiiodecoder.cpp b/app/codec/oiio/oiiodecoder.cpp index e9a84ca28..7c7dd8878 100644 --- a/app/codec/oiio/oiiodecoder.cpp +++ b/app/codec/oiio/oiiodecoder.cpp @@ -73,7 +73,8 @@ FootageDescription OIIODecoder::Probe(const QString &filename, CancelAtom *cance bool stream_enabled = true; - for (int i=0; in->seek_subimage(i, 0); i++) { + int i; + for (i=0; in->seek_subimage(i, 0); i++) { OIIO::ImageSpec spec = in->spec(); VideoParams video_params = GetVideoParamsFromImageSpec(spec); @@ -104,6 +105,8 @@ FootageDescription OIIODecoder::Probe(const QString &filename, CancelAtom *cance desc.AddVideoStream(video_params); } + desc.SetStreamCount(i); + // If we're here, we have a successful image open in->close(); diff --git a/app/node/project/footage/footagedescription.h b/app/node/project/footage/footagedescription.h index 0c55cb044..c05849273 100644 --- a/app/node/project/footage/footagedescription.h +++ b/app/node/project/footage/footagedescription.h @@ -142,7 +142,7 @@ public: } private: - static constexpr unsigned kFootageMetaVersion = 3; + static constexpr unsigned kFootageMetaVersion = 4; QString decoder_; From 77391984374ca40ebd6dc782f76d0f494c3809e3 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Mon, 25 Jul 2022 17:59:54 -0700 Subject: [PATCH 05/22] viewer: skip subtitle drawing if transformed font size is NaN --- app/widget/viewer/viewerdisplay.cpp | 166 +++++++++++++++------------- app/widget/viewer/viewerdisplay.h | 2 + 2 files changed, 90 insertions(+), 78 deletions(-) diff --git a/app/widget/viewer/viewerdisplay.cpp b/app/widget/viewer/viewerdisplay.cpp index 92f9bd119..1357af21a 100644 --- a/app/widget/viewer/viewerdisplay.cpp +++ b/app/widget/viewer/viewerdisplay.cpp @@ -456,84 +456,7 @@ void ViewerDisplayWidget::OnPaint() } // Extraordinarily basic subtitle renderer. Hoping to swap this out with libass at some point. - if (show_subtitles_ && subtitle_tracks_) { - const QVector &subtitle_tracklist = subtitle_tracks_->track_list(Track::kSubtitle)->GetTracks(); - - if (!subtitle_tracklist.empty()) { - QPainterPath path; - - QTransform transform = GenerateWorldTransform(); - QRect bounding_box = transform.mapRect(rect()); - - QFont f; - qreal font_sz = OLIVE_CONFIG("DefaultSubtitleSize").toInt(); - { - // Scale font size by transform - QTransform display_transform = GenerateDisplayTransform(); - font_sz *= display_transform.m11(); - } - f.setPointSizeF(font_sz); - - QString family = OLIVE_CONFIG("DefaultSubtitleFamily").toString(); - if (!family.isEmpty()) { - f.setFamily(family); - } - - f.setWeight(OLIVE_CONFIG("DefaultSubtitleWeight").toInt()); - - bounding_box.adjust(bounding_box.width()/10, bounding_box.height()/10, -bounding_box.width()/10, -bounding_box.height()/10); - - QFontMetrics fm(f); - - for (int j=subtitle_tracklist.size()-1; j>=0; j--) { - Track *sub_track = subtitle_tracklist.at(j); - if (!sub_track->IsMuted()) { - if (SubtitleBlock *sub = dynamic_cast(sub_track->BlockAtTime(time_))) { - // Split into lines - QStringList list = QtUtils::WordWrapString(sub->GetText(), fm, bounding_box.width()); - - for (int i=list.size()-1; i>=0; i--) { - int w = QtUtils::QFontMetricsWidth(fm, list.at(i)); - path.addText(bounding_box.width()/2 - w/2, bounding_box.height() - fm.height() * (list.size() - i) + fm.ascent(), f, list.at(i)); - } - } - } - } - - bool antialias = OLIVE_CONFIG("AntialiasSubtitles").toBool(); - - QPixmap *aa_pixmap; - QPainter *text_painter; - if (antialias) { - // QPainter only supports anti-aliasing in software, so to achieve it, we draw to a - // software buffer first and then draw that onto the hardware - aa_pixmap = new QPixmap(bounding_box.width(), bounding_box.height()); - aa_pixmap->fill(Qt::transparent); - text_painter = new QPainter(aa_pixmap); - } else { - // Just draw straight to the hardware - text_painter = new QPainter(paint_device()); - - // Offset path by however much is necessary - path.translate(bounding_box.x(), bounding_box.y()); - } - - text_painter->setPen(QPen(Qt::black, f.pointSizeF() / 16)); - text_painter->setBrush(Qt::white); - text_painter->setRenderHint(QPainter::Antialiasing); - - text_painter->drawPath(path); - - delete text_painter; - - if (antialias) { - // We just drew to a software buffer, now draw this image onto the hardware device - QPainter p(paint_device()); - p.drawPixmap(bounding_box.x(), bounding_box.y(), *aa_pixmap); - delete aa_pixmap; - } - } - } + DrawSubtitleTracks(); if (add_band_) { QPainter p(paint_device()); @@ -974,6 +897,93 @@ void ViewerDisplayWidget::EmitColorAtCursor(QMouseEvent *e) } } +void ViewerDisplayWidget::DrawSubtitleTracks() +{ + if (!show_subtitles_ || !subtitle_tracks_) { + return; + } + + const QVector &subtitle_tracklist = subtitle_tracks_->track_list(Track::kSubtitle)->GetTracks(); + if (subtitle_tracklist.empty()) { + return; + } + + // Scale font size by transform + QTransform display_transform = GenerateDisplayTransform(); + qreal font_sz = OLIVE_CONFIG("DefaultSubtitleSize").toInt(); + font_sz *= display_transform.m11(); + if (qIsNaN(font_sz)) { + return; + } + + QPainterPath path; + + QTransform transform = GenerateWorldTransform(); + QRect bounding_box = transform.mapRect(rect()); + + QFont f; + f.setPointSizeF(font_sz); + + QString family = OLIVE_CONFIG("DefaultSubtitleFamily").toString(); + if (!family.isEmpty()) { + f.setFamily(family); + } + + f.setWeight(OLIVE_CONFIG("DefaultSubtitleWeight").toInt()); + + bounding_box.adjust(bounding_box.width()/10, bounding_box.height()/10, -bounding_box.width()/10, -bounding_box.height()/10); + + QFontMetrics fm(f); + + for (int j=subtitle_tracklist.size()-1; j>=0; j--) { + Track *sub_track = subtitle_tracklist.at(j); + if (!sub_track->IsMuted()) { + if (SubtitleBlock *sub = dynamic_cast(sub_track->BlockAtTime(time_))) { + // Split into lines + QStringList list = QtUtils::WordWrapString(sub->GetText(), fm, bounding_box.width()); + + for (int i=list.size()-1; i>=0; i--) { + int w = QtUtils::QFontMetricsWidth(fm, list.at(i)); + path.addText(bounding_box.width()/2 - w/2, bounding_box.height() - fm.height() * (list.size() - i) + fm.ascent(), f, list.at(i)); + } + } + } + } + + bool antialias = OLIVE_CONFIG("AntialiasSubtitles").toBool(); + + QPixmap *aa_pixmap; + QPainter *text_painter; + if (antialias) { + // QPainter only supports anti-aliasing in software, so to achieve it, we draw to a + // software buffer first and then draw that onto the hardware + aa_pixmap = new QPixmap(bounding_box.width(), bounding_box.height()); + aa_pixmap->fill(Qt::transparent); + text_painter = new QPainter(aa_pixmap); + } else { + // Just draw straight to the hardware + text_painter = new QPainter(paint_device()); + + // Offset path by however much is necessary + path.translate(bounding_box.x(), bounding_box.y()); + } + + text_painter->setPen(QPen(Qt::black, f.pointSizeF() / 16)); + text_painter->setBrush(Qt::white); + text_painter->setRenderHint(QPainter::Antialiasing); + + text_painter->drawPath(path); + + delete text_painter; + + if (antialias) { + // We just drew to a software buffer, now draw this image onto the hardware device + QPainter p(paint_device()); + p.drawPixmap(bounding_box.x(), bounding_box.y(), *aa_pixmap); + delete aa_pixmap; + } +} + void ViewerDisplayWidget::SetShowFPS(bool e) { show_fps_ = e; diff --git a/app/widget/viewer/viewerdisplay.h b/app/widget/viewer/viewerdisplay.h index e88b845a3..430f11b54 100644 --- a/app/widget/viewer/viewerdisplay.h +++ b/app/widget/viewer/viewerdisplay.h @@ -263,6 +263,8 @@ private: void EmitColorAtCursor(QMouseEvent* e); + void DrawSubtitleTracks(); + /** * @brief Internal reference to the OpenGL texture to draw. Set in SetTexture() and used in paintGL(). */ From 9eb0aa80e1b4c56020c57ad77cf3281bdb0e0fa8 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Mon, 25 Jul 2022 18:17:51 -0700 Subject: [PATCH 06/22] timeline: when ripple removing area, don't split gaps --- app/widget/timelinewidget/undo/timelineundoripple.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/widget/timelinewidget/undo/timelineundoripple.cpp b/app/widget/timelinewidget/undo/timelineundoripple.cpp index 3b7bdcac9..d940ea00d 100644 --- a/app/widget/timelinewidget/undo/timelineundoripple.cpp +++ b/app/widget/timelinewidget/undo/timelineundoripple.cpp @@ -63,8 +63,14 @@ void TrackRippleRemoveAreaCommand::prepare() // If it's getting trimmed, determine if it's actually getting spliced if (first_block_is_out_trimmed && first_block_is_in_trimmed) { - // This block is getting spliced, so we'll handle that later - splice_split_command_ = new BlockSplitCommand(first_block, range_.in()); + if (dynamic_cast(first_block)) { + trim_out_ = {first_block, + first_block->length(), + first_block->length() - range_.length()}; + } else { + // This block is getting spliced, so we'll handle that later + splice_split_command_ = new BlockSplitCommand(first_block, range_.in()); + } } else { // It's just getting trimmed or removed, so we'll append that operation if (first_block_is_out_trimmed) { From fb8bb654a09f335861814b51816e576b0896d6bf Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 26 Jul 2022 08:33:05 -0700 Subject: [PATCH 07/22] timeline: add comment to recent code addition --- app/widget/timelinewidget/undo/timelineundoripple.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/app/widget/timelinewidget/undo/timelineundoripple.cpp b/app/widget/timelinewidget/undo/timelineundoripple.cpp index d940ea00d..71550f64c 100644 --- a/app/widget/timelinewidget/undo/timelineundoripple.cpp +++ b/app/widget/timelinewidget/undo/timelineundoripple.cpp @@ -64,6 +64,7 @@ void TrackRippleRemoveAreaCommand::prepare() // If it's getting trimmed, determine if it's actually getting spliced if (first_block_is_out_trimmed && first_block_is_in_trimmed) { if (dynamic_cast(first_block)) { + // As a rule, we don't split gaps, so we just treat it as a trim of the range requested trim_out_ = {first_block, first_block->length(), first_block->length() - range_.length()}; From e3403a5a5352acfa89bb32e3617e21428b8f1d3f Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 26 Jul 2022 09:40:26 -0700 Subject: [PATCH 08/22] render: create less textures --- app/codec/ffmpeg/ffmpegdecoder.cpp | 33 +++++++++-- app/codec/ffmpeg/ffmpegdecoder.h | 4 ++ app/render/opengl/openglrenderer.cpp | 80 +++++++++----------------- app/render/opengl/openglrenderer.h | 19 +++---- app/render/renderer.cpp | 84 +++++++++++++++++++--------- app/render/renderer.h | 39 +++++++++---- app/render/texture.cpp | 2 +- app/render/texture.h | 20 +------ app/render/videoparams.h | 2 + 9 files changed, 156 insertions(+), 127 deletions(-) diff --git a/app/codec/ffmpeg/ffmpegdecoder.cpp b/app/codec/ffmpeg/ffmpegdecoder.cpp index c2dbe2488..9f66a9e3e 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.cpp +++ b/app/codec/ffmpeg/ffmpegdecoder.cpp @@ -61,6 +61,9 @@ FFmpegDecoder::FFmpegDecoder() : input_fmt_(AV_PIX_FMT_NONE), native_internal_pix_fmt_(VideoParams::kFormatInvalid), native_output_pix_fmt_(VideoParams::kFormatInvalid), + y_tex_(nullptr), + u_tex_(nullptr), + v_tex_(nullptr), working_frame_(nullptr), working_packet_(nullptr), cache_at_zero_(false), @@ -216,7 +219,12 @@ TexturePtr FFmpegDecoder::RetrieveVideoInternal(Renderer *renderer, const ration plane_params.set_channel_count(1); plane_params.set_divider(1); plane_params.set_format(native_internal_pix_fmt_); - TexturePtr y_plane = renderer->CreateTexture(plane_params, f->data[0], f->linesize[0] / px_size); + + if (!y_tex_) { + y_tex_ = renderer->CreateTexture(plane_params, f->data[0], f->linesize[0] / px_size); + } else { + y_tex_->Upload(f->data[0], f->linesize[0] / px_size); + } if (src_fmt == AV_PIX_FMT_YUV420P || src_fmt == AV_PIX_FMT_YUV422P @@ -236,13 +244,22 @@ TexturePtr FFmpegDecoder::RetrieveVideoInternal(Renderer *renderer, const ration plane_params.set_height(plane_params.height()/2); } - TexturePtr u_plane = renderer->CreateTexture(plane_params, f->data[1], f->linesize[1] / px_size); - TexturePtr v_plane = renderer->CreateTexture(plane_params, f->data[2], f->linesize[2] / px_size); + if (!u_tex_) { + u_tex_ = renderer->CreateTexture(plane_params, f->data[1], f->linesize[1] / px_size); + } else { + u_tex_->Upload(f->data[1], f->linesize[1] / px_size); + } + + if (!v_tex_) { + v_tex_ = renderer->CreateTexture(plane_params, f->data[2], f->linesize[2] / px_size); + } else { + v_tex_->Upload(f->data[2], f->linesize[2] / px_size); + } ShaderJob job; - job.Insert(QStringLiteral("y_channel"), NodeValue(NodeValue::kTexture, QVariant::fromValue(y_plane))); - job.Insert(QStringLiteral("u_channel"), NodeValue(NodeValue::kTexture, QVariant::fromValue(u_plane))); - job.Insert(QStringLiteral("v_channel"), NodeValue(NodeValue::kTexture, QVariant::fromValue(v_plane))); + job.Insert(QStringLiteral("y_channel"), NodeValue(NodeValue::kTexture, QVariant::fromValue(y_tex_))); + job.Insert(QStringLiteral("u_channel"), NodeValue(NodeValue::kTexture, QVariant::fromValue(u_tex_))); + job.Insert(QStringLiteral("v_channel"), NodeValue(NodeValue::kTexture, QVariant::fromValue(v_tex_))); job.Insert(QStringLiteral("bits_per_pixel"), NodeValue(NodeValue::kInt, bits_per_pixel)); job.Insert(QStringLiteral("jpeg_range"), NodeValue(NodeValue::kBoolean, jpeg_range)); @@ -296,6 +313,10 @@ void FFmpegDecoder::CloseInternal() input_fmt_ = AV_PIX_FMT_NONE; native_internal_pix_fmt_ = VideoParams::kFormatInvalid; native_output_pix_fmt_ = VideoParams::kFormatInvalid; + + y_tex_ = nullptr; + u_tex_ = nullptr; + v_tex_ = nullptr; } QString FFmpegDecoder::id() const diff --git a/app/codec/ffmpeg/ffmpegdecoder.h b/app/codec/ffmpeg/ffmpegdecoder.h index 1c0d25bb8..837d5e04c 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.h +++ b/app/codec/ffmpeg/ffmpegdecoder.h @@ -165,6 +165,10 @@ private: VideoParams::Format native_output_pix_fmt_; int native_channel_count_; + TexturePtr y_tex_; + TexturePtr u_tex_; + TexturePtr v_tex_; + AVFrame *working_frame_; AVPacket *working_packet_; diff --git a/app/render/opengl/openglrenderer.cpp b/app/render/opengl/openglrenderer.cpp index 5078b963c..a06c44ffd 100644 --- a/app/render/opengl/openglrenderer.cpp +++ b/app/render/opengl/openglrenderer.cpp @@ -107,7 +107,7 @@ void OpenGLRenderer::Init(QOpenGLContext *existing_ctx) bool OpenGLRenderer::Init() { - QMutexLocker locker(&global_opengl_mutex); + GL_PREAMBLE; if (context_) { qCritical() << "Can't initialize already initialized OpenGLRenderer"; @@ -187,16 +187,11 @@ void OpenGLRenderer::ClearDestination(Texture *texture, double r, double g, doub } } -QVariant OpenGLRenderer::CreateNativeTexture2D(int width, int height, VideoParams::Format format, int channel_count, const void *data, int linesize) +QVariant OpenGLRenderer::CreateNativeTexture(int width, int height, int depth, VideoParams::Format format, int channel_count, const void *data, int linesize) { GL_PREAMBLE; - return CreateNativeTexture2DInternal(width, height, format, channel_count, data, linesize); -} - -QVariant OpenGLRenderer::CreateNativeTexture3D(int width, int height, int depth, VideoParams::Format format, int channel_count, const void *data, int linesize) -{ - GL_PREAMBLE; + bool is_3d = depth > 1; // Generate new texture GLuint texture; @@ -205,18 +200,27 @@ QVariant OpenGLRenderer::CreateNativeTexture3D(int width, int height, int depth, functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, linesize); + GLenum target_current = is_3d ? GL_TEXTURE_BINDING_3D : GL_TEXTURE_BINDING_2D; + GLenum target = is_3d ? GL_TEXTURE_3D : GL_TEXTURE_2D; + GLint current_tex; - functions_->glGetIntegerv(GL_TEXTURE_BINDING_3D, ¤t_tex); + functions_->glGetIntegerv(target_current, ¤t_tex); - functions_->glBindTexture(GL_TEXTURE_3D, texture); + functions_->glBindTexture(target, texture); - context_->extraFunctions()->glTexImage3D(GL_TEXTURE_3D, 0, GetInternalFormat(format, channel_count), - width, height, depth, 0, GetPixelFormat(channel_count), - GetPixelType(format), data); + if (is_3d) { + context_->extraFunctions()->glTexImage3D(target, 0, GetInternalFormat(format, channel_count), + width, height, depth, 0, GetPixelFormat(channel_count), + GetPixelType(format), data); + } else { + functions_->glTexImage2D(target, 0, GetInternalFormat(format, channel_count), + width, height, 0, GetPixelFormat(channel_count), + GetPixelType(format), data); + } functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - functions_->glBindTexture(GL_TEXTURE_3D, current_tex); + functions_->glBindTexture(target, current_tex); return texture; } @@ -294,8 +298,10 @@ void OpenGLRenderer::UploadToTexture(Texture *texture, const void *data, int lin GLuint t = texture->id().value(); const VideoParams& p = texture->params(); - GLenum tex_type = texture->type() == Texture::k2D ? GL_TEXTURE_2D : GL_TEXTURE_3D; - GLenum tex_binding = texture->type() == Texture::k2D ? GL_TEXTURE_BINDING_2D : GL_TEXTURE_BINDING_3D; + bool is_3d = texture->params().is_3d(); + + GLenum tex_type = !is_3d ? GL_TEXTURE_2D : GL_TEXTURE_3D; + GLenum tex_binding = !is_3d ? GL_TEXTURE_BINDING_2D : GL_TEXTURE_BINDING_3D; // Store currently bound texture so it can be restored later GLint current_tex; @@ -308,7 +314,7 @@ void OpenGLRenderer::UploadToTexture(Texture *texture, const void *data, int lin { PRINT_GL_ERRORS; - if (texture->type() == Texture::k2D) { + if (!is_3d) { functions_->glTexSubImage2D(tex_type, 0, 0, 0, p.effective_width(), p.effective_height(), GetPixelFormat(p.channel_count()), GetPixelType(p.format()), @@ -502,7 +508,7 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video functions_->glActiveTexture(GL_TEXTURE0 + i); - GLenum target = (texture && texture->type() == Texture::k3D) ? GL_TEXTURE_3D : GL_TEXTURE_2D; + GLenum target = (texture && texture->params().is_3d()) ? GL_TEXTURE_3D : GL_TEXTURE_2D; functions_->glBindTexture(target, tex_id); if (tex_id) { @@ -584,11 +590,11 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video TexturePtr output_tex, input_tex; if (real_iteration_count > 1) { // Create one texture to bounce off - output_tex = CreateTextureFromNativeHandle(CreateNativeTexture2DInternal(destination_params), destination_params); + output_tex = CreateTexture(destination_params); if (real_iteration_count > 2) { // Create a second texture bounce off - input_tex = CreateTextureFromNativeHandle(CreateNativeTexture2DInternal(destination_params), destination_params); + input_tex = CreateTexture(destination_params); } } @@ -648,7 +654,7 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video // Release any textures we bound before for (int i=textures_to_bind.size()-1; i>=0; i--) { TexturePtr texture = textures_to_bind.at(i).texture; - GLenum target = (texture && texture->type() == Texture::k3D) ? GL_TEXTURE_3D : GL_TEXTURE_2D; + GLenum target = (texture && texture->params().is_3d()) ? GL_TEXTURE_3D : GL_TEXTURE_2D; functions_->glActiveTexture(GL_TEXTURE0 + i); functions_->glBindTexture(target, 0); } @@ -788,38 +794,6 @@ void OpenGLRenderer::ClearDestinationInternal(double r, double g, double b, doub functions_->glClear(GL_COLOR_BUFFER_BIT); } -QVariant OpenGLRenderer::CreateNativeTexture2DInternal(int width, int height, VideoParams::Format format, int channel_count, const void *data, int linesize) -{ - GLuint texture; - functions_->glGenTextures(1, &texture); - texture_params_.insert(texture, {width, height, 1, format, channel_count}); - - functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, linesize); - - GLint current_tex; - functions_->glGetIntegerv(GL_TEXTURE_BINDING_2D, ¤t_tex); - - functions_->glBindTexture(GL_TEXTURE_2D, texture); - - { - PRINT_GL_ERRORS; - functions_->glTexImage2D(GL_TEXTURE_2D, 0, GetInternalFormat(format, channel_count), - width, height, 0, GetPixelFormat(channel_count), - GetPixelType(format), data); - } - - functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - - functions_->glBindTexture(GL_TEXTURE_2D, current_tex); - - return texture; -} - -QVariant OpenGLRenderer::CreateNativeTexture2DInternal(const VideoParams ¶ms, const void *data, int linesize) -{ - return CreateNativeTexture2DInternal(params.effective_width(), params.effective_height(), params.format(), params.channel_count(), data, linesize); -} - GLuint OpenGLRenderer::CompileShader(GLenum type, const QString &code) { static const QString shader_preamble = diff --git a/app/render/opengl/openglrenderer.h b/app/render/opengl/openglrenderer.h index 2e51480b1..1377e475c 100644 --- a/app/render/opengl/openglrenderer.h +++ b/app/render/opengl/openglrenderer.h @@ -47,18 +47,10 @@ public: virtual void PostDestroy() override; -public slots: virtual void PostInit() override; - virtual void DestroyInternal() override; - virtual void ClearDestination(olive::Texture *texture = nullptr, double r = 0.0, double g = 0.0, double b = 0.0, double a = 0.0) override; - virtual QVariant CreateNativeTexture2D(int width, int height, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0) override; - virtual QVariant CreateNativeTexture3D(int width, int height, int depth, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0) override; - - virtual void DestroyNativeTexture(QVariant texture) override; - virtual QVariant CreateNativeShader(olive::ShaderCode code) override; virtual void DestroyNativeShader(QVariant shader) override; @@ -71,13 +63,19 @@ public slots: virtual Color GetPixelFromTexture(olive::Texture *texture, const QPointF &pt) override; -protected slots: +protected: virtual void Blit(QVariant shader, olive::ShaderJob job, olive::Texture* destination, olive::VideoParams destination_params, bool clear_destination) override; + virtual QVariant CreateNativeTexture(int width, int height, int depth, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0) override; + + virtual void DestroyNativeTexture(QVariant texture) override; + + virtual void DestroyInternal() override; + private: static GLint GetInternalFormat(VideoParams::Format format, int channel_layout); @@ -93,9 +91,6 @@ private: void ClearDestinationInternal(double r = 0.0, double g = 0.0, double b = 0.0, double a = 0.0); - QVariant CreateNativeTexture2DInternal(int width, int height, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0); - QVariant CreateNativeTexture2DInternal(const VideoParams ¶ms, const void* data = nullptr, int linesize = 0); - GLuint CompileShader(GLenum type, const QString &code); QOpenGLContext* context_; diff --git a/app/render/renderer.cpp b/app/render/renderer.cpp index e174a4b35..39a0aaff0 100644 --- a/app/render/renderer.cpp +++ b/app/render/renderer.cpp @@ -20,36 +20,54 @@ #include "renderer.h" +#include +#include #include -#include "common/ocioutils.h" - namespace olive { Renderer::Renderer(QObject *parent) : QObject(parent) { - -} - -TexturePtr Renderer::CreateTexture(const VideoParams ¶ms, Texture::Type type, const void *data, int linesize) -{ - QVariant v; - - if (type == Texture::k3D) { - v = CreateNativeTexture3D(params.effective_width(), params.effective_height(), - params.effective_depth(), params.format(), params.channel_count(), data, linesize); - } else { - v = CreateNativeTexture2D(params.effective_width(), params.effective_height(), params.format(), - params.channel_count(), data, linesize); - } - - return CreateTextureFromNativeHandle(v, params, type); + QTimer *texture_garbage_collector = new QTimer(this); + texture_garbage_collector->setInterval(MAX_TEXTURE_LIFE); + connect(texture_garbage_collector, &QTimer::timeout, this, &Renderer::ClearOldTextures); + texture_garbage_collector->start(); } TexturePtr Renderer::CreateTexture(const VideoParams ¶ms, const void *data, int linesize) { - return CreateTexture(params, Texture::k2D, data, linesize); + QVariant v; + + /*for (auto it=texture_cache_.begin(); it!=texture_cache_.end(); it++) { + if (it->width == params.effective_width() + && it->height == params.effective_height() + && it->depth == params.effective_depth() + && it->format == params.format() + && it->channel_count == params.channel_count()) { + this->Flush(); + v = it->handle; + texture_cache_.erase(it); + break; + } + }*/ + + v = CreateNativeTexture(params.effective_width(), params.effective_height(), params.effective_depth(), + params.format(), params.channel_count(), data, linesize); + + return CreateTextureFromNativeHandle(v, params); +} + +void Renderer::DestroyTexture(Texture *texture) +{ + /*texture_cache_.push_back({texture->params().effective_width(), + texture->params().effective_height(), + texture->params().effective_depth(), + texture->params().format(), + texture->params().channel_count(), + texture->id(), + QDateTime::currentMSecsSinceEpoch()});*/ + DestroyNativeTexture(texture->id()); } TexturePtr Renderer::InterlaceTexture(TexturePtr top, TexturePtr bottom, const VideoParams ¶ms) @@ -83,6 +101,11 @@ QVariant Renderer::GetDefaultShader() void Renderer::Destroy() { + for (auto it=texture_cache_.begin(); it!=texture_cache_.end(); it++) { + DestroyNativeTexture(it->handle); + } + texture_cache_.clear(); + if (!default_shader_.isNull()) { DestroyNativeShader(default_shader_); default_shader_.clear(); @@ -98,13 +121,13 @@ void Renderer::Destroy() DestroyInternal(); } -TexturePtr Renderer::CreateTextureFromNativeHandle(const QVariant &v, const VideoParams ¶ms, Texture::Type type) +TexturePtr Renderer::CreateTextureFromNativeHandle(const QVariant &v, const VideoParams ¶ms) { if (v.isNull()) { return nullptr; } - return std::make_shared(this, v, params, type); + return std::make_shared(this, v, params); } bool Renderer::GetColorContext(const ColorTransformJob &color_job, Renderer::ColorContext *ctx) @@ -175,8 +198,7 @@ bool Renderer::GetColorContext(const ColorTransformJob &color_job, Renderer::Col } // Allocate 3D LUT - color_ctx.lut3d_textures[i].texture = CreateTexture(VideoParams(edge_len, edge_len, edge_len, VideoParams::kFormatFloat32, VideoParams::kRGBChannelCount), - Texture::k3D, values); + color_ctx.lut3d_textures[i].texture = CreateTexture(VideoParams(edge_len, edge_len, edge_len, VideoParams::kFormatFloat32, VideoParams::kRGBChannelCount), values); color_ctx.lut3d_textures[i].name = sampler_name; color_ctx.lut3d_textures[i].interpolation = (interpolation == OCIO::INTERP_NEAREST) ? Texture::kNearest : Texture::kLinear; } @@ -206,9 +228,7 @@ bool Renderer::GetColorContext(const ColorTransformJob &color_job, Renderer::Col } // Allocate 1D LUT - color_ctx.lut1d_textures[i].texture = CreateTexture(VideoParams(width, height, VideoParams::kFormatFloat32, (channel == OCIO::GpuShaderDesc::TEXTURE_RED_CHANNEL) ? 1 : VideoParams::kRGBChannelCount), - Texture::k2D, - values); + color_ctx.lut1d_textures[i].texture = CreateTexture(VideoParams(width, height, VideoParams::kFormatFloat32, (channel == OCIO::GpuShaderDesc::TEXTURE_RED_CHANNEL) ? 1 : VideoParams::kRGBChannelCount), values); color_ctx.lut1d_textures[i].name = sampler_name; color_ctx.lut1d_textures[i].interpolation = (interpolation == OCIO::INTERP_NEAREST) ? Texture::kNearest : Texture::kLinear; } @@ -219,6 +239,18 @@ bool Renderer::GetColorContext(const ColorTransformJob &color_job, Renderer::Col } } +void Renderer::ClearOldTextures() +{ + for (auto it=texture_cache_.begin(); it!=texture_cache_.end(); ) { + if (it->accessed < QDateTime::currentMSecsSinceEpoch() - MAX_TEXTURE_LIFE) { + DestroyNativeTexture(it->handle); + it = texture_cache_.erase(it); + } else { + it++; + } + } +} + void Renderer::BlitColorManaged(const ColorTransformJob &color_job, Texture *destination, const VideoParams ¶ms) { ColorContext color_ctx; diff --git a/app/render/renderer.h b/app/render/renderer.h index 839be32cf..dc1fb19a0 100644 --- a/app/render/renderer.h +++ b/app/render/renderer.h @@ -45,9 +45,10 @@ public: virtual bool Init() = 0; - TexturePtr CreateTexture(const VideoParams& params, Texture::Type type, const void* data = nullptr, int linesize = 0); TexturePtr CreateTexture(const VideoParams& params, const void *data = nullptr, int linesize = 0); + void DestroyTexture(Texture *texture); + void BlitToTexture(QVariant shader, olive::ShaderJob job, olive::Texture* destination, @@ -82,18 +83,10 @@ public: virtual void PostDestroy() = 0; -public slots: virtual void PostInit() = 0; - virtual void DestroyInternal() = 0; - virtual void ClearDestination(olive::Texture *texture = nullptr, double r = 0.0, double g = 0.0, double b = 0.0, double a = 0.0) = 0; - virtual QVariant CreateNativeTexture2D(int width, int height, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0) = 0; - virtual QVariant CreateNativeTexture3D(int width, int height, int depth, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0) = 0; - - virtual void DestroyNativeTexture(QVariant texture) = 0; - virtual QVariant CreateNativeShader(olive::ShaderCode code) = 0; virtual void DestroyNativeShader(QVariant shader) = 0; @@ -106,15 +99,18 @@ public slots: virtual Color GetPixelFromTexture(olive::Texture *texture, const QPointF &pt) = 0; -protected slots: +protected: virtual void Blit(QVariant shader, olive::ShaderJob job, olive::Texture* destination, olive::VideoParams destination_params, bool clear_destination) = 0; -protected: - TexturePtr CreateTextureFromNativeHandle(const QVariant &v, const VideoParams ¶ms, Texture::Type type = Texture::k2D); + virtual QVariant CreateNativeTexture(int width, int height, int depth, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0) = 0; + + virtual void DestroyNativeTexture(QVariant texture) = 0; + + virtual void DestroyInternal() = 0; private: struct ColorContext { @@ -130,16 +126,35 @@ private: }; + TexturePtr CreateTextureFromNativeHandle(const QVariant &v, const VideoParams ¶ms); + bool GetColorContext(const ColorTransformJob &color_job, ColorContext* ctx); QHash color_cache_; + struct CachedTexture + { + int width; + int height; + int depth; + VideoParams::Format format; + int channel_count; + QVariant handle; + qint64 accessed; + }; + + const int MAX_TEXTURE_LIFE = 10000; + std::list texture_cache_; + QMutex color_cache_mutex_; QVariant default_shader_; QVariant interlace_texture_; +private slots: + void ClearOldTextures(); + }; } diff --git a/app/render/texture.cpp b/app/render/texture.cpp index 33973ddaf..f3b5fb9d0 100644 --- a/app/render/texture.cpp +++ b/app/render/texture.cpp @@ -29,7 +29,7 @@ const Texture::Interpolation Texture::kDefaultInterpolation = Texture::kMipmappe Texture::~Texture() { if (renderer_) { - renderer_->DestroyNativeTexture(id_); + renderer_->DestroyTexture(this); } } diff --git a/app/render/texture.h b/app/render/texture.h index 612b3e2f2..6179878a8 100644 --- a/app/render/texture.h +++ b/app/render/texture.h @@ -32,11 +32,6 @@ class Renderer; class Texture { public: - enum Type { - k2D, - k3D - }; - enum Interpolation { kNearest, kLinear, @@ -50,19 +45,17 @@ public: */ Texture(const VideoParams& param) : renderer_(nullptr), - params_(param), - type_(k2D) + params_(param) { } /** * @brief Construct a real texture linked to a renderer backend */ - Texture(Renderer* renderer, const QVariant& native, const VideoParams& param, Type type) : + Texture(Renderer* renderer, const QVariant& native, const VideoParams& param) : renderer_(renderer), params_(param), - id_(native), - type_(type) + id_(native) { } @@ -117,11 +110,6 @@ public: return params_.pixel_aspect_ratio(); } - Type type() const - { - return type_; - } - Renderer* renderer() const { return renderer_; @@ -134,8 +122,6 @@ private: QVariant id_; - Type type_; - }; using TexturePtr = std::shared_ptr; diff --git a/app/render/videoparams.h b/app/render/videoparams.h index f3cedd28b..28d8c29fe 100644 --- a/app/render/videoparams.h +++ b/app/render/videoparams.h @@ -121,6 +121,8 @@ public: calculate_effective_size(); } + bool is_3d() const { return depth_ > 1; } + const rational& time_base() const { return time_base_; From 07f63ddc17cf2128658385932b523cbf82b6e057 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 26 Jul 2022 10:12:19 -0700 Subject: [PATCH 09/22] renderer: reinstated new and improved texture cache --- app/codec/ffmpeg/ffmpegdecoder.cpp | 33 +++------------- app/codec/ffmpeg/ffmpegdecoder.h | 4 -- app/render/opengl/openglrenderer.cpp | 25 ++++++------ app/render/opengl/openglrenderer.h | 6 +-- app/render/renderer.cpp | 57 ++++++++++++++++------------ app/render/renderer.h | 12 +++--- app/render/texture.cpp | 4 +- 7 files changed, 60 insertions(+), 81 deletions(-) diff --git a/app/codec/ffmpeg/ffmpegdecoder.cpp b/app/codec/ffmpeg/ffmpegdecoder.cpp index 9f66a9e3e..c2dbe2488 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.cpp +++ b/app/codec/ffmpeg/ffmpegdecoder.cpp @@ -61,9 +61,6 @@ FFmpegDecoder::FFmpegDecoder() : input_fmt_(AV_PIX_FMT_NONE), native_internal_pix_fmt_(VideoParams::kFormatInvalid), native_output_pix_fmt_(VideoParams::kFormatInvalid), - y_tex_(nullptr), - u_tex_(nullptr), - v_tex_(nullptr), working_frame_(nullptr), working_packet_(nullptr), cache_at_zero_(false), @@ -219,12 +216,7 @@ TexturePtr FFmpegDecoder::RetrieveVideoInternal(Renderer *renderer, const ration plane_params.set_channel_count(1); plane_params.set_divider(1); plane_params.set_format(native_internal_pix_fmt_); - - if (!y_tex_) { - y_tex_ = renderer->CreateTexture(plane_params, f->data[0], f->linesize[0] / px_size); - } else { - y_tex_->Upload(f->data[0], f->linesize[0] / px_size); - } + TexturePtr y_plane = renderer->CreateTexture(plane_params, f->data[0], f->linesize[0] / px_size); if (src_fmt == AV_PIX_FMT_YUV420P || src_fmt == AV_PIX_FMT_YUV422P @@ -244,22 +236,13 @@ TexturePtr FFmpegDecoder::RetrieveVideoInternal(Renderer *renderer, const ration plane_params.set_height(plane_params.height()/2); } - if (!u_tex_) { - u_tex_ = renderer->CreateTexture(plane_params, f->data[1], f->linesize[1] / px_size); - } else { - u_tex_->Upload(f->data[1], f->linesize[1] / px_size); - } - - if (!v_tex_) { - v_tex_ = renderer->CreateTexture(plane_params, f->data[2], f->linesize[2] / px_size); - } else { - v_tex_->Upload(f->data[2], f->linesize[2] / px_size); - } + TexturePtr u_plane = renderer->CreateTexture(plane_params, f->data[1], f->linesize[1] / px_size); + TexturePtr v_plane = renderer->CreateTexture(plane_params, f->data[2], f->linesize[2] / px_size); ShaderJob job; - job.Insert(QStringLiteral("y_channel"), NodeValue(NodeValue::kTexture, QVariant::fromValue(y_tex_))); - job.Insert(QStringLiteral("u_channel"), NodeValue(NodeValue::kTexture, QVariant::fromValue(u_tex_))); - job.Insert(QStringLiteral("v_channel"), NodeValue(NodeValue::kTexture, QVariant::fromValue(v_tex_))); + job.Insert(QStringLiteral("y_channel"), NodeValue(NodeValue::kTexture, QVariant::fromValue(y_plane))); + job.Insert(QStringLiteral("u_channel"), NodeValue(NodeValue::kTexture, QVariant::fromValue(u_plane))); + job.Insert(QStringLiteral("v_channel"), NodeValue(NodeValue::kTexture, QVariant::fromValue(v_plane))); job.Insert(QStringLiteral("bits_per_pixel"), NodeValue(NodeValue::kInt, bits_per_pixel)); job.Insert(QStringLiteral("jpeg_range"), NodeValue(NodeValue::kBoolean, jpeg_range)); @@ -313,10 +296,6 @@ void FFmpegDecoder::CloseInternal() input_fmt_ = AV_PIX_FMT_NONE; native_internal_pix_fmt_ = VideoParams::kFormatInvalid; native_output_pix_fmt_ = VideoParams::kFormatInvalid; - - y_tex_ = nullptr; - u_tex_ = nullptr; - v_tex_ = nullptr; } QString FFmpegDecoder::id() const diff --git a/app/codec/ffmpeg/ffmpegdecoder.h b/app/codec/ffmpeg/ffmpegdecoder.h index 837d5e04c..1c0d25bb8 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.h +++ b/app/codec/ffmpeg/ffmpegdecoder.h @@ -165,10 +165,6 @@ private: VideoParams::Format native_output_pix_fmt_; int native_channel_count_; - TexturePtr y_tex_; - TexturePtr u_tex_; - TexturePtr v_tex_; - AVFrame *working_frame_; AVPacket *working_packet_; diff --git a/app/render/opengl/openglrenderer.cpp b/app/render/opengl/openglrenderer.cpp index a06c44ffd..014c124bc 100644 --- a/app/render/opengl/openglrenderer.cpp +++ b/app/render/opengl/openglrenderer.cpp @@ -177,7 +177,7 @@ void OpenGLRenderer::ClearDestination(Texture *texture, double r, double g, doub GL_PREAMBLE; if (texture) { - AttachTextureAsDestination(texture); + AttachTextureAsDestination(texture->id()); } ClearDestinationInternal(r, g, b, a); @@ -225,7 +225,7 @@ QVariant OpenGLRenderer::CreateNativeTexture(int width, int height, int depth, V return texture; } -void OpenGLRenderer::AttachTextureAsDestination(Texture* texture) +void OpenGLRenderer::AttachTextureAsDestination(const QVariant &texture) { PRINT_GL_ERRORS; @@ -233,7 +233,7 @@ void OpenGLRenderer::AttachTextureAsDestination(Texture* texture) functions_->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - texture->id().value(), + texture.value(), 0); } @@ -291,14 +291,13 @@ void OpenGLRenderer::DestroyNativeShader(QVariant shader) functions_->glDeleteProgram(program); } -void OpenGLRenderer::UploadToTexture(Texture *texture, const void *data, int linesize) +void OpenGLRenderer::UploadToTexture(const QVariant &handle, const VideoParams &p, const void *data, int linesize) { GL_PREAMBLE; - GLuint t = texture->id().value(); - const VideoParams& p = texture->params(); + GLuint t = handle.value(); - bool is_3d = texture->params().is_3d(); + bool is_3d = p.is_3d(); GLenum tex_type = !is_3d ? GL_TEXTURE_2D : GL_TEXTURE_3D; GLenum tex_binding = !is_3d ? GL_TEXTURE_BINDING_2D : GL_TEXTURE_BINDING_3D; @@ -332,16 +331,14 @@ void OpenGLRenderer::UploadToTexture(Texture *texture, const void *data, int lin functions_->glBindTexture(tex_type, current_tex); } -void OpenGLRenderer::DownloadFromTexture(Texture* texture, void *data, int linesize) +void OpenGLRenderer::DownloadFromTexture(const QVariant &id, const VideoParams &p, void *data, int linesize) { GL_PREAMBLE; - const VideoParams& p = texture->params(); - GLint current_tex; functions_->glGetIntegerv(GL_TEXTURE_BINDING_2D, ¤t_tex); - AttachTextureAsDestination(texture); + AttachTextureAsDestination(id); functions_->glPixelStorei(GL_PACK_ROW_LENGTH, linesize); @@ -372,7 +369,7 @@ void OpenGLRenderer::Flush() Color OpenGLRenderer::GetPixelFromTexture(Texture *texture, const QPointF &pt) { - AttachTextureAsDestination(texture); + AttachTextureAsDestination(texture->id()); QByteArray data(VideoParams::GetBytesPerPixel(texture->format(), texture->channel_count()), Qt::Uninitialized); @@ -610,7 +607,7 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video // This is the last iteration, draw to the destination if (destination) { // If we have a destination texture, draw to it - AttachTextureAsDestination(destination); + AttachTextureAsDestination(destination->id()); } else if (iteration > 0) { // Otherwise, if we were iterating before, detach texture now DetachTextureAsDestination(); @@ -622,7 +619,7 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video } } else { // Always draw to output_tex, which gets swapped with input_tex every iteration - AttachTextureAsDestination(output_tex.get()); + AttachTextureAsDestination(output_tex->id()); } if (iteration > 0) { diff --git a/app/render/opengl/openglrenderer.h b/app/render/opengl/openglrenderer.h index 1377e475c..fe7f0098a 100644 --- a/app/render/opengl/openglrenderer.h +++ b/app/render/opengl/openglrenderer.h @@ -55,9 +55,9 @@ public: virtual void DestroyNativeShader(QVariant shader) override; - virtual void UploadToTexture(olive::Texture* texture, const void* data, int linesize) override; + virtual void UploadToTexture(const QVariant &handle, const VideoParams ¶ms, const void* data, int linesize) override; - virtual void DownloadFromTexture(olive::Texture* texture, void* data, int linesize) override; + virtual void DownloadFromTexture(const QVariant &handle, const VideoParams ¶ms, void* data, int linesize) override; virtual void Flush() override; @@ -83,7 +83,7 @@ private: static GLenum GetPixelFormat(int channel_count); - void AttachTextureAsDestination(olive::Texture* texture); + void AttachTextureAsDestination(const QVariant &texture); void DetachTextureAsDestination(); diff --git a/app/render/renderer.cpp b/app/render/renderer.cpp index 39a0aaff0..98566a244 100644 --- a/app/render/renderer.cpp +++ b/app/render/renderer.cpp @@ -29,45 +29,52 @@ namespace olive { Renderer::Renderer(QObject *parent) : QObject(parent) { - QTimer *texture_garbage_collector = new QTimer(this); - texture_garbage_collector->setInterval(MAX_TEXTURE_LIFE); - connect(texture_garbage_collector, &QTimer::timeout, this, &Renderer::ClearOldTextures); - texture_garbage_collector->start(); } TexturePtr Renderer::CreateTexture(const VideoParams ¶ms, const void *data, int linesize) { QVariant v; - /*for (auto it=texture_cache_.begin(); it!=texture_cache_.end(); it++) { - if (it->width == params.effective_width() - && it->height == params.effective_height() - && it->depth == params.effective_depth() - && it->format == params.format() - && it->channel_count == params.channel_count()) { - this->Flush(); - v = it->handle; - texture_cache_.erase(it); - break; + if (USE_TEXTURE_CACHE) { + for (auto it=texture_cache_.begin(); it!=texture_cache_.end(); it++) { + if (it->width == params.effective_width() + && it->height == params.effective_height() + && it->depth == params.effective_depth() + && it->format == params.format() + && it->channel_count == params.channel_count()) { + this->Flush(); + v = it->handle; + texture_cache_.erase(it); + break; + } } - }*/ + } - v = CreateNativeTexture(params.effective_width(), params.effective_height(), params.effective_depth(), - params.format(), params.channel_count(), data, linesize); + if (v.isNull()) { + v = CreateNativeTexture(params.effective_width(), params.effective_height(), params.effective_depth(), + params.format(), params.channel_count(), data, linesize); + } else { + UploadToTexture(v, params, data, linesize); + } return CreateTextureFromNativeHandle(v, params); } void Renderer::DestroyTexture(Texture *texture) { - /*texture_cache_.push_back({texture->params().effective_width(), - texture->params().effective_height(), - texture->params().effective_depth(), - texture->params().format(), - texture->params().channel_count(), - texture->id(), - QDateTime::currentMSecsSinceEpoch()});*/ - DestroyNativeTexture(texture->id()); + if (USE_TEXTURE_CACHE) { + texture_cache_.push_back({texture->params().effective_width(), + texture->params().effective_height(), + texture->params().effective_depth(), + texture->params().format(), + texture->params().channel_count(), + texture->id(), + QDateTime::currentMSecsSinceEpoch()}); + + ClearOldTextures(); + } else { + DestroyNativeTexture(texture->id()); + } } TexturePtr Renderer::InterlaceTexture(TexturePtr top, TexturePtr bottom, const VideoParams ¶ms) diff --git a/app/render/renderer.h b/app/render/renderer.h index dc1fb19a0..96141df4e 100644 --- a/app/render/renderer.h +++ b/app/render/renderer.h @@ -91,9 +91,9 @@ public: virtual void DestroyNativeShader(QVariant shader) = 0; - virtual void UploadToTexture(olive::Texture* texture, const void* data, int linesize) = 0; + virtual void UploadToTexture(const QVariant &handle, const VideoParams ¶ms, const void* data, int linesize) = 0; - virtual void DownloadFromTexture(olive::Texture* texture, void* data, int linesize) = 0; + virtual void DownloadFromTexture(const QVariant &handle, const VideoParams ¶ms, void* data, int linesize) = 0; virtual void Flush() = 0; @@ -130,6 +130,8 @@ private: bool GetColorContext(const ColorTransformJob &color_job, ColorContext* ctx); + void ClearOldTextures(); + QHash color_cache_; struct CachedTexture @@ -143,7 +145,8 @@ private: qint64 accessed; }; - const int MAX_TEXTURE_LIFE = 10000; + static const int MAX_TEXTURE_LIFE = 5000; + static const bool USE_TEXTURE_CACHE = true; std::list texture_cache_; QMutex color_cache_mutex_; @@ -152,9 +155,6 @@ private: QVariant interlace_texture_; -private slots: - void ClearOldTextures(); - }; } diff --git a/app/render/texture.cpp b/app/render/texture.cpp index f3b5fb9d0..2c5c9fdea 100644 --- a/app/render/texture.cpp +++ b/app/render/texture.cpp @@ -36,14 +36,14 @@ Texture::~Texture() void Texture::Upload(void *data, int linesize) { if (renderer_) { - renderer_->UploadToTexture(this, data, linesize); + renderer_->UploadToTexture(this->id(), this->params(), data, linesize); } } void Texture::Download(void *data, int linesize) { if (renderer_) { - renderer_->DownloadFromTexture(this, data, linesize); + renderer_->DownloadFromTexture(this->id(), this->params(), data, linesize); } } From e067acb9993c1bcc25502fa5a98226ad137ddb85 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 26 Jul 2022 10:14:46 -0700 Subject: [PATCH 10/22] renderprocessor: commit line that github desktop undid for no reason --- app/render/renderprocessor.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index c8617e095..e0219cbb1 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -45,6 +45,9 @@ RenderProcessor::RenderProcessor(RenderTicketPtr ticket, Renderer *render_ctx, D TexturePtr RenderProcessor::GenerateTexture(const rational &time, const rational &frame_length) { + QElapsedTimer t; + t.restart(); + TimeRange range = TimeRange(time, time + frame_length); NodeValueTable table; @@ -56,6 +59,8 @@ TexturePtr RenderProcessor::GenerateTexture(const rational &time, const rational ResolveJobs(tex_val, range); + qDebug() << "Frame took:" << t.elapsed(); + return tex_val.toTexture(); } @@ -107,6 +112,8 @@ FramePtr RenderProcessor::GenerateFrame(TexturePtr texture, const rational& time job.SetInputAlphaAssociation(OLIVE_CONFIG("ReassocLinToNonLin").toBool() ? kAlphaAssociated : kAlphaNone); job.SetTransformMatrix(matrix); + qDebug() << "Blitting with" << output_color_transform.get(); + render_ctx_->BlitColorManaged(job, blit_tex.get()); } else { // No color transform, just blit @@ -123,7 +130,7 @@ FramePtr RenderProcessor::GenerateFrame(TexturePtr texture, const rational& time render_ctx_->Flush(); - render_ctx_->DownloadFromTexture(texture.get(), frame->data(), frame->linesize_pixels()); + render_ctx_->DownloadFromTexture(texture->id(), texture->params(), frame->data(), frame->linesize_pixels()); } return frame; From b0446e237befeeb0fd088d140b9980648b5af88a Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 26 Jul 2022 10:37:44 -0700 Subject: [PATCH 11/22] render: how the fuck did it do this? --- app/render/renderprocessor.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index e0219cbb1..08a4fbdbc 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -45,9 +45,6 @@ RenderProcessor::RenderProcessor(RenderTicketPtr ticket, Renderer *render_ctx, D TexturePtr RenderProcessor::GenerateTexture(const rational &time, const rational &frame_length) { - QElapsedTimer t; - t.restart(); - TimeRange range = TimeRange(time, time + frame_length); NodeValueTable table; @@ -59,8 +56,6 @@ TexturePtr RenderProcessor::GenerateTexture(const rational &time, const rational ResolveJobs(tex_val, range); - qDebug() << "Frame took:" << t.elapsed(); - return tex_val.toTexture(); } @@ -112,8 +107,6 @@ FramePtr RenderProcessor::GenerateFrame(TexturePtr texture, const rational& time job.SetInputAlphaAssociation(OLIVE_CONFIG("ReassocLinToNonLin").toBool() ? kAlphaAssociated : kAlphaNone); job.SetTransformMatrix(matrix); - qDebug() << "Blitting with" << output_color_transform.get(); - render_ctx_->BlitColorManaged(job, blit_tex.get()); } else { // No color transform, just blit From 3caa5352f84fc02dd02cf1df04c7c6413a4e7d3b Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 26 Jul 2022 10:37:56 -0700 Subject: [PATCH 12/22] render: only upload to texture if data was passed --- app/render/renderer.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/render/renderer.cpp b/app/render/renderer.cpp index 98566a244..37294759a 100644 --- a/app/render/renderer.cpp +++ b/app/render/renderer.cpp @@ -42,7 +42,6 @@ TexturePtr Renderer::CreateTexture(const VideoParams ¶ms, const void *data, && it->depth == params.effective_depth() && it->format == params.format() && it->channel_count == params.channel_count()) { - this->Flush(); v = it->handle; texture_cache_.erase(it); break; @@ -53,8 +52,10 @@ TexturePtr Renderer::CreateTexture(const VideoParams ¶ms, const void *data, if (v.isNull()) { v = CreateNativeTexture(params.effective_width(), params.effective_height(), params.effective_depth(), params.format(), params.channel_count(), data, linesize); - } else { + } else if (data) { UploadToTexture(v, params, data, linesize); + } else { + this->Flush(); } return CreateTextureFromNativeHandle(v, params); From f7b7f515814f6c4363d0fcb0b49ff099bfcd7d0c Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 26 Jul 2022 10:39:33 -0700 Subject: [PATCH 13/22] manageddisplaywidget: switch back to QOpenGLWindow --- app/widget/manageddisplay/manageddisplay.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/widget/manageddisplay/manageddisplay.h b/app/widget/manageddisplay/manageddisplay.h index 644ab0711..10e805d77 100644 --- a/app/widget/manageddisplay/manageddisplay.h +++ b/app/widget/manageddisplay/manageddisplay.h @@ -21,7 +21,7 @@ #ifndef MANAGEDDISPLAYOBJECT_H #define MANAGEDDISPLAYOBJECT_H -//#define USE_QOPENGLWINDOW +#define USE_QOPENGLWINDOW #include #include From d05146170edde593101a6ccb5d5a9f79e48e2ca3 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 26 Jul 2022 10:50:42 -0700 Subject: [PATCH 14/22] viewer: increase playback interval --- app/widget/viewer/viewer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 2a9135780..1b023cefb 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -60,7 +60,7 @@ QVector ViewerWidget::instances_; // changing values. 1/4 second seems to be a good middleground. const rational ViewerWidget::kAudioPlaybackInterval = rational(1, 4); -const rational kVideoPlaybackInterval = rational(1, 2); +const rational kVideoPlaybackInterval = rational(2); ViewerWidget::ViewerWidget(QWidget *parent) : super(false, true, parent), From f6dc3c7fcc7a630b79e4d969fc636266c808ee3e Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 26 Jul 2022 11:04:24 -0700 Subject: [PATCH 15/22] node: implement transform parenting --- .../transform/transformdistortnode.cpp | 32 ++++++++++--------- .../distort/transform/transformdistortnode.h | 1 + app/node/generator/matrix/matrix.cpp | 12 +++---- app/node/generator/matrix/matrix.h | 5 +-- 4 files changed, 27 insertions(+), 23 deletions(-) diff --git a/app/node/distort/transform/transformdistortnode.cpp b/app/node/distort/transform/transformdistortnode.cpp index 24d199811..c839c93f0 100644 --- a/app/node/distort/transform/transformdistortnode.cpp +++ b/app/node/distort/transform/transformdistortnode.cpp @@ -22,12 +22,9 @@ #include -#include "common/range.h" -#include "core.h" -#include "node/traverser.h" - namespace olive { +const QString TransformDistortNode::kParentInput = QStringLiteral("parent_in"); const QString TransformDistortNode::kTextureInput = QStringLiteral("tex_in"); const QString TransformDistortNode::kAutoscaleInput = QStringLiteral("autoscale_in"); const QString TransformDistortNode::kInterpolationInput = QStringLiteral("interpolation_in"); @@ -36,6 +33,8 @@ const QString TransformDistortNode::kInterpolationInput = QStringLiteral("interp TransformDistortNode::TransformDistortNode() { + AddInput(kParentInput, NodeValue::kMatrix); + AddInput(kAutoscaleInput, NodeValue::kCombo, 0); AddInput(kInterpolationInput, NodeValue::kCombo, 2); @@ -73,6 +72,7 @@ void TransformDistortNode::Retranslate() { super::Retranslate(); + SetInputName(kParentInput, tr("Parent")); SetInputName(kAutoscaleInput, tr("Auto-Scale")); SetInputName(kTextureInput, tr("Texture")); SetInputName(kInterpolationInput, tr("Interpolation")); @@ -84,12 +84,12 @@ void TransformDistortNode::Retranslate() void TransformDistortNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const { // Generate matrix - QMatrix4x4 generated_matrix = GenerateMatrix(value, false, false, false); + QMatrix4x4 generated_matrix = GenerateMatrix(value, false, false, false, value[kParentInput].toMatrix()); // Pop texture NodeValue texture_meta = value[kTextureInput]; - bool pushed_job = false; + QVariant job_to_push; // If we have a texture, generate a matrix and make it happen if (TexturePtr texture = texture_meta.toTexture()) { @@ -103,15 +103,17 @@ void TransformDistortNode::Value(const NodeValueRow &value, const NodeGlobals &g job.Insert(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, real_matrix, this)); job.SetInterpolation(QStringLiteral("ove_maintex"), static_cast(value[kInterpolationInput].toInt())); - table->Push(NodeValue::kTexture, QVariant::fromValue(job), this); - - pushed_job = true; + job_to_push = QVariant::fromValue(job); } } - if (!pushed_job) { + table->Push(NodeValue::kMatrix, QVariant::fromValue(generated_matrix), this); + + if (job_to_push.isNull()) { // Re-push whatever value we received table->Push(texture_meta); + } else { + table->Push(NodeValue::kTexture, job_to_push, this); } } @@ -129,7 +131,7 @@ void TransformDistortNode::GizmoDragStart(const NodeValueRow &row, double x, dou if (gizmo == anchor_gizmo_) { - gizmo_inverted_transform_ = GenerateMatrix(row, true, true, false).toTransform().inverted(); + gizmo_inverted_transform_ = GenerateMatrix(row, true, true, false, row[kParentInput].toMatrix()).toTransform().inverted(); } else if (IsAScaleGizmo(gizmo)) { @@ -171,7 +173,7 @@ void TransformDistortNode::GizmoDragStart(const NodeValueRow &row, double x, dou } // Store current matrix - gizmo_inverted_transform_ = GenerateMatrix(row, true, true, true).toTransform().inverted(); + gizmo_inverted_transform_ = GenerateMatrix(row, true, true, true, row[kParentInput].toMatrix()).toTransform().inverted(); } else if (gizmo == rotation_gizmo_) { @@ -356,7 +358,7 @@ void TransformDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const N // Fold values into a matrix for the rectangle QMatrix4x4 rectangle_matrix; rectangle_matrix.scale(sequence_half_res); - rectangle_matrix *= AdjustMatrixByResolutions(GenerateMatrix(row, false, false, false), + rectangle_matrix *= AdjustMatrixByResolutions(GenerateMatrix(row, false, false, false, row[kParentInput].toMatrix()), sequence_res, tex_sz, tex_offset, @@ -376,7 +378,7 @@ void TransformDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const N // Draw anchor point QMatrix4x4 anchor_matrix; anchor_matrix.scale(sequence_half_res); - anchor_matrix *= AdjustMatrixByResolutions(GenerateMatrix(row, true, false, false), + anchor_matrix *= AdjustMatrixByResolutions(GenerateMatrix(row, true, false, false, row[kParentInput].toMatrix()), sequence_res, tex_sz, tex_offset, @@ -402,7 +404,7 @@ void TransformDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const N QTransform TransformDistortNode::GizmoTransformation(const NodeValueRow &row, const NodeGlobals &globals) const { if (TexturePtr texture = row[kTextureInput].toTexture()) { - auto m = GenerateMatrix(row, false, false, false); + auto m = GenerateMatrix(row, false, false, false, row[kParentInput].toMatrix()); return GenerateAutoScaledMatrix(m, row, globals, texture->params()).toTransform(); } return super::GizmoTransformation(row, globals); diff --git a/app/node/distort/transform/transformdistortnode.h b/app/node/distort/transform/transformdistortnode.h index b6ae714b9..be17de561 100644 --- a/app/node/distort/transform/transformdistortnode.h +++ b/app/node/distort/transform/transformdistortnode.h @@ -84,6 +84,7 @@ public: virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override; virtual QTransform GizmoTransformation(const NodeValueRow &row, const NodeGlobals &globals) const override; + static const QString kParentInput; static const QString kTextureInput; static const QString kAutoscaleInput; static const QString kInterpolationInput; diff --git a/app/node/generator/matrix/matrix.cpp b/app/node/generator/matrix/matrix.cpp index 81e43c4ea..81dbdb8f6 100644 --- a/app/node/generator/matrix/matrix.cpp +++ b/app/node/generator/matrix/matrix.cpp @@ -90,11 +90,11 @@ void MatrixGenerator::Retranslate() void MatrixGenerator::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const { // Push matrix output - QMatrix4x4 mat = GenerateMatrix(value, false, false, false); + QMatrix4x4 mat = GenerateMatrix(value, false, false, false, QMatrix4x4()); table->Push(NodeValue::kMatrix, mat, this); } -QMatrix4x4 MatrixGenerator::GenerateMatrix(const NodeValueRow &value, bool ignore_anchor, bool ignore_position, bool ignore_scale) const +QMatrix4x4 MatrixGenerator::GenerateMatrix(const NodeValueRow &value, bool ignore_anchor, bool ignore_position, bool ignore_scale, const QMatrix4x4 &mat) const { QVector2D anchor; QVector2D position; @@ -116,17 +116,17 @@ QMatrix4x4 MatrixGenerator::GenerateMatrix(const NodeValueRow &value, bool ignor value[kRotationInput].toDouble(), scale, value[kUniformScaleInput].toBool(), - anchor); + anchor, + mat); } QMatrix4x4 MatrixGenerator::GenerateMatrix(const QVector2D& pos, const float& rot, const QVector2D& scale, bool uniform_scale, - const QVector2D& anchor) + const QVector2D& anchor, + QMatrix4x4 mat) { - QMatrix4x4 mat; - // Position mat.translate(pos); diff --git a/app/node/generator/matrix/matrix.h b/app/node/generator/matrix/matrix.h index 5d93cb51d..60ea1a035 100644 --- a/app/node/generator/matrix/matrix.h +++ b/app/node/generator/matrix/matrix.h @@ -53,12 +53,13 @@ public: static const QString kAnchorInput; protected: - QMatrix4x4 GenerateMatrix(const NodeValueRow &value, bool ignore_anchor, bool ignore_position, bool ignore_scale) const; + QMatrix4x4 GenerateMatrix(const NodeValueRow &value, bool ignore_anchor, bool ignore_position, bool ignore_scale, const QMatrix4x4 &mat) const; static QMatrix4x4 GenerateMatrix(const QVector2D &pos, const float &rot, const QVector2D &scale, bool uniform_scale, - const QVector2D &anchor); + const QVector2D &anchor, + QMatrix4x4 mat); virtual void InputValueChangedEvent(const QString& input, int element) override; From 15a67bc12af044f109270ba02ac0e8fcdc2d1e67 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 26 Jul 2022 11:39:35 -0700 Subject: [PATCH 16/22] project: auto-select imported files --- app/core.cpp | 2 ++ app/node/project/folder/folder.cpp | 15 ++++++++++++++ app/node/project/folder/folder.h | 2 ++ app/panel/project/project.h | 4 ++-- .../projectexplorer/projectexplorer.cpp | 6 ++++-- app/widget/projectexplorer/projectexplorer.h | 2 +- app/window/mainwindow/mainwindow.cpp | 20 +++++++++++++++++++ app/window/mainwindow/mainwindow.h | 4 ++++ 8 files changed, 50 insertions(+), 5 deletions(-) diff --git a/app/core.cpp b/app/core.cpp index c9b701ead..d6989319a 100644 --- a/app/core.cpp +++ b/app/core.cpp @@ -586,6 +586,8 @@ void Core::ImportTaskComplete(Task* task) } undo_stack_.pushIfHasChildren(command); + + main_window_->SelectFootage(import_task->GetImportedFootage()); } bool Core::ConfirmImageSequence(const QString& filename) diff --git a/app/node/project/folder/folder.cpp b/app/node/project/folder/folder.cpp index 3f8ffe700..4bcd7cabc 100644 --- a/app/node/project/folder/folder.cpp +++ b/app/node/project/folder/folder.cpp @@ -73,6 +73,21 @@ bool Folder::ChildExistsWithName(const QString &s) const return ChildExistsWithNameInternal(this, s); } +bool Folder::HasChildRecursive(Node *child) const +{ + for (Node *i : item_children_) { + if (i == child) { + return true; + } else if (Folder *f = dynamic_cast(i)) { + if (f->HasChildRecursive(child)) { + return true; + } + } + } + + return false; +} + int Folder::index_of_child_in_array(Node *item) const { int index_of_item = item_children_.indexOf(item); diff --git a/app/node/project/folder/folder.h b/app/node/project/folder/folder.h index 7274960f1..22d25f110 100644 --- a/app/node/project/folder/folder.h +++ b/app/node/project/folder/folder.h @@ -65,6 +65,8 @@ public: bool ChildExistsWithName(const QString& s) const; + bool HasChildRecursive(Node *child) const; + int item_child_count() const { return item_children_.size(); diff --git a/app/panel/project/project.h b/app/panel/project/project.h index 39f6299ee..d4918b7b3 100644 --- a/app/panel/project/project.h +++ b/app/panel/project/project.h @@ -52,9 +52,9 @@ public: ProjectViewModel* model() const; - bool SelectItem(Node *n) + bool SelectItem(Node *n, bool deselect_all_first = true) { - return explorer_->SelectItem(n); + return explorer_->SelectItem(n, deselect_all_first); } virtual void SelectAll() override; diff --git a/app/widget/projectexplorer/projectexplorer.cpp b/app/widget/projectexplorer/projectexplorer.cpp index 05c2b5e38..1a3cc36a4 100644 --- a/app/widget/projectexplorer/projectexplorer.cpp +++ b/app/widget/projectexplorer/projectexplorer.cpp @@ -653,9 +653,11 @@ void ProjectExplorer::DeleteSelected() } } -bool ProjectExplorer::SelectItem(Node *n) +bool ProjectExplorer::SelectItem(Node *n, bool deselect_all_first) { - DeselectAll(); + if (deselect_all_first) { + DeselectAll(); + } QModelIndex index = model_.CreateIndexFromItem(n); diff --git a/app/widget/projectexplorer/projectexplorer.h b/app/widget/projectexplorer/projectexplorer.h index 086c2eab3..d69f1b8e3 100644 --- a/app/widget/projectexplorer/projectexplorer.h +++ b/app/widget/projectexplorer/projectexplorer.h @@ -85,7 +85,7 @@ public: void DeleteSelected(); - bool SelectItem(Node *n); + bool SelectItem(Node *n, bool deselect_all_first = true); public slots: void set_view_type(ProjectToolbar::ViewType type); diff --git a/app/window/mainwindow/mainwindow.cpp b/app/window/mainwindow/mainwindow.cpp index e7f017a69..39d943eea 100644 --- a/app/window/mainwindow/mainwindow.cpp +++ b/app/window/mainwindow/mainwindow.cpp @@ -410,6 +410,16 @@ void MainWindow::SetApplicationProgressValue(int value) #endif } +void MainWindow::SelectFootage(const QVector &e) +{ + for (ProjectPanel *p : project_panels_) { + SelectFootageForProjectPanel(e, p); + } + for (ProjectPanel *p : folder_panels_) { + SelectFootageForProjectPanel(e, p); + } +} + void MainWindow::closeEvent(QCloseEvent *e) { // Try to close all projects (this will return false if the user chooses not to close) @@ -759,6 +769,16 @@ void MainWindow::UpdateNodePanelContextFromTimelinePanel(TimelinePanel *panel) param_panel_->SetContexts(context); } +void MainWindow::SelectFootageForProjectPanel(const QVector &e, ProjectPanel *p) +{ + p->DeselectAll(); + for (Footage *f : e) { + if (p->get_root()->HasChildRecursive(f)) { + p->SelectItem(f, false); + } + } +} + void MainWindow::FocusedPanelChanged(PanelWidget *panel) { // Update audio monitor panel diff --git a/app/window/mainwindow/mainwindow.h b/app/window/mainwindow/mainwindow.h index 20498b684..1f6504c6f 100644 --- a/app/window/mainwindow/mainwindow.h +++ b/app/window/mainwindow/mainwindow.h @@ -92,6 +92,8 @@ public: */ void SetApplicationProgressValue(int value); + void SelectFootage(const QVector &e); + public slots: void ProjectOpen(Project *p); @@ -142,6 +144,8 @@ private: void UpdateNodePanelContextFromTimelinePanel(TimelinePanel *panel); + void SelectFootageForProjectPanel(const QVector &e, ProjectPanel *p); + QByteArray premaximized_state_; // Standard panels From a09d2f41e920547e44c80673402938861387b90a Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 26 Jul 2022 12:40:00 -0700 Subject: [PATCH 17/22] opengl: disable mutex Shouldn't really need this anymore and it fixes a deadlock --- app/render/opengl/openglrenderer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/render/opengl/openglrenderer.cpp b/app/render/opengl/openglrenderer.cpp index 014c124bc..8c6f2a703 100644 --- a/app/render/opengl/openglrenderer.cpp +++ b/app/render/opengl/openglrenderer.cpp @@ -78,9 +78,9 @@ private: #define PRINT_GL_ERRORS ErrorPrinter __e(__FUNCTION__, functions_) #define GL_PREAMBLE \ - QMutexLocker __l(&global_opengl_mutex); + //QMutexLocker __l(&global_opengl_mutex); -QMutex global_opengl_mutex; +//QMutex global_opengl_mutex; OpenGLRenderer::OpenGLRenderer(QObject* parent) : Renderer(parent), From 66152b4aa3d5d6a8a81aae0151d949dc2365a812 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 26 Jul 2022 12:50:16 -0700 Subject: [PATCH 18/22] renderer: clear texture cache after deleting interlace texture --- app/render/renderer.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/render/renderer.cpp b/app/render/renderer.cpp index 37294759a..85f3aed69 100644 --- a/app/render/renderer.cpp +++ b/app/render/renderer.cpp @@ -109,11 +109,6 @@ QVariant Renderer::GetDefaultShader() void Renderer::Destroy() { - for (auto it=texture_cache_.begin(); it!=texture_cache_.end(); it++) { - DestroyNativeTexture(it->handle); - } - texture_cache_.clear(); - if (!default_shader_.isNull()) { DestroyNativeShader(default_shader_); default_shader_.clear(); @@ -126,6 +121,11 @@ void Renderer::Destroy() interlace_texture_.clear(); } + for (auto it=texture_cache_.begin(); it!=texture_cache_.end(); it++) { + DestroyNativeTexture(it->handle); + } + texture_cache_.clear(); + DestroyInternal(); } From f0ebbda16c8db4fa0381fe7e00e143d7bd595b09 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 26 Jul 2022 14:08:49 -0700 Subject: [PATCH 19/22] viewer: fix invalid remaining length when queuing --- app/widget/viewer/viewer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 1b023cefb..18d086386 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -1048,7 +1048,7 @@ int ViewerWidget::DeterminePlaybackQueueSize() end_ts = 0; } - int remaining_frames = (end_ts - GetTimestamp()) / playback_speed_; + int remaining_frames = (end_ts - GetTimestamp() - 1) / playback_speed_; // Generate maximum queue int max_frames = qCeil(kVideoPlaybackInterval.toDouble() / timebase().toDouble()); From d1ec2b909b8a4eeb29bbec438174b6e8cf4125b3 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 26 Jul 2022 14:41:01 -0700 Subject: [PATCH 20/22] cmake: default werror and tests to off, and enable on CI --- .github/workflows/ci.yml | 10 +++++++--- CMakeLists.txt | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 857057649..8841062c1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,6 +24,7 @@ on: env: DOWNLOAD_TOOL: curl -fLOSs --retry 2 --retry-delay 60 UPLOAD_TOOL: curl -X POST --retry 2 --retry-delay 60 + CMAKE_ARGS: -DUSE_WERROR=ON -DBUILD_TESTS=ON jobs: linux: @@ -73,7 +74,8 @@ jobs: cmake .. -G "${{ matrix.cmake-gen }}" \ -DCMAKE_BUILD_TYPE="${{ matrix.build-type }}" \ -DCMAKE_C_COMPILER="${{ matrix.cc-compiler }}" \ - -DCMAKE_CXX_COMPILER="${{ matrix.cxx-compiler }}" + -DCMAKE_CXX_COMPILER="${{ matrix.cxx-compiler }}" \ + $CMAKE_ARGS - name: Build working-directory: build @@ -204,7 +206,8 @@ jobs: working-directory: ${{ runner.workspace }}/build run: | cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} -G "${{ matrix.cmake-gen }}" \ - -DCMAKE_TOOLCHAIN_FILE="$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" + -DCMAKE_TOOLCHAIN_FILE="$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" \ + $CMAKE_ARGS - name: Build working-directory: ${{ runner.workspace }}/build @@ -356,7 +359,8 @@ jobs: PATH=$DEP_LOCATION:$DEP_LOCATION/bin:$DEP_LOCATION/include:$DEP_LOCATION/lib:$DEP_LOCATION/crashpad:$PATH \ cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} \ -DCMAKE_OSX_DEPLOYMENT_TARGET=${{ matrix.min-deploy }} -G "${{ matrix.cmake-gen }}" \ - -DCMAKE_OSX_ARCHITECTURES="${{ matrix.os-arch }}" + -DCMAKE_OSX_ARCHITECTURES="${{ matrix.os-arch }}" \ + $CMAKE_ARGS - name: Build working-directory: ${{ runner.workspace }}/build diff --git a/CMakeLists.txt b/CMakeLists.txt index b46d11f6f..1206118dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,8 +19,8 @@ cmake_minimum_required(VERSION 3.13 FATAL_ERROR) project(olive-editor VERSION 0.2.0 LANGUAGES CXX) option(BUILD_DOXYGEN "Build Doxygen documentation" OFF) -option(BUILD_TESTS "Build unit tests" ON) -option(USE_WERROR "Error on compile warning" ON) +option(BUILD_TESTS "Build unit tests" OFF) +option(USE_WERROR "Error on compile warning" OFF) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) From 25e94112f8723266799b5d35ab0211e747db7ea7 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 26 Jul 2022 14:49:19 -0700 Subject: [PATCH 21/22] viewer: implement lookahead to ensure footage is ready in advance --- app/codec/decoder.cpp | 6 ++ app/codec/decoder.h | 2 + app/render/previewautocacher.cpp | 13 ++-- app/render/previewautocacher.h | 8 +-- app/render/rendermanager.cpp | 14 +++- app/render/rendermanager.h | 6 +- app/render/renderprocessor.cpp | 107 ++++++++++++++++--------------- app/widget/viewer/viewer.cpp | 36 ++++++++++- app/widget/viewer/viewer.h | 7 ++ 9 files changed, 137 insertions(+), 62 deletions(-) diff --git a/app/codec/decoder.cpp b/app/codec/decoder.cpp index fd845737e..c98cb5c2a 100644 --- a/app/codec/decoder.cpp +++ b/app/codec/decoder.cpp @@ -43,6 +43,12 @@ Decoder::Decoder() : UpdateLastAccessed(); } +void Decoder::IncrementAccessTime(qint64 t) +{ + QMutexLocker locker(&mutex_); + last_accessed_ += t; +} + bool Decoder::Open(const CodecStream &stream) { QMutexLocker locker(&mutex_); diff --git a/app/codec/decoder.h b/app/codec/decoder.h index 08ccf6e32..665f21cc8 100644 --- a/app/codec/decoder.h +++ b/app/codec/decoder.h @@ -87,6 +87,8 @@ public: virtual bool SupportsVideo(){return false;} virtual bool SupportsAudio(){return false;} + void IncrementAccessTime(qint64 t); + class CodecStream { public: diff --git a/app/render/previewautocacher.cpp b/app/render/previewautocacher.cpp index d5707c845..e876ddafa 100644 --- a/app/render/previewautocacher.cpp +++ b/app/render/previewautocacher.cpp @@ -61,7 +61,7 @@ PreviewAutoCacher::~PreviewAutoCacher() SetViewerNode(nullptr); } -RenderTicketPtr PreviewAutoCacher::GetSingleFrame(const rational &t) +RenderTicketPtr PreviewAutoCacher::GetSingleFrame(const rational &t, bool dry) { // If we have a single frame render queued (but not yet sent to the RenderManager), cancel it now CancelQueuedSingleFrameRender(); @@ -70,6 +70,7 @@ RenderTicketPtr PreviewAutoCacher::GetSingleFrame(const rational &t) auto sfr = std::make_shared(); sfr->Start(); sfr->setProperty("time", QVariant::fromValue(t)); + sfr->setProperty("dry", dry); // Queue it and try to render single_frame_render_ = sfr; @@ -566,7 +567,8 @@ void PreviewAutoCacher::TryRender() if (single_frame_render_) { // Check if already caching this RenderTicketWatcher *watcher = RenderFrame(single_frame_render_->property("time").value(), - nullptr); + nullptr, + single_frame_render_->property("dry").toBool()); video_immediate_passthroughs_[watcher].append(single_frame_render_); single_frame_render_ = nullptr; @@ -583,7 +585,7 @@ void PreviewAutoCacher::TryRender() // We want this hash, if we're not already rendering, start render now if (!render_task) { // Don't render any hash more than once - RenderFrame(t, viewer_node_->video_frame_cache()); + RenderFrame(t, viewer_node_->video_frame_cache(), false); } emit SignalCacheProxyTaskProgress(double(queued_frame_iterator_.frame_index()) / double(queued_frame_iterator_.size())); @@ -609,7 +611,7 @@ void PreviewAutoCacher::TryRender() } } -RenderTicketWatcher* PreviewAutoCacher::RenderFrame(Node *node, const rational& time, FrameHashCache *cache) +RenderTicketWatcher* PreviewAutoCacher::RenderFrame(Node *node, const rational& time, FrameHashCache *cache, bool dry) { RenderTicketWatcher* watcher = new RenderTicketWatcher(); watcher->setProperty("job", QVariant::fromValue(last_update_time_)); @@ -623,7 +625,8 @@ RenderTicketWatcher* PreviewAutoCacher::RenderFrame(Node *node, const rational& time, RenderMode::kOffline, cache, - RenderManager::kTexture)); + dry ? RenderManager::kNull : RenderManager::kTexture)); + return watcher; } diff --git a/app/render/previewautocacher.h b/app/render/previewautocacher.h index c9343021f..0ce429673 100644 --- a/app/render/previewautocacher.h +++ b/app/render/previewautocacher.h @@ -49,7 +49,7 @@ public: virtual ~PreviewAutoCacher() override; - RenderTicketPtr GetSingleFrame(const rational& t); + RenderTicketPtr GetSingleFrame(const rational& t, bool dry = false); RenderTicketPtr GetRangeOfAudio(TimeRange range); @@ -98,10 +98,10 @@ signals: private: void TryRender(); - RenderTicketWatcher *RenderFrame(Node *node, const rational &time, FrameHashCache *cache); - RenderTicketWatcher *RenderFrame(const rational &time, FrameHashCache *cache) + RenderTicketWatcher *RenderFrame(Node *node, const rational &time, FrameHashCache *cache, bool dry); + RenderTicketWatcher *RenderFrame(const rational &time, FrameHashCache *cache, bool dry) { - return RenderFrame(copied_viewer_node_->GetConnectedTextureOutput(), time, cache); + return RenderFrame(copied_viewer_node_->GetConnectedTextureOutput(), time, cache, dry); } RenderTicketPtr RenderAudio(Node *node, const TimeRange &range, bool generate_waveforms); diff --git a/app/render/rendermanager.cpp b/app/render/rendermanager.cpp index 202820be5..1df5590b3 100644 --- a/app/render/rendermanager.cpp +++ b/app/render/rendermanager.cpp @@ -35,6 +35,7 @@ namespace olive { RenderManager* RenderManager::instance_ = nullptr; +const rational RenderManager::kDryRunInterval = rational(10); RenderManager::RenderManager(QObject *parent) : backend_(kOpenGL), @@ -52,9 +53,11 @@ RenderManager::RenderManager(QObject *parent) : if (context_) { video_thread_ = new RenderThread(context_, decoder_cache_, shader_cache_, this); + dry_run_thread_ = new RenderThread(nullptr, decoder_cache_, shader_cache_, this); audio_thread_ = new RenderThread(nullptr, decoder_cache_, shader_cache_, this); video_thread_->start(QThread::IdlePriority); + dry_run_thread_->start(QThread::IdlePriority); audio_thread_->start(QThread::IdlePriority); } @@ -73,6 +76,9 @@ RenderManager::~RenderManager() video_thread_->quit(); video_thread_->wait(); + dry_run_thread_->quit(); + dry_run_thread_->wait(); + context_->PostDestroy(); delete context_; @@ -129,7 +135,11 @@ RenderTicketPtr RenderManager::RenderFrame(Node *node, ColorManager* color_manag ticket->setProperty("cacheuuid", QVariant::fromValue(cache->GetUuid())); } - video_thread_->AddTicket(ticket); + if (return_type == ReturnType::kNull) { + dry_run_thread_->AddTicket(ticket); + } else { + video_thread_->AddTicket(ticket); + } return ticket; } @@ -157,6 +167,8 @@ bool RenderManager::RemoveTicket(RenderTicketPtr ticket) return true; } else if (audio_thread_->RemoveTicket(ticket)) { return true; + } else if (dry_run_thread_->RemoveTicket(ticket)) { + return true; } else { return false; } diff --git a/app/render/rendermanager.h b/app/render/rendermanager.h index a75c58aaf..891e2fffc 100644 --- a/app/render/rendermanager.h +++ b/app/render/rendermanager.h @@ -97,9 +97,12 @@ public: enum ReturnType { kTexture, - kFrame + kFrame, + kNull }; + static const rational kDryRunInterval; + /** * @brief Asynchronously generate a frame at a given time * @@ -168,6 +171,7 @@ private: QTimer *decoder_clear_timer_; RenderThread *video_thread_; + RenderThread *dry_run_thread_; RenderThread *audio_thread_; private slots: diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index 08a4fbdbc..0a6d78078 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -151,53 +151,57 @@ void RenderProcessor::Run() TexturePtr texture = GenerateTexture(time, frame_length); - if (GetCacheVideoParams().interlacing() != VideoParams::kInterlaceNone) { - // Get next between frame and interlace it - TexturePtr top = texture; - TexturePtr bottom = GenerateTexture(time + frame_length, frame_length); - - if (GetCacheVideoParams().interlacing() == VideoParams::kInterlacedBottomFirst) { - std::swap(top, bottom); - } - - texture = render_ctx_->InterlaceTexture(top, bottom, GetCacheVideoParams()); - } - - if (HeardCancel()) { - // Finish cancelled ticket with nothing since we can't guarantee the frame we generated - // is actually "complete + if (!render_ctx_) { ticket_->Finish(); } else { - RenderManager::ReturnType return_type = RenderManager::ReturnType(ticket_->property("return").toInt()); + if (GetCacheVideoParams().interlacing() != VideoParams::kInterlaceNone) { + // Get next between frame and interlace it + TexturePtr top = texture; + TexturePtr bottom = GenerateTexture(time + frame_length, frame_length); - FramePtr frame; - QString cache = ticket_->property("cache").toString(); - - if (return_type == RenderManager::kFrame || !cache.isEmpty()) { - // Convert to CPU frame - frame = GenerateFrame(texture, time); - - // Save to cache if requested - if (!cache.isEmpty()) { - rational timebase = ticket_->property("cachetimebase").value(); - QUuid uuid = ticket_->property("cacheuuid").value(); - bool cache_result = FrameHashCache::SaveCacheFrame(cache, uuid, time, timebase, frame); - ticket_->setProperty("cached", cache_result); + if (GetCacheVideoParams().interlacing() == VideoParams::kInterlacedBottomFirst) { + std::swap(top, bottom); } + + texture = render_ctx_->InterlaceTexture(top, bottom, GetCacheVideoParams()); } - if (return_type == RenderManager::kTexture) { - // Return GPU texture - if (!texture) { - texture = render_ctx_->CreateTexture(GetCacheVideoParams()); - render_ctx_->ClearDestination(texture.get()); + if (HeardCancel()) { + // Finish cancelled ticket with nothing since we can't guarantee the frame we generated + // is actually "complete + ticket_->Finish(); + } else { + RenderManager::ReturnType return_type = RenderManager::ReturnType(ticket_->property("return").toInt()); + + FramePtr frame; + QString cache = ticket_->property("cache").toString(); + + if (return_type == RenderManager::kFrame || !cache.isEmpty()) { + // Convert to CPU frame + frame = GenerateFrame(texture, time); + + // Save to cache if requested + if (!cache.isEmpty()) { + rational timebase = ticket_->property("cachetimebase").value(); + QUuid uuid = ticket_->property("cacheuuid").value(); + bool cache_result = FrameHashCache::SaveCacheFrame(cache, uuid, time, timebase, frame); + ticket_->setProperty("cached", cache_result); + } } - render_ctx_->Flush(); + if (return_type == RenderManager::kTexture) { + // Return GPU texture + if (!texture) { + texture = render_ctx_->CreateTexture(GetCacheVideoParams()); + render_ctx_->ClearDestination(texture.get()); + } - ticket_->Finish(QVariant::fromValue(texture)); - } else { - ticket_->Finish(QVariant::fromValue(frame)); + render_ctx_->Flush(); + + ticket_->Finish(QVariant::fromValue(texture)); + } else { + ticket_->Finish(QVariant::fromValue(frame)); + } } } break; @@ -265,6 +269,11 @@ DecoderPtr RenderProcessor::ResolveDecoderFromInput(const QString& decoder_id, c << "::" << stream.stream(); return nullptr; } + + if (!render_ctx_) { + // Assume dry run and increment access time + decoder.decoder->IncrementAccessTime(RenderManager::kDryRunInterval.toDouble() * 1000); + } } return decoder.decoder; @@ -404,10 +413,6 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const Track *track, const Tim void RenderProcessor::ProcessVideoFootage(TexturePtr destination, const FootageJob &stream, const rational &input_time) { - if (!render_ctx_) { - return; - } - if (ticket_->property("type").value() != RenderManager::kTypeVideo) { // Video cannot contribute to audio, so we do nothing here return; @@ -440,21 +445,23 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination, const FootageJ break; case VideoParams::kVideoTypeImageSequence: { - // Since image sequences involve multiple files, we don't engage the decoder cache - decoder = Decoder::CreateFromID(decoder_id); + if (render_ctx_) { + // Since image sequences involve multiple files, we don't engage the decoder cache + decoder = Decoder::CreateFromID(decoder_id); - QString frame_filename; + QString frame_filename; - int64_t frame_number = stream_data.get_time_in_timebase_units(input_time); - frame_filename = Decoder::TransformImageSequenceFileName(stream.filename(), frame_number); + int64_t frame_number = stream_data.get_time_in_timebase_units(input_time); + frame_filename = Decoder::TransformImageSequenceFileName(stream.filename(), frame_number); - // Decoder will close automatically since it's a stream_ptr - decoder->Open(Decoder::CodecStream(frame_filename, stream_data.stream_index(), GetCurrentBlock())); + // Decoder will close automatically since it's a stream_ptr + decoder->Open(Decoder::CodecStream(frame_filename, stream_data.stream_index(), GetCurrentBlock())); + } break; } } - if (decoder) { + if (decoder && render_ctx_) { Decoder::RetrieveVideoParams p; p.divider = stream.video_params().divider(); p.maximum_format = destination->format(); diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 18d086386..c5635cc86 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -60,7 +60,7 @@ QVector ViewerWidget::instances_; // changing values. 1/4 second seems to be a good middleground. const rational ViewerWidget::kAudioPlaybackInterval = rational(1, 4); -const rational kVideoPlaybackInterval = rational(2); +const rational kVideoPlaybackInterval = rational(1, 2); ViewerWidget::ViewerWidget(QWidget *parent) : super(false, true, parent), @@ -560,6 +560,35 @@ void ViewerWidget::ShowSubtitleProperties() } } +void ViewerWidget::DryRunFinished() +{ + RenderTicketWatcher *w = static_cast(sender()); + + if (dry_run_watchers_.contains(w)) { + RequestNextDryRun(); + } + + delete w; +} + +void ViewerWidget::RequestNextDryRun() +{ + if (IsPlaying()) { + rational next_time = Timecode::timestamp_to_time(dry_run_next_frame_, timebase()); + if (FrameExistsAtTime(next_time)) { + if (next_time > GetTime() + RenderManager::kDryRunInterval) { + QTimer::singleShot(timebase().toDouble() / playback_speed_, this, &ViewerWidget::RequestNextDryRun); + } else { + RenderTicketWatcher *watcher = new RenderTicketWatcher(this); + connect(watcher, &RenderTicketWatcher::Finished, this, &ViewerWidget::DryRunFinished); + watcher->SetTicket(auto_cacher_.GetSingleFrame(next_time, true)); + dry_run_next_frame_ += playback_speed_; + dry_run_watchers_.append(watcher); + } + } + } +} + void ViewerWidget::CloseAudioProcessor() { audio_processor_.Close(); @@ -828,6 +857,9 @@ void ViewerWidget::PlayInternal(int speed, bool in_to_out_only) for (int i=0; i playback_devices_; bool prequeuing_video_; @@ -294,6 +295,8 @@ private: WaveformMode waveform_mode_; + QVector dry_run_watchers_; + private slots: void PlaybackTimerUpdate(); @@ -354,6 +357,10 @@ private slots: void ShowSubtitleProperties(); + void DryRunFinished(); + + void RequestNextDryRun(); + }; } From 3f6ee1dc1b14b765ec85a777b93ee44b244711a5 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Wed, 27 Jul 2022 10:03:01 -0700 Subject: [PATCH 22/22] timeline: make timecode secondary tab item Fixes usability issue where maximizing the timeline panel would focus the timecode label and make it impossible to unmaximize it using a keyboard shortcut --- app/widget/timelinewidget/timelinewidget.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index a293545fb..033c50fa8 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -86,6 +86,9 @@ TimelineWidget::TimelineWidget(QWidget *parent) : ruler_and_time_layout->addWidget(ruler()); + ruler()->setFocusPolicy(Qt::TabFocus); + QWidget::setTabOrder(ruler(), timecode_label_); + // Create list of TimelineViews - these MUST correspond to the ViewType enum view_splitter_ = new QSplitter(Qt::Vertical);