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