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 | |
8 | QT_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 | */ |
325 | QQuickGraphicsConfiguration::QQuickGraphicsConfiguration() |
326 | : d(new QQuickGraphicsConfigurationPrivate) |
327 | { |
328 | } |
329 | |
330 | /*! |
331 | \internal |
332 | */ |
333 | void QQuickGraphicsConfiguration::detach() |
334 | { |
335 | qAtomicDetach(d); |
336 | } |
337 | |
338 | /*! |
339 | \internal |
340 | */ |
341 | QQuickGraphicsConfiguration::QQuickGraphicsConfiguration(const QQuickGraphicsConfiguration &other) |
342 | : d(other.d) |
343 | { |
344 | d->ref.ref(); |
345 | } |
346 | |
347 | /*! |
348 | \internal |
349 | */ |
350 | QQuickGraphicsConfiguration &QQuickGraphicsConfiguration::operator=(const QQuickGraphicsConfiguration &other) |
351 | { |
352 | qAtomicAssign(d, x: other.d); |
353 | return *this; |
354 | } |
355 | |
356 | /*! |
357 | Destructor. |
358 | */ |
359 | QQuickGraphicsConfiguration::~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 | */ |
380 | QByteArrayList 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 | */ |
399 | void 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 | */ |
410 | QByteArrayList 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 | */ |
449 | void 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 | */ |
463 | bool 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 | */ |
507 | void 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 | */ |
522 | bool 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 | */ |
553 | void 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 | */ |
568 | bool 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 | */ |
594 | void 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 | */ |
610 | bool 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 | */ |
639 | void 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 | */ |
654 | bool QQuickGraphicsConfiguration::() 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 | */ |
670 | void 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 | */ |
689 | bool 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 | */ |
728 | void 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 | */ |
741 | QString 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 | */ |
788 | void 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 | */ |
801 | QString QQuickGraphicsConfiguration::pipelineCacheLoadFile() const |
802 | { |
803 | return d->pipelineCacheLoadFile; |
804 | } |
805 | |
806 | QQuickGraphicsConfigurationPrivate::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 | |
845 | QQuickGraphicsConfigurationPrivate::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 |
855 | QDebug 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 | |
874 | QT_END_NAMESPACE |
875 | |