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

source code of qtdeclarative/src/quick/items/qquickgraphicsconfiguration.cpp