From 6e183fe4be11ff6f1ac577625490b35916b06161 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Thu, 29 Sep 2022 17:50:09 -0700 Subject: [PATCH] multicam: refactor to heavily optimize multicam monitor retrieval --- app/node/input/multicam/multicamnode.cpp | 97 +------------------- app/node/input/multicam/multicamnode.h | 7 -- app/node/traverser.h | 2 +- app/panel/multicam/multicampanel.cpp | 5 +- app/panel/multicam/multicampanel.h | 11 +-- app/panel/viewer/viewerbase.cpp | 24 ++--- app/panel/viewer/viewerbase.h | 21 ++++- app/render/previewautocacher.cpp | 16 ++-- app/render/previewautocacher.h | 6 +- app/render/rendermanager.cpp | 1 + app/render/rendermanager.h | 2 + app/render/renderprocessor.cpp | 24 +++++ app/render/renderprocessor.h | 2 + app/widget/multicam/multicamdisplay.cpp | 111 ++++++++++++++++++++++- app/widget/multicam/multicamdisplay.h | 10 ++ app/widget/multicam/multicamwidget.cpp | 51 ++++++++--- app/widget/multicam/multicamwidget.h | 11 ++- app/widget/viewer/viewer.cpp | 27 ++++-- app/widget/viewer/viewer.h | 12 ++- app/widget/viewer/viewerdisplay.cpp | 2 + app/widget/viewer/viewerdisplay.h | 5 + app/widget/viewer/viewerqueue.h | 6 +- app/widget/viewer/viewersizer.cpp | 2 + app/widget/viewer/viewersizer.h | 2 +- app/window/mainwindow/mainwindow.cpp | 5 + 25 files changed, 300 insertions(+), 162 deletions(-) diff --git a/app/node/input/multicam/multicamnode.cpp b/app/node/input/multicam/multicamnode.cpp index b468af862..5984e4b98 100644 --- a/app/node/input/multicam/multicamnode.cpp +++ b/app/node/input/multicam/multicamnode.cpp @@ -17,8 +17,6 @@ MultiCamNode::MultiCamNode() AddInput(kSourcesInput, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable | kInputFlagArray)); SetInputProperty(kSourcesInput, QStringLiteral("arraystart"), 1); - - monitor_ = false; } QString MultiCamNode::Name() const @@ -43,7 +41,7 @@ QString MultiCamNode::Description() const Node::ActiveElements MultiCamNode::GetActiveElementsAtTime(const QString &input, const TimeRange &r) const { - if (input == kSourcesInput && !monitor_) { + if (input == kSourcesInput) { Node::ActiveElements a; a.add(GetCurrentSource()); return a; @@ -52,98 +50,11 @@ Node::ActiveElements MultiCamNode::GetActiveElementsAtTime(const QString &input, } } -QString dblToGlsl(double d) -{ - return QString::number(d, 'f'); -} - -ShaderCode MultiCamNode::GetShaderCode(const ShaderRequest &id) const -{ - QStringList pieces = id.id.split(','); - int rows = pieces.at(0).toInt(); - int cols = pieces.at(1).toInt(); - int multiplier = std::max(cols, rows); - - QStringList shader; - - shader.append(QStringLiteral("in vec2 ove_texcoord;")); - shader.append(QStringLiteral("out vec4 frag_color;")); - - for (int x=0;x 0) { - shader.append(QStringLiteral(" else")); - } - if (x == cols-1) { - shader.append(QStringLiteral(" {")); - } else { - shader.append(QStringLiteral(" if (ove_texcoord.x < %1) {").arg(dblToGlsl(double(x+1)/double(multiplier)))); - } - - for (int y=0;y 0) { - shader.append(QStringLiteral(" else")); - } - if (y == rows-1) { - shader.append(QStringLiteral(" {")); - } else { - shader.append(QStringLiteral(" if (ove_texcoord.y < %1) {").arg(dblToGlsl(double(y+1)/double(multiplier)))); - } - QString input = QStringLiteral("tex_%1_%2").arg(QString::number(y), QString::number(x)); - shader.append(QStringLiteral(" vec2 coord = vec2((ove_texcoord.x+%1)*%2, (ove_texcoord.y+%3)*%4);").arg( - dblToGlsl( - double(x)/double(multiplier)), - dblToGlsl(multiplier), - dblToGlsl( - double(y)/double(multiplier)), - dblToGlsl(multiplier) - )); - shader.append(QStringLiteral(" if (%1_enabled && coord.x >= 0.0 && coord.x < 1.0 && coord.y >= 0.0 && coord.y < 1.0) {").arg(input)); - shader.append(QStringLiteral(" frag_color = texture(%1, coord);").arg(input)); - shader.append(QStringLiteral(" } else {")); - shader.append(QStringLiteral(" discard;")); - shader.append(QStringLiteral(" }")); - shader.append(QStringLiteral(" }")); - } - - shader.append(QStringLiteral(" }")); - } - - shader.append(QStringLiteral("}")); - - return ShaderCode(shader.join('\n')); -} - void MultiCamNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const { - if (!monitor_) { - NodeValueArray arr = value[kSourcesInput].toArray(); - if (!arr.empty()) { - table->Push(arr.begin()->second); - } - } else { - NodeValueArray arr = value[kSourcesInput].toArray(); - - int rows, cols; - GetRowsAndColumns(arr.size(), &rows, &cols); - - ShaderJob job; - - job.SetShaderID(QStringLiteral("%1,%2").arg(QString::number(rows), QString::number(cols))); - - for (int i=0; iPush(NodeValue::kTexture, Texture::Job(globals.vparams(), job), this); + NodeValueArray arr = value[kSourcesInput].toArray(); + if (!arr.empty()) { + table->Push(arr.begin()->second); } } diff --git a/app/node/input/multicam/multicamnode.h b/app/node/input/multicam/multicamnode.h index d3e57734b..56a9ed858 100644 --- a/app/node/input/multicam/multicamnode.h +++ b/app/node/input/multicam/multicamnode.h @@ -20,8 +20,6 @@ public: virtual ActiveElements GetActiveElementsAtTime(const QString &input, const TimeRange &r) const override; - virtual ShaderCode GetShaderCode(const ShaderRequest &id) const override; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; virtual void Retranslate() override; @@ -29,8 +27,6 @@ public: static const QString kCurrentInput; static const QString kSourcesInput; - void SetMonitorMode(bool e) { monitor_ = e; } - int GetCurrentSource() const { return GetStandardValue(kCurrentInput).toInt(); @@ -54,9 +50,6 @@ public: return col + row * total_cols; } -private: - bool monitor_; - }; } diff --git a/app/node/traverser.h b/app/node/traverser.h index e34a708b5..4f2041d57 100644 --- a/app/node/traverser.h +++ b/app/node/traverser.h @@ -42,7 +42,7 @@ public: NodeValueTable GenerateTable(const Node *n, const TimeRange &range, const Node *next_node = nullptr); - NodeValueDatabase GenerateDatabase(const Node *node, const TimeRange &range); + virtual NodeValueDatabase GenerateDatabase(const Node *node, const TimeRange &range); NodeValueRow GenerateRow(NodeValueDatabase *database, const Node *node, const TimeRange &range); NodeValueRow GenerateRow(const Node *node, const TimeRange &range); diff --git a/app/panel/multicam/multicampanel.cpp b/app/panel/multicam/multicampanel.cpp index 322b51458..7218e43e3 100644 --- a/app/panel/multicam/multicampanel.cpp +++ b/app/panel/multicam/multicampanel.cpp @@ -2,13 +2,12 @@ namespace olive { -#define super ViewerPanelBase +#define super TimeBasedPanel MulticamPanel::MulticamPanel(QWidget *parent) : super(QStringLiteral("MultiCamPanel"), parent) { - widget_ = new MulticamWidget(); - SetViewerWidget(widget_); + SetTimeBasedWidget(new MulticamWidget()); Retranslate(); } diff --git a/app/panel/multicam/multicampanel.h b/app/panel/multicam/multicampanel.h index ac271aedb..86137f2bd 100644 --- a/app/panel/multicam/multicampanel.h +++ b/app/panel/multicam/multicampanel.h @@ -6,28 +6,27 @@ namespace olive { -class MulticamPanel : public ViewerPanelBase +class MulticamPanel : public TimeBasedPanel { Q_OBJECT public: MulticamPanel(QWidget* parent = nullptr); + MulticamWidget *GetMulticamWidget() const { return static_cast(GetTimeBasedWidget()); } + void SetMulticamNode(MultiCamNode *n) { - widget_->SetMulticamNode(n); + GetMulticamWidget()->SetMulticamNode(n); } void SetClip(ClipBlock* clip) { - widget_->SetClip(clip); + GetMulticamWidget()->SetClip(clip); } protected: virtual void Retranslate() override; -private: - MulticamWidget *widget_; - }; } diff --git a/app/panel/viewer/viewerbase.cpp b/app/panel/viewer/viewerbase.cpp index 6c90506b6..5d1bf7b95 100644 --- a/app/panel/viewer/viewerbase.cpp +++ b/app/panel/viewer/viewerbase.cpp @@ -24,35 +24,37 @@ namespace olive { +#define super TimeBasedPanel + ViewerPanelBase::ViewerPanelBase(const QString& object_name, QWidget *parent) : - TimeBasedPanel(object_name, parent) + super(object_name, parent) { connect(PanelManager::instance(), &PanelManager::FocusedPanelChanged, this, &ViewerPanelBase::FocusedPanelChanged); } void ViewerPanelBase::PlayPause() { - static_cast(GetTimeBasedWidget())->TogglePlayPause(); + GetViewerWidget()->TogglePlayPause(); } void ViewerPanelBase::PlayInToOut() { - static_cast(GetTimeBasedWidget())->Play(true); + GetViewerWidget()->Play(true); } void ViewerPanelBase::ShuttleLeft() { - static_cast(GetTimeBasedWidget())->ShuttleLeft(); + GetViewerWidget()->ShuttleLeft(); } void ViewerPanelBase::ShuttleStop() { - static_cast(GetTimeBasedWidget())->ShuttleStop(); + GetViewerWidget()->ShuttleStop(); } void ViewerPanelBase::ShuttleRight() { - static_cast(GetTimeBasedWidget())->ShuttleRight(); + GetViewerWidget()->ShuttleRight(); } void ViewerPanelBase::ConnectTimeBasedPanel(TimeBasedPanel *panel) @@ -75,22 +77,22 @@ void ViewerPanelBase::DisconnectTimeBasedPanel(TimeBasedPanel *panel) void ViewerPanelBase::SetFullScreen(QScreen *screen) { - static_cast(GetTimeBasedWidget())->SetFullScreen(screen); + GetViewerWidget()->SetFullScreen(screen); } void ViewerPanelBase::SetGizmos(Node *node) { - static_cast(GetTimeBasedWidget())->SetGizmos(node); + GetViewerWidget()->SetGizmos(node); } void ViewerPanelBase::CacheEntireSequence() { - static_cast(GetTimeBasedWidget())->CacheEntireSequence(); + GetViewerWidget()->CacheEntireSequence(); } void ViewerPanelBase::CacheSequenceInOut() { - static_cast(GetTimeBasedWidget())->CacheSequenceInOut(); + GetViewerWidget()->CacheSequenceInOut(); } void ViewerPanelBase::SetViewerWidget(ViewerWidget *vw) @@ -104,7 +106,7 @@ void ViewerPanelBase::SetViewerWidget(ViewerWidget *vw) void ViewerPanelBase::FocusedPanelChanged(PanelWidget *panel) { - auto vw = static_cast(GetTimeBasedWidget()); + auto vw = GetViewerWidget(); if (vw->IsPlaying() && panel != this) { vw->Pause(); } diff --git a/app/panel/viewer/viewerbase.h b/app/panel/viewer/viewerbase.h index 1ca7c06ff..feaf4045e 100644 --- a/app/panel/viewer/viewerbase.h +++ b/app/panel/viewer/viewerbase.h @@ -33,6 +33,11 @@ class ViewerPanelBase : public TimeBasedPanel public: ViewerPanelBase(const QString& object_name, QWidget* parent = nullptr); + ViewerWidget *GetViewerWidget() const + { + return static_cast(GetTimeBasedWidget()); + } + virtual void PlayPause() override; virtual void PlayInToOut() override; @@ -54,12 +59,22 @@ public: ColorManager *GetColorManager() { - return static_cast(GetTimeBasedWidget())->color_manager(); + return GetViewerWidget()->color_manager(); } void UpdateTextureFromNode() { - static_cast(GetTimeBasedWidget())->UpdateTextureFromNode(); + GetViewerWidget()->UpdateTextureFromNode(); + } + + void AddPlaybackDevice(ViewerDisplayWidget *vw) + { + GetViewerWidget()->AddPlaybackDevice(vw); + } + + void SetMulticamNode(MultiCamNode *n) + { + GetViewerWidget()->SetMulticamNode(n); } public slots: @@ -71,7 +86,7 @@ public slots: void RequestStartEditingText() { - static_cast(GetTimeBasedWidget())->RequestStartEditingText(); + GetViewerWidget()->RequestStartEditingText(); } signals: diff --git a/app/render/previewautocacher.cpp b/app/render/previewautocacher.cpp index 4bb23ee7f..226ebe69a 100644 --- a/app/render/previewautocacher.cpp +++ b/app/render/previewautocacher.cpp @@ -43,7 +43,7 @@ PreviewAutoCacher::PreviewAutoCacher(QObject *parent) : pause_renders_(false), single_frame_render_(nullptr), display_color_processor_(nullptr), - multicam_mode_(false), + multicam_(nullptr), ignore_cache_requests_(false) { // Set defaults @@ -216,6 +216,7 @@ void PreviewAutoCacher::VideoRendered() QVector tickets = video_immediate_passthroughs_.take(watcher); foreach (RenderTicketPtr t, tickets) { if (watcher->HasResult()) { + t->setProperty("multicam_output", watcher->GetTicket()->property("multicam_output")); t->Finish(watcher->Get()); } else { t->Finish(); @@ -288,13 +289,6 @@ void PreviewAutoCacher::AddNode(Node *node) // Copy node Node* copy = node->copy(); - // Fairly hacky way of getting multicam nodes to produce a monitor rather than a single source - if (multicam_mode_) { - if (MultiCamNode *m = dynamic_cast(copy)) { - m->SetMonitorMode(true); - } - } - // Add to project copy->setParent(&copied_project_); @@ -742,6 +736,9 @@ RenderTicketWatcher* PreviewAutoCacher::RenderFrame(Node *node, const rational& // Allow using cached images for this render job rvp.use_cache = true; + // Multicam + rvp.multicam = static_cast(copy_map_.value(multicam_)); + watcher->SetTicket(RenderManager::instance()->RenderFrame(rvp)); return watcher; @@ -848,6 +845,9 @@ void PreviewAutoCacher::SetViewerNode(ViewerOutput *viewer_node) video_cache_data_.clear(); audio_cache_data_.clear(); + // Clear multicam reference + multicam_ = nullptr; + // Disconnect signals for future node additions/deletions NodeGraph* graph = viewer_node_->parent(); diff --git a/app/render/previewautocacher.h b/app/render/previewautocacher.h index 9f68bb9e5..af8fff0bb 100644 --- a/app/render/previewautocacher.h +++ b/app/render/previewautocacher.h @@ -90,7 +90,8 @@ public: void SetRendersPaused(bool e); - void SetMulticamMode(bool e) { multicam_mode_ = e; } + void SetMulticamNode(MultiCamNode *n) { multicam_ = n; } + void SetIgnoreCacheRequests(bool e) { ignore_cache_requests_ = e; } public slots: @@ -221,7 +222,8 @@ private: ColorProcessorPtr display_color_processor_; - bool multicam_mode_; + MultiCamNode *multicam_; + bool ignore_cache_requests_; private slots: diff --git a/app/render/rendermanager.cpp b/app/render/rendermanager.cpp index 19a0d8efc..13966a892 100644 --- a/app/render/rendermanager.cpp +++ b/app/render/rendermanager.cpp @@ -111,6 +111,7 @@ RenderTicketPtr RenderManager::RenderFrame(const RenderVideoParams ¶ms) ticket->setProperty("cache", params.cache_dir); ticket->setProperty("cachetimebase", QVariant::fromValue(params.cache_timebase)); ticket->setProperty("cacheid", QVariant::fromValue(params.cache_id)); + ticket->setProperty("multicam", Node::PtrToValue(params.multicam)); if (params.return_type == ReturnType::kNull) { dry_run_thread_->AddTicket(ticket); diff --git a/app/render/rendermanager.h b/app/render/rendermanager.h index b33818b58..da52892c4 100644 --- a/app/render/rendermanager.h +++ b/app/render/rendermanager.h @@ -117,6 +117,7 @@ public: force_size = QSize(0, 0); force_channel_count = 0; mode = m; + multicam = nullptr; } void AddCache(FrameHashCache *cache) @@ -134,6 +135,7 @@ public: bool use_cache; ReturnType return_type; RenderMode::Mode mode; + MultiCamNode *multicam; QString cache_dir; rational cache_timebase; diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index bd5702f1f..2b42f9e67 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -294,6 +294,30 @@ DecoderPtr RenderProcessor::ResolveDecoderFromInput(const QString& decoder_id, c return dec; } +NodeValueDatabase RenderProcessor::GenerateDatabase(const Node *node, const TimeRange &range) +{ + NodeValueDatabase db = super::GenerateDatabase(node, range); + + if (const MultiCamNode *multicam = dynamic_cast(node)) { + if (Node::ValueToPtr(ticket_->property("multicam")) == multicam) { + int sz = multicam->InputArraySize(multicam->kSourcesInput); + NodeValueTableArray arr; + QVector multicam_tex(sz); + for (int i=0; ikSourcesInput, i, range); + + NodeValue val = GenerateRowValueElement(multicam, multicam->kSourcesInput, i, &arr.at(i), range); + ResolveJobs(val); + + multicam_tex[i] = val.toTexture(); + } + ticket_->setProperty("multicam_output", QVariant::fromValue(multicam_tex)); + } + } + + return db; +} + void RenderProcessor::Process(RenderTicketPtr ticket, Renderer *render_ctx, DecoderCache *decoder_cache, ShaderCache *shader_cache) { RenderProcessor p(ticket, render_ctx, decoder_cache, shader_cache); diff --git a/app/render/renderprocessor.h b/app/render/renderprocessor.h index 3eb477b14..b3dd29f59 100644 --- a/app/render/renderprocessor.h +++ b/app/render/renderprocessor.h @@ -32,6 +32,8 @@ namespace olive { class RenderProcessor : public NodeTraverser { public: + virtual NodeValueDatabase GenerateDatabase(const Node *node, const TimeRange &range) override; + static void Process(RenderTicketPtr ticket, Renderer* render_ctx, DecoderCache* decoder_cache, ShaderCache* shader_cache); struct RenderedWaveform { diff --git a/app/widget/multicam/multicamdisplay.cpp b/app/widget/multicam/multicamdisplay.cpp index a5b79a67b..9fce75e68 100644 --- a/app/widget/multicam/multicamdisplay.cpp +++ b/app/widget/multicam/multicamdisplay.cpp @@ -26,7 +26,9 @@ namespace olive { MulticamDisplay::MulticamDisplay(QWidget *parent) : super(parent), - node_(nullptr) + node_(nullptr), + rows_(0), + cols_(0) { } @@ -55,6 +57,113 @@ void MulticamDisplay::OnPaint() } } +void MulticamDisplay::OnDestroy() +{ + shader_ = QVariant(); +} + +TexturePtr MulticamDisplay::LoadCustomTextureFromFrame(const QVariant &v) +{ + if (v.canConvert >()) { + QVector tex = v.value >(); + + TexturePtr main = renderer()->CreateTexture(this->GetViewportParams()); + + int rows, cols; + MultiCamNode::GetRowsAndColumns(tex.size(), &rows, &cols); + + if (shader_.isNull() || rows_ != rows || cols_ != cols) { + if (!shader_.isNull()) { + renderer()->DestroyNativeShader(shader_); + } + + shader_ = renderer()->CreateNativeShader(ShaderCode(GenerateShaderCode(rows, cols))); + + rows_ = rows; + cols_ = cols; + } + + ShaderJob job; + + for (int i=0; iBlitToTexture(shader_, job, main.get()); + + return main; + } else { + return super::LoadCustomTextureFromFrame(v); + } +} + +QString dblToGlsl(double d) +{ + return QString::number(d, 'f'); +} + +QString MulticamDisplay::GenerateShaderCode(int rows, int cols) +{ + int multiplier = std::max(cols, rows); + + QStringList shader; + + shader.append(QStringLiteral("in vec2 ove_texcoord;")); + shader.append(QStringLiteral("out vec4 frag_color;")); + + for (int x=0;x 0) { + shader.append(QStringLiteral(" else")); + } + if (x == cols-1) { + shader.append(QStringLiteral(" {")); + } else { + shader.append(QStringLiteral(" if (ove_texcoord.x < %1) {").arg(dblToGlsl(double(x+1)/double(multiplier)))); + } + + for (int y=0;y 0) { + shader.append(QStringLiteral(" else")); + } + if (y == rows-1) { + shader.append(QStringLiteral(" {")); + } else { + shader.append(QStringLiteral(" if (ove_texcoord.y < %1) {").arg(dblToGlsl(double(y+1)/double(multiplier)))); + } + QString input = QStringLiteral("tex_%1_%2").arg(QString::number(y), QString::number(x)); + shader.append(QStringLiteral(" vec2 coord = vec2((ove_texcoord.x+%1)*%2, (ove_texcoord.y+%3)*%4);").arg( + dblToGlsl( - double(x)/double(multiplier)), + dblToGlsl(multiplier), + dblToGlsl( - double(y)/double(multiplier)), + dblToGlsl(multiplier) + )); + shader.append(QStringLiteral(" if (%1_enabled && coord.x >= 0.0 && coord.x < 1.0 && coord.y >= 0.0 && coord.y < 1.0) {").arg(input)); + shader.append(QStringLiteral(" frag_color = texture(%1, coord);").arg(input)); + shader.append(QStringLiteral(" } else {")); + shader.append(QStringLiteral(" discard;")); + shader.append(QStringLiteral(" }")); + shader.append(QStringLiteral(" }")); + } + + shader.append(QStringLiteral(" }")); + } + + shader.append(QStringLiteral("}")); + + return shader.join('\n'); +} + void MulticamDisplay::SetMulticamNode(MultiCamNode *n) { node_ = n; diff --git a/app/widget/multicam/multicamdisplay.h b/app/widget/multicam/multicamdisplay.h index 2898142d1..4d926f5f1 100644 --- a/app/widget/multicam/multicamdisplay.h +++ b/app/widget/multicam/multicamdisplay.h @@ -37,9 +37,19 @@ public: protected: virtual void OnPaint() override; + virtual void OnDestroy() override; + + virtual TexturePtr LoadCustomTextureFromFrame(const QVariant &v) override; + private: + static QString GenerateShaderCode(int rows, int cols); + MultiCamNode *node_; + QVariant shader_; + int rows_; + int cols_; + }; } diff --git a/app/widget/multicam/multicamwidget.cpp b/app/widget/multicam/multicamwidget.cpp index bca0c8db5..6e8e6f22a 100644 --- a/app/widget/multicam/multicamwidget.cpp +++ b/app/widget/multicam/multicamwidget.cpp @@ -20,27 +20,41 @@ #include "multicamwidget.h" #include "widget/nodeparamview/nodeparamviewundo.h" +#include "widget/timeruler/timeruler.h" #include "widget/timelinewidget/undo/timelineundosplit.h" namespace olive { -#define super ViewerWidget +#define super TimeBasedWidget MulticamWidget::MulticamWidget(QWidget *parent) : - super{new MulticamDisplay(), parent}, + super{false, false, parent}, node_(nullptr), clip_(nullptr) { - auto_cacher()->SetMulticamMode(true); - auto_cacher()->SetIgnoreCacheRequests(true); + auto layout = new QVBoxLayout(this); - connect(display_widget(), &ViewerDisplayWidget::DragStarted, this, &MulticamWidget::DisplayClicked); + sizer_ = new ViewerSizer(this); + layout->addWidget(sizer_); + + display_ = new MulticamDisplay(this); + display_->SetShowWidgetBackground(true); + connect(display_, &ViewerDisplayWidget::DragStarted, this, &MulticamWidget::DisplayClicked); + + connect(sizer_, &ViewerSizer::RequestScale, display_, &ViewerDisplayWidget::SetMatrixZoom); + connect(sizer_, &ViewerSizer::RequestTranslate, display_, &ViewerDisplayWidget::SetMatrixTranslate); + connect(display_, &ViewerDisplayWidget::HandDragMoved, sizer_, &ViewerSizer::HandDragMove); + sizer_->SetWidget(display_); + + layout->addWidget(this->ruler()); + layout->addWidget(this->scrollbar()); } void MulticamWidget::SetMulticamNode(MultiCamNode *n) { node_ = n; - static_cast(display_widget())->SetMulticamNode(n); + display_->SetMulticamNode(n); + display_->update(); } void MulticamWidget::SetClip(ClipBlock *clip) @@ -48,13 +62,20 @@ void MulticamWidget::SetClip(ClipBlock *clip) clip_ = clip; } -RenderTicketPtr MulticamWidget::GetSingleFrame(const rational &t, bool dry) +void MulticamWidget::ConnectNodeEvent(ViewerOutput *n) { - if (node_) { - return auto_cacher()->GetSingleFrame(node_, t, dry); - } else { - return super::GetSingleFrame(t, dry); - } + connect(n, &ViewerOutput::SizeChanged, sizer_, &ViewerSizer::SetChildSize); + connect(n, &ViewerOutput::PixelAspectChanged, sizer_, &ViewerSizer::SetPixelAspectRatio); + + VideoParams vp = n->GetVideoParams(); + sizer_->SetChildSize(vp.width(), vp.height()); + sizer_->SetPixelAspectRatio(vp.pixel_aspect_ratio()); +} + +void MulticamWidget::DisconnectNodeEvent(ViewerOutput *n) +{ + disconnect(n, &ViewerOutput::SizeChanged, sizer_, &ViewerSizer::SetChildSize); + disconnect(n, &ViewerOutput::PixelAspectChanged, sizer_, &ViewerSizer::SetPixelAspectRatio); } void MulticamWidget::DisplayClicked(const QPoint &p) @@ -63,9 +84,9 @@ void MulticamWidget::DisplayClicked(const QPoint &p) return; } - QPointF click = display_widget()->ScreenToScenePoint(p); - int width = display_widget()->GetVideoParams().width(); - int height = display_widget()->GetVideoParams().height(); + QPointF click = display_->ScreenToScenePoint(p); + int width = display_->GetVideoParams().width(); + int height = display_->GetVideoParams().height(); if (click.x() < 0 || click.y() < 0 || click.x() >= width || click.y() >= height) { return; diff --git a/app/widget/multicam/multicamwidget.h b/app/widget/multicam/multicamwidget.h index b32fce9e8..36380861b 100644 --- a/app/widget/multicam/multicamwidget.h +++ b/app/widget/multicam/multicamwidget.h @@ -27,20 +27,27 @@ namespace olive { -class MulticamWidget : public ViewerWidget +class MulticamWidget : public TimeBasedWidget { Q_OBJECT public: explicit MulticamWidget(QWidget *parent = nullptr); + MulticamDisplay *GetDisplayWidget() const { return display_; } + void SetMulticamNode(MultiCamNode *n); void SetClip(ClipBlock *clip); protected: - virtual RenderTicketPtr GetSingleFrame(const rational &t, bool dry = false) override; + virtual void ConnectNodeEvent(ViewerOutput *n) override; + virtual void DisconnectNodeEvent(ViewerOutput *n) override; private: + ViewerSizer *sizer_; + + MulticamDisplay *display_; + MultiCamNode *node_; ClipBlock *clip_; diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index bdf28a4de..f8515c145 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -44,6 +44,7 @@ #include "viewerpreventsleep.h" #include "widget/audiomonitor/audiomonitor.h" #include "widget/menu/menu.h" +#include "widget/multicam/multicamdisplay.h" #include "widget/nodeparamview/nodeparamviewundo.h" #include "widget/timelinewidget/tool/add.h" #include "widget/timeruler/timeruler.h" @@ -82,7 +83,6 @@ ViewerWidget::ViewerWidget(ViewerDisplayWidget *display, QWidget *parent) : // Create main OpenGL-based view and sizer sizer_ = new ViewerSizer(); - sizer_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); layout->addWidget(sizer_); display_widget_ = display; @@ -260,7 +260,7 @@ void ViewerWidget::DisconnectNodeEvent(ViewerOutput *n) CloseAudioProcessor(); audio_scrub_watchers_.clear(); - SetDisplayImage(QVariant()); + SetDisplayImage(nullptr); ruler()->SetPlaybackCache(nullptr); @@ -998,10 +998,18 @@ bool ViewerWidget::ViewerMightBeAStill() return GetConnectedNode() && GetConnectedNode()->GetConnectedTextureOutput() && GetConnectedNode()->GetVideoLength().isNull(); } -void ViewerWidget::SetDisplayImage(QVariant frame) +void ViewerWidget::SetDisplayImage(RenderTicketPtr ticket) { foreach (ViewerDisplayWidget *dw, playback_devices_) { - dw->SetImage(frame); + QVariant push; + if (ticket) { + if (dynamic_cast(dw)) { + push = ticket->property("multicam_output"); + } else { + push = ticket->Get(); + } + } + dw->SetImage(push); } } @@ -1160,7 +1168,7 @@ void ViewerWidget::RendererGeneratedFrame() } } - SetDisplayImage(ticket->Get()); + SetDisplayImage(ticket->GetTicket()); } } @@ -1182,7 +1190,14 @@ void ViewerWidget::RendererGeneratedFrameForQueue() rational ts = watcher->property("time").value(); foreach (ViewerDisplayWidget *dw, playback_devices_) { - dw->queue()->AppendTimewise({ts, frame}, playback_speed_); + QVariant push; + if (dynamic_cast(dw)) { + push = watcher->GetTicket()->property("multicam_output"); + } else { + push = frame; + } + + dw->queue()->AppendTimewise({ts, push}, playback_speed_); } if (prequeuing_video_) { diff --git a/app/widget/viewer/viewer.h b/app/widget/viewer/viewer.h index 400853652..a47c0d6d2 100644 --- a/app/widget/viewer/viewer.h +++ b/app/widget/viewer/viewer.h @@ -104,6 +104,16 @@ public: PreviewAutoCacher *GetCacher() const { return auto_cacher_; } + void AddPlaybackDevice(ViewerDisplayWidget *vw) + { + playback_devices_.push_back(vw); + } + + void SetMulticamNode(MultiCamNode *n) + { + auto_cacher()->SetMulticamNode(n); + } + public slots: void Play(bool in_to_out_only); @@ -219,7 +229,7 @@ private: bool ViewerMightBeAStill(); - void SetDisplayImage(QVariant frame); + void SetDisplayImage(RenderTicketPtr ticket); RenderTicketWatcher *RequestNextFrameForQueue(bool increment = true); diff --git a/app/widget/viewer/viewerdisplay.cpp b/app/widget/viewer/viewerdisplay.cpp index 37a1ab08d..e7ec99fb5 100644 --- a/app/widget/viewer/viewerdisplay.cpp +++ b/app/widget/viewer/viewerdisplay.cpp @@ -398,6 +398,8 @@ void ViewerDisplayWidget::OnPaint() } else if (TexturePtr texture = load_frame_.value()) { // This is a GPU texture, switch to it directly texture_ = texture; + } else { + texture_ = LoadCustomTextureFromFrame(load_frame_); } emit TextureChanged(texture_); diff --git a/app/widget/viewer/viewerdisplay.h b/app/widget/viewer/viewerdisplay.h index 077ea6023..f2e89102f 100644 --- a/app/widget/viewer/viewerdisplay.h +++ b/app/widget/viewer/viewerdisplay.h @@ -245,6 +245,11 @@ protected: return TimeRange(node_time, node_time + gizmo_params_.frame_rate_as_time_base()); } + virtual TexturePtr LoadCustomTextureFromFrame(const QVariant &v) + { + return nullptr; + } + protected slots: /** * @brief Paint function to display the texture (received in SetTexture()) on screen. diff --git a/app/widget/viewer/viewerqueue.h b/app/widget/viewer/viewerqueue.h index f8117eac9..053d8f013 100644 --- a/app/widget/viewer/viewerqueue.h +++ b/app/widget/viewer/viewerqueue.h @@ -27,12 +27,14 @@ namespace olive { -struct ViewerPlaybackFrame { +struct ViewerPlaybackFrame +{ rational timestamp; QVariant frame; }; -class ViewerQueue : public std::list { +class ViewerQueue : public std::list +{ public: ViewerQueue() = default; diff --git a/app/widget/viewer/viewersizer.cpp b/app/widget/viewer/viewersizer.cpp index 7041da533..d8ae52b84 100644 --- a/app/widget/viewer/viewersizer.cpp +++ b/app/widget/viewer/viewersizer.cpp @@ -39,6 +39,8 @@ ViewerSizer::ViewerSizer(QWidget *parent) : vert_scrollbar_ = new QScrollBar(Qt::Vertical, this); vert_scrollbar_->setVisible(false); connect(vert_scrollbar_, &QScrollBar::valueChanged, this, &ViewerSizer::ScrollBarMoved); + + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); } void ViewerSizer::SetWidget(QWidget *widget) diff --git a/app/widget/viewer/viewersizer.h b/app/widget/viewer/viewersizer.h index 7acb76965..b4afe5015 100644 --- a/app/widget/viewer/viewersizer.h +++ b/app/widget/viewer/viewersizer.h @@ -51,6 +51,7 @@ public: */ void SetWidget(QWidget* widget); +public slots: /** * @brief Set resolution to use * @@ -70,7 +71,6 @@ public: */ void SetZoom(int percent); -public slots: void HandDragMove(int x, int y); signals: diff --git a/app/window/mainwindow/mainwindow.cpp b/app/window/mainwindow/mainwindow.cpp index 184b847f4..346650d0d 100644 --- a/app/window/mainwindow/mainwindow.cpp +++ b/app/window/mainwindow/mainwindow.cpp @@ -125,6 +125,8 @@ MainWindow::MainWindow(QWidget *parent) : sequence_viewer_panel_->ConnectTimeBasedPanel(curve_panel_); sequence_viewer_panel_->ConnectTimeBasedPanel(multicam_panel_); + sequence_viewer_panel_->AddPlaybackDevice(multicam_panel_->GetMulticamWidget()->GetDisplayWidget()); + scope_panel_->SetViewerPanel(sequence_viewer_panel_); UpdateTitle(); @@ -510,10 +512,12 @@ void MainWindow::TimelinePanelSelectionChanged(const QVector &blocks) } if (multicam) { + sequence_viewer_panel_->SetMulticamNode(multicam); multicam_panel_->SetMulticamNode(multicam); multicam_panel_->SetClip(clip); multicam_panel_->ConnectViewerNode(panel->GetConnectedViewer()); } else { + sequence_viewer_panel_->SetMulticamNode(nullptr); multicam_panel_->ConnectViewerNode(nullptr); multicam_panel_->SetMulticamNode(nullptr); multicam_panel_->SetClip(nullptr); @@ -678,6 +682,7 @@ void MainWindow::RemoveProjectPanel(ProjectPanel *panel) void MainWindow::TimelineFocused(ViewerOutput* viewer) { sequence_viewer_panel_->ConnectViewerNode(viewer); + multicam_panel_->ConnectViewerNode(viewer); param_panel_->ConnectViewerNode(viewer); curve_panel_->ConnectViewerNode(viewer); }