1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
|
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquickgraphicsconfiguration_p.h"
#include <QCoreApplication>
#include <rhi/qrhi.h>
QT_BEGIN_NAMESPACE
/*!
\class QQuickGraphicsConfiguration
\since 6.0
\inmodule QtQuick
\brief QQuickGraphicsConfiguration controls lower level graphics settings
for the QQuickWindow.
The QQuickGraphicsConfiguration class is a container for low-level graphics
settings that can affect how the underlying graphics API, such as Vulkan,
is initialized by the Qt Quick scene graph. It can also control certain
aspects of the scene graph renderer.
\note Setting a QQuickGraphicsConfiguration on a QQuickWindow must happen
early enough, before the scene graph is initialized for the first time for
that window. With on-screen windows this means the call must be done before
invoking show() on the QQuickWindow or QQuickView. With QQuickRenderControl
the configuration must be finalized before calling
\l{QQuickRenderControl::initialize()}{initialize()}.
\section1 Configuration for External Rendering Engines or XR APIs
When constructing and showing a QQuickWindow that uses Vulkan to render, a
Vulkan instance (\c VkInstance), a physical device (\c VkPhysicalDevice), a
device (\c VkDevice) and associated objects (queues, pools) are initialized
through the Vulkan API. The same is mostly true when using
QQuickRenderControl to redirect the rendering into a custom render target,
such as a texture. While QVulkanInstance construction is under the
application's control then, the initialization of other graphics objects
happen the same way in QQuickRenderControl::initialize() as with an
on-screen QQuickWindow.
For the majority of applications no additional configuration is needed
because Qt Quick provides reasonable defaults for many low-level graphics
settings, for example which device extensions to enable.
This will not alway be sufficient, however. In advanced use cases, when
integrating direct Vulkan or other graphics API content, or when
integrating with an external 3D or VR engine, such as, OpenXR, the
application will want to specify its own set of settings when it comes to
details, such as which device extensions to enable.
That is what this class enables. It allows specifying, for example, a list
of device extensions that is then picked up by the scene graph when using
Vulkan, or graphics APIs where the concept is applicable. Where some
concepts are not applicable, the related settings are simply ignored.
Examples of functions in this category are setDeviceExtensions() and
preferredInstanceExtensions(). The latter is useful when the application
manages its own \l QVulkanInstance which is then associated with the
QQuickWindow via \l QWindow::setVulkanInstance().
\section1 Qt Quick Scene Graph Renderer Configuration
Another class of settings are related to the scene graph's renderer. In
some cases applications may want to control certain behavior,such as using
the depth buffer when rendering 2D content. In Qt 5 such settings were
either not controllable at all, or were managed through environment
variables. In Qt 6, QQuickGraphicsConfiguration provides a new home for
these settings, while keeping support for the legacy environment variables,
where applicable.
An example in this category is setDepthBufferFor2D().
\section1 Graphics Device Configuration
When the graphics instance and device objects (for example, the VkInstance
and VkDevice with Vulkan, the ID3D11Device with Direct 3D, etc.) are
created by Qt when initializing a QQuickWindow, there are settings which
applications or libraries will want to control under certain circumstances.
Before Qt 6.5, some of such settings were available to control via
environment variables. For example, \c QSG_RHI_DEBUG_LAYER or \c
QSG_RHI_PREFER_SOFTWARE_RENDERER. These are still available and continue to
function as before. QQuickGraphicsConfiguration provides C++ setters in
addition.
For example, the following main() function opens a QQuickView while
specifying that the Vulkan validation or Direct3D debug layer should be
enabled:
\code
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQuickGraphicsConfiguration config;
config.setDebugLayer(true);
QQuickView *view = new QQuickView;
view->setGraphicsConfiguration(config);
view->setSource(QUrl::fromLocalFile("myqmlfile.qml"));
view->show();
return app.exec();
}
\endcode
\section1 Pipeline Cache Save and Load
Qt Quick supports storing the graphics/compute pipeline cache to disk, and
reloading it in subsequent runs of an application. What exactly the
pipeline cache contains, how lookups work, and what exactly gets
accelerated all depend on the Qt RHI backend and the underlying native
graphics API that is used at run time. Different 3D APIs have different
concepts when it comes to shaders, programs, and pipeline state objects,
and corresponding cache mechanisms. The high level pipeline cache concept
here abstracts all this to storing and retrieving a single binary blob to
and from a file.
\note Storing the cache on disk can lead to improvements, sometimes
significant, in subsequent runs of the application.
When the same shader program and/or pipeline state is encountered as in a
previous run, a number of operations are likely skipped, leading to faster
shader and material initialization times, which means startup may become
faster and lags and "janks" during rendering may be reduced or avoided.
When running with a graphics API where retrieving and reloading the
pipeline cache (or shader/program binaries) is not applicable or not
supported, attempting to use a file to save and load the cache has no
effect.
\note In many cases the retrieved data is dependent on and tied to the
graphics driver (and possibly the exact version of it). Qt performs the
necessary checks automatically, by storing additional metadata in the
pipeline cache file. If the data in the file does not match the graphics
device and driver version at run time, the contents will be ignored
transparently to the application. It is therefore safe to reference a cache
that was generated on another device or driver.
There are exceptions to the driver dependency problem, most notably Direct
3D 11, where the "pipeline cache" is used only to store the results of
runtime HLSL->DXBC compilation and is therefore device and vendor
independent.
In some cases it may be desirable to improve the very first run of the
application, by "pre-seeding" the cache. This is possible by shipping the
cache file saved from a previous run, and referencing it on another machine
or device. This way, the application or device has the shader
programs/pipelines that have been encountered before in the run that saved
the cache file available already during its first run. Shipping and
deploying the cache file only makes sense if the device and graphics
drivers are the same on the target system, otherwise the cache file is
ignored if the device or driver version does not match (with the exception
of D3D11), as described above.
Once the cache contents is loaded, there is still a chance that the
application builds graphics and compute pipelines that have not been
encountered in previous runs. In this cases the cache is grown, with the
pipelines / shader programs added to it. If the application also chooses to
save the contents (perhaps to the same file even), then both the old and
new pipelines will get stored. Loading from and saving to the same file in
every run allows an ever growing cache that stores all encountered
pipelines and shader programs.
In practice the Qt pipeline cache can be expected to map to the following
native graphics API features:
\list
\li Vulkan -
\l{https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipelineCache.html}{VkPipelineCache}
- Saving the pipeline cache effectively stores the blob retrieved from
\l{https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetPipelineCacheData.html}{vkGetPipelineCacheData},
with additional metadata to safely identify the device and the driver
since the pipeline cache blob is dependent on the exact driver.
\li Metal -
\l{https://developer.apple.com/documentation/metal/mtlbinaryarchive?language=objc}{MTLBinaryArchive}
- With pipeline cache saving enabled, Qt stores all render and compute
pipelines encountered into an MTLBinaryArchive. Saving the pipeline cache
stores the blob retrieved from the archive, with additional metadata to
identify the device. \b{Note:} currently MTLBinaryArchive usage is disabled
on macOS and iOS due to various issues on some hardware and OS versions.
\li OpenGL - There is no native concept of pipelines, the "pipeline cache"
stores a collection of program binaries retrieved via
\l{https://registry.khronos.org/OpenGL-Refpages/gl4/html/glGetProgramBinary.xhtml}{glGetProgramBinary}.
The program binaries are packaged into a single blob, with additional
metadata to identify the device, driver, and its version that the binaries
were retrieved from. Persistent caching of program binaries is not new in
Qt: Qt 5 already had similar functionality in QOpenGLShaderProgram, see
\l{QOpenGLShaderProgram::}{addCacheableShaderFromSourceCode()}
for example. In fact that mechanism is always active in Qt 6 as well when
using Qt Quick with OpenGL. However, when using the new, graphics API
independent pipeline cache abstraction provided here, the Qt 5 era program
binary cache gets automatically disabled, since the same content is
packaged in the "pipeline cache" now.
\li Direct 3D 11 - There is no native concept of pipelines or retrieving
binaries for the second phase compilation (where the vendor independent,
intermediate bytecode is compiled into the device specific instruction
set). Drivers will typically employ their own caching system on that level.
Instead, the Qt Quick "pipeline cache" is used to speed up cases where the
shaders contain HLSL source code that needs to be compiled into the
intermediate bytecode format first. This can present significant
performance improvements in application and libraries that compose shader
code at run time, because in subsequent runs the potentially expensive,
uncached calls to
\l{https://docs.microsoft.com/en-us/windows/win32/api/d3dcompiler/nf-d3dcompiler-d3dcompile}{D3DCompile()}
can be avoided if the bytecode is already available for the encountered
HLSL shader. A good example is Qt Quick 3D, where the runtime-generated
shaders for materials imply having to deal with HLSL source code. Saving
and reloading the Qt Quick pipeline cache can therefore bring considerable
improvements in scenes with one or more \l{View3D} items in
them. A counterexample may be Qt Quick itself: as most built-in shaders for
2D content ship with DirectX bytecode generated at build time, the cache is
not going to present any significant improvements.
\endlist
All this is independent from the shader processing performed by the
\l [QtShaderTools]{Qt Shader Tools} module and its command-line tools such
as \c qsb. As an example, take Vulkan. Having the Vulkan-compatible GLSL
source code compiled to SPIR-V either at offline or build time (directly
via qsb or CMake) is good, because the expensive compilation from source
form is avoided at run time. SPIR-V is however a vendor-independent
intermediate format. At runtime, when constructing graphics or compute
pipelines, there is likely another round of compilation happening, this
time from the intermediate format to the vendor-specific instruction set of
the GPU (and this may be dependent on certain state in the graphics
pipeline and the render targets as well). The pipeline cache helps with
this latter phase.
\note Many graphics API implementation employ their own persistent disk
cache transparently to the applications. Using the pipeline cache feature
of Qt Quick will likely provide improvements in this case, but the gains
might be smaller.
Call setPipelineCacheSaveFile() and setPipelineCacheLoadFile() to control
which files a QQuickWindow or QQuickView saves and loads the pipeline cache
to/from.
To get an idea of the effects of enabling disk storage of the pipeline
cache, enable the most important scenegraph and graphics logs either via
the environment variable \c{QSG_INFO=1}, or both the
\c{qt.scenegraph.general} and \c{qt.rhi.general} logging categories. When
closing the QQuickWindow, there is log message like the following:
\badcode
Total time spent on pipeline creation during the lifetime of the QRhi was 123 ms
\endcode
This gives an approximate idea of how much time was spent in graphics and
compute pipeline creation (which may include various stages of shader
compilation) during the lifetime of the window.
When loading from a pipeline cache file is enabled, this is confirmed with
a message:
\badcode
Attempting to seed pipeline cache from 'filename'
\endcode
Similarly, to check if saving of the cache is successfully enabled, look
for a message such as this:
\badcode
Writing pipeline cache contents to 'filename'
\endcode
\section1 The Automatic Pipeline Cache
When no filename is provided for save and load, the automatic pipeline
caching strategy is used. This involves storing data to the
application-specific cache location of the system (\l
QStandardPaths::CacheLocation).
This can be disabled by one of the following means:
\list
\li Set the application attribute Qt::AA_DisableShaderDiskCache.
(completely disables the automatic storage)
\li Set the environment variable QT_DISABLE_SHADER_DISK_CACHE to a non-zero
value. (completely disables the automatic storage)
\li Set the environment variable QSG_RHI_DISABLE_SHADER_DISK_CACHE to a
non-zero value. (completely disables the automatic storage)
\li Call setAutomaticPiplineCache() with the enable argument set to false.
(completely disables the automatic storage)
\li Set a filename by calling setPipelineCacheLoadFile(). (only disables
loading from the automatic storage, prefering the specified file instead)
\li Set a filename by calling setPipelineCacheSaveFile(). (only disables
writing to the automatic storage, prefering the specified file instead)
\endlist
The first two are existing mechanisms that are used since Qt 5.9 to control
the OpenGL program binary cache. For compatibility and familiarity the same
attribute and environment variable are supported for Qt 6's enhanced
pipeline cache.
The automatic pipeline cache uses a single file per application, but a
different one for each RHI backend (graphics API). This means that changing
to another graphics API in the next run of the application will not lead to
losing the pipeline cache generated in the previous run. Applications with
multiple QQuickWindow instances shown simultaneously may however not
benefit 100% since the automatic cache can only store the data collected
from one RHI object at a time. (and with the default \c threaded render
loop each window has its own RHI as rendering operates independently on
dedicated threads). To fully benefit from the disk cache in application
with multiple windows, prefer setting the filename explicitly, per-window
via setPipelineCacheSaveFile().
\sa QQuickWindow::setGraphicsConfiguration(), QQuickWindow, QQuickRenderControl
*/
/*!
Constructs a default QQuickGraphicsConfiguration that does not specify any
additional settings for the scene graph to take into account.
*/
QQuickGraphicsConfiguration::QQuickGraphicsConfiguration()
: d(new QQuickGraphicsConfigurationPrivate)
{
}
/*!
\internal
*/
void QQuickGraphicsConfiguration::detach()
{
qAtomicDetach(d);
}
/*!
\internal
*/
QQuickGraphicsConfiguration::QQuickGraphicsConfiguration(const QQuickGraphicsConfiguration &other)
: d(other.d)
{
d->ref.ref();
}
/*!
\internal
*/
QQuickGraphicsConfiguration &QQuickGraphicsConfiguration::operator=(const QQuickGraphicsConfiguration &other)
{
qAtomicAssign(d, other.d);
return *this;
}
/*!
Destructor.
*/
QQuickGraphicsConfiguration::~QQuickGraphicsConfiguration()
{
if (!d->ref.deref())
delete d;
}
/*!
\return the list of Vulkan instance extensions Qt Quick prefers to
have enabled on the VkInstance.
In most cases Qt Quick is responsible for creating a QVulkanInstance. This
function is not relevant then. On the other hand, when using
QQuickRenderControl in combination with Vulkan-based rendering, it is the
application's responsibility to create a QVulkanInstance and associate it
with the (offscreen) QQuickWindow. In this case, it is expected that the
application queries the list of instance extensions to enable, and passes
them to QVulkanInstance::setExtensions() before calling
QVulkanInstance::create().
\since 6.1
*/
QByteArrayList QQuickGraphicsConfiguration::preferredInstanceExtensions()
{
#if QT_CONFIG(vulkan)
return QRhiVulkanInitParams::preferredInstanceExtensions();
#else
return {};
#endif
}
/*!
Sets the list of additional \a extensions to enable on the graphics device
(such as, the \c VkDevice).
When rendering with a graphics API where the concept is not applicable, \a
extensions will be ignored.
\note The list specifies additional, extra extensions. Qt Quick always
enables extensions that are required by the scene graph.
*/
void QQuickGraphicsConfiguration::setDeviceExtensions(const QByteArrayList &extensions)
{
if (d->deviceExtensions != extensions) {
detach();
d->deviceExtensions = extensions;
}
}
/*!
\return the list of the requested additional device extensions.
*/
QByteArrayList QQuickGraphicsConfiguration::deviceExtensions() const
{
return d->deviceExtensions;
}
/*!
Sets the usage of depth buffer for 2D content to \a enable. When disabled,
the Qt Quick scene graph never writes into the depth buffer.
By default the value is true, unless the \c{QSG_NO_DEPTH_BUFFER}
environment variable is set.
The default value of true is the most optimal setting for the vast majority
of scenes. Disabling depth buffer usage reduces the efficiency of the scene
graph's batching.
There are cases however, when allowing the 2D content write to the depth
buffer is not ideal. Consider a 3D scene as an "overlay" on top the 2D
scene, rendered via Qt Quick 3D using a \l View3D with
\l{View3D::renderMode}{renderMode} set to \c Overlay. In this case, having
the depth buffer filled by 2D content can cause unexpected results. This is
because the way the 2D scene graph renderer generates and handles depth
values is not necessarily compatible with how a 3D scene works. This may end
up in depth value clashes, collisions, and unexpected depth test
failures. Therefore, the robust approach here is to call this function with
\a enable set to false, and disable depth buffer writes for the 2D content
in the QQuickWindow.
\note This flag is not fully identical to setting the
\c{QSG_NO_DEPTH_BUFFER} environment variable. This flag does not control the
depth-stencil buffers' presence. It is rather relevant for the rendering
pipeline. To force not having depth/stencil attachments at all, set
\c{QSG_NO_DEPTH_BUFFER} and \c{QSG_NO_STENCIL_BUFFER}. Be aware however
that such a QQuickWindow, and any Item layers in it, may then become
incompatible with items, such as View3D with certain operating modes,
because 3D content requires a depth buffer. Calling this function is always
safe, but can mean that resources, such as depth buffers, are created even
though they are not actively used.
*/
void QQuickGraphicsConfiguration::setDepthBufferFor2D(bool enable)
{
if (d->flags.testFlag(QQuickGraphicsConfigurationPrivate::UseDepthBufferFor2D) != enable) {
detach();
d->flags.setFlag(QQuickGraphicsConfigurationPrivate::UseDepthBufferFor2D, enable);
}
}
/*!
\return true if depth buffer usage is enabled for 2D content.
By default the value is true, unless the \c{QSG_NO_DEPTH_BUFFER}
environment variable is set.
*/
bool QQuickGraphicsConfiguration::isDepthBufferEnabledFor2D() const
{
return d->flags.testFlag(QQuickGraphicsConfigurationPrivate::UseDepthBufferFor2D);
}
/*!
Enables the graphics API implementation's debug or validation layers, if
available.
In practice this is supported with Vulkan and Direct 3D 11, assuming the
necessary support (validation layers, Windows SDK) is installed and
available at runtime. When \a enable is true, Qt will attempt to enable the
standard validation layer on the VkInstance, or set
\c{D3D11_CREATE_DEVICE_DEBUG} on the graphics device.
For Metal on \macos, set the environment variable
\c{METAL_DEVICE_WRAPPER_TYPE=1} instead before launching the application.
Calling this function with \a enable set to true is equivalent to setting
the environment variable \c{QSG_RHI_DEBUG_LAYER} to a non-zero value.
The default value is false.
\note Enabling debug or validation layers may have a non-insignificant
performance impact. Shipping applications to production with the flag
enabled is strongly discouraged.
\note Be aware that due to differences in the design of the underlying
graphics APIs, this setting cannot always be a per-QQuickWindow setting,
even though each QQuickWindow has their own QQuickGraphicsConfiguration.
With Vulkan in particular, the instance object (VkInstance) is only created
once and then used by all windows in the application. Therefore, enabling
the validation layer is something that affects all windows. This also means
that attempting to enable validation via a window that only gets shown after
some other windows have already started rendering has no effect with Vulkan.
Other APIs, such as D3D11, expose the debug layer concept as a per-device
(ID3D11Device) setting, and so it is controlled on a true per-window basis
(assuming the scenegraph render loop uses a dedicated graphics
device/context for each QQuickWindow).
\since 6.5
\sa isDebugLayerEnabled()
*/
void QQuickGraphicsConfiguration::setDebugLayer(bool enable)
{
if (d->flags.testFlag(QQuickGraphicsConfigurationPrivate::EnableDebugLayer) != enable) {
detach();
d->flags.setFlag(QQuickGraphicsConfigurationPrivate::EnableDebugLayer, enable);
}
}
/*!
\return true if the debug/validation layers are to be enabled.
By default the value is false.
\sa setDebugLayer()
*/
bool QQuickGraphicsConfiguration::isDebugLayerEnabled() const
{
return d->flags.testFlag(QQuickGraphicsConfigurationPrivate::EnableDebugLayer);
}
/*!
Where applicable, \a enable controls inserting debug markers and object
names into the graphics command stream.
Some frameworks, such as Qt Quick 3D, have the ability to annotate the
graphics objects they create (buffers, textures) with names and also
indicate the beginning and end of render passes in the command buffer. These
are then visible in frame captures made with tools like
\l{https://renderdoc.org/}{RenderDoc} or XCode.
Graphics APIs where this can be expected to be supported are Vulkan (if
VK_EXT_debug_utils is available), Direct 3D 11, and Metal.
Calling this function with \a enable set to true is equivalent to setting
the environment variable \c{QSG_RHI_PROFILE} to a non-zero
value.
The default value is false.
\note Enabling debug markers may have a performance impact. Shipping
applications to production with the flag enabled is not recommended.
\since 6.5
\sa isDebugMarkersEnabled()
*/
void QQuickGraphicsConfiguration::setDebugMarkers(bool enable)
{
if (d->flags.testFlag(QQuickGraphicsConfigurationPrivate::EnableDebugMarkers) != enable) {
detach();
d->flags.setFlag(QQuickGraphicsConfigurationPrivate::EnableDebugMarkers, enable);
}
}
/*!
\return true if debug markers are enabled.
By default the value is false.
\sa setDebugMarkers()
*/
bool QQuickGraphicsConfiguration::isDebugMarkersEnabled() const
{
return d->flags.testFlag(QQuickGraphicsConfigurationPrivate::EnableDebugMarkers);
}
/*!
When enabled, GPU timing data is collected from command buffers on
platforms and 3D APIs where this is supported. This data is then printed in
the renderer logs that can be enabled via \c{QSG_RENDER_TIMING} environment
variable or logging categories such as \c{qt.scenegraph.time.renderloop},
and may also be made visible to other modules, such as Qt Quick 3D's
\l DebugView item.
By default this is disabled, because collecting the data may involve
additional work, such as inserting timestamp queries in the command stream,
depending on the underlying graphics API. To enable, either call this
function with \a enable set to true, or set the \c{QSG_RHI_PROFILE}
environment variable to a non-zero value.
Graphics APIs where this can be expected to be supported are Direct 3D 11,
Direct 3D 12, Vulkan (as long as the underlying Vulkan implementation
supports timestamp queries), Metal, and OpenGL with a core or compatibility
profile context for version 3.3 or newer. Timestamps are not supported with
OpenGL ES.
\since 6.6
\sa timestampsEnabled(), setDebugMarkers()
*/
void QQuickGraphicsConfiguration::setTimestamps(bool enable)
{
if (d->flags.testFlag(QQuickGraphicsConfigurationPrivate::EnableTimestamps) != enable) {
detach();
d->flags.setFlag(QQuickGraphicsConfigurationPrivate::EnableTimestamps, enable);
}
}
/*!
\return true if GPU timing collection is enabled.
By default the value is false.
\since 6.6
\sa setTimestamps()
*/
bool QQuickGraphicsConfiguration::timestampsEnabled() const
{
return d->flags.testFlag(QQuickGraphicsConfigurationPrivate::EnableTimestamps);
}
/*!
Requests choosing an adapter or physical device that uses software-based
rasterization. Applicable only when the underlying API has support for
enumerating adapters (for example, Direct 3D or Vulkan), and is ignored
otherwise.
If the graphics API implementation has no such graphics adapter or physical
device available, the request is ignored. With Direct 3D it can be expected
that a
\l{https://docs.microsoft.com/en-us/windows/win32/direct3darticles/directx-warp}{WARP}-based
rasterizer is always available. With Vulkan, the flag only has an effect if
Mesa's \c lavapipe, or some other physical device reporting
\c{VK_PHYSICAL_DEVICE_TYPE_CPU} is available.
Calling this function with \a enable set to true is equivalent to setting
the environment variable \c{QSG_RHI_PREFER_SOFTWARE_RENDERER} to a non-zero
value.
The default value is false.
\since 6.5
\sa prefersSoftwareDevice()
*/
void QQuickGraphicsConfiguration::setPreferSoftwareDevice(bool enable)
{
if (d->flags.testFlag(QQuickGraphicsConfigurationPrivate::PreferSoftwareDevice) != enable) {
detach();
d->flags.setFlag(QQuickGraphicsConfigurationPrivate::PreferSoftwareDevice, enable);
}
}
/*!
\return true if a software rasterizer-based graphics device is prioritized.
By default the value is false.
\sa setPreferSoftwareDevice()
*/
bool QQuickGraphicsConfiguration::prefersSoftwareDevice() const
{
return d->flags.testFlag(QQuickGraphicsConfigurationPrivate::PreferSoftwareDevice);
}
/*!
Changes the usage of the automatic pipeline cache based on \a enable.
The default value is true, unless certain application attributes or
environment variables are set. See \l{The Automatic Pipeline Cache} for
more information.
\since 6.5
\sa isAutomaticPipelineCacheEnabled()
*/
void QQuickGraphicsConfiguration::setAutomaticPipelineCache(bool enable)
{
if (d->flags.testFlag(QQuickGraphicsConfigurationPrivate::AutoPipelineCache) != enable) {
detach();
d->flags.setFlag(QQuickGraphicsConfigurationPrivate::AutoPipelineCache, enable);
}
}
/*!
\return true if the automatic pipeline cache is enabled.
By default this is true, unless certain application attributes or
environment variables are set. See \l{The Automatic Pipeline Cache} for
more information.
\since 6.5
\sa setAutomaticPipelineCache()
*/
bool QQuickGraphicsConfiguration::isAutomaticPipelineCacheEnabled() const
{
return d->flags.testFlag(QQuickGraphicsConfigurationPrivate::AutoPipelineCache);
}
/*!
Sets the \a filename where the QQuickWindow is expected to store its
graphics/compute pipeline cache contents. The default value is empty, which
means pipeline cache loading is disabled.
See \l{Pipeline Cache Save and Load} for a discussion on pipeline caches.
Persistently storing the pipeline cache can lead to performance
improvements in future runs of the application since expensive shader
compilation and pipeline construction steps may be avoided.
If and when the writing of the file happens is not defined. It will likely
happen at some point when tearing down the scenegraph due to closing the
window. Therefore, applications should not assume availability of the file
until the QQuickWindow is fully destructed. QQuickGraphicsConfiguration
only stores the filename, it does not perform any actual I/O and graphics
operations on its own.
When running with a graphics API where retrieving the pipeline cache (or
shader/program binaries) is not applicable or not supported, calling this
function has no effect.
Calling this function is mostly equivalent to setting the environment
variable \c{QSG_RHI_PIPELINE_CACHE_SAVE} to \a filename, with one important
difference: this function controls the pipeline cache storage for the
associated QQuickWindow only. Applications with multiple QQuickWindow or
QQuickView instances can therefore store and later reload the cache contents
via files dedicated to each window. The environment variable does not allow
this.
\since 6.5
\sa pipelineCacheLoadFile(), setPipelineCacheSaveFile()
*/
void QQuickGraphicsConfiguration::setPipelineCacheSaveFile(const QString &filename)
{
if (d->pipelineCacheSaveFile != filename) {
detach();
d->pipelineCacheSaveFile = filename;
}
}
/*!
\return the currently set filename for storing the pipeline cache.
By default the value is an empty string.
*/
QString QQuickGraphicsConfiguration::pipelineCacheSaveFile() const
{
return d->pipelineCacheSaveFile;
}
/*!
Sets the \a filename where the QQuickWindow is expected to load the initial
contents of its graphics/compute pipeline cache from. The default value is
empty, which means pipeline cache loading is disabled.
See \l{Pipeline Cache Save and Load} for a discussion on pipeline caches.
Persistently storing the pipeline cache can lead to performance
improvements in future runs of the application since expensive shader
compilation and pipeline construction steps may be avoided.
If and when the loading of the file's contents happens is not defined, apart
from that it will happen at some point during the initialization of the
scenegraph of the QQuickWindow. Therefore, the file must continue to exist
after calling this function. QQuickGraphicsConfiguration only stores the
filename, it cannot perform any actual I/O and graphics operations on its
own. The real work is going to happen later on, possibly on another thread.
When running with a graphics API where retrieving and reloading the
pipeline cache (or shader/program binaries) is not applicable or not
supported, calling this function has no effect.
Calling this function is mostly equivalent to setting the environment
variable \c{QSG_RHI_PIPELINE_CACHE_LOAD} to \a filename, with one important
difference: this function controls the pipeline cache storage for the
associated QQuickWindow only. Applications with multiple QQuickWindow or
QQuickView instances can therefore store and later reload the cache contents
via files dedicated to each window. The environment variable does not allow
this.
\note If the data in the file does not match the graphics device and driver
version at run time, the contents will be ignored, transparently to the
application. This applies to a number of graphics APIs, and the necessary
checks are taken care of by Qt. There are exceptions, most notably Direct
3D 11, where the "pipeline cache" is used only to store the results of
runtime HLSL->DXBC compilation and is therefore device and vendor
independent.
\since 6.5
\sa pipelineCacheLoadFile(), setPipelineCacheSaveFile()
*/
void QQuickGraphicsConfiguration::setPipelineCacheLoadFile(const QString &filename)
{
if (d->pipelineCacheLoadFile != filename) {
detach();
d->pipelineCacheLoadFile = filename;
}
}
/*!
\return the currently set filename for loading the pipeline cache.
By default the value is an empty string.
*/
QString QQuickGraphicsConfiguration::pipelineCacheLoadFile() const
{
return d->pipelineCacheLoadFile;
}
QQuickGraphicsConfigurationPrivate::QQuickGraphicsConfigurationPrivate()
: ref(1)
{
// Defaults based on env.vars. NB! many of these variables are documented
// and should be considered (semi-)public API. Changing the env.var. names
// is therefore not allowed.
flags = {};
static const bool useDepthBufferFor2D = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER");
if (useDepthBufferFor2D)
flags |= UseDepthBufferFor2D;
static const bool enableDebugLayer = qEnvironmentVariableIntValue("QSG_RHI_DEBUG_LAYER");
if (enableDebugLayer)
flags |= EnableDebugLayer;
static const bool enableProfilingRelated = qEnvironmentVariableIntValue("QSG_RHI_PROFILE");
if (enableProfilingRelated)
flags |= EnableDebugMarkers | EnableTimestamps;
static const bool preferSoftwareDevice = qEnvironmentVariableIntValue("QSG_RHI_PREFER_SOFTWARE_RENDERER");
if (preferSoftwareDevice)
flags |= PreferSoftwareDevice;
// here take the existing QOpenGL disk cache attribute and env.var. into account as well
static const bool autoPipelineCache = !QCoreApplication::instance()->testAttribute(Qt::AA_DisableShaderDiskCache)
&& !qEnvironmentVariableIntValue("QT_DISABLE_SHADER_DISK_CACHE")
&& !qEnvironmentVariableIntValue("QSG_RHI_DISABLE_DISK_CACHE");
if (autoPipelineCache)
flags |= AutoPipelineCache;
static const QString pipelineCacheSaveFileEnv = qEnvironmentVariable("QSG_RHI_PIPELINE_CACHE_SAVE");
pipelineCacheSaveFile = pipelineCacheSaveFileEnv;
static const QString pipelineCacheLoadFileEnv = qEnvironmentVariable("QSG_RHI_PIPELINE_CACHE_LOAD");
pipelineCacheLoadFile = pipelineCacheLoadFileEnv;
}
QQuickGraphicsConfigurationPrivate::QQuickGraphicsConfigurationPrivate(const QQuickGraphicsConfigurationPrivate &other)
: ref(1),
deviceExtensions(other.deviceExtensions),
flags(other.flags),
pipelineCacheSaveFile(other.pipelineCacheSaveFile),
pipelineCacheLoadFile(other.pipelineCacheLoadFile)
{
}
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QQuickGraphicsConfiguration &config)
{
QDebugStateSaver saver(dbg);
const QQuickGraphicsConfigurationPrivate *cd = QQuickGraphicsConfigurationPrivate::get(&config);
dbg.nospace() << "QQuickGraphicsConfiguration("
<< "flags=0x" << Qt::hex << cd->flags << Qt::dec
<< " flag-isDepthBufferEnabledFor2D=" << config.isDepthBufferEnabledFor2D()
<< " flag-isDebugLayerEnabled=" << config.isDebugLayerEnabled()
<< " flag-isDebugMarkersEnabled=" << config.isDebugMarkersEnabled()
<< " flag-prefersSoftwareDevice=" << config.prefersSoftwareDevice()
<< " flag-isAutomaticPipelineCacheEnabled=" << config.isAutomaticPipelineCacheEnabled()
<< " pipelineCacheSaveFile=" << cd->pipelineCacheSaveFile
<< " piplineCacheLoadFile=" << cd->pipelineCacheLoadFile
<< " extra-device-extension-requests=" << cd->deviceExtensions
<< ')';
return dbg;
}
#endif // QT_NO_DEBUG_STREAM
QT_END_NAMESPACE
|