From 11017923815956cc17b1536c8b16b6150f3fb891 Mon Sep 17 00:00:00 2001 From: "Sam.Richards@taurich.org" Date: Tue, 27 Jan 2026 10:55:49 +0000 Subject: [PATCH] Fix for [ #193 ] viewer goes black if the window is dragged from one screen to another on osx. Signed-off-by: Sam.Richards@taurich.org --- include/xstudio/ui/qml/qml_viewport.hpp | 3 + src/ui/qml/viewport/src/qml_viewport.cpp | 85 ++++++++++++++++++++---- 2 files changed, 75 insertions(+), 13 deletions(-) diff --git a/include/xstudio/ui/qml/qml_viewport.hpp b/include/xstudio/ui/qml/qml_viewport.hpp index d571ea3ac..eba8844ed 100644 --- a/include/xstudio/ui/qml/qml_viewport.hpp +++ b/include/xstudio/ui/qml/qml_viewport.hpp @@ -85,6 +85,7 @@ namespace ui { void keyReleaseEvent(QKeyEvent *event) override; public slots: + void initRenderer(); void sync(); void cleanup(); @@ -140,6 +141,7 @@ namespace ui { void hasOverlaysChanged(); private: + void createRenderer(); void releaseResources() override; void sendPointerEvent(EventType t, QMouseEvent *event, int force_modifiers = 0); @@ -158,6 +160,7 @@ namespace ui { bool has_overlays_ = {true}; caf::actor keypress_monitor_; + caf::actor m_playhead; }; } // namespace qml diff --git a/src/ui/qml/viewport/src/qml_viewport.cpp b/src/ui/qml/viewport/src/qml_viewport.cpp index 74b6c7557..8a8fb7560 100644 --- a/src/ui/qml/viewport/src/qml_viewport.cpp +++ b/src/ui/qml/viewport/src/qml_viewport.cpp @@ -64,13 +64,31 @@ int qtModifierToOurs(const Qt::KeyboardModifiers qt_modifiers) { QMLViewport::QMLViewport(QQuickItem *parent) : QQuickItem(parent), cursor_(Qt::ArrowCursor) { connect(this, &QQuickItem::windowChanged, this, &QMLViewport::handleWindowChanged); - static int index = 0; - - renderer_actor = new QMLViewportRenderer(this); keypress_monitor_ = CafSystemObject::get_actor_system().registry().template get( xstudio::keyboard_events); + connect(this, SIGNAL(visibleChanged()), this, SLOT(onVisibleChanged())); + + setAcceptedMouseButtons(Qt::AllButtons); + setAcceptHoverEvents(true); + + createRenderer(); +} + +void QMLViewport::initRenderer() { + if (!renderer_actor) { + spdlog::info("QMLViewport::initRenderer recreating renderer"); + createRenderer(); + } +} + +void QMLViewport::createRenderer() { + if (renderer_actor) + return; + + renderer_actor = new QMLViewportRenderer(this); + connect( renderer_actor, SIGNAL(translationChanged()), @@ -104,20 +122,40 @@ QMLViewport::QMLViewport(QQuickItem *parent) : QQuickItem(parent), cursor_(Qt::A this, SIGNAL(snapshotRequestResult(QString))); - connect(this, SIGNAL(visibleChanged()), this, SLOT(onVisibleChanged())); - - setAcceptedMouseButtons(Qt::AllButtons); - setAcceptHoverEvents(true); - if (renderer_actor) renderer_actor->visibleChanged(isVisible()); + + if (window()) { + connect( + window(), + &QQuickWindow::frameSwapped, + renderer_actor, + &QMLViewportRenderer::frameSwapped, + Qt::DirectConnection); + + connect( + renderer_actor, &QMLViewportRenderer::doRedraw, window(), &QQuickWindow::update); + + renderer_actor->setWindow(window()); + + // Reset connected state so sync() will reconnect the render loop signal + connected_ = false; + } + + // Restore state + renderer_actor->setIsQuickViewer(is_quickview_); + renderer_actor->setHasOverlays(has_overlays_); + if (m_playhead) renderer_actor->set_playhead(m_playhead); + + emit nameChanged(); + emit playheadUuidChanged(); // Ensure bindings update } QMLViewport::~QMLViewport() { delete renderer_actor; } void QMLViewport::handleWindowChanged(QQuickWindow *win) { - spdlog::debug("QMLViewport::handleWindowChanged"); + spdlog::info("QMLViewport::handleWindowChanged"); if (win) { // Send screen info for the first time QScreen *screen = win->screen(); @@ -137,6 +175,13 @@ void QMLViewport::handleWindowChanged(QQuickWindow *win) { &QMLViewport::cleanup, Qt::DirectConnection); + connect( + win, + &QQuickWindow::sceneGraphInitialized, + this, + &QMLViewport::initRenderer, + Qt::DirectConnection); + connect( win, &QQuickWindow::screenChanged, @@ -166,7 +211,18 @@ void QMLViewport::handleWindowChanged(QQuickWindow *win) { void QMLViewport::handleScreenChanged(QScreen *screen) { - spdlog::debug("QMLViewport::handleScreenChanged"); + spdlog::info("QMLViewport::handleScreenChanged {}", StdFromQString(screen->name())); + + // If we have a renderer, destroy and recreate it. + // This is necessary because on some platforms (macOS), moving screens can change + // the underlying OpenGL context/pixel format without firing sceneGraphInvalidated, + // leaving us with invalid shared resources (VAOs). + if (renderer_actor) { + spdlog::info("Forcing renderer recreation on screen change"); + cleanup(); + initRenderer(); + } + if (renderer_actor) renderer_actor->setScreenInfos( screen->name(), @@ -258,7 +314,7 @@ void QMLViewport::setHasOverlays(const bool hasOverlays) { void QMLViewport::cleanup() { - spdlog::debug("QMLViewport::cleanup"); + spdlog::info("QMLViewport::cleanup"); if (renderer_actor) { // delete renderer_actor; delete renderer_actor; @@ -522,8 +578,11 @@ void QMLViewport::setPlayhead(const QString actorAddress) { if (actorAddress != "") { caf::actor playhead = actorFromQString(CafSystemObject::get_actor_system(), actorAddress); - if (playhead && renderer_actor) { - renderer_actor->set_playhead(playhead); + if (playhead) { + m_playhead = playhead; + if (renderer_actor) { + renderer_actor->set_playhead(playhead); + } } else { spdlog::warn( "{} bad playhead actor address: {}",