diff options
Diffstat (limited to 'src/quick/items/qquickgraphicsconfiguration.cpp')
-rw-r--r-- | src/quick/items/qquickgraphicsconfiguration.cpp | 879 |
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 |