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