Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qbackingstorerhisupport.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6#include <qpa/qplatformintegration.h>
7#include <private/qguiapplication_p.h>
8
9#if QT_CONFIG(opengl)
10#include <QtGui/qoffscreensurface.h>
11#include <QtGui/private/qopenglcontext_p.h>
12#endif
13
14#if QT_CONFIG(vulkan)
15#include <QtGui/private/qvulkandefaultinstance_p.h>
16#endif
17
19
20QBackingStoreRhiSupport::~QBackingStoreRhiSupport()
21{
22 reset();
23}
24
25void QBackingStoreRhiSupport::SwapchainData::reset()
26{
27 delete swapchain;
28 delete renderPassDescriptor;
29 delete windowWatcher;
30 *this = {};
31}
32
33void QBackingStoreRhiSupport::reset()
34{
35 for (SwapchainData &d : m_swapchains)
36 d.reset();
37
38 m_swapchains.clear();
39
40 delete m_rhi;
41 m_rhi = nullptr;
42
43 delete m_openGLFallbackSurface;
44 m_openGLFallbackSurface = nullptr;
45}
46
47bool QBackingStoreRhiSupport::create()
48{
49 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::RhiBasedRendering))
50 return false;
51
52 // note: m_window may be null (special case for fully offscreen rendering)
53
54 QRhi *rhi = nullptr;
55 QOffscreenSurface *surface = nullptr;
56 QRhi::Flags flags;
57
58 // These must be the same env.vars Qt Quick uses (as documented), in order
59 // to ensure symmetry in the behavior between a QQuickWindow and a
60 // (QRhi-based) widget top-level window.
61 if (qEnvironmentVariableIntValue("QSG_RHI_PREFER_SOFTWARE_RENDERER"))
62 flags |= QRhi::PreferSoftwareRenderer;
63 if (qEnvironmentVariableIntValue("QSG_RHI_PROFILE"))
64 flags |= QRhi::EnableDebugMarkers | QRhi::EnableTimestamps;
65
66 if (m_config.api() == QPlatformBackingStoreRhiConfig::Null) {
67 QRhiNullInitParams params;
68 rhi = QRhi::create(QRhi::Null, &params, flags);
69 }
70
71#if QT_CONFIG(opengl)
72 if (!rhi && m_config.api() == QPlatformBackingStoreRhiConfig::OpenGL) {
73 surface = QRhiGles2InitParams::newFallbackSurface(m_format);
74 QRhiGles2InitParams params;
75 params.fallbackSurface = surface;
76 params.window = m_window;
77 params.format = m_format;
78 params.shareContext = QOpenGLContext::globalShareContext();
79 rhi = QRhi::create(QRhi::OpenGLES2, &params, flags);
80 }
81#endif
82
83#ifdef Q_OS_WIN
84 if (!rhi) {
85 if (m_config.api() == QPlatformBackingStoreRhiConfig::D3D11) {
86 QRhiD3D11InitParams params;
87 params.enableDebugLayer = m_config.isDebugLayerEnabled();
88 rhi = QRhi::create(QRhi::D3D11, &params, flags);
89 if (!rhi && !flags.testFlag(QRhi::PreferSoftwareRenderer)) {
90 qCDebug(lcQpaBackingStore, "Failed to create a D3D11 device with default settings; "
91 "attempting to get a software rasterizer backed device instead");
92 flags |= QRhi::PreferSoftwareRenderer;
93 rhi = QRhi::create(QRhi::D3D11, &params, flags);
94 }
95 } else if (m_config.api() == QPlatformBackingStoreRhiConfig::D3D12) {
96 QRhiD3D12InitParams params;
97 params.enableDebugLayer = m_config.isDebugLayerEnabled();
98 rhi = QRhi::create(QRhi::D3D12, &params, flags);
99 if (!rhi && !flags.testFlag(QRhi::PreferSoftwareRenderer)) {
100 qCDebug(lcQpaBackingStore, "Failed to create a D3D12 device with default settings; "
101 "attempting to get a software rasterizer backed device instead");
102 flags |= QRhi::PreferSoftwareRenderer;
103 rhi = QRhi::create(QRhi::D3D12, &params, flags);
104 }
105 }
106 }
107#endif
108
109#if QT_CONFIG(metal)
110 if (!rhi && m_config.api() == QPlatformBackingStoreRhiConfig::Metal) {
111 QRhiMetalInitParams params;
112 // For parity with Qt Quick, fall back to OpenGL when there is no Metal (f.ex. in macOS virtual machines).
113 if (QRhi::probe(QRhi::Metal, &params)) {
114 rhi = QRhi::create(QRhi::Metal, &params, flags);
115 } else {
116 qCDebug(lcQpaBackingStore, "Metal does not seem to be supported");
117 return false;
118 }
119 }
120#endif
121
122#if QT_CONFIG(vulkan)
123 if (!rhi && m_config.api() == QPlatformBackingStoreRhiConfig::Vulkan) {
124 if (m_config.isDebugLayerEnabled())
125 QVulkanDefaultInstance::setFlag(QVulkanDefaultInstance::EnableValidation);
126 QRhiVulkanInitParams params;
127 if (m_window) {
128 if (!m_window->vulkanInstance())
129 m_window->setVulkanInstance(QVulkanDefaultInstance::instance());
130 params.inst = m_window->vulkanInstance();
131 } else {
132 params.inst = QVulkanDefaultInstance::instance();
133 }
134 if (!params.inst) {
135 qWarning("No QVulkanInstance set for the top-level window, this is wrong.");
136 return false;
137 }
138 params.window = m_window;
139 rhi = QRhi::create(QRhi::Vulkan, &params, flags);
140 }
141#endif
142
143 if (!rhi) {
144 qWarning("Failed to create QRhi for QBackingStoreRhiSupport");
145 delete surface;
146 return false;
147 }
148
149 m_rhi = rhi;
150 m_openGLFallbackSurface = surface;
151 return true;
152}
153
154QRhiSwapChain *QBackingStoreRhiSupport::swapChainForWindow(QWindow *window)
155{
156 auto it = m_swapchains.constFind(window);
157 if (it != m_swapchains.constEnd())
158 return it.value().swapchain;
159
160 QRhiSwapChain *swapchain = nullptr;
161 QRhiRenderPassDescriptor *rp = nullptr;
162 if (window && m_rhi) {
163 QRhiSwapChain::Flags flags;
164 const QSurfaceFormat format = window->requestedFormat();
165 if (format.swapInterval() == 0)
166 flags |= QRhiSwapChain::NoVSync;
167 if (format.alphaBufferSize() > 0)
168 flags |= QRhiSwapChain::SurfaceHasNonPreMulAlpha;
169#if QT_CONFIG(vulkan)
170 if (m_config.api() == QPlatformBackingStoreRhiConfig::Vulkan && !window->vulkanInstance())
171 window->setVulkanInstance(QVulkanDefaultInstance::instance());
172#endif
173 qCDebug(lcQpaBackingStore) << "Creating swapchain for window" << window;
174 swapchain = m_rhi->newSwapChain();
175 swapchain->setWindow(window);
176 swapchain->setFlags(flags);
177 rp = swapchain->newCompatibleRenderPassDescriptor();
178 swapchain->setRenderPassDescriptor(rp);
179 if (!swapchain->createOrResize()) {
180 qWarning("Failed to create swapchain for window flushed with an RHI-enabled backingstore");
181 delete rp;
182 return nullptr;
183 }
184 }
185 if (swapchain) {
186 SwapchainData d;
187 d.swapchain = swapchain;
188 d.renderPassDescriptor = rp;
189 d.windowWatcher = new QBackingStoreRhiSupportWindowWatcher(this);
190 m_swapchains.insert(window, d);
191 window->installEventFilter(d.windowWatcher);
192 }
193 return swapchain;
194}
195
196bool QBackingStoreRhiSupportWindowWatcher::eventFilter(QObject *obj, QEvent *event)
197{
198 if (event->type() == QEvent::WindowAboutToChangeInternal
199 || (event->type() == QEvent::PlatformSurface
200 && static_cast<QPlatformSurfaceEvent *>(event)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed))
201 {
202 QWindow *window = qobject_cast<QWindow *>(obj);
203 auto it = m_rhiSupport->m_swapchains.find(window);
204 if (it != m_rhiSupport->m_swapchains.end()) {
205 qCDebug(lcQpaBackingStore) << event << "received for" << window << "- cleaning up swapchain";
206 auto data = *it;
207 m_rhiSupport->m_swapchains.erase(it);
208 data.reset(); // deletes 'this'
209 }
210 }
211 return false;
212}
213
214QSurface::SurfaceType QBackingStoreRhiSupport::surfaceTypeForConfig(const QPlatformBackingStoreRhiConfig &config)
215{
216 QSurface::SurfaceType type = QSurface::RasterSurface;
217 switch (config.api()) {
218 case QPlatformBackingStoreRhiConfig::D3D11:
219 case QPlatformBackingStoreRhiConfig::D3D12:
220 type = QSurface::Direct3DSurface;
221 break;
222 case QPlatformBackingStoreRhiConfig::Vulkan:
223 type = QSurface::VulkanSurface;
224 break;
225 case QPlatformBackingStoreRhiConfig::Metal:
226 type = QSurface::MetalSurface;
227 break;
228 case QPlatformBackingStoreRhiConfig::OpenGL:
229 type = QSurface::OpenGLSurface;
230 break;
231 default:
232 break;
233 }
234 return type;
235}
236
237QRhi::Implementation QBackingStoreRhiSupport::apiToRhiBackend(QPlatformBackingStoreRhiConfig::Api api)
238{
239 switch (api) {
240 case QPlatformBackingStoreRhiConfig::OpenGL:
241 return QRhi::OpenGLES2;
242 case QPlatformBackingStoreRhiConfig::Metal:
243 return QRhi::Metal;
244 case QPlatformBackingStoreRhiConfig::Vulkan:
245 return QRhi::Vulkan;
246 case QPlatformBackingStoreRhiConfig::D3D11:
247 return QRhi::D3D11;
248 case QPlatformBackingStoreRhiConfig::D3D12:
249 return QRhi::D3D12;
250 case QPlatformBackingStoreRhiConfig::Null:
251 return QRhi::Null;
252 default:
253 break;
254 }
255 return QRhi::Null;
256}
257
258bool QBackingStoreRhiSupport::checkForceRhi(QPlatformBackingStoreRhiConfig *outConfig, QSurface::SurfaceType *outType)
259{
260 static QPlatformBackingStoreRhiConfig config;
261 static bool checked = false;
262
263 if (!checked) {
264 checked = true;
265
266 const bool alwaysRhi = qEnvironmentVariableIntValue("QT_WIDGETS_RHI");
267 const bool highdpiDownscale = qEnvironmentVariableIntValue("QT_WIDGETS_HIGHDPI_DOWNSCALE");
268 if (highdpiDownscale)
269 qCDebug(lcQpaBackingStore) << "Enabling QT_WIDGETS_RHI due to QT_WIDGETS_HIGHDPI_DOWNSCALE";
270 if (alwaysRhi || highdpiDownscale)
271 config.setEnabled(true);
272
273 // if enabled, choose an api
274 if (config.isEnabled()) {
275#if defined(Q_OS_WIN)
276 config.setApi(QPlatformBackingStoreRhiConfig::D3D11);
277#elif QT_CONFIG(metal)
278 config.setApi(QPlatformBackingStoreRhiConfig::Metal);
279#elif QT_CONFIG(opengl)
280 config.setApi(QPlatformBackingStoreRhiConfig::OpenGL);
281#elif QT_CONFIG(vulkan)
282 config.setApi(QPlatformBackingStoreRhiConfig::Vulkan);
283#else
284 qWarning("QT_WIDGETS_RHI is set but no backend is available; ignoring");
285 return false;
286#endif
287
288 // the env.var. will always override
289 if (qEnvironmentVariableIsSet("QT_WIDGETS_RHI_BACKEND")) {
290 const QString backend = qEnvironmentVariable("QT_WIDGETS_RHI_BACKEND");
291#ifdef Q_OS_WIN
292 if (backend == QStringLiteral("d3d11") || backend == QStringLiteral("d3d"))
293 config.setApi(QPlatformBackingStoreRhiConfig::D3D11);
294 if (backend == QStringLiteral("d3d12"))
295 config.setApi(QPlatformBackingStoreRhiConfig::D3D12);
296#endif
297#if QT_CONFIG(metal)
298 if (backend == QStringLiteral("metal"))
299 config.setApi(QPlatformBackingStoreRhiConfig::Metal);
300#endif
301#if QT_CONFIG(opengl)
302 if (backend == QStringLiteral("opengl") || backend == QStringLiteral("gl"))
303 config.setApi(QPlatformBackingStoreRhiConfig::OpenGL);
304#endif
305#if QT_CONFIG(vulkan)
306 if (backend == QStringLiteral("vulkan"))
307 config.setApi(QPlatformBackingStoreRhiConfig::Vulkan);
308#endif
309 }
310
311 if (qEnvironmentVariableIntValue("QT_WIDGETS_RHI_DEBUG_LAYER"))
312 config.setDebugLayer(true);
313 }
314
315 qCDebug(lcQpaBackingStore) << "Check for forced use of QRhi resulted in enable"
316 << config.isEnabled() << "with api" << QRhi::backendName(apiToRhiBackend(config.api()));
317 }
318
319 if (config.isEnabled()) {
320 if (outConfig)
321 *outConfig = config;
322 if (outType)
323 *outType = surfaceTypeForConfig(config);
324 return true;
325 }
326 return false;
327}
328
329QT_END_NAMESPACE
bool eventFilter(QObject *obj, QEvent *ev) override
Filters events if this object has been installed as an event filter for the watched object.
Combined button and popup list for selecting options.