Skip to content

Commit ab61ec8

Browse files
committed
Add support capturing QtQuick windows backed by Qt Rhi
This is an interesting QtQuick backend to support because of some of the inherent design limitations we have to work around. But we are now able to capture and draw target decorations for these windows backed by Rhi now. The first problem we have to tackle is actually capturing the window contents, but fortunately Qt already has public API to do this. They don't provide a mapping from their bespoke Rhi texture format to QImage unfortunately, but I added a user-visible error message so it's easier for people to submit bugreports for any missing cases. I tried to optimize the readback as best I could, and the performance is about on-par to the OpenGL screen grabber. (I didn't actually benchmark, but it looks good enough to my own eyes.) Another tricky problem to tackle is drawing the remote target decorations, because we previously assumed we always had a QPainter to draw in. (For context, the client's viewport is drawn in QPainter and in the OpenGL there's QOpenGLPaintDevice available.) Not only that, but we can't exactly tell *when* to let Qt readback the swapchain image. The pre-existing screengrabbers grabbed the backbuffer before we drew target decorations so we could draw them client-side. For the drawing part I don't think there's anything we could do save for rewriting it in QtRHI, so we upload it into a texture and draw it onto the backbuffer. Yes it is "slow" but it's more than good enough than it not working at all. For the target decoration part that's not going to be easily possible, so I decided to not do it! Instead, we let ourselves capture the full window contents *including* the decorations we drew. We then skip drawing decorations client-side, and it's practically the same experience save for anything drawn outside the window. That limitation is going to be planned to be addressed in a future patch.
1 parent a39b78c commit ab61ec8

10 files changed

Lines changed: 476 additions & 9 deletions

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,8 @@ if(Qt6Gui_VERSION VERSION_GREATER_EQUAL "6.10.0")
506506
find_package(Qt6WidgetsPrivate REQUIRED NO_MODULE)
507507
find_package(Qt6QmlPrivate NO_MODULE)
508508
find_package(Qt6QuickPrivate NO_MODULE)
509+
find_package(Qt6QuickWidgetsPrivate NO_MODULE)
510+
find_package(Qt6GuiPrivate NO_MODULE)
509511
endif()
510512

511513
# these need to included after the last Qt-related find_package() call

common/remoteviewframe.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,19 @@ void RemoteViewFrame::setImage(const QImage &image, const QTransform &transform)
6868
m_image.setTransform(transform);
6969
}
7070

71+
void RemoteViewFrame::setGraphicsApi(const int value)
72+
{
73+
m_graphicsApi = value;
74+
}
75+
76+
int RemoteViewFrame::graphicsApi() const
77+
{
78+
return m_graphicsApi;
79+
}
80+
7181
QDataStream &operator<<(QDataStream &stream, const RemoteViewFrame &frame)
7282
{
73-
stream << frame.m_image << frame.data << frame.m_viewRect << frame.m_sceneRect;
83+
stream << frame.m_image << frame.data << frame.m_viewRect << frame.m_sceneRect << frame.m_graphicsApi;
7484
return stream;
7585
}
7686

@@ -80,6 +90,7 @@ QDataStream &operator>>(QDataStream &stream, RemoteViewFrame &frame)
8090
stream >> frame.data;
8191
stream >> frame.m_viewRect;
8292
stream >> frame.m_sceneRect;
93+
stream >> frame.m_graphicsApi;
8394
return stream;
8495
}
8596
}

common/remoteviewframe.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ class GAMMARAY_COMMON_EXPORT RemoteViewFrame
4646
void setImage(const QImage &image);
4747
void setImage(const QImage &image, const QTransform &transform);
4848

49+
void setGraphicsApi(int value);
50+
int graphicsApi() const;
51+
4952
/// tool specific frame data
5053
QVariant data;
5154

@@ -55,6 +58,7 @@ class GAMMARAY_COMMON_EXPORT RemoteViewFrame
5558
TransferImage m_image;
5659
QRectF m_viewRect;
5760
QRectF m_sceneRect;
61+
int m_graphicsApi; // Only relevant and used in QtQuick preview.
5862
};
5963
}
6064

plugins/quickinspector/CMakeLists.txt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,30 @@ if(NOT GAMMARAY_CLIENT_ONLY_BUILD)
7979
)
8080
endif()
8181

82+
# Only needed for the RHI screen grabber for >=6.6
83+
if(QT_VERSION_MAJOR EQUAL 6 AND Qt${QT_VERSION_MAJOR}Core_VERSION_MINOR GREATER_EQUAL 6)
84+
find_package(Qt${QT_VERSION_MAJOR}ShaderTools REQUIRED)
85+
qt_add_shaders(
86+
gammaray_quickinspector
87+
"gammaray_quickinspector_shaders"
88+
PRECOMPILE
89+
PREFIX
90+
"/gammaray_quickinspector_shaders/"
91+
FILES
92+
"shaders/rhi_inspector_decorations.vert"
93+
"shaders/rhi_inspector_decorations.frag"
94+
)
95+
endif()
96+
8297
target_link_libraries(
8398
gammaray_quickinspector
8499
gammaray_quickinspector_shared
85100
gammaray_core
86101
Qt::Quick
87102
Qt::QuickPrivate
103+
Qt::QuickWidgets
104+
Qt::QuickWidgetsPrivate
105+
Qt::GuiPrivate
88106
gammaray_kitemmodels
89107
)
90108

plugins/quickinspector/quickinspector.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ void RenderModeRequest::apply()
292292
if (connection)
293293
disconnect(connection);
294294

295-
if (window && window->rendererInterface()->graphicsApi() != QSGRendererInterface::OpenGL)
295+
if (window && window->rendererInterface()->graphicsApi() == QSGRendererInterface::Software)
296296
return;
297297

298298
if (window) {
@@ -574,6 +574,7 @@ void QuickInspector::sendRenderedScene(const GammaRay::GrabbedFrame &grabbedFram
574574
frame.data = QVariant::fromValue(grabbedFrame.itemsGeometry);
575575
else if (!grabbedFrame.itemsGeometry.isEmpty())
576576
frame.data = QVariant::fromValue(grabbedFrame.itemsGeometry.at(0));
577+
frame.setGraphicsApi(m_window->rendererInterface()->graphicsApi());
577578
m_remoteView->sendFrame(frame);
578579
}
579580

@@ -612,10 +613,10 @@ void QuickInspector::checkFeatures()
612613
return;
613614
}
614615

615-
if (m_window->rendererInterface()->graphicsApi() == QSGRendererInterface::OpenGL)
616-
f = AllCustomRenderModes;
617-
else if (m_window->rendererInterface()->graphicsApi() == QSGRendererInterface::Software)
616+
if (m_window->rendererInterface()->graphicsApi() == QSGRendererInterface::Software)
618617
f = AnalyzePainting;
618+
else
619+
f = AllCustomRenderModes;
619620

620621
emit features(f);
621622
}

plugins/quickinspector/quickscenepreviewwidget.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
#include <QMouseEvent>
2121
#include <QPainter>
2222

23+
#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
24+
#include <qsgrendererinterface.h>
25+
#endif
26+
2327
using namespace GammaRay;
2428
static const qint32 QuickScenePreviewWidgetStateVersion = 4;
2529

@@ -152,6 +156,14 @@ void QuickScenePreviewWidget::resizeEvent(QResizeEvent *e)
152156

153157
void QuickScenePreviewWidget::renderDecoration(QPainter *p, double zoom) const
154158
{
159+
#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
160+
// We want to skip rendering decorations in the client, if we draw it on RHI targets and this rendering backend cannot discern between the two.
161+
const auto graphicsApi = static_cast<QSGRendererInterface::GraphicsApi>(frame().graphicsApi());
162+
if (QSGRendererInterface::isApiRhiBased(graphicsApi) && m_control->serverSideDecorationsEnabled()) {
163+
return;
164+
}
165+
#endif
166+
155167
// Scaling and translations on QuickItemGeometry will be done on demand
156168

157169
if (frame().data.userType() == qMetaTypeId<QuickItemGeometry>()) {

0 commit comments

Comments
 (0)