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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtbase/src/gui/painting/qbackingstorerhisupport.cpp