aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items/qquickgraphicsconfiguration.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/items/qquickgraphicsconfiguration.cpp')
-rw-r--r--src/quick/items/qquickgraphicsconfiguration.cpp879
1 files changed, 879 insertions, 0 deletions
diff --git a/src/quick/items/qquickgraphicsconfiguration.cpp b/src/quick/items/qquickgraphicsconfiguration.cpp
new file mode 100644
index 0000000000..1cfa160e18
--- /dev/null
+++ b/src/quick/items/qquickgraphicsconfiguration.cpp
@@ -0,0 +1,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