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
qsgrhisupport.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 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 "qsgcontext_p.h"
8
9#include <QtQuick/private/qquickitem_p.h>
10#include <QtQuick/private/qquickwindow_p.h>
11
12#include <QtGui/qwindow.h>
13#include <QtGui/qpa/qplatformintegration.h>
14#include <QtGui/private/qguiapplication_p.h>
15
16#if QT_CONFIG(vulkan)
17#include <QtGui/private/qvulkandefaultinstance_p.h>
18#endif
19
20#include <QOperatingSystemVersion>
21#include <QLockFile>
22#include <QSaveFile>
23#include <QStandardPaths>
24#include <QDir>
25#include <QFileInfo>
26#include <QSysInfo>
27#include <QOffscreenSurface>
28
29#ifdef Q_OS_WIN
30#include <dxgiformat.h>
31#endif
32
33#include <memory>
34
35QT_BEGIN_NAMESPACE
36
37QSGRhiSupport::QSGRhiSupport()
38{
39}
40
41void QSGRhiSupport::applySettings()
42{
43 // Multiple calls to this function are perfectly possible!
44 // Just store that it was called at least once.
45 m_settingsApplied = true;
46
47 // This is also done when creating the renderloop but we may be before that
48 // in case we get here due to a setGraphicsApi() -> configure() early
49 // on in main(). Avoid losing info logs since troubleshooting gets
50 // confusing otherwise.
51 QSGRhiSupport::checkEnvQSgInfo();
52
53 if (m_requested.valid) {
54 // explicit rhi backend request from C++ (e.g. via QQuickWindow)
55 switch (m_requested.api) {
56 case QSGRendererInterface::OpenGL:
57 m_rhiBackend = QRhi::OpenGLES2;
58 break;
59 case QSGRendererInterface::Direct3D11:
60 m_rhiBackend = QRhi::D3D11;
61 break;
62 case QSGRendererInterface::Direct3D12:
63 m_rhiBackend = QRhi::D3D12;
64 break;
65 case QSGRendererInterface::Vulkan:
66 m_rhiBackend = QRhi::Vulkan;
67 break;
68 case QSGRendererInterface::Metal:
69 m_rhiBackend = QRhi::Metal;
70 break;
71 case QSGRendererInterface::Null:
72 m_rhiBackend = QRhi::Null;
73 break;
74 default:
75 Q_ASSERT_X(false, "QSGRhiSupport", "Internal error: unhandled GraphicsApi type");
76 break;
77 }
78 } else {
79 // check env.vars., fall back to platform-specific defaults when backend is not set
80 const QByteArray rhiBackend = qgetenv("QSG_RHI_BACKEND");
81 if (rhiBackend == QByteArrayLiteral("gl")
82 || rhiBackend == QByteArrayLiteral("gles2")
83 || rhiBackend == QByteArrayLiteral("opengl"))
84 {
85 m_rhiBackend = QRhi::OpenGLES2;
86 } else if (rhiBackend == QByteArrayLiteral("d3d11") || rhiBackend == QByteArrayLiteral("d3d")) {
87 m_rhiBackend = QRhi::D3D11;
88 } else if (rhiBackend == QByteArrayLiteral("d3d12")) {
89 m_rhiBackend = QRhi::D3D12;
90 } else if (rhiBackend == QByteArrayLiteral("vulkan")) {
91 m_rhiBackend = QRhi::Vulkan;
92 } else if (rhiBackend == QByteArrayLiteral("metal")) {
93 m_rhiBackend = QRhi::Metal;
94 } else if (rhiBackend == QByteArrayLiteral("null")) {
95 m_rhiBackend = QRhi::Null;
96 } else {
97 if (!rhiBackend.isEmpty()) {
98 qWarning("Unknown key \"%s\" for QSG_RHI_BACKEND, falling back to default backend.",
99 rhiBackend.constData());
100 }
101#if defined(Q_OS_WIN)
102 m_rhiBackend = QRhi::D3D11;
103#elif QT_CONFIG(metal)
104 m_rhiBackend = QRhi::Metal;
105#elif QT_CONFIG(opengl)
106 m_rhiBackend = QRhi::OpenGLES2;
107#else
108 m_rhiBackend = QRhi::Vulkan;
109#endif
110
111 // Now that we established our initial choice, we may want to opt
112 // for another backend under certain special circumstances.
113 adjustToPlatformQuirks();
114 }
115 }
116
117 // At this point the RHI backend is fixed, it cannot be changed once we
118 // return from this function. This is because things like the QWindow
119 // (QQuickWindow) may depend on the graphics API as well (surfaceType
120 // f.ex.), and all that is based on what we report from here. So further
121 // adjustments are not possible (or, at minimum, not safe and portable).
122}
123
124void QSGRhiSupport::adjustToPlatformQuirks()
125{
126#if QT_CONFIG(metal)
127 // A macOS VM may not have Metal support at all. We have to decide at this
128 // point, it will be too late afterwards, and the only way is to see if
129 // MTLCreateSystemDefaultDevice succeeds.
130 if (m_rhiBackend == QRhi::Metal) {
131 QRhiMetalInitParams rhiParams;
132 if (!QRhi::probe(m_rhiBackend, &rhiParams)) {
133 qCDebug(QSG_LOG_INFO, "Metal does not seem to be supported. Falling back to OpenGL.");
134 m_rhiBackend = QRhi::OpenGLES2;
135 auto *platformIntegration = QGuiApplicationPrivate::platformIntegration();
136 if (platformIntegration
137 && !platformIntegration->hasCapability(QPlatformIntegration::OpenGL)) {
138 qCWarning(QSG_LOG_INFO, "OpenGL not available. Falling back to Null backend.");
139 m_rhiBackend = QRhi::Null;
140 }
141 }
142 }
143#endif
144}
145
146void QSGRhiSupport::checkEnvQSgInfo()
147{
148 // For compatibility with 5.3 and earlier's QSG_INFO environment variables
149 if (qEnvironmentVariableIsSet("QSG_INFO"))
150 const_cast<QLoggingCategory &>(QSG_LOG_INFO()).setEnabled(QtDebugMsg, true);
151}
152
153
154#if QT_CONFIG(opengl)
155#ifndef GL_BGRA
156#define GL_BGRA 0x80E1
157#endif
158
159#ifndef GL_R8
160#define GL_R8 0x8229
161#endif
162
163#ifndef GL_RG8
164#define GL_RG8 0x822B
165#endif
166
167#ifndef GL_RG
168#define GL_RG 0x8227
169#endif
170
171#ifndef GL_R16
172#define GL_R16 0x822A
173#endif
174
175#ifndef GL_RG16
176#define GL_RG16 0x822C
177#endif
178
179#ifndef GL_RED
180#define GL_RED 0x1903
181#endif
182
183#ifndef GL_RGBA8
184#define GL_RGBA8 0x8058
185#endif
186
187#ifndef GL_RGBA32F
188#define GL_RGBA32F 0x8814
189#endif
190
191#ifndef GL_RGBA16F
192#define GL_RGBA16F 0x881A
193#endif
194
195#ifndef GL_R16F
196#define GL_R16F 0x822D
197#endif
198
199#ifndef GL_R32F
200#define GL_R32F 0x822E
201#endif
202
203#ifndef GL_DEPTH_COMPONENT16
204#define GL_DEPTH_COMPONENT16 0x81A5
205#endif
206
207#ifndef GL_DEPTH_COMPONENT24
208#define GL_DEPTH_COMPONENT24 0x81A6
209#endif
210
211#ifndef GL_DEPTH_COMPONENT32F
212#define GL_DEPTH_COMPONENT32F 0x8CAC
213#endif
214
215#ifndef GL_DEPTH24_STENCIL8
216#define GL_DEPTH24_STENCIL8 0x88F0
217#endif
218
219#ifndef GL_DEPTH_STENCIL
220#define GL_DEPTH_STENCIL 0x84F9
221#endif
222
223#ifndef GL_RGB10_A2
224#define GL_RGB10_A2 0x8059
225#endif
226
227#ifndef GL_SRGB_ALPHA
228#define GL_SRGB_ALPHA 0x8C42
229#endif
230
231#ifndef GL_SRGB8_ALPHA8
232#define GL_SRGB8_ALPHA8 0x8C43
233#endif
234
235QRhiTexture::Format QSGRhiSupport::toRhiTextureFormatFromGL(uint format, QRhiTexture::Flags *flags)
236{
237 bool sRGB = false;
238 auto rhiFormat = QRhiTexture::UnknownFormat;
239 switch (format) {
240 case GL_SRGB_ALPHA:
241 case GL_SRGB8_ALPHA8:
242 sRGB = true;
243 Q_FALLTHROUGH();
244 case GL_RGBA:
245 case GL_RGBA8:
246 case 0:
247 rhiFormat = QRhiTexture::RGBA8;
248 break;
249 case GL_BGRA:
250 rhiFormat = QRhiTexture::BGRA8;
251 break;
252 case GL_R16:
253 rhiFormat = QRhiTexture::R16;
254 break;
255 case GL_RG16:
256 rhiFormat = QRhiTexture::RG16;
257 break;
258 case GL_RED:
259 Q_FALLTHROUGH();
260 case GL_R8:
261 rhiFormat = QRhiTexture::R8;
262 break;
263 case GL_RG:
264 Q_FALLTHROUGH();
265 case GL_RG8:
266 rhiFormat = QRhiTexture::RG8;
267 break;
268 case GL_ALPHA:
269 rhiFormat = QRhiTexture::RED_OR_ALPHA8;
270 break;
271 case GL_RGBA16F:
272 rhiFormat = QRhiTexture::RGBA16F;
273 break;
274 case GL_RGBA32F:
275 rhiFormat = QRhiTexture::RGBA32F;
276 break;
277 case GL_R16F:
278 rhiFormat = QRhiTexture::R16F;
279 break;
280 case GL_R32F:
281 rhiFormat = QRhiTexture::R32F;
282 break;
283 case GL_RGB10_A2:
284 rhiFormat = QRhiTexture::RGB10A2;
285 break;
286 case GL_DEPTH_COMPONENT:
287 Q_FALLTHROUGH();
288 case GL_DEPTH_COMPONENT16:
289 rhiFormat = QRhiTexture::D16;
290 break;
291 case GL_DEPTH_COMPONENT24:
292 rhiFormat = QRhiTexture::D24;
293 break;
294 case GL_DEPTH_STENCIL:
295 Q_FALLTHROUGH();
296 case GL_DEPTH24_STENCIL8:
297 rhiFormat = QRhiTexture::D24S8;
298 break;
299 case GL_DEPTH_COMPONENT32F:
300 rhiFormat = QRhiTexture::D32F;
301 break;
302 default:
303 qWarning("GL format %d is not supported", format);
304 break;
305 }
306 if (sRGB)
307 (*flags) |=(QRhiTexture::sRGB);
308 return rhiFormat;
309}
310#endif
311
312#if QT_CONFIG(vulkan)
313QRhiTexture::Format QSGRhiSupport::toRhiTextureFormatFromVulkan(uint format, QRhiTexture::Flags *flags)
314{
315 auto rhiFormat = QRhiTexture::UnknownFormat;
316 bool sRGB = false;
317 switch (format) {
318 case VK_FORMAT_R8G8B8A8_SRGB:
319 sRGB = true;
320 Q_FALLTHROUGH();
321 case VK_FORMAT_R8G8B8A8_UNORM:
322 case VK_FORMAT_UNDEFINED:
323 rhiFormat = QRhiTexture::RGBA8;
324 break;
325 case VK_FORMAT_B8G8R8A8_SRGB:
326 sRGB = true;
327 Q_FALLTHROUGH();
328 case VK_FORMAT_B8G8R8A8_UNORM:
329 rhiFormat = QRhiTexture::BGRA8;
330 break;
331 case VK_FORMAT_R8_SRGB:
332 sRGB = true;
333 Q_FALLTHROUGH();
334 case VK_FORMAT_R8_UNORM:
335 rhiFormat = QRhiTexture::R8;
336 break;
337 case VK_FORMAT_R8G8_SRGB:
338 sRGB = true;
339 Q_FALLTHROUGH();
340 case VK_FORMAT_R8G8_UNORM:
341 rhiFormat = QRhiTexture::RG8;
342 break;
343 case VK_FORMAT_R16_UNORM:
344 rhiFormat = QRhiTexture::R16;
345 break;
346 case VK_FORMAT_R16G16_UNORM:
347 rhiFormat = QRhiTexture::RG16;
348 break;
349 case VK_FORMAT_R16G16B16A16_SFLOAT:
350 rhiFormat = QRhiTexture::RGBA16F;
351 break;
352 case VK_FORMAT_R32G32B32A32_SFLOAT:
353 rhiFormat = QRhiTexture::RGBA32F;
354 break;
355 case VK_FORMAT_R16_SFLOAT:
356 rhiFormat = QRhiTexture::R16F;
357 break;
358 case VK_FORMAT_R32_SFLOAT:
359 rhiFormat = QRhiTexture::R32F;
360 break;
361 case VK_FORMAT_A2B10G10R10_UNORM_PACK32: // intentionally
362 Q_FALLTHROUGH();
363 case VK_FORMAT_A2R10G10B10_UNORM_PACK32:
364 rhiFormat = QRhiTexture::RGB10A2;
365 break;
366 case VK_FORMAT_D16_UNORM:
367 rhiFormat = QRhiTexture::D16;
368 break;
369 case VK_FORMAT_X8_D24_UNORM_PACK32:
370 rhiFormat = QRhiTexture::D24;
371 break;
372 case VK_FORMAT_D24_UNORM_S8_UINT:
373 rhiFormat = QRhiTexture::D24S8;
374 break;
375 case VK_FORMAT_D32_SFLOAT:
376 rhiFormat = QRhiTexture::D32F;
377 break;
378 case VK_FORMAT_BC1_RGB_SRGB_BLOCK:
379 sRGB = true;
380 Q_FALLTHROUGH();
381 case VK_FORMAT_BC1_RGB_UNORM_BLOCK:
382 rhiFormat = QRhiTexture::BC1;
383 break;
384 case VK_FORMAT_BC2_SRGB_BLOCK:
385 sRGB = true;
386 Q_FALLTHROUGH();
387 case VK_FORMAT_BC2_UNORM_BLOCK:
388 rhiFormat = QRhiTexture::BC2;
389 break;
390 case VK_FORMAT_BC3_SRGB_BLOCK:
391 sRGB = true;
392 Q_FALLTHROUGH();
393 case VK_FORMAT_BC3_UNORM_BLOCK:
394 rhiFormat = QRhiTexture::BC3;
395 break;
396 case VK_FORMAT_BC4_UNORM_BLOCK:
397 rhiFormat = QRhiTexture::BC4;
398 break;
399 case VK_FORMAT_BC5_UNORM_BLOCK:
400 rhiFormat = QRhiTexture::BC5;
401 break;
402 case VK_FORMAT_BC6H_UFLOAT_BLOCK:
403 rhiFormat = QRhiTexture::BC6H;
404 break;
405 case VK_FORMAT_BC7_SRGB_BLOCK:
406 sRGB = true;
407 Q_FALLTHROUGH();
408 case VK_FORMAT_BC7_UNORM_BLOCK:
409 rhiFormat = QRhiTexture::BC7;
410 break;
411 case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
412 sRGB = true;
413 Q_FALLTHROUGH();
414 case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
415 rhiFormat = QRhiTexture::ETC2_RGB8;
416 break;
417 case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
418 sRGB = true;
419 Q_FALLTHROUGH();
420 case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
421 rhiFormat = QRhiTexture::ETC2_RGB8A1;
422 break;
423 case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
424 sRGB = true;
425 Q_FALLTHROUGH();
426 case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
427 rhiFormat = QRhiTexture::ETC2_RGBA8;
428 break;
429 case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
430 sRGB = true;
431 Q_FALLTHROUGH();
432 case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
433 rhiFormat = QRhiTexture::ASTC_4x4;
434 break;
435 case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:
436 sRGB = true;
437 Q_FALLTHROUGH();
438 case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
439 rhiFormat = QRhiTexture::ASTC_5x4;
440 break;
441 case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:
442 sRGB = true;
443 Q_FALLTHROUGH();
444 case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
445 rhiFormat = QRhiTexture::ASTC_5x5;
446 break;
447 case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:
448 sRGB = true;
449 Q_FALLTHROUGH();
450 case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
451 rhiFormat = QRhiTexture::ASTC_6x5;
452 break;
453 case VK_FORMAT_ASTC_6x6_SRGB_BLOCK:
454 sRGB = true;
455 Q_FALLTHROUGH();
456 case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:
457 rhiFormat = QRhiTexture::ASTC_6x6;
458 break;
459 case VK_FORMAT_ASTC_8x5_SRGB_BLOCK:
460 sRGB = true;
461 Q_FALLTHROUGH();
462 case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:
463 rhiFormat = QRhiTexture::ASTC_8x5;
464 break;
465 case VK_FORMAT_ASTC_8x6_SRGB_BLOCK:
466 sRGB = true;
467 Q_FALLTHROUGH();
468 case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:
469 rhiFormat = QRhiTexture::ASTC_8x6;
470 break;
471 case VK_FORMAT_ASTC_8x8_SRGB_BLOCK:
472 sRGB = true;
473 Q_FALLTHROUGH();
474 case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:
475 rhiFormat = QRhiTexture::ASTC_8x8;
476 break;
477 case VK_FORMAT_ASTC_10x5_SRGB_BLOCK:
478 sRGB = true;
479 Q_FALLTHROUGH();
480 case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:
481 rhiFormat = QRhiTexture::ASTC_10x5;
482 break;
483 case VK_FORMAT_ASTC_10x6_SRGB_BLOCK:
484 sRGB = true;
485 Q_FALLTHROUGH();
486 case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:
487 rhiFormat = QRhiTexture::ASTC_10x6;
488 break;
489 case VK_FORMAT_ASTC_10x8_SRGB_BLOCK:
490 sRGB = true;
491 Q_FALLTHROUGH();
492 case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:
493 rhiFormat = QRhiTexture::ASTC_10x8;
494 break;
495 case VK_FORMAT_ASTC_10x10_SRGB_BLOCK:
496 sRGB = true;
497 Q_FALLTHROUGH();
498 case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:
499 rhiFormat = QRhiTexture::ASTC_10x10;
500 break;
501 case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
502 sRGB = true;
503 Q_FALLTHROUGH();
504 case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:
505 rhiFormat = QRhiTexture::ASTC_12x10;
506 break;
507 case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
508 sRGB = true;
509 Q_FALLTHROUGH();
510 case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
511 rhiFormat = QRhiTexture::ASTC_12x12;
512 break;
513 default:
514 qWarning("VkFormat %d is not supported", format);
515 break;
516 }
517 if (sRGB)
518 (*flags) |=(QRhiTexture::sRGB);
519 return rhiFormat;
520}
521#endif
522
523#ifdef Q_OS_WIN
524QRhiTexture::Format QSGRhiSupport::toRhiTextureFormatFromDXGI(uint format, QRhiTexture::Flags *flags)
525{
526 auto rhiFormat = QRhiTexture::UnknownFormat;
527 bool sRGB = false;
528 switch (format) {
529 case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
530 sRGB = true;
531 Q_FALLTHROUGH();
532 case DXGI_FORMAT_R8G8B8A8_UNORM:
533 case DXGI_FORMAT_UNKNOWN:
534 rhiFormat = QRhiTexture::RGBA8;
535 break;
536 case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
537 sRGB = true;
538 Q_FALLTHROUGH();
539 case DXGI_FORMAT_B8G8R8A8_UNORM:
540 rhiFormat = QRhiTexture::BGRA8;
541 break;
542 case DXGI_FORMAT_R8_UNORM:
543 rhiFormat = QRhiTexture::R8;
544 break;
545 case DXGI_FORMAT_R8G8_UNORM:
546 rhiFormat = QRhiTexture::RG8;
547 break;
548 case DXGI_FORMAT_R16_UNORM:
549 rhiFormat = QRhiTexture::R16;
550 break;
551 case DXGI_FORMAT_R16G16_UNORM:
552 rhiFormat = QRhiTexture::RG16;
553 break;
554 case DXGI_FORMAT_R16G16B16A16_FLOAT:
555 rhiFormat = QRhiTexture::RGBA16F;
556 break;
557 case DXGI_FORMAT_R32G32B32A32_FLOAT:
558 rhiFormat = QRhiTexture::RGBA32F;
559 break;
560 case DXGI_FORMAT_R16_FLOAT:
561 rhiFormat = QRhiTexture::R16F;
562 break;
563 case DXGI_FORMAT_R32_FLOAT:
564 rhiFormat = QRhiTexture::R32F;
565 break;
566 case DXGI_FORMAT_R10G10B10A2_UNORM:
567 rhiFormat = QRhiTexture::RGB10A2;
568 break;
569 case DXGI_FORMAT_R16_TYPELESS:
570 rhiFormat = QRhiTexture::D16;
571 break;
572 case DXGI_FORMAT_R24_UNORM_X8_TYPELESS:
573 rhiFormat = QRhiTexture::D24;
574 break;
575 case DXGI_FORMAT_D24_UNORM_S8_UINT:
576 rhiFormat = QRhiTexture::D24S8;
577 break;
578 case DXGI_FORMAT_R32_TYPELESS:
579 rhiFormat = QRhiTexture::D32F;
580 break;
581 case DXGI_FORMAT_BC1_UNORM_SRGB:
582 sRGB = true;
583 Q_FALLTHROUGH();
584 case DXGI_FORMAT_BC1_UNORM:
585 rhiFormat = QRhiTexture::BC1;
586 break;
587 case DXGI_FORMAT_BC2_UNORM_SRGB:
588 sRGB = true;
589 Q_FALLTHROUGH();
590 case DXGI_FORMAT_BC2_UNORM:
591 rhiFormat = QRhiTexture::BC2;
592 break;
593 case DXGI_FORMAT_BC3_UNORM_SRGB:
594 sRGB = true;
595 Q_FALLTHROUGH();
596 case DXGI_FORMAT_BC3_UNORM:
597 rhiFormat = QRhiTexture::BC3;
598 break;
599 case DXGI_FORMAT_BC4_UNORM:
600 rhiFormat = QRhiTexture::BC4;
601 break;
602 case DXGI_FORMAT_BC5_UNORM:
603 rhiFormat = QRhiTexture::BC5;
604 break;
605 case DXGI_FORMAT_BC6H_UF16:
606 rhiFormat = QRhiTexture::BC6H;
607 break;
608 case DXGI_FORMAT_BC7_UNORM_SRGB:
609 sRGB = true;
610 Q_FALLTHROUGH();
611 case DXGI_FORMAT_BC7_UNORM:
612 rhiFormat = QRhiTexture::BC7;
613 break;
614 default:
615 qWarning("DXGI_FORMAT %d is not supported", format);
616 break;
617 }
618 if (sRGB)
619 (*flags) |=(QRhiTexture::sRGB);
620 return rhiFormat;
621}
622#endif
623
624#if QT_CONFIG(metal)
625namespace QSGRhiSupportMac {
626 QRhiTexture::Format toRhiTextureFormatFromMetal(uint format, QRhiTexture::Flags *flags);
627}
628QRhiTexture::Format QSGRhiSupport::toRhiTextureFormatFromMetal(uint format, QRhiTexture::Flags *flags)
629{
630 return QSGRhiSupportMac::toRhiTextureFormatFromMetal(format, flags);
631}
632#endif
633
634void QSGRhiSupport::configure(QSGRendererInterface::GraphicsApi api)
635{
636 if (api == QSGRendererInterface::Unknown) {
637 // behave as if nothing was explicitly requested
638 m_requested.valid = false;
639 applySettings();
640 } else {
641 Q_ASSERT(QSGRendererInterface::isApiRhiBased(api));
642 m_requested.valid = true;
643 m_requested.api = api;
644 applySettings();
645 }
646}
647
648QSGRhiSupport *QSGRhiSupport::instance_internal()
649{
650 static QSGRhiSupport inst;
651 return &inst;
652}
653
654QSGRhiSupport *QSGRhiSupport::instance()
655{
656 QSGRhiSupport *inst = instance_internal();
657 if (!inst->m_settingsApplied)
658 inst->applySettings();
659 return inst;
660}
661
662QString QSGRhiSupport::rhiBackendName() const
663{
664 return QString::fromUtf8(QRhi::backendName(m_rhiBackend));
665}
666
667QSGRendererInterface::GraphicsApi QSGRhiSupport::graphicsApi() const
668{
669 switch (m_rhiBackend) {
670 case QRhi::Null:
671 return QSGRendererInterface::Null;
672 case QRhi::Vulkan:
673 return QSGRendererInterface::Vulkan;
674 case QRhi::OpenGLES2:
675 return QSGRendererInterface::OpenGL;
676 case QRhi::D3D11:
677 return QSGRendererInterface::Direct3D11;
678 case QRhi::D3D12:
679 return QSGRendererInterface::Direct3D12;
680 case QRhi::Metal:
681 return QSGRendererInterface::Metal;
682 default:
683 return QSGRendererInterface::Unknown;
684 }
685}
686
687QSurface::SurfaceType QSGRhiSupport::windowSurfaceType() const
688{
689 switch (m_rhiBackend) {
690 case QRhi::Vulkan:
691 return QSurface::VulkanSurface;
692 case QRhi::OpenGLES2:
693 return QSurface::OpenGLSurface;
694 case QRhi::D3D11:
695 case QRhi::D3D12:
696 return QSurface::Direct3DSurface;
697 case QRhi::Metal:
698 return QSurface::MetalSurface;
699 default:
700 return QSurface::OpenGLSurface;
701 }
702}
703
704#if QT_CONFIG(vulkan)
705static const void *qsgrhi_vk_rifResource(QSGRendererInterface::Resource res,
706 const QRhiNativeHandles *nat,
707 const QRhiNativeHandles *cbNat,
708 const QRhiNativeHandles *rpNat)
709{
710 const QRhiVulkanNativeHandles *vknat = static_cast<const QRhiVulkanNativeHandles *>(nat);
711 const QRhiVulkanCommandBufferNativeHandles *maybeVkCbNat =
712 static_cast<const QRhiVulkanCommandBufferNativeHandles *>(cbNat);
713 const QRhiVulkanRenderPassNativeHandles *maybeVkRpNat =
714 static_cast<const QRhiVulkanRenderPassNativeHandles *>(rpNat);
715
716 switch (res) {
717 case QSGRendererInterface::DeviceResource:
718 return &vknat->dev;
719 case QSGRendererInterface::CommandQueueResource:
720 return &vknat->gfxQueue;
721 case QSGRendererInterface::CommandListResource:
722 if (maybeVkCbNat)
723 return &maybeVkCbNat->commandBuffer;
724 else
725 return nullptr;
726 case QSGRendererInterface::PhysicalDeviceResource:
727 return &vknat->physDev;
728 case QSGRendererInterface::RenderPassResource:
729 if (maybeVkRpNat)
730 return &maybeVkRpNat->renderPass;
731 else
732 return nullptr;
733 case QSGRendererInterface::GraphicsQueueFamilyIndexResource:
734 return &vknat->gfxQueueFamilyIdx;
735 case QSGRendererInterface::GraphicsQueueIndexResource:
736 return &vknat->gfxQueueIdx;
737 default:
738 return nullptr;
739 }
740}
741#endif
742
743#if QT_CONFIG(opengl)
744static const void *qsgrhi_gl_rifResource(QSGRendererInterface::Resource res, const QRhiNativeHandles *nat)
745{
746 const QRhiGles2NativeHandles *glnat = static_cast<const QRhiGles2NativeHandles *>(nat);
747 switch (res) {
748 case QSGRendererInterface::OpenGLContextResource:
749 return glnat->context;
750 default:
751 return nullptr;
752 }
753}
754#endif
755
756#ifdef Q_OS_WIN
757static const void *qsgrhi_d3d11_rifResource(QSGRendererInterface::Resource res, const QRhiNativeHandles *nat)
758{
759 const QRhiD3D11NativeHandles *d3dnat = static_cast<const QRhiD3D11NativeHandles *>(nat);
760 switch (res) {
761 case QSGRendererInterface::DeviceResource:
762 return d3dnat->dev;
763 case QSGRendererInterface::DeviceContextResource:
764 return d3dnat->context;
765 default:
766 return nullptr;
767 }
768}
769
770static const void *qsgrhi_d3d12_rifResource(QSGRendererInterface::Resource res, const QRhiNativeHandles *nat)
771{
772 const QRhiD3D12NativeHandles *d3dnat = static_cast<const QRhiD3D12NativeHandles *>(nat);
773 switch (res) {
774 case QSGRendererInterface::DeviceResource:
775 return d3dnat->dev;
776 case QSGRendererInterface::CommandQueueResource:
777 return d3dnat->commandQueue;
778 default:
779 return nullptr;
780 }
781}
782#endif
783
784#if QT_CONFIG(metal)
785static const void *qsgrhi_mtl_rifResource(QSGRendererInterface::Resource res, const QRhiNativeHandles *nat,
786 const QRhiNativeHandles *cbNat)
787{
788 const QRhiMetalNativeHandles *mtlnat = static_cast<const QRhiMetalNativeHandles *>(nat);
789 const QRhiMetalCommandBufferNativeHandles *maybeMtlCbNat =
790 static_cast<const QRhiMetalCommandBufferNativeHandles *>(cbNat);
791
792 switch (res) {
793 case QSGRendererInterface::DeviceResource:
794 return mtlnat->dev;
795 case QSGRendererInterface::CommandQueueResource:
796 return mtlnat->cmdQueue;
797 case QSGRendererInterface::CommandListResource:
798 if (maybeMtlCbNat)
799 return maybeMtlCbNat->commandBuffer;
800 else
801 return nullptr;
802 case QSGRendererInterface::CommandEncoderResource:
803 if (maybeMtlCbNat)
804 return maybeMtlCbNat->encoder;
805 else
806 return nullptr;
807 default:
808 return nullptr;
809 }
810}
811#endif
812
813const void *QSGRhiSupport::rifResource(QSGRendererInterface::Resource res,
814 const QSGDefaultRenderContext *rc,
815 const QQuickWindow *w)
816{
817 QRhi *rhi = rc->rhi();
818 if (!rhi)
819 return nullptr;
820
821 // Accessing the underlying QRhi* objects are essential both for Qt Quick
822 // 3D and advanced solutions, such as VR engine integrations.
823 switch (res) {
824 case QSGRendererInterface::RhiResource:
825 return rhi;
826 case QSGRendererInterface::RhiSwapchainResource:
827 return QQuickWindowPrivate::get(w)->swapchain;
828 case QSGRendererInterface::RhiRedirectCommandBuffer:
829 return QQuickWindowPrivate::get(w)->redirect.commandBuffer;
830 case QSGRendererInterface::RhiRedirectRenderTarget:
831 return QQuickWindowPrivate::get(w)->redirect.rt.rt.renderTarget;
832 default:
833 break;
834 }
835
836 const QRhiNativeHandles *nat = rhi->nativeHandles();
837 if (!nat)
838 return nullptr;
839
840 switch (m_rhiBackend) {
841#if QT_CONFIG(vulkan)
842 case QRhi::Vulkan:
843 {
844 QRhiCommandBuffer *cb = rc->currentFrameCommandBuffer();
845 QRhiRenderPassDescriptor *rp = rc->currentFrameRenderPass();
846 return qsgrhi_vk_rifResource(res, nat,
847 cb ? cb->nativeHandles() : nullptr,
848 rp ? rp->nativeHandles() : nullptr);
849 }
850#endif
851#if QT_CONFIG(opengl)
852 case QRhi::OpenGLES2:
853 return qsgrhi_gl_rifResource(res, nat);
854#endif
855#ifdef Q_OS_WIN
856 case QRhi::D3D11:
857 return qsgrhi_d3d11_rifResource(res, nat);
858 case QRhi::D3D12:
859 return qsgrhi_d3d12_rifResource(res, nat);
860#endif
861#if QT_CONFIG(metal)
862 case QRhi::Metal:
863 {
864 QRhiCommandBuffer *cb = rc->currentFrameCommandBuffer();
865 return qsgrhi_mtl_rifResource(res, nat, cb ? cb->nativeHandles() : nullptr);
866 }
867#endif
868 default:
869 return nullptr;
870 }
871}
872
873int QSGRhiSupport::chooseSampleCount(int samples, QRhi *rhi)
874{
875 int msaaSampleCount = samples;
876 if (qEnvironmentVariableIsSet("QSG_SAMPLES"))
877 msaaSampleCount = qEnvironmentVariableIntValue("QSG_SAMPLES");
878 msaaSampleCount = qMax(1, msaaSampleCount);
879 if (msaaSampleCount > 1) {
880 const QList<int> supportedSampleCounts = rhi->supportedSampleCounts();
881 if (!supportedSampleCounts.contains(msaaSampleCount)) {
882 int reducedSampleCount = 1;
883 for (int i = supportedSampleCounts.size() - 1; i >= 0; --i) {
884 if (supportedSampleCounts[i] <= msaaSampleCount) {
885 reducedSampleCount = supportedSampleCounts[i];
886 break;
887 }
888 }
889 qWarning() << "Requested MSAA sample count" << msaaSampleCount
890 << "but supported sample counts are" << supportedSampleCounts
891 << ", using sample count" << reducedSampleCount << "instead";
892 msaaSampleCount = reducedSampleCount;
893 }
894 }
895 return msaaSampleCount;
896}
897
898int QSGRhiSupport::chooseSampleCountForWindowWithRhi(QWindow *window, QRhi *rhi)
899{
900 return chooseSampleCount(qMax(QSurfaceFormat::defaultFormat().samples(), window->requestedFormat().samples()), rhi);
901}
902
903// must be called on the main thread
904QOffscreenSurface *QSGRhiSupport::maybeCreateOffscreenSurface(QWindow *window)
905{
906 QOffscreenSurface *offscreenSurface = nullptr;
907#if QT_CONFIG(opengl)
908 if (rhiBackend() == QRhi::OpenGLES2) {
909 const QSurfaceFormat format = window->requestedFormat();
910 offscreenSurface = QRhiGles2InitParams::newFallbackSurface(format);
911 }
912#else
913 Q_UNUSED(window);
914#endif
915 return offscreenSurface;
916}
917
918void QSGRhiSupport::prepareWindowForRhi(QQuickWindow *window)
919{
920#if QT_CONFIG(vulkan)
921 if (rhiBackend() == QRhi::Vulkan) {
922 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
923 // QQuickWindows must get a QVulkanInstance automatically (it is
924 // created when the first window is constructed and is destroyed only
925 // on exit), unless the application decided to set its own. With
926 // QQuickRenderControl, no QVulkanInstance is created, because it must
927 // always be under the application's control then (since the default
928 // instance we could create here would not be configurable by the
929 // application in any way, and that is often not acceptable).
930 if (!window->vulkanInstance() && !wd->renderControl) {
931 QVulkanInstance *vkinst = QVulkanDefaultInstance::instance();
932 if (vkinst)
933 qCDebug(QSG_LOG_INFO) << "Got Vulkan instance from QVulkanDefaultInstance, requested api version was" << vkinst->apiVersion();
934 else
935 qCDebug(QSG_LOG_INFO) << "No Vulkan instance from QVulkanDefaultInstance, expect problems";
936 window->setVulkanInstance(vkinst);
937 }
938 }
939#else
940 Q_UNUSED(window);
941#endif
942}
943
944static inline bool ensureWritableDir(const QString &name)
945{
946 QDir::root().mkpath(name);
947 return QFileInfo(name).isWritable();
948}
949
951{
952 static bool checked = false;
953 static QString currentCacheDir;
954 static bool cacheWritable = false;
955
956 if (checked)
957 return cacheWritable ? currentCacheDir : QString();
958
959 checked = true;
960
961 // Intentionally not using the global cache path (GenericCacheLocation) -
962 // we do not want forever growing pipeline cache files that contain
963 // everything from all Qt apps ever run (that would affect load times
964 // eventually, resource use, etc.). Stick to being application-specific.
965
966 const QString cachePath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
967 const QString subPath = QLatin1String("/qtpipelinecache-") + QSysInfo::buildAbi() + QLatin1Char('/');
968
969 if (!cachePath.isEmpty()) {
970 currentCacheDir = cachePath + subPath;
971 cacheWritable = ensureWritableDir(currentCacheDir);
972 }
973
974 return cacheWritable ? currentCacheDir : QString();
975}
976
978{
979 const QString cacheDir = automaticPipelineCacheDir();
980 if (!cacheDir.isEmpty())
981 return cacheDir + QLatin1String("qqpc_") + QString::fromLatin1(rhi->backendName()).toLower();
982
983 return QString();
984}
985
986static inline bool isAutomaticPipelineCacheLoadSkippedForWindow(Qt::WindowFlags wflags)
987{
988 return wflags.testFlag(Qt::ToolTip) || wflags.testFlag(Qt::SplashScreen);
989}
990
991static inline bool isAutomaticPipelineCacheSaveSkippedForWindow(Qt::WindowFlags wflags)
992{
993 // this catches Tool, ToolTip, SplashScreen as well
994 return wflags.testFlag(Qt::Dialog) || wflags.testFlag(Qt::Popup);
995}
996
997#if !QT_CONFIG(temporaryfile)
998static inline QString pipelineCacheLockFileName(const QString &name)
999{
1000 return name + QLatin1String(".lck");
1001}
1002#endif
1003
1004void QSGRhiSupport::preparePipelineCache(QRhi *rhi, QQuickWindow *window)
1005{
1006 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
1007
1008 // the explicitly set filename always takes priority as per docs
1009 QString pipelineCacheLoad = wd->graphicsConfig.pipelineCacheLoadFile();
1010 bool isAutomatic = false;
1011 if (pipelineCacheLoad.isEmpty() && wd->graphicsConfig.isAutomaticPipelineCacheEnabled()) {
1012 if (!isAutomaticPipelineCacheLoadSkippedForWindow(window->flags())) {
1013 pipelineCacheLoad = automaticPipelineCacheFileName(rhi);
1014 isAutomatic = true;
1015 }
1016 }
1017
1018 if (pipelineCacheLoad.isEmpty())
1019 return;
1020
1021#if !QT_CONFIG(temporaryfile)
1022 QLockFile lock(pipelineCacheLockFileName(pipelineCacheLoad));
1023 if (!lock.lock()) {
1024 qWarning("Could not create pipeline cache lock file '%s'",
1025 qPrintable(lock.fileName()));
1026 return;
1027 }
1028#endif
1029
1030 QFile f(pipelineCacheLoad);
1031 if (!f.open(QIODevice::ReadOnly)) {
1032 if (!isAutomatic) {
1033 qWarning("Could not open pipeline cache source file '%s'",
1034 qPrintable(pipelineCacheLoad));
1035 }
1036 return;
1037 }
1038
1039 const QByteArray buf = f.readAll();
1040 if (!buf.isEmpty()) {
1041 qCDebug(QSG_LOG_INFO, "Attempting to seed pipeline cache for QRhi %p from '%s'",
1042 rhi, qPrintable(pipelineCacheLoad));
1043 rhi->setPipelineCacheData(buf);
1044 }
1045}
1046
1047void QSGRhiSupport::finalizePipelineCache(QRhi *rhi, const QQuickGraphicsConfiguration &config)
1048{
1049 // output the rhi statistics about pipelines, as promised by the documentation
1050 qCDebug(QSG_LOG_INFO, "Total time spent on pipeline creation during the lifetime of the QRhi %p was %lld ms",
1051 rhi, rhi->statistics().totalPipelineCreationTime);
1052
1053 // the explicitly set filename always takes priority as per docs
1054 QString pipelineCacheSave = config.pipelineCacheSaveFile();
1055 bool isAutomatic = false;
1056 if (pipelineCacheSave.isEmpty() && config.isAutomaticPipelineCacheEnabled()) {
1057 pipelineCacheSave = automaticPipelineCacheFileName(rhi);
1058 isAutomatic = true;
1059 }
1060
1061 if (pipelineCacheSave.isEmpty())
1062 return;
1063
1064 const QByteArray buf = rhi->pipelineCacheData();
1065
1066 // If empty, do nothing. This is exactly what will happen if the rhi was
1067 // created without QRhi::EnablePipelineCacheDataSave set.
1068 if (buf.isEmpty()) {
1069 if (isAutomatic) {
1070 // Attempt to remove the file. If it does not exist or this fails,
1071 // that's fine. The goal is just to prevent warnings from
1072 // setPipelineCacheData in future runs, e.g. if the Qt or driver
1073 // version does not match _and_ we do not generate any data at run
1074 // time, then not writing the file out also means the warning would
1075 // appear again and again on every run. Prevent that.
1076 QDir().remove(pipelineCacheSave);
1077 }
1078 return;
1079 }
1080
1081
1082#if QT_CONFIG(temporaryfile)
1083 QSaveFile f(pipelineCacheSave);
1084#else
1085 QLockFile lock(pipelineCacheLockFileName(pipelineCacheSave));
1086 if (!lock.lock()) {
1087 qWarning("Could not create pipeline cache lock file '%s'",
1088 qPrintable(lock.fileName()));
1089 return;
1090 }
1091 QFile f(pipelineCacheSave);
1092#endif
1093 if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
1094 if (!isAutomatic) {
1095 const QString msg = f.errorString();
1096 qWarning("Could not open pipeline cache output file '%s': %s",
1097 qPrintable(pipelineCacheSave), qPrintable(msg));
1098 }
1099 return;
1100 }
1101
1102 qCDebug(QSG_LOG_INFO, "Writing pipeline cache contents (%d bytes) for QRhi %p to '%s'",
1103 int(buf.size()), rhi, qPrintable(pipelineCacheSave));
1104
1105 if (f.write(buf) != buf.size()
1106#if QT_CONFIG(temporaryfile)
1107 || !f.commit()
1108#endif
1109 )
1110 {
1111 if (!isAutomatic) {
1112 const QString msg = f.errorString();
1113 qWarning("Could not write pipeline cache: %s", qPrintable(msg));
1114 }
1115 return;
1116 }
1117}
1118
1119// must be called on the render thread
1120QSGRhiSupport::RhiCreateResult QSGRhiSupport::createRhi(QQuickWindow *window, QSurface *offscreenSurface, bool forcePreferSwRenderer)
1121{
1122 QRhi *rhi = nullptr;
1123 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
1124 const QQuickGraphicsDevicePrivate *customDevD = QQuickGraphicsDevicePrivate::get(&wd->customDeviceObjects);
1125 if (customDevD->type == QQuickGraphicsDevicePrivate::Type::Rhi) {
1126 rhi = customDevD->u.rhi;
1127 if (rhi) {
1128 preparePipelineCache(rhi, window);
1129 return { rhi, false };
1130 }
1131 }
1132
1133 const bool debugLayer = wd->graphicsConfig.isDebugLayerEnabled();
1134 const bool debugMarkers = wd->graphicsConfig.isDebugMarkersEnabled();
1135 const bool timestamps = wd->graphicsConfig.timestampsEnabled();
1136 const bool preferSoftware = wd->graphicsConfig.prefersSoftwareDevice() || forcePreferSwRenderer;
1137 const bool pipelineCacheSave = !wd->graphicsConfig.pipelineCacheSaveFile().isEmpty()
1138 || (wd->graphicsConfig.isAutomaticPipelineCacheEnabled()
1139 && !isAutomaticPipelineCacheSaveSkippedForWindow(window->flags()));
1140
1141 const QString backendName = rhiBackendName();
1142 qCDebug(QSG_LOG_INFO,
1143 "Creating QRhi with backend %s for window %p (wflags 0x%X)\n"
1144 " Graphics API debug/validation layers: %d\n"
1145 " Debug markers: %d\n"
1146 " Timestamps: %d\n"
1147 " Prefer software device: %d%s\n"
1148 " Shader/pipeline cache collection: %d",
1149 qPrintable(backendName), window, int(window->flags()), debugLayer,
1150 debugMarkers, timestamps, preferSoftware, forcePreferSwRenderer ? " [FORCED]" : "", pipelineCacheSave);
1151
1152 QRhi::Flags flags;
1153 flags |= QRhi::SuppressSmokeTestWarnings;
1154 if (debugMarkers)
1155 flags |= QRhi::EnableDebugMarkers;
1156 if (timestamps)
1157 flags |= QRhi::EnableTimestamps;
1158 if (preferSoftware)
1159 flags |= QRhi::PreferSoftwareRenderer;
1160 if (pipelineCacheSave)
1161 flags |= QRhi::EnablePipelineCacheDataSave;
1162
1163 QRhiAdapter *rhiAdapter = customDevD->type == QQuickGraphicsDevicePrivate::Type::RhiAdapter ? customDevD->u.rhiAdapter : nullptr;
1164 const QRhi::Implementation backend = rhiBackend();
1165 if (backend == QRhi::Null) {
1166 QRhiNullInitParams rhiParams;
1167 rhi = QRhi::create(backend, &rhiParams, flags, nullptr, rhiAdapter);
1168 }
1169#if QT_CONFIG(opengl)
1170 if (backend == QRhi::OpenGLES2) {
1171 const QSurfaceFormat format = window->requestedFormat();
1172 QRhiGles2InitParams rhiParams;
1173 rhiParams.format = format;
1174 rhiParams.fallbackSurface = offscreenSurface;
1175 rhiParams.window = window;
1176 if (customDevD->type == QQuickGraphicsDevicePrivate::Type::OpenGLContext) {
1177 QRhiGles2NativeHandles importDev;
1178 importDev.context = customDevD->u.context;
1179 qCDebug(QSG_LOG_INFO, "Using existing QOpenGLContext %p", importDev.context);
1180 rhi = QRhi::create(backend, &rhiParams, flags, &importDev);
1181 } else {
1182 rhi = QRhi::create(backend, &rhiParams, flags, nullptr, rhiAdapter);
1183 }
1184 }
1185#else
1186 Q_UNUSED(offscreenSurface);
1187 if (backend == QRhi::OpenGLES2)
1188 qWarning("OpenGL was requested for Qt Quick, but this build of Qt has no OpenGL support.");
1189#endif
1190#if QT_CONFIG(vulkan)
1191 if (backend == QRhi::Vulkan) {
1192 if (debugLayer)
1193 QVulkanDefaultInstance::setFlag(QVulkanDefaultInstance::EnableValidation, true);
1194 QRhiVulkanInitParams rhiParams;
1195 prepareWindowForRhi(window); // sets a vulkanInstance if not yet present
1196 rhiParams.inst = window->vulkanInstance();
1197 if (!rhiParams.inst)
1198 qWarning("No QVulkanInstance set for QQuickWindow, this is wrong.");
1199 if (window->handle()) // only used for vkGetPhysicalDeviceSurfaceSupportKHR and that implies having a valid native window
1200 rhiParams.window = window;
1201 rhiParams.deviceExtensions = wd->graphicsConfig.deviceExtensions();
1202 if (customDevD->type == QQuickGraphicsDevicePrivate::Type::DeviceObjects) {
1203 QRhiVulkanNativeHandles importDev;
1204 importDev.physDev = reinterpret_cast<VkPhysicalDevice>(customDevD->u.deviceObjects.physicalDevice);
1205 importDev.dev = reinterpret_cast<VkDevice>(customDevD->u.deviceObjects.device);
1206 importDev.gfxQueueFamilyIdx = customDevD->u.deviceObjects.queueFamilyIndex;
1207 importDev.gfxQueueIdx = customDevD->u.deviceObjects.queueIndex;
1208 qCDebug(QSG_LOG_INFO, "Using existing native Vulkan physical device %p device %p graphics queue family index %d",
1209 importDev.physDev, importDev.dev, importDev.gfxQueueFamilyIdx);
1210 rhi = QRhi::create(backend, &rhiParams, flags, &importDev);
1211 } else if (customDevD->type == QQuickGraphicsDevicePrivate::Type::PhysicalDevice) {
1212 QRhiVulkanNativeHandles importDev;
1213 importDev.physDev = reinterpret_cast<VkPhysicalDevice>(customDevD->u.physicalDevice.physicalDevice);
1214 qCDebug(QSG_LOG_INFO, "Using existing native Vulkan physical device %p", importDev.physDev);
1215 rhi = QRhi::create(backend, &rhiParams, flags, &importDev);
1216 } else {
1217 rhi = QRhi::create(backend, &rhiParams, flags, nullptr, rhiAdapter);
1218 }
1219 }
1220#else
1221 if (backend == QRhi::Vulkan)
1222 qWarning("Vulkan was requested for Qt Quick, but this build of Qt has no Vulkan support.");
1223#endif
1224#ifdef Q_OS_WIN
1225 if (backend == QRhi::D3D11) {
1226 QRhiD3D11InitParams rhiParams;
1227 rhiParams.enableDebugLayer = debugLayer;
1228 if (customDevD->type == QQuickGraphicsDevicePrivate::Type::DeviceAndContext) {
1229 QRhiD3D11NativeHandles importDev;
1230 importDev.dev = customDevD->u.deviceAndContext.device;
1231 importDev.context = customDevD->u.deviceAndContext.context;
1232 qCDebug(QSG_LOG_INFO, "Using existing native D3D11 device %p and context %p",
1233 importDev.dev, importDev.context);
1234 rhi = QRhi::create(backend, &rhiParams, flags, &importDev);
1235 } else if (customDevD->type == QQuickGraphicsDevicePrivate::Type::Adapter) {
1236 QRhiD3D11NativeHandles importDev;
1237 importDev.adapterLuidLow = customDevD->u.adapter.luidLow;
1238 importDev.adapterLuidHigh = customDevD->u.adapter.luidHigh;
1239 importDev.featureLevel = customDevD->u.adapter.featureLevel;
1240 qCDebug(QSG_LOG_INFO, "Using D3D11 adapter LUID %u, %d and feature level %d",
1241 importDev.adapterLuidLow, importDev.adapterLuidHigh, importDev.featureLevel);
1242 rhi = QRhi::create(backend, &rhiParams, flags, &importDev);
1243 } else {
1244 rhi = QRhi::create(backend, &rhiParams, flags, nullptr, rhiAdapter);
1245 if (!rhi && attemptReinitWithSwRastUponFail() && !flags.testFlag(QRhi::PreferSoftwareRenderer) && !rhiAdapter) {
1246 qCDebug(QSG_LOG_INFO, "Failed to create a D3D device with default settings; "
1247 "attempting to get a software rasterizer backed device instead");
1248 flags |= QRhi::PreferSoftwareRenderer;
1249 rhi = QRhi::create(backend, &rhiParams, flags);
1250 }
1251 }
1252 } else if (backend == QRhi::D3D12) {
1253 QRhiD3D12InitParams rhiParams;
1254 rhiParams.enableDebugLayer = debugLayer;
1255 if (customDevD->type == QQuickGraphicsDevicePrivate::Type::DeviceAndContext) {
1256 QRhiD3D12NativeHandles importDev;
1257 importDev.dev = customDevD->u.deviceAndContext.device;
1258 qCDebug(QSG_LOG_INFO, "Using existing native D3D12 device %p", importDev.dev);
1259 rhi = QRhi::create(backend, &rhiParams, flags, &importDev);
1260 } else if (customDevD->type == QQuickGraphicsDevicePrivate::Type::Adapter) {
1261 QRhiD3D12NativeHandles importDev;
1262 importDev.adapterLuidLow = customDevD->u.adapter.luidLow;
1263 importDev.adapterLuidHigh = customDevD->u.adapter.luidHigh;
1264 importDev.minimumFeatureLevel = customDevD->u.adapter.featureLevel;
1265 qCDebug(QSG_LOG_INFO, "Using D3D12 adapter LUID %u, %d and minimum feature level %d",
1266 importDev.adapterLuidLow, importDev.adapterLuidHigh, importDev.minimumFeatureLevel);
1267 rhi = QRhi::create(backend, &rhiParams, flags, &importDev);
1268 } else {
1269 QRhiAdapter *rhiAdapter = customDevD->type == QQuickGraphicsDevicePrivate::Type::RhiAdapter ? customDevD->u.rhiAdapter : nullptr;
1270 rhi = QRhi::create(backend, &rhiParams, flags, nullptr, rhiAdapter);
1271 if (!rhi && attemptReinitWithSwRastUponFail() && !flags.testFlag(QRhi::PreferSoftwareRenderer) && !rhiAdapter) {
1272 qCDebug(QSG_LOG_INFO, "Failed to create a D3D device with default settings; "
1273 "attempting to get a software rasterizer backed device instead");
1274 flags |= QRhi::PreferSoftwareRenderer;
1275 rhi = QRhi::create(backend, &rhiParams, flags);
1276 }
1277 }
1278 }
1279#endif
1280#if QT_CONFIG(metal)
1281 if (backend == QRhi::Metal) {
1282 QRhiMetalInitParams rhiParams;
1283 if (customDevD->type == QQuickGraphicsDevicePrivate::Type::DeviceAndCommandQueue) {
1284 QRhiMetalNativeHandles importDev;
1285 importDev.dev = (MTLDevice *) customDevD->u.deviceAndCommandQueue.device;
1286 importDev.cmdQueue = (MTLCommandQueue *) customDevD->u.deviceAndCommandQueue.cmdQueue;
1287 qCDebug(QSG_LOG_INFO, "Using existing native Metal device %p and command queue %p",
1288 importDev.dev, importDev.cmdQueue);
1289 rhi = QRhi::create(backend, &rhiParams, flags, &importDev);
1290 } else {
1291 rhi = QRhi::create(backend, &rhiParams, flags, nullptr, rhiAdapter);
1292 }
1293 }
1294#endif
1295
1296 if (rhi) {
1297 qCDebug(QSG_LOG_INFO, "Created QRhi %p for window %p", rhi, window);
1298 preparePipelineCache(rhi, window);
1299 } else {
1300 qWarning("Failed to create RHI (backend %d)", backend);
1301 }
1302
1303 return { rhi, true };
1304}
1305
1306void QSGRhiSupport::destroyRhi(QRhi *rhi, const QQuickGraphicsConfiguration &config)
1307{
1308 if (!rhi)
1309 return;
1310
1311 if (!rhi->isDeviceLost())
1312 finalizePipelineCache(rhi, config);
1313
1314 delete rhi;
1315}
1316
1317QImage QSGRhiSupport::grabAndBlockInCurrentFrame(QRhi *rhi, QRhiCommandBuffer *cb, QRhiTexture *src)
1318{
1319 Q_ASSERT(rhi->isRecordingFrame());
1320
1321 QRhiReadbackResult result;
1322 QRhiReadbackDescription readbackDesc(src); // null src == read from swapchain backbuffer
1323 QRhiResourceUpdateBatch *resourceUpdates = rhi->nextResourceUpdateBatch();
1324 resourceUpdates->readBackTexture(readbackDesc, &result);
1325
1326 cb->resourceUpdate(resourceUpdates);
1327 rhi->finish(); // make sure the readback has finished, stall the pipeline if needed
1328
1329 // May be RGBA or BGRA. Plus premultiplied alpha.
1330 QImage::Format imageFormat;
1331 if (result.format == QRhiTexture::BGRA8) {
1332#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
1333 imageFormat = QImage::Format_ARGB32_Premultiplied;
1334#else
1335 imageFormat = QImage::Format_RGBA8888_Premultiplied;
1336 // ### and should swap too
1337#endif
1338 } else {
1339 imageFormat = QImage::Format_RGBA8888_Premultiplied;
1340 }
1341
1342 const uchar *p = reinterpret_cast<const uchar *>(result.data.constData());
1343 const QImage img(p, result.pixelSize.width(), result.pixelSize.height(), imageFormat);
1344
1345 if (rhi->isYUpInFramebuffer())
1346 return img.flipped();
1347
1348 return img.copy();
1349}
1350
1351QImage QSGRhiSupport::grabOffscreen(QQuickWindow *window)
1352{
1353 // Set up and then tear down the entire rendering infrastructure. This
1354 // function is called on the gui/main thread - but that's alright because
1355 // there is no onscreen rendering initialized at this point (so no render
1356 // thread for instance).
1357
1358 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
1359 // It is expected that window is not using QQuickRenderControl, i.e. it is
1360 // a normal QQuickWindow that just happens to be not exposed.
1361 Q_ASSERT(!wd->renderControl);
1362
1363 QScopedPointer<QOffscreenSurface> offscreenSurface(maybeCreateOffscreenSurface(window));
1364 RhiCreateResult rhiResult = createRhi(window, offscreenSurface.data());
1365 if (!rhiResult.rhi) {
1366 qWarning("Failed to initialize QRhi for offscreen readback");
1367 return QImage();
1368 }
1369 std::unique_ptr<QRhi> rhiOwner(rhiResult.rhi);
1370 QRhi *rhi = rhiResult.own ? rhiOwner.get() : rhiOwner.release();
1371
1372 const QSize pixelSize = window->size() * window->devicePixelRatio();
1373 QScopedPointer<QRhiTexture> texture(rhi->newTexture(QRhiTexture::RGBA8, pixelSize, 1,
1374 QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource));
1375 if (!texture->create()) {
1376 qWarning("Failed to build texture for offscreen readback");
1377 return QImage();
1378 }
1379 QScopedPointer<QRhiRenderBuffer> depthStencil(rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, pixelSize, 1));
1380 if (!depthStencil->create()) {
1381 qWarning("Failed to create depth/stencil buffer for offscreen readback");
1382 return QImage();
1383 }
1384 QRhiTextureRenderTargetDescription rtDesc(texture.data());
1385 rtDesc.setDepthStencilBuffer(depthStencil.data());
1386 QScopedPointer<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget(rtDesc));
1387 QScopedPointer<QRhiRenderPassDescriptor> rpDesc(rt->newCompatibleRenderPassDescriptor());
1388 rt->setRenderPassDescriptor(rpDesc.data());
1389 if (!rt->create()) {
1390 qWarning("Failed to build render target for offscreen readback");
1391 return QImage();
1392 }
1393
1394 wd->rhi = rhi;
1395
1396 QSGDefaultRenderContext::InitParams params;
1397 params.rhi = rhi;
1398 params.sampleCount = 1;
1399 params.initialSurfacePixelSize = pixelSize;
1400 params.maybeSurface = window;
1401 wd->context->initialize(&params);
1402
1403 // There was no rendercontrol which means a custom render target
1404 // should not be set either. Set our own, temporarily.
1405 auto renderTarget = QQuickRenderTarget::fromRhiRenderTarget(rt.data());
1406 // Pass on the scale factor. No need to use effectiveDevicePixelRatio(),
1407 // because there is no rendercontrol anyway.
1408 renderTarget.setDevicePixelRatio(window->devicePixelRatio());
1409 window->setRenderTarget(renderTarget);
1410
1411 QRhiCommandBuffer *cb = nullptr;
1412 if (rhi->beginOffscreenFrame(&cb) != QRhi::FrameOpSuccess) {
1413 qWarning("Failed to start recording the frame for offscreen readback");
1414 return QImage();
1415 }
1416
1417 wd->setCustomCommandBuffer(cb);
1418 wd->polishItems();
1419 wd->syncSceneGraph();
1420 wd->renderSceneGraph();
1421 wd->setCustomCommandBuffer(nullptr);
1422
1423 QImage image = grabAndBlockInCurrentFrame(rhi, cb, texture.data());
1424 rhi->endOffscreenFrame();
1425
1426 image.setDevicePixelRatio(window->devicePixelRatio());
1427 wd->cleanupNodesOnShutdown();
1428 wd->context->invalidate();
1429
1430 window->setRenderTarget(QQuickRenderTarget());
1431 wd->rhi = nullptr;
1432
1433 return image;
1434}
1435
1436#ifdef Q_OS_WEBOS
1437QImage QSGRhiSupport::grabOffscreenForProtectedContent(QQuickWindow *window)
1438{
1439 // If a context is created for protected content, grabbing GPU
1440 // resources are restricted. For the case, normal context
1441 // and surface are needed to allow CPU access.
1442 // So dummy offscreen window is used here
1443 // This function is called in rendering thread.
1444
1445 QScopedPointer<QQuickWindow> offscreenWindow;
1446 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
1447 // It is expected that window is not using QQuickRenderControl, i.e. it is
1448 // a normal QQuickWindow that just happens to be not exposed.
1449 Q_ASSERT(!wd->renderControl);
1450
1451 // If context and surface are created for protected content,
1452 // CPU can't read the frame resources. So normal context and surface are needed.
1453 if (window->requestedFormat().testOption(QSurfaceFormat::ProtectedContent)) {
1454 QSurfaceFormat surfaceFormat = window->requestedFormat();
1455 surfaceFormat.setOption(QSurfaceFormat::ProtectedContent, false);
1456 offscreenWindow.reset(new QQuickWindow());
1457 offscreenWindow->setFormat(surfaceFormat);
1458 }
1459
1460 QScopedPointer<QOffscreenSurface> offscreenSurface(maybeCreateOffscreenSurface(window));
1461 RhiCreateResult rhiResult = createRhi(offscreenWindow.data() ? offscreenWindow.data() : window, offscreenSurface.data());
1462 if (!rhiResult.rhi) {
1463 qWarning("Failed to initialize QRhi for offscreen readback");
1464 return QImage();
1465 }
1466 QScopedPointer<QRhi> rhiOwner(rhiResult.rhi);
1467 QRhi *rhi = rhiResult.own ? rhiOwner.data() : rhiOwner.take();
1468
1469 const QSize pixelSize = window->size() * window->devicePixelRatio();
1470 QScopedPointer<QRhiTexture> texture(rhi->newTexture(QRhiTexture::RGBA8, pixelSize, 1,
1471 QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource));
1472 if (!texture->create()) {
1473 qWarning("Failed to build texture for offscreen readback");
1474 return QImage();
1475 }
1476 QScopedPointer<QRhiRenderBuffer> depthStencil(rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, pixelSize, 1));
1477 if (!depthStencil->create()) {
1478 qWarning("Failed to create depth/stencil buffer for offscreen readback");
1479 return QImage();
1480 }
1481 QRhiTextureRenderTargetDescription rtDesc(texture.data());
1482 rtDesc.setDepthStencilBuffer(depthStencil.data());
1483 QScopedPointer<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget(rtDesc));
1484 QScopedPointer<QRhiRenderPassDescriptor> rpDesc(rt->newCompatibleRenderPassDescriptor());
1485 rt->setRenderPassDescriptor(rpDesc.data());
1486 if (!rt->create()) {
1487 qWarning("Failed to build render target for offscreen readback");
1488 return QImage();
1489 }
1490
1491 // Backup the original Rhi
1492 QRhi *currentRhi = wd->rhi;
1493 wd->rhi = rhi;
1494
1495 QSGDefaultRenderContext::InitParams params;
1496 params.rhi = rhi;
1497 params.sampleCount = 1;
1498 params.initialSurfacePixelSize = pixelSize;
1499 params.maybeSurface = window;
1500 wd->context->initialize(&params);
1501
1502 // Backup the original RenderTarget
1503 QQuickRenderTarget currentRenderTarget = window->renderTarget();
1504 // There was no rendercontrol which means a custom render target
1505 // should not be set either. Set our own, temporarily.
1506 window->setRenderTarget(QQuickRenderTarget::fromRhiRenderTarget(rt.data()));
1507
1508 QRhiCommandBuffer *cb = nullptr;
1509 if (rhi->beginOffscreenFrame(&cb) != QRhi::FrameOpSuccess) {
1510 qWarning("Failed to start recording the frame for offscreen readback");
1511 return QImage();
1512 }
1513
1514 wd->setCustomCommandBuffer(cb);
1515 wd->polishItems();
1516 wd->syncSceneGraph();
1517 wd->renderSceneGraph();
1518 wd->setCustomCommandBuffer(nullptr);
1519
1520 QImage image = grabAndBlockInCurrentFrame(rhi, cb, texture.data());
1521 rhi->endOffscreenFrame();
1522
1523 image.setDevicePixelRatio(window->devicePixelRatio());
1524
1525 // Called from gui/main thread on no onscreen rendering initialized
1526 if (!currentRhi) {
1527 wd->cleanupNodesOnShutdown();
1528 wd->context->invalidate();
1529
1530 window->setRenderTarget(QQuickRenderTarget());
1531 wd->rhi = nullptr;
1532 } else {
1533 // Called from rendering thread for protected content
1534 // Restore to original Rhi, RenderTarget and Context
1535 window->setRenderTarget(currentRenderTarget);
1536 wd->rhi = currentRhi;
1537 params.rhi = currentRhi;
1538 wd->context->initialize(&params);
1539 }
1540
1541 return image;
1542}
1543#endif
1544
1545void QSGRhiSupport::applySwapChainFormat(QRhiSwapChain *scWithWindowSet, QQuickWindow *window)
1546{
1547 Q_ASSERT(scWithWindowSet->window() == window);
1548
1549 QRhiSwapChain::Format swapChainFormat = QRhiSwapChain::SDR;
1550
1551 QByteArray hdrRequest = qgetenv("QSG_RHI_HDR");
1552 if (hdrRequest.isEmpty())
1553 hdrRequest = window->property("_qt_sg_hdr_format").toByteArray();
1554
1555 if (!hdrRequest.isEmpty()) {
1556 hdrRequest = hdrRequest.toLower();
1557 if (hdrRequest == QByteArrayLiteral("scrgb") || hdrRequest == QByteArrayLiteral("extendedsrgblinear"))
1558 swapChainFormat = QRhiSwapChain::HDRExtendedSrgbLinear;
1559 else if (hdrRequest == QByteArrayLiteral("hdr10"))
1560 swapChainFormat = QRhiSwapChain::HDR10;
1561 else if (hdrRequest == QByteArrayLiteral("p3"))
1562 swapChainFormat = QRhiSwapChain::HDRExtendedDisplayP3Linear;
1563 }
1564
1565 const char *fmtStr = "unknown";
1566 switch (swapChainFormat) {
1567 case QRhiSwapChain::SDR:
1568 fmtStr = "SDR";
1569 break;
1570 case QRhiSwapChain::HDRExtendedSrgbLinear:
1571 fmtStr = "scRGB";
1572 break;
1573 case QRhiSwapChain::HDR10:
1574 fmtStr = "HDR10";
1575 break;
1576 case QRhiSwapChain::HDRExtendedDisplayP3Linear:
1577 fmtStr = "Extended Linear Display P3";
1578 break;
1579 default:
1580 break;
1581 }
1582
1583 if (!scWithWindowSet->isFormatSupported(swapChainFormat)) {
1584 if (swapChainFormat != QRhiSwapChain::SDR) {
1585 qCDebug(QSG_LOG_INFO, "Requested a %s swapchain but it is reported to be unsupported with the current display(s). "
1586 "In multi-screen configurations make sure the window is located on a HDR-enabled screen. "
1587 "Request ignored, using SDR swapchain.", fmtStr);
1588 }
1589 return;
1590 }
1591
1592 scWithWindowSet->setFormat(swapChainFormat);
1593
1594 if (swapChainFormat != QRhiSwapChain::SDR) {
1595 qCDebug(QSG_LOG_INFO, "Creating %s swapchain", fmtStr);
1596 qCDebug(QSG_LOG_INFO) << "HDR output info:" << scWithWindowSet->hdrInfo();
1597 }
1598}
1599
1600QRhiTexture::Format QSGRhiSupport::toRhiTextureFormat(uint nativeFormat, QRhiTexture::Flags *flags) const
1601{
1602 switch (m_rhiBackend) {
1603#if QT_CONFIG(vulkan)
1604 case QRhi::Vulkan:
1605 return toRhiTextureFormatFromVulkan(nativeFormat, flags);
1606#endif
1607#if QT_CONFIG(opengl)
1608 case QRhi::OpenGLES2:
1609 Q_UNUSED(flags);
1610 return toRhiTextureFormatFromGL(nativeFormat, flags);
1611#endif
1612#ifdef Q_OS_WIN
1613 case QRhi::D3D11:
1614 case QRhi::D3D12:
1615 return toRhiTextureFormatFromDXGI(nativeFormat, flags);
1616#endif
1617#if QT_CONFIG(metal)
1618 case QRhi::Metal:
1619 return toRhiTextureFormatFromMetal(nativeFormat, flags);
1620#endif
1621 default:
1622 return QRhiTexture::UnknownFormat;
1623 }
1624 Q_UNUSED(nativeFormat)
1625 Q_UNUSED(flags)
1626}
1627
1628bool QSGRhiSupport::attemptReinitWithSwRastUponFail() const
1629{
1630 const QRhi::Implementation backend = rhiBackend();
1631
1632 // On Windows it makes sense to retry using a software adapter whenever
1633 // device creation or swapchain creation fails, as WARP is usually available
1634 // (built in to the OS) and is good quality. This helps a lot in particular
1635 // when running in a VM that cripples proper 3D graphics.
1636 if (backend == QRhi::D3D11 || backend == QRhi::D3D12)
1637 return true;
1638
1639 return false;
1640}
1641
1642QT_END_NAMESPACE
static bool isAutomaticPipelineCacheLoadSkippedForWindow(Qt::WindowFlags wflags)
static QString automaticPipelineCacheDir()
static bool ensureWritableDir(const QString &name)
static QString automaticPipelineCacheFileName(QRhi *rhi)
static bool isAutomaticPipelineCacheSaveSkippedForWindow(Qt::WindowFlags wflags)