aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/scenegraph/qsgrhisupport.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/scenegraph/qsgrhisupport.cpp')
-rw-r--r--src/quick/scenegraph/qsgrhisupport.cpp1317
1 files changed, 1022 insertions, 295 deletions
diff --git a/src/quick/scenegraph/qsgrhisupport.cpp b/src/quick/scenegraph/qsgrhisupport.cpp
index 88bbf77e40..45c183a5f8 100644
--- a/src/quick/scenegraph/qsgrhisupport.cpp
+++ b/src/quick/scenegraph/qsgrhisupport.cpp
@@ -1,116 +1,37 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQuick module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 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 "qsgrhisupport_p.h"
#include "qsgcontext_p.h"
-# include "qsgdefaultrendercontext_p.h"
+#include "qsgdefaultrendercontext_p.h"
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuick/private/qquickwindow_p.h>
#include <QtGui/qwindow.h>
+
#if QT_CONFIG(vulkan)
-#include <QtGui/qvulkaninstance.h>
+#include <QtGui/private/qvulkandefaultinstance_p.h>
#endif
#include <QOperatingSystemVersion>
+#include <QLockFile>
+#include <QSaveFile>
+#include <QStandardPaths>
+#include <QDir>
+#include <QFileInfo>
+#include <QSysInfo>
#include <QOffscreenSurface>
-#include <QtQml/private/qqmlengine_p.h>
-
-QT_BEGIN_NAMESPACE
-
-#if QT_CONFIG(vulkan)
-QVulkanInstance *s_vulkanInstance = nullptr;
+#ifdef Q_OS_WIN
+#include <dxgiformat.h>
#endif
-QVulkanInstance *QSGRhiSupport::defaultVulkanInstance()
-{
-#if QT_CONFIG(vulkan)
- QSGRhiSupport *inst = QSGRhiSupport::instance();
- if (!inst->isRhiEnabled() || inst->rhiBackend() != QRhi::Vulkan)
- return nullptr;
-
- if (!s_vulkanInstance) {
- s_vulkanInstance = new QVulkanInstance;
- if (inst->isDebugLayerRequested()) {
-#ifndef Q_OS_ANDROID
- s_vulkanInstance->setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation");
-#else
- s_vulkanInstance->setLayers(QByteArrayList()
- << "VK_LAYER_GOOGLE_threading"
- << "VK_LAYER_LUNARG_parameter_validation"
- << "VK_LAYER_LUNARG_object_tracker"
- << "VK_LAYER_LUNARG_core_validation"
- << "VK_LAYER_LUNARG_image"
- << "VK_LAYER_LUNARG_swapchain"
- << "VK_LAYER_GOOGLE_unique_objects");
-#endif
- }
- s_vulkanInstance->setExtensions(QRhiVulkanInitParams::preferredInstanceExtensions());
- if (!s_vulkanInstance->create()) {
- qWarning("Failed to create Vulkan instance");
- delete s_vulkanInstance;
- s_vulkanInstance = nullptr;
- }
- }
- return s_vulkanInstance;
-#else
- return nullptr;
-#endif
-}
+#include <memory>
-void QSGRhiSupport::cleanupDefaultVulkanInstance()
-{
-#if QT_CONFIG(vulkan)
- delete s_vulkanInstance;
- s_vulkanInstance = nullptr;
-#endif
-}
+QT_BEGIN_NAMESPACE
QSGRhiSupport::QSGRhiSupport()
- : m_settingsApplied(false),
- m_enableRhi(false),
- m_debugLayer(false),
- m_profile(false),
- m_shaderEffectDebug(false),
- m_preferSoftwareRenderer(false)
{
}
@@ -128,21 +49,23 @@ void QSGRhiSupport::applySettings()
if (m_requested.valid) {
// explicit rhi backend request from C++ (e.g. via QQuickWindow)
- m_enableRhi = true;
switch (m_requested.api) {
- case QSGRendererInterface::OpenGLRhi:
+ case QSGRendererInterface::OpenGL:
m_rhiBackend = QRhi::OpenGLES2;
break;
- case QSGRendererInterface::Direct3D11Rhi:
+ case QSGRendererInterface::Direct3D11:
m_rhiBackend = QRhi::D3D11;
break;
- case QSGRendererInterface::VulkanRhi:
+ case QSGRendererInterface::Direct3D12:
+ m_rhiBackend = QRhi::D3D12;
+ break;
+ case QSGRendererInterface::Vulkan:
m_rhiBackend = QRhi::Vulkan;
break;
- case QSGRendererInterface::MetalRhi:
+ case QSGRendererInterface::Metal:
m_rhiBackend = QRhi::Metal;
break;
- case QSGRendererInterface::NullRhi:
+ case QSGRendererInterface::Null:
m_rhiBackend = QRhi::Null;
break;
default:
@@ -150,10 +73,6 @@ void QSGRhiSupport::applySettings()
break;
}
} else {
-
- // There is no other way in Qt 6. The direct OpenGL rendering path of Qt 5 has been removed.
- m_enableRhi = true;
-
// check env.vars., fall back to platform-specific defaults when backend is not set
const QByteArray rhiBackend = qgetenv("QSG_RHI_BACKEND");
if (rhiBackend == QByteArrayLiteral("gl")
@@ -163,6 +82,8 @@ void QSGRhiSupport::applySettings()
m_rhiBackend = QRhi::OpenGLES2;
} else if (rhiBackend == QByteArrayLiteral("d3d11") || rhiBackend == QByteArrayLiteral("d3d")) {
m_rhiBackend = QRhi::D3D11;
+ } else if (rhiBackend == QByteArrayLiteral("d3d12")) {
+ m_rhiBackend = QRhi::D3D12;
} else if (rhiBackend == QByteArrayLiteral("vulkan")) {
m_rhiBackend = QRhi::Vulkan;
} else if (rhiBackend == QByteArrayLiteral("metal")) {
@@ -176,7 +97,7 @@ void QSGRhiSupport::applySettings()
}
#if defined(Q_OS_WIN)
m_rhiBackend = QRhi::D3D11;
-#elif defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+#elif QT_CONFIG(metal)
m_rhiBackend = QRhi::Metal;
#elif QT_CONFIG(opengl)
m_rhiBackend = QRhi::OpenGLES2;
@@ -186,78 +107,33 @@ void QSGRhiSupport::applySettings()
// Now that we established our initial choice, we may want to opt
// for another backend under certain special circumstances.
- if (m_enableRhi) // guard because this may do actual graphics calls on some platforms
- adjustToPlatformQuirks();
+ adjustToPlatformQuirks();
}
}
- Q_ASSERT(m_enableRhi); // cannot be anything else in Qt 6
-
// At this point the RHI backend is fixed, it cannot be changed once we
// return from this function. This is because things like the QWindow
// (QQuickWindow) may depend on the graphics API as well (surfaceType
// f.ex.), and all that is based on what we report from here. So further
// adjustments are not possible (or, at minimum, not safe and portable).
-
- // validation layers (Vulkan) or debug layer (D3D)
- m_debugLayer = uint(qEnvironmentVariableIntValue("QSG_RHI_DEBUG_LAYER"));
-
- // EnableProfiling + DebugMarkers
- m_profile = uint(qEnvironmentVariableIntValue("QSG_RHI_PROFILE"));
-
- // EnablePipelineCacheDataSave
- m_pipelineCacheSave = qEnvironmentVariable("QSG_RHI_PIPELINE_CACHE_SAVE");
-
- m_pipelineCacheLoad = qEnvironmentVariable("QSG_RHI_PIPELINE_CACHE_LOAD");
-
- m_shaderEffectDebug = uint(qEnvironmentVariableIntValue("QSG_RHI_SHADEREFFECT_DEBUG"));
-
- m_preferSoftwareRenderer = uint(qEnvironmentVariableIntValue("QSG_RHI_PREFER_SOFTWARE_RENDERER"));
-
- m_killDeviceFrameCount = qEnvironmentVariableIntValue("QSG_RHI_SIMULATE_DEVICE_LOSS");
- if (m_killDeviceFrameCount > 0 && m_rhiBackend == QRhi::D3D11)
- qDebug("Graphics device will be reset every %d frames", m_killDeviceFrameCount);
-
- const QString backendName = rhiBackendName();
- qCDebug(QSG_LOG_INFO,
- "Using QRhi with backend %s\n"
- " Graphics API debug/validation layers: %d\n"
- " QRhi profiling and debug markers: %d\n"
- " Shader/pipeline cache collection: %d",
- qPrintable(backendName), m_debugLayer, m_profile, !m_pipelineCacheSave.isEmpty());
- if (m_preferSoftwareRenderer)
- qCDebug(QSG_LOG_INFO, "Prioritizing software renderers");
}
void QSGRhiSupport::adjustToPlatformQuirks()
{
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
-
- // ### For now just create a throwaway QRhi instance. This will be replaced
- // by a more lightweight way, once a helper function is added gui/rhi.
-
+#if QT_CONFIG(metal)
// A macOS VM may not have Metal support at all. We have to decide at this
// point, it will be too late afterwards, and the only way is to see if
// MTLCreateSystemDefaultDevice succeeds.
if (m_rhiBackend == QRhi::Metal) {
QRhiMetalInitParams rhiParams;
- QRhi *tempRhi = QRhi::create(m_rhiBackend, &rhiParams, {});
- if (!tempRhi) {
+ if (!QRhi::probe(m_rhiBackend, &rhiParams)) {
m_rhiBackend = QRhi::OpenGLES2;
qCDebug(QSG_LOG_INFO, "Metal does not seem to be supported. Falling back to OpenGL.");
- } else {
- delete tempRhi;
}
}
#endif
}
-QSGRhiSupport *QSGRhiSupport::staticInst()
-{
- static QSGRhiSupport inst;
- return &inst;
-}
-
void QSGRhiSupport::checkEnvQSgInfo()
{
// For compatibility with 5.3 and earlier's QSG_INFO environment variables
@@ -265,25 +141,510 @@ void QSGRhiSupport::checkEnvQSgInfo()
const_cast<QLoggingCategory &>(QSG_LOG_INFO()).setEnabled(QtDebugMsg, true);
}
+
+#if QT_CONFIG(opengl)
+#ifndef GL_BGRA
+#define GL_BGRA 0x80E1
+#endif
+
+#ifndef GL_R8
+#define GL_R8 0x8229
+#endif
+
+#ifndef GL_RG8
+#define GL_RG8 0x822B
+#endif
+
+#ifndef GL_RG
+#define GL_RG 0x8227
+#endif
+
+#ifndef GL_R16
+#define GL_R16 0x822A
+#endif
+
+#ifndef GL_RG16
+#define GL_RG16 0x822C
+#endif
+
+#ifndef GL_RED
+#define GL_RED 0x1903
+#endif
+
+#ifndef GL_RGBA8
+#define GL_RGBA8 0x8058
+#endif
+
+#ifndef GL_RGBA32F
+#define GL_RGBA32F 0x8814
+#endif
+
+#ifndef GL_RGBA16F
+#define GL_RGBA16F 0x881A
+#endif
+
+#ifndef GL_R16F
+#define GL_R16F 0x822D
+#endif
+
+#ifndef GL_R32F
+#define GL_R32F 0x822E
+#endif
+
+#ifndef GL_DEPTH_COMPONENT16
+#define GL_DEPTH_COMPONENT16 0x81A5
+#endif
+
+#ifndef GL_DEPTH_COMPONENT24
+#define GL_DEPTH_COMPONENT24 0x81A6
+#endif
+
+#ifndef GL_DEPTH_COMPONENT32F
+#define GL_DEPTH_COMPONENT32F 0x8CAC
+#endif
+
+#ifndef GL_DEPTH24_STENCIL8
+#define GL_DEPTH24_STENCIL8 0x88F0
+#endif
+
+#ifndef GL_DEPTH_STENCIL
+#define GL_DEPTH_STENCIL 0x84F9
+#endif
+
+#ifndef GL_RGB10_A2
+#define GL_RGB10_A2 0x8059
+#endif
+
+#ifndef GL_SRGB_ALPHA
+#define GL_SRGB_ALPHA 0x8C42
+#endif
+
+#ifndef GL_SRGB8_ALPHA8
+#define GL_SRGB8_ALPHA8 0x8C43
+#endif
+
+QRhiTexture::Format QSGRhiSupport::toRhiTextureFormatFromGL(uint format, QRhiTexture::Flags *flags)
+{
+ bool sRGB = false;
+ auto rhiFormat = QRhiTexture::UnknownFormat;
+ switch (format) {
+ case GL_SRGB_ALPHA:
+ case GL_SRGB8_ALPHA8:
+ sRGB = true;
+ Q_FALLTHROUGH();
+ case GL_RGBA:
+ case GL_RGBA8:
+ case 0:
+ rhiFormat = QRhiTexture::RGBA8;
+ break;
+ case GL_BGRA:
+ rhiFormat = QRhiTexture::BGRA8;
+ break;
+ case GL_R16:
+ rhiFormat = QRhiTexture::R16;
+ break;
+ case GL_RG16:
+ rhiFormat = QRhiTexture::RG16;
+ break;
+ case GL_RED:
+ Q_FALLTHROUGH();
+ case GL_R8:
+ rhiFormat = QRhiTexture::R8;
+ break;
+ case GL_RG:
+ Q_FALLTHROUGH();
+ case GL_RG8:
+ rhiFormat = QRhiTexture::RG8;
+ break;
+ case GL_ALPHA:
+ rhiFormat = QRhiTexture::RED_OR_ALPHA8;
+ break;
+ case GL_RGBA16F:
+ rhiFormat = QRhiTexture::RGBA16F;
+ break;
+ case GL_RGBA32F:
+ rhiFormat = QRhiTexture::RGBA32F;
+ break;
+ case GL_R16F:
+ rhiFormat = QRhiTexture::R16F;
+ break;
+ case GL_R32F:
+ rhiFormat = QRhiTexture::R32F;
+ break;
+ case GL_RGB10_A2:
+ rhiFormat = QRhiTexture::RGB10A2;
+ break;
+ case GL_DEPTH_COMPONENT:
+ Q_FALLTHROUGH();
+ case GL_DEPTH_COMPONENT16:
+ rhiFormat = QRhiTexture::D16;
+ break;
+ case GL_DEPTH_COMPONENT24:
+ rhiFormat = QRhiTexture::D24;
+ break;
+ case GL_DEPTH_STENCIL:
+ Q_FALLTHROUGH();
+ case GL_DEPTH24_STENCIL8:
+ rhiFormat = QRhiTexture::D24S8;
+ break;
+ case GL_DEPTH_COMPONENT32F:
+ rhiFormat = QRhiTexture::D32F;
+ break;
+ default:
+ qWarning("GL format %d is not supported", format);
+ break;
+ }
+ if (sRGB)
+ (*flags) |=(QRhiTexture::sRGB);
+ return rhiFormat;
+}
+#endif
+
+#if QT_CONFIG(vulkan)
+QRhiTexture::Format QSGRhiSupport::toRhiTextureFormatFromVulkan(uint format, QRhiTexture::Flags *flags)
+{
+ auto rhiFormat = QRhiTexture::UnknownFormat;
+ bool sRGB = false;
+ switch (format) {
+ case VK_FORMAT_R8G8B8A8_SRGB:
+ sRGB = true;
+ Q_FALLTHROUGH();
+ case VK_FORMAT_R8G8B8A8_UNORM:
+ case VK_FORMAT_UNDEFINED:
+ rhiFormat = QRhiTexture::RGBA8;
+ break;
+ case VK_FORMAT_B8G8R8A8_SRGB:
+ sRGB = true;
+ Q_FALLTHROUGH();
+ case VK_FORMAT_B8G8R8A8_UNORM:
+ rhiFormat = QRhiTexture::BGRA8;
+ break;
+ case VK_FORMAT_R8_SRGB:
+ sRGB = true;
+ Q_FALLTHROUGH();
+ case VK_FORMAT_R8_UNORM:
+ rhiFormat = QRhiTexture::R8;
+ break;
+ case VK_FORMAT_R8G8_SRGB:
+ sRGB = true;
+ Q_FALLTHROUGH();
+ case VK_FORMAT_R8G8_UNORM:
+ rhiFormat = QRhiTexture::RG8;
+ break;
+ case VK_FORMAT_R16_UNORM:
+ rhiFormat = QRhiTexture::R16;
+ break;
+ case VK_FORMAT_R16G16_UNORM:
+ rhiFormat = QRhiTexture::RG16;
+ break;
+ case VK_FORMAT_R16G16B16A16_SFLOAT:
+ rhiFormat = QRhiTexture::RGBA16F;
+ break;
+ case VK_FORMAT_R32G32B32A32_SFLOAT:
+ rhiFormat = QRhiTexture::RGBA32F;
+ break;
+ case VK_FORMAT_R16_SFLOAT:
+ rhiFormat = QRhiTexture::R16F;
+ break;
+ case VK_FORMAT_R32_SFLOAT:
+ rhiFormat = QRhiTexture::R32F;
+ break;
+ case VK_FORMAT_A2B10G10R10_UNORM_PACK32: // intentionally
+ Q_FALLTHROUGH();
+ case VK_FORMAT_A2R10G10B10_UNORM_PACK32:
+ rhiFormat = QRhiTexture::RGB10A2;
+ break;
+ case VK_FORMAT_D16_UNORM:
+ rhiFormat = QRhiTexture::D16;
+ break;
+ case VK_FORMAT_X8_D24_UNORM_PACK32:
+ rhiFormat = QRhiTexture::D24;
+ break;
+ case VK_FORMAT_D24_UNORM_S8_UINT:
+ rhiFormat = QRhiTexture::D24S8;
+ break;
+ case VK_FORMAT_D32_SFLOAT:
+ rhiFormat = QRhiTexture::D32F;
+ break;
+ case VK_FORMAT_BC1_RGB_SRGB_BLOCK:
+ sRGB = true;
+ Q_FALLTHROUGH();
+ case VK_FORMAT_BC1_RGB_UNORM_BLOCK:
+ rhiFormat = QRhiTexture::BC1;
+ break;
+ case VK_FORMAT_BC2_SRGB_BLOCK:
+ sRGB = true;
+ Q_FALLTHROUGH();
+ case VK_FORMAT_BC2_UNORM_BLOCK:
+ rhiFormat = QRhiTexture::BC2;
+ break;
+ case VK_FORMAT_BC3_SRGB_BLOCK:
+ sRGB = true;
+ Q_FALLTHROUGH();
+ case VK_FORMAT_BC3_UNORM_BLOCK:
+ rhiFormat = QRhiTexture::BC3;
+ break;
+ case VK_FORMAT_BC4_UNORM_BLOCK:
+ rhiFormat = QRhiTexture::BC4;
+ break;
+ case VK_FORMAT_BC5_UNORM_BLOCK:
+ rhiFormat = QRhiTexture::BC5;
+ break;
+ case VK_FORMAT_BC6H_UFLOAT_BLOCK:
+ rhiFormat = QRhiTexture::BC6H;
+ break;
+ case VK_FORMAT_BC7_SRGB_BLOCK:
+ sRGB = true;
+ Q_FALLTHROUGH();
+ case VK_FORMAT_BC7_UNORM_BLOCK:
+ rhiFormat = QRhiTexture::BC7;
+ break;
+ case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
+ sRGB = true;
+ Q_FALLTHROUGH();
+ case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
+ rhiFormat = QRhiTexture::ETC2_RGB8;
+ break;
+ case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
+ sRGB = true;
+ Q_FALLTHROUGH();
+ case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
+ rhiFormat = QRhiTexture::ETC2_RGB8A1;
+ break;
+ case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
+ sRGB = true;
+ Q_FALLTHROUGH();
+ case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
+ rhiFormat = QRhiTexture::ETC2_RGBA8;
+ break;
+ case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
+ sRGB = true;
+ Q_FALLTHROUGH();
+ case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
+ rhiFormat = QRhiTexture::ASTC_4x4;
+ break;
+ case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:
+ sRGB = true;
+ Q_FALLTHROUGH();
+ case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
+ rhiFormat = QRhiTexture::ASTC_5x4;
+ break;
+ case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:
+ sRGB = true;
+ Q_FALLTHROUGH();
+ case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
+ rhiFormat = QRhiTexture::ASTC_5x5;
+ break;
+ case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:
+ sRGB = true;
+ Q_FALLTHROUGH();
+ case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
+ rhiFormat = QRhiTexture::ASTC_6x5;
+ break;
+ case VK_FORMAT_ASTC_6x6_SRGB_BLOCK:
+ sRGB = true;
+ Q_FALLTHROUGH();
+ case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:
+ rhiFormat = QRhiTexture::ASTC_6x6;
+ break;
+ case VK_FORMAT_ASTC_8x5_SRGB_BLOCK:
+ sRGB = true;
+ Q_FALLTHROUGH();
+ case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:
+ rhiFormat = QRhiTexture::ASTC_8x5;
+ break;
+ case VK_FORMAT_ASTC_8x6_SRGB_BLOCK:
+ sRGB = true;
+ Q_FALLTHROUGH();
+ case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:
+ rhiFormat = QRhiTexture::ASTC_8x6;
+ break;
+ case VK_FORMAT_ASTC_8x8_SRGB_BLOCK:
+ sRGB = true;
+ Q_FALLTHROUGH();
+ case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:
+ rhiFormat = QRhiTexture::ASTC_8x8;
+ break;
+ case VK_FORMAT_ASTC_10x5_SRGB_BLOCK:
+ sRGB = true;
+ Q_FALLTHROUGH();
+ case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:
+ rhiFormat = QRhiTexture::ASTC_10x5;
+ break;
+ case VK_FORMAT_ASTC_10x6_SRGB_BLOCK:
+ sRGB = true;
+ Q_FALLTHROUGH();
+ case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:
+ rhiFormat = QRhiTexture::ASTC_10x6;
+ break;
+ case VK_FORMAT_ASTC_10x8_SRGB_BLOCK:
+ sRGB = true;
+ Q_FALLTHROUGH();
+ case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:
+ rhiFormat = QRhiTexture::ASTC_10x8;
+ break;
+ case VK_FORMAT_ASTC_10x10_SRGB_BLOCK:
+ sRGB = true;
+ Q_FALLTHROUGH();
+ case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:
+ rhiFormat = QRhiTexture::ASTC_10x10;
+ break;
+ case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
+ sRGB = true;
+ Q_FALLTHROUGH();
+ case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:
+ rhiFormat = QRhiTexture::ASTC_12x10;
+ break;
+ case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
+ sRGB = true;
+ Q_FALLTHROUGH();
+ case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
+ rhiFormat = QRhiTexture::ASTC_12x12;
+ break;
+ default:
+ qWarning("VkFormat %d is not supported", format);
+ break;
+ }
+ if (sRGB)
+ (*flags) |=(QRhiTexture::sRGB);
+ return rhiFormat;
+}
+#endif
+
+#ifdef Q_OS_WIN
+QRhiTexture::Format QSGRhiSupport::toRhiTextureFormatFromDXGI(uint format, QRhiTexture::Flags *flags)
+{
+ auto rhiFormat = QRhiTexture::UnknownFormat;
+ bool sRGB = false;
+ switch (format) {
+ case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
+ sRGB = true;
+ Q_FALLTHROUGH();
+ case DXGI_FORMAT_R8G8B8A8_UNORM:
+ case DXGI_FORMAT_UNKNOWN:
+ rhiFormat = QRhiTexture::RGBA8;
+ break;
+ case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
+ sRGB = true;
+ Q_FALLTHROUGH();
+ case DXGI_FORMAT_B8G8R8A8_UNORM:
+ rhiFormat = QRhiTexture::BGRA8;
+ break;
+ case DXGI_FORMAT_R8_UNORM:
+ rhiFormat = QRhiTexture::R8;
+ break;
+ case DXGI_FORMAT_R8G8_UNORM:
+ rhiFormat = QRhiTexture::RG8;
+ break;
+ case DXGI_FORMAT_R16_UNORM:
+ rhiFormat = QRhiTexture::R16;
+ break;
+ case DXGI_FORMAT_R16G16_UNORM:
+ rhiFormat = QRhiTexture::RG16;
+ break;
+ case DXGI_FORMAT_R16G16B16A16_FLOAT:
+ rhiFormat = QRhiTexture::RGBA16F;
+ break;
+ case DXGI_FORMAT_R32G32B32A32_FLOAT:
+ rhiFormat = QRhiTexture::RGBA32F;
+ break;
+ case DXGI_FORMAT_R16_FLOAT:
+ rhiFormat = QRhiTexture::R16F;
+ break;
+ case DXGI_FORMAT_R32_FLOAT:
+ rhiFormat = QRhiTexture::R32F;
+ break;
+ case DXGI_FORMAT_R10G10B10A2_UNORM:
+ rhiFormat = QRhiTexture::RGB10A2;
+ break;
+ case DXGI_FORMAT_R16_TYPELESS:
+ rhiFormat = QRhiTexture::D16;
+ break;
+ case DXGI_FORMAT_R24_UNORM_X8_TYPELESS:
+ rhiFormat = QRhiTexture::D24;
+ break;
+ case DXGI_FORMAT_D24_UNORM_S8_UINT:
+ rhiFormat = QRhiTexture::D24S8;
+ break;
+ case DXGI_FORMAT_R32_TYPELESS:
+ rhiFormat = QRhiTexture::D32F;
+ break;
+ case DXGI_FORMAT_BC1_UNORM_SRGB:
+ sRGB = true;
+ Q_FALLTHROUGH();
+ case DXGI_FORMAT_BC1_UNORM:
+ rhiFormat = QRhiTexture::BC1;
+ break;
+ case DXGI_FORMAT_BC2_UNORM_SRGB:
+ sRGB = true;
+ Q_FALLTHROUGH();
+ case DXGI_FORMAT_BC2_UNORM:
+ rhiFormat = QRhiTexture::BC2;
+ break;
+ case DXGI_FORMAT_BC3_UNORM_SRGB:
+ sRGB = true;
+ Q_FALLTHROUGH();
+ case DXGI_FORMAT_BC3_UNORM:
+ rhiFormat = QRhiTexture::BC3;
+ break;
+ case DXGI_FORMAT_BC4_UNORM:
+ rhiFormat = QRhiTexture::BC4;
+ break;
+ case DXGI_FORMAT_BC5_UNORM:
+ rhiFormat = QRhiTexture::BC5;
+ break;
+ case DXGI_FORMAT_BC6H_UF16:
+ rhiFormat = QRhiTexture::BC6H;
+ break;
+ case DXGI_FORMAT_BC7_UNORM_SRGB:
+ sRGB = true;
+ Q_FALLTHROUGH();
+ case DXGI_FORMAT_BC7_UNORM:
+ rhiFormat = QRhiTexture::BC7;
+ break;
+ default:
+ qWarning("DXGI_FORMAT %d is not supported", format);
+ break;
+ }
+ if (sRGB)
+ (*flags) |=(QRhiTexture::sRGB);
+ return rhiFormat;
+}
+#endif
+
+#if QT_CONFIG(metal)
+namespace QSGRhiSupportMac {
+ QRhiTexture::Format toRhiTextureFormatFromMetal(uint format, QRhiTexture::Flags *flags);
+}
+QRhiTexture::Format QSGRhiSupport::toRhiTextureFormatFromMetal(uint format, QRhiTexture::Flags *flags)
+{
+ return QSGRhiSupportMac::toRhiTextureFormatFromMetal(format, flags);
+}
+#endif
+
void QSGRhiSupport::configure(QSGRendererInterface::GraphicsApi api)
{
if (api == QSGRendererInterface::Unknown) {
// behave as if nothing was explicitly requested
- QSGRhiSupport *inst = staticInst();
- inst->m_requested.valid = false;
- inst->applySettings();
+ m_requested.valid = false;
+ applySettings();
} else {
Q_ASSERT(QSGRendererInterface::isApiRhiBased(api));
- QSGRhiSupport *inst = staticInst();
- inst->m_requested.valid = true;
- inst->m_requested.api = api;
- inst->applySettings();
+ m_requested.valid = true;
+ m_requested.api = api;
+ applySettings();
}
}
+QSGRhiSupport *QSGRhiSupport::instance_internal()
+{
+ static QSGRhiSupport inst;
+ return &inst;
+}
+
QSGRhiSupport *QSGRhiSupport::instance()
{
- QSGRhiSupport *inst = staticInst();
+ QSGRhiSupport *inst = instance_internal();
if (!inst->m_settingsApplied)
inst->applySettings();
return inst;
@@ -291,41 +652,24 @@ QSGRhiSupport *QSGRhiSupport::instance()
QString QSGRhiSupport::rhiBackendName() const
{
- if (m_enableRhi) {
- switch (m_rhiBackend) {
- case QRhi::Null:
- return QLatin1String("Null");
- case QRhi::Vulkan:
- return QLatin1String("Vulkan");
- case QRhi::OpenGLES2:
- return QLatin1String("OpenGL");
- case QRhi::D3D11:
- return QLatin1String("D3D11");
- case QRhi::Metal:
- return QLatin1String("Metal");
- default:
- return QLatin1String("Unknown");
- }
- }
- return QLatin1String("Unknown (RHI not enabled)");
+ return QString::fromUtf8(QRhi::backendName(m_rhiBackend));
}
QSGRendererInterface::GraphicsApi QSGRhiSupport::graphicsApi() const
{
- if (!m_enableRhi)
- return QSGRendererInterface::OpenGL;
-
switch (m_rhiBackend) {
case QRhi::Null:
- return QSGRendererInterface::NullRhi;
+ return QSGRendererInterface::Null;
case QRhi::Vulkan:
- return QSGRendererInterface::VulkanRhi;
+ return QSGRendererInterface::Vulkan;
case QRhi::OpenGLES2:
- return QSGRendererInterface::OpenGLRhi;
+ return QSGRendererInterface::OpenGL;
case QRhi::D3D11:
- return QSGRendererInterface::Direct3D11Rhi;
+ return QSGRendererInterface::Direct3D11;
+ case QRhi::D3D12:
+ return QSGRendererInterface::Direct3D12;
case QRhi::Metal:
- return QSGRendererInterface::MetalRhi;
+ return QSGRendererInterface::Metal;
default:
return QSGRendererInterface::Unknown;
}
@@ -333,15 +677,13 @@ QSGRendererInterface::GraphicsApi QSGRhiSupport::graphicsApi() const
QSurface::SurfaceType QSGRhiSupport::windowSurfaceType() const
{
- if (!m_enableRhi)
- return QSurface::OpenGLSurface;
-
switch (m_rhiBackend) {
case QRhi::Vulkan:
return QSurface::VulkanSurface;
case QRhi::OpenGLES2:
return QSurface::OpenGLSurface;
case QRhi::D3D11:
+ case QRhi::D3D12:
return QSurface::Direct3DSurface;
case QRhi::Metal:
return QSurface::MetalSurface;
@@ -379,6 +721,10 @@ static const void *qsgrhi_vk_rifResource(QSGRendererInterface::Resource res,
return &maybeVkRpNat->renderPass;
else
return nullptr;
+ case QSGRendererInterface::GraphicsQueueFamilyIndexResource:
+ return &vknat->gfxQueueFamilyIdx;
+ case QSGRendererInterface::GraphicsQueueIndexResource:
+ return &vknat->gfxQueueIdx;
default:
return nullptr;
}
@@ -411,9 +757,22 @@ static const void *qsgrhi_d3d11_rifResource(QSGRendererInterface::Resource res,
return nullptr;
}
}
+
+static const void *qsgrhi_d3d12_rifResource(QSGRendererInterface::Resource res, const QRhiNativeHandles *nat)
+{
+ const QRhiD3D12NativeHandles *d3dnat = static_cast<const QRhiD3D12NativeHandles *>(nat);
+ switch (res) {
+ case QSGRendererInterface::DeviceResource:
+ return d3dnat->dev;
+ case QSGRendererInterface::CommandQueueResource:
+ return d3dnat->commandQueue;
+ default:
+ return nullptr;
+ }
+}
#endif
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+#if QT_CONFIG(metal)
static const void *qsgrhi_mtl_rifResource(QSGRendererInterface::Resource res, const QRhiNativeHandles *nat,
const QRhiNativeHandles *cbNat)
{
@@ -460,7 +819,7 @@ const void *QSGRhiSupport::rifResource(QSGRendererInterface::Resource res,
case QSGRendererInterface::RhiRedirectCommandBuffer:
return QQuickWindowPrivate::get(w)->redirect.commandBuffer;
case QSGRendererInterface::RhiRedirectRenderTarget:
- return QQuickWindowPrivate::get(w)->redirect.rt.renderTarget;
+ return QQuickWindowPrivate::get(w)->redirect.rt.rt.renderTarget;
default:
break;
}
@@ -487,8 +846,10 @@ const void *QSGRhiSupport::rifResource(QSGRendererInterface::Resource res,
#ifdef Q_OS_WIN
case QRhi::D3D11:
return qsgrhi_d3d11_rifResource(res, nat);
+ case QRhi::D3D12:
+ return qsgrhi_d3d12_rifResource(res, nat);
#endif
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+#if QT_CONFIG(metal)
case QRhi::Metal:
{
QRhiCommandBuffer *cb = rc->currentFrameCommandBuffer();
@@ -500,9 +861,9 @@ const void *QSGRhiSupport::rifResource(QSGRendererInterface::Resource res,
}
}
-int QSGRhiSupport::chooseSampleCountForWindowWithRhi(QWindow *window, QRhi *rhi)
+int QSGRhiSupport::chooseSampleCount(int samples, QRhi *rhi)
{
- int msaaSampleCount = qMax(QSurfaceFormat::defaultFormat().samples(), window->requestedFormat().samples());
+ int msaaSampleCount = samples;
if (qEnvironmentVariableIsSet("QSG_SAMPLES"))
msaaSampleCount = qEnvironmentVariableIntValue("QSG_SAMPLES");
msaaSampleCount = qMax(1, msaaSampleCount);
@@ -510,7 +871,7 @@ int QSGRhiSupport::chooseSampleCountForWindowWithRhi(QWindow *window, QRhi *rhi)
const QVector<int> supportedSampleCounts = rhi->supportedSampleCounts();
if (!supportedSampleCounts.contains(msaaSampleCount)) {
int reducedSampleCount = 1;
- for (int i = supportedSampleCounts.count() - 1; i >= 0; --i) {
+ for (int i = supportedSampleCounts.size() - 1; i >= 0; --i) {
if (supportedSampleCounts[i] <= msaaSampleCount) {
reducedSampleCount = supportedSampleCounts[i];
break;
@@ -525,6 +886,11 @@ int QSGRhiSupport::chooseSampleCountForWindowWithRhi(QWindow *window, QRhi *rhi)
return msaaSampleCount;
}
+int QSGRhiSupport::chooseSampleCountForWindowWithRhi(QWindow *window, QRhi *rhi)
+{
+ return chooseSampleCount(qMax(QSurfaceFormat::defaultFormat().samples(), window->requestedFormat().samples()), rhi);
+}
+
// must be called on the main thread
QOffscreenSurface *QSGRhiSupport::maybeCreateOffscreenSurface(QWindow *window)
{
@@ -552,29 +918,233 @@ void QSGRhiSupport::prepareWindowForRhi(QQuickWindow *window)
// always be under the application's control then (since the default
// instance we could create here would not be configurable by the
// application in any way, and that is often not acceptable).
- if (!window->vulkanInstance() && !wd->renderControl)
- window->setVulkanInstance(QSGRhiSupport::defaultVulkanInstance());
+ if (!window->vulkanInstance() && !wd->renderControl) {
+ QVulkanInstance *vkinst = QVulkanDefaultInstance::instance();
+ if (vkinst)
+ qCDebug(QSG_LOG_INFO) << "Got Vulkan instance from QVulkanDefaultInstance, requested api version was" << vkinst->apiVersion();
+ else
+ qCDebug(QSG_LOG_INFO) << "No Vulkan instance from QVulkanDefaultInstance, expect problems";
+ window->setVulkanInstance(vkinst);
+ }
}
#else
Q_UNUSED(window);
#endif
}
-// must be called on the render thread
-QRhi *QSGRhiSupport::createRhi(QQuickWindow *window, QOffscreenSurface *offscreenSurface)
+static inline bool ensureWritableDir(const QString &name)
{
-#if !QT_CONFIG(opengl) && !QT_CONFIG(vulkan) && !defined(Q_OS_WIN) && !defined(Q_OS_MACOS) && !defined(Q_OS_IOS)
- Q_UNUSED(window);
+ QDir::root().mkpath(name);
+ return QFileInfo(name).isWritable();
+}
+
+static QString automaticPipelineCacheDir()
+{
+ static bool checked = false;
+ static QString currentCacheDir;
+ static bool cacheWritable = false;
+
+ if (checked)
+ return cacheWritable ? currentCacheDir : QString();
+
+ checked = true;
+
+ // Intentionally not using the global cache path (GenericCacheLocation) -
+ // we do not want forever growing pipeline cache files that contain
+ // everything from all Qt apps ever run (that would affect load times
+ // eventually, resource use, etc.). Stick to being application-specific.
+
+ const QString cachePath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
+ const QString subPath = QLatin1String("/qtpipelinecache-") + QSysInfo::buildAbi() + QLatin1Char('/');
+
+ if (!cachePath.isEmpty()) {
+ currentCacheDir = cachePath + subPath;
+ cacheWritable = ensureWritableDir(currentCacheDir);
+ }
+
+ return cacheWritable ? currentCacheDir : QString();
+}
+
+static inline QString automaticPipelineCacheFileName(QRhi *rhi)
+{
+ const QString cacheDir = automaticPipelineCacheDir();
+ if (!cacheDir.isEmpty())
+ return cacheDir + QLatin1String("qqpc_") + QString::fromLatin1(rhi->backendName()).toLower();
+
+ return QString();
+}
+
+static inline bool isAutomaticPipelineCacheLoadSkippedForWindow(Qt::WindowFlags wflags)
+{
+ return wflags.testFlag(Qt::ToolTip) || wflags.testFlag(Qt::SplashScreen);
+}
+
+static inline bool isAutomaticPipelineCacheSaveSkippedForWindow(Qt::WindowFlags wflags)
+{
+ // this catches Tool, ToolTip, SplashScreen as well
+ return wflags.testFlag(Qt::Dialog) || wflags.testFlag(Qt::Popup);
+}
+
+static inline QString pipelineCacheLockFileName(const QString &name)
+{
+ return name + QLatin1String(".lck");
+}
+
+void QSGRhiSupport::preparePipelineCache(QRhi *rhi, QQuickWindow *window)
+{
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
+
+ // the explicitly set filename always takes priority as per docs
+ QString pipelineCacheLoad = wd->graphicsConfig.pipelineCacheLoadFile();
+ bool isAutomatic = false;
+ if (pipelineCacheLoad.isEmpty() && wd->graphicsConfig.isAutomaticPipelineCacheEnabled()) {
+ if (!isAutomaticPipelineCacheLoadSkippedForWindow(window->flags())) {
+ pipelineCacheLoad = automaticPipelineCacheFileName(rhi);
+ isAutomatic = true;
+ }
+ }
+
+ if (pipelineCacheLoad.isEmpty())
+ return;
+
+ QLockFile lock(pipelineCacheLockFileName(pipelineCacheLoad));
+ if (!lock.lock()) {
+ qWarning("Could not create pipeline cache lock file '%s'",
+ qPrintable(lock.fileName()));
+ return;
+ }
+
+ QFile f(pipelineCacheLoad);
+ if (!f.open(QIODevice::ReadOnly)) {
+ if (!isAutomatic) {
+ qWarning("Could not open pipeline cache source file '%s'",
+ qPrintable(pipelineCacheLoad));
+ }
+ return;
+ }
+
+ const QByteArray buf = f.readAll();
+ if (!buf.isEmpty()) {
+ qCDebug(QSG_LOG_INFO, "Attempting to seed pipeline cache for QRhi %p from '%s'",
+ rhi, qPrintable(pipelineCacheLoad));
+ rhi->setPipelineCacheData(buf);
+ }
+}
+
+void QSGRhiSupport::finalizePipelineCache(QRhi *rhi, const QQuickGraphicsConfiguration &config)
+{
+ // output the rhi statistics about pipelines, as promised by the documentation
+ qCDebug(QSG_LOG_INFO, "Total time spent on pipeline creation during the lifetime of the QRhi %p was %lld ms",
+ rhi, rhi->statistics().totalPipelineCreationTime);
+
+ // the explicitly set filename always takes priority as per docs
+ QString pipelineCacheSave = config.pipelineCacheSaveFile();
+ bool isAutomatic = false;
+ if (pipelineCacheSave.isEmpty() && config.isAutomaticPipelineCacheEnabled()) {
+ pipelineCacheSave = automaticPipelineCacheFileName(rhi);
+ isAutomatic = true;
+ }
+
+ if (pipelineCacheSave.isEmpty())
+ return;
+
+ const QByteArray buf = rhi->pipelineCacheData();
+
+ // If empty, do nothing. This is exactly what will happen if the rhi was
+ // created without QRhi::EnablePipelineCacheDataSave set.
+ if (buf.isEmpty()) {
+ if (isAutomatic) {
+ // Attempt to remove the file. If it does not exist or this fails,
+ // that's fine. The goal is just to prevent warnings from
+ // setPipelineCacheData in future runs, e.g. if the Qt or driver
+ // version does not match _and_ we do not generate any data at run
+ // time, then not writing the file out also means the warning would
+ // appear again and again on every run. Prevent that.
+ QDir().remove(pipelineCacheSave);
+ }
+ return;
+ }
+
+ QLockFile lock(pipelineCacheLockFileName(pipelineCacheSave));
+ if (!lock.lock()) {
+ qWarning("Could not create pipeline cache lock file '%s'",
+ qPrintable(lock.fileName()));
+ return;
+ }
+
+#if QT_CONFIG(temporaryfile)
+ QSaveFile f(pipelineCacheSave);
+#else
+ QFile f(pipelineCacheSave);
+#endif
+ if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
+ if (!isAutomatic) {
+ const QString msg = f.errorString();
+ qWarning("Could not open pipeline cache output file '%s': %s",
+ qPrintable(pipelineCacheSave), qPrintable(msg));
+ }
+ return;
+ }
+
+ qCDebug(QSG_LOG_INFO, "Writing pipeline cache contents (%d bytes) for QRhi %p to '%s'",
+ int(buf.size()), rhi, qPrintable(pipelineCacheSave));
+
+ if (f.write(buf) != buf.size()
+#if QT_CONFIG(temporaryfile)
+ || !f.commit()
#endif
+ )
+ {
+ if (!isAutomatic) {
+ const QString msg = f.errorString();
+ qWarning("Could not write pipeline cache: %s", qPrintable(msg));
+ }
+ return;
+ }
+}
+// must be called on the render thread
+QSGRhiSupport::RhiCreateResult QSGRhiSupport::createRhi(QQuickWindow *window, QSurface *offscreenSurface, bool forcePreferSwRenderer)
+{
QRhi *rhi = nullptr;
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
+ const QQuickGraphicsDevicePrivate *customDevD = QQuickGraphicsDevicePrivate::get(&wd->customDeviceObjects);
+ if (customDevD->type == QQuickGraphicsDevicePrivate::Type::Rhi) {
+ rhi = customDevD->u.rhi;
+ if (rhi) {
+ preparePipelineCache(rhi, window);
+ return { rhi, false };
+ }
+ }
+
+ const bool debugLayer = wd->graphicsConfig.isDebugLayerEnabled();
+ const bool debugMarkers = wd->graphicsConfig.isDebugMarkersEnabled();
+ const bool timestamps = wd->graphicsConfig.timestampsEnabled();
+ const bool preferSoftware = wd->graphicsConfig.prefersSoftwareDevice() || forcePreferSwRenderer;
+ const bool pipelineCacheSave = !wd->graphicsConfig.pipelineCacheSaveFile().isEmpty()
+ || (wd->graphicsConfig.isAutomaticPipelineCacheEnabled()
+ && !isAutomaticPipelineCacheSaveSkippedForWindow(window->flags()));
+
+ const QString backendName = rhiBackendName();
+ qCDebug(QSG_LOG_INFO,
+ "Creating QRhi with backend %s for window %p (wflags 0x%X)\n"
+ " Graphics API debug/validation layers: %d\n"
+ " Debug markers: %d\n"
+ " Timestamps: %d\n"
+ " Prefer software device: %d%s\n"
+ " Shader/pipeline cache collection: %d",
+ qPrintable(backendName), window, int(window->flags()), debugLayer,
+ debugMarkers, timestamps, preferSoftware, forcePreferSwRenderer ? " [FORCED]" : "", pipelineCacheSave);
QRhi::Flags flags;
- if (isProfilingRequested())
- flags |= QRhi::EnableProfiling | QRhi::EnableDebugMarkers;
- if (isSoftwareRendererRequested())
+ flags |= QRhi::SuppressSmokeTestWarnings;
+ if (debugMarkers)
+ flags |= QRhi::EnableDebugMarkers;
+ if (timestamps)
+ flags |= QRhi::EnableTimestamps;
+ if (preferSoftware)
flags |= QRhi::PreferSoftwareRenderer;
- if (!m_pipelineCacheSave.isEmpty())
+ if (pipelineCacheSave)
flags |= QRhi::EnablePipelineCacheDataSave;
const QRhi::Implementation backend = rhiBackend();
@@ -584,8 +1154,6 @@ QRhi *QSGRhiSupport::createRhi(QQuickWindow *window, QOffscreenSurface *offscree
}
#if QT_CONFIG(opengl)
if (backend == QRhi::OpenGLES2) {
- QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
- const QQuickGraphicsDevicePrivate *customDevD = QQuickGraphicsDevicePrivate::get(&wd->customDeviceObjects);
const QSurfaceFormat format = window->requestedFormat();
QRhiGles2InitParams rhiParams;
rhiParams.format = format;
@@ -602,11 +1170,13 @@ QRhi *QSGRhiSupport::createRhi(QQuickWindow *window, QOffscreenSurface *offscree
}
#else
Q_UNUSED(offscreenSurface);
+ if (backend == QRhi::OpenGLES2)
+ qWarning("OpenGL was requested for Qt Quick, but this build of Qt has no OpenGL support.");
#endif
#if QT_CONFIG(vulkan)
if (backend == QRhi::Vulkan) {
- QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
- const QQuickGraphicsDevicePrivate *customDevD = QQuickGraphicsDevicePrivate::get(&wd->customDeviceObjects);
+ if (debugLayer)
+ QVulkanDefaultInstance::setFlag(QVulkanDefaultInstance::EnableValidation, true);
QRhiVulkanInitParams rhiParams;
prepareWindowForRhi(window); // sets a vulkanInstance if not yet present
rhiParams.inst = window->vulkanInstance();
@@ -633,17 +1203,14 @@ QRhi *QSGRhiSupport::createRhi(QQuickWindow *window, QOffscreenSurface *offscree
rhi = QRhi::create(backend, &rhiParams, flags);
}
}
+#else
+ if (backend == QRhi::Vulkan)
+ qWarning("Vulkan was requested for Qt Quick, but this build of Qt has no Vulkan support.");
#endif
#ifdef Q_OS_WIN
if (backend == QRhi::D3D11) {
- QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
- const QQuickGraphicsDevicePrivate *customDevD = QQuickGraphicsDevicePrivate::get(&wd->customDeviceObjects);
QRhiD3D11InitParams rhiParams;
- rhiParams.enableDebugLayer = isDebugLayerRequested();
- if (m_killDeviceFrameCount > 0) {
- rhiParams.framesUntilKillingDeviceViaTdr = m_killDeviceFrameCount;
- rhiParams.repeatDeviceKill = true;
- }
+ rhiParams.enableDebugLayer = debugLayer;
if (customDevD->type == QQuickGraphicsDevicePrivate::Type::DeviceAndContext) {
QRhiD3D11NativeHandles importDev;
importDev.dev = customDevD->u.deviceAndContext.device;
@@ -661,13 +1228,42 @@ QRhi *QSGRhiSupport::createRhi(QQuickWindow *window, QOffscreenSurface *offscree
rhi = QRhi::create(backend, &rhiParams, flags, &importDev);
} else {
rhi = QRhi::create(backend, &rhiParams, flags);
+ if (!rhi && attemptReinitWithSwRastUponFail() && !flags.testFlag(QRhi::PreferSoftwareRenderer)) {
+ qCDebug(QSG_LOG_INFO, "Failed to create a D3D device with default settings; "
+ "attempting to get a software rasterizer backed device instead");
+ flags |= QRhi::PreferSoftwareRenderer;
+ rhi = QRhi::create(backend, &rhiParams, flags);
+ }
+ }
+ } else if (backend == QRhi::D3D12) {
+ QRhiD3D12InitParams rhiParams;
+ rhiParams.enableDebugLayer = debugLayer;
+ if (customDevD->type == QQuickGraphicsDevicePrivate::Type::DeviceAndContext) {
+ QRhiD3D12NativeHandles importDev;
+ importDev.dev = customDevD->u.deviceAndContext.device;
+ qCDebug(QSG_LOG_INFO, "Using existing native D3D12 device %p", importDev.dev);
+ rhi = QRhi::create(backend, &rhiParams, flags, &importDev);
+ } else if (customDevD->type == QQuickGraphicsDevicePrivate::Type::Adapter) {
+ QRhiD3D12NativeHandles importDev;
+ importDev.adapterLuidLow = customDevD->u.adapter.luidLow;
+ importDev.adapterLuidHigh = customDevD->u.adapter.luidHigh;
+ importDev.minimumFeatureLevel = customDevD->u.adapter.featureLevel;
+ qCDebug(QSG_LOG_INFO, "Using D3D12 adapter LUID %u, %d and minimum feature level %d",
+ importDev.adapterLuidLow, importDev.adapterLuidHigh, importDev.minimumFeatureLevel);
+ rhi = QRhi::create(backend, &rhiParams, flags, &importDev);
+ } else {
+ rhi = QRhi::create(backend, &rhiParams, flags);
+ if (!rhi && attemptReinitWithSwRastUponFail() && !flags.testFlag(QRhi::PreferSoftwareRenderer)) {
+ qCDebug(QSG_LOG_INFO, "Failed to create a D3D device with default settings; "
+ "attempting to get a software rasterizer backed device instead");
+ flags |= QRhi::PreferSoftwareRenderer;
+ rhi = QRhi::create(backend, &rhiParams, flags);
+ }
}
}
#endif
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+#if QT_CONFIG(metal)
if (backend == QRhi::Metal) {
- QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
- const QQuickGraphicsDevicePrivate *customDevD = QQuickGraphicsDevicePrivate::get(&wd->customDeviceObjects);
QRhiMetalInitParams rhiParams;
if (customDevD->type == QQuickGraphicsDevicePrivate::Type::DeviceAndCommandQueue) {
QRhiMetalNativeHandles importDev;
@@ -682,44 +1278,23 @@ QRhi *QSGRhiSupport::createRhi(QQuickWindow *window, QOffscreenSurface *offscree
}
#endif
- if (!rhi) {
+ if (rhi) {
+ qCDebug(QSG_LOG_INFO, "Created QRhi %p for window %p", rhi, window);
+ preparePipelineCache(rhi, window);
+ } else {
qWarning("Failed to create RHI (backend %d)", backend);
- return nullptr;
}
- if (!m_pipelineCacheLoad.isEmpty()) {
- QFile f(m_pipelineCacheLoad);
- if (f.open(QIODevice::ReadOnly)) {
- qCDebug(QSG_LOG_INFO, "Attempting to seed pipeline cache from '%s'",
- qPrintable(m_pipelineCacheLoad));
- rhi->setPipelineCacheData(f.readAll());
- } else {
- qWarning("Could not open pipeline cache source file '%s'",
- qPrintable(m_pipelineCacheLoad));
- }
- }
-
- return rhi;
+ return { rhi, true };
}
-void QSGRhiSupport::destroyRhi(QRhi *rhi)
+void QSGRhiSupport::destroyRhi(QRhi *rhi, const QQuickGraphicsConfiguration &config)
{
if (!rhi)
return;
- if (!rhi->isDeviceLost()) {
- if (!m_pipelineCacheSave.isEmpty()) {
- QFile f(m_pipelineCacheSave);
- if (f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
- qCDebug(QSG_LOG_INFO, "Writing pipeline cache contents to '%s'",
- qPrintable(m_pipelineCacheSave));
- f.write(rhi->pipelineCacheData());
- } else {
- qWarning("Could not open pipeline cache output file '%s'",
- qPrintable(m_pipelineCacheSave));
- }
- }
- }
+ if (!rhi->isDeviceLost())
+ finalizePipelineCache(rhi, config);
delete rhi;
}
@@ -771,11 +1346,13 @@ QImage QSGRhiSupport::grabOffscreen(QQuickWindow *window)
Q_ASSERT(!wd->renderControl);
QScopedPointer<QOffscreenSurface> offscreenSurface(maybeCreateOffscreenSurface(window));
- QScopedPointer<QRhi> rhi(createRhi(window, offscreenSurface.data()));
- if (!rhi) {
+ RhiCreateResult rhiResult = createRhi(window, offscreenSurface.data());
+ if (!rhiResult.rhi) {
qWarning("Failed to initialize QRhi for offscreen readback");
return QImage();
}
+ std::unique_ptr<QRhi> rhiOwner(rhiResult.rhi);
+ QRhi *rhi = rhiResult.own ? rhiOwner.get() : rhiOwner.release();
const QSize pixelSize = window->size() * window->devicePixelRatio();
QScopedPointer<QRhiTexture> texture(rhi->newTexture(QRhiTexture::RGBA8, pixelSize, 1,
@@ -799,10 +1376,10 @@ QImage QSGRhiSupport::grabOffscreen(QQuickWindow *window)
return QImage();
}
- wd->rhi = rhi.data();
+ wd->rhi = rhi;
QSGDefaultRenderContext::InitParams params;
- params.rhi = rhi.data();
+ params.rhi = rhi;
params.sampleCount = 1;
params.initialSurfacePixelSize = pixelSize;
params.maybeSurface = window;
@@ -821,10 +1398,10 @@ QImage QSGRhiSupport::grabOffscreen(QQuickWindow *window)
wd->setCustomCommandBuffer(cb);
wd->polishItems();
wd->syncSceneGraph();
- wd->renderSceneGraph(window->size());
+ wd->renderSceneGraph();
wd->setCustomCommandBuffer(nullptr);
- QImage image = grabAndBlockInCurrentFrame(rhi.data(), cb, texture.data());
+ QImage image = grabAndBlockInCurrentFrame(rhi, cb, texture.data());
rhi->endOffscreenFrame();
image.setDevicePixelRatio(window->devicePixelRatio());
@@ -837,60 +1414,210 @@ QImage QSGRhiSupport::grabOffscreen(QQuickWindow *window)
return image;
}
-QSGRhiProfileConnection *QSGRhiProfileConnection::instance()
+#ifdef Q_OS_WEBOS
+QImage QSGRhiSupport::grabOffscreenForProtectedContent(QQuickWindow *window)
{
- static QSGRhiProfileConnection inst;
- return &inst;
+ // If a context is created for protected content, grabbing GPU
+ // resources are restricted. For the case, normal context
+ // and surface are needed to allow CPU access.
+ // So dummy offscreen window is used here
+ // This function is called in rendering thread.
+
+ QScopedPointer<QQuickWindow> offscreenWindow;
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
+ // It is expected that window is not using QQuickRenderControl, i.e. it is
+ // a normal QQuickWindow that just happens to be not exposed.
+ Q_ASSERT(!wd->renderControl);
+
+ // If context and surface are created for protected content,
+ // CPU can't read the frame resources. So normal context and surface are needed.
+ if (window->requestedFormat().testOption(QSurfaceFormat::ProtectedContent)) {
+ QSurfaceFormat surfaceFormat = window->requestedFormat();
+ surfaceFormat.setOption(QSurfaceFormat::ProtectedContent, false);
+ offscreenWindow.reset(new QQuickWindow());
+ offscreenWindow->setFormat(surfaceFormat);
+ }
+
+ QScopedPointer<QOffscreenSurface> offscreenSurface(maybeCreateOffscreenSurface(window));
+ RhiCreateResult rhiResult = createRhi(offscreenWindow.data() ? offscreenWindow.data() : window, offscreenSurface.data());
+ if (!rhiResult.rhi) {
+ qWarning("Failed to initialize QRhi for offscreen readback");
+ return QImage();
+ }
+ QScopedPointer<QRhi> rhiOwner(rhiResult.rhi);
+ QRhi *rhi = rhiResult.own ? rhiOwner.data() : rhiOwner.take();
+
+ const QSize pixelSize = window->size() * window->devicePixelRatio();
+ QScopedPointer<QRhiTexture> texture(rhi->newTexture(QRhiTexture::RGBA8, pixelSize, 1,
+ QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource));
+ if (!texture->create()) {
+ qWarning("Failed to build texture for offscreen readback");
+ return QImage();
+ }
+ QScopedPointer<QRhiRenderBuffer> depthStencil(rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, pixelSize, 1));
+ if (!depthStencil->create()) {
+ qWarning("Failed to create depth/stencil buffer for offscreen readback");
+ return QImage();
+ }
+ QRhiTextureRenderTargetDescription rtDesc(texture.data());
+ rtDesc.setDepthStencilBuffer(depthStencil.data());
+ QScopedPointer<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget(rtDesc));
+ QScopedPointer<QRhiRenderPassDescriptor> rpDesc(rt->newCompatibleRenderPassDescriptor());
+ rt->setRenderPassDescriptor(rpDesc.data());
+ if (!rt->create()) {
+ qWarning("Failed to build render target for offscreen readback");
+ return QImage();
+ }
+
+ // Backup the original Rhi
+ QRhi *currentRhi = wd->rhi;
+ wd->rhi = rhi;
+
+ QSGDefaultRenderContext::InitParams params;
+ params.rhi = rhi;
+ params.sampleCount = 1;
+ params.initialSurfacePixelSize = pixelSize;
+ params.maybeSurface = window;
+ wd->context->initialize(&params);
+
+ // Backup the original RenderTarget
+ QQuickRenderTarget currentRenderTarget = window->renderTarget();
+ // There was no rendercontrol which means a custom render target
+ // should not be set either. Set our own, temporarily.
+ window->setRenderTarget(QQuickRenderTarget::fromRhiRenderTarget(rt.data()));
+
+ QRhiCommandBuffer *cb = nullptr;
+ if (rhi->beginOffscreenFrame(&cb) != QRhi::FrameOpSuccess) {
+ qWarning("Failed to start recording the frame for offscreen readback");
+ return QImage();
+ }
+
+ wd->setCustomCommandBuffer(cb);
+ wd->polishItems();
+ wd->syncSceneGraph();
+ wd->renderSceneGraph();
+ wd->setCustomCommandBuffer(nullptr);
+
+ QImage image = grabAndBlockInCurrentFrame(rhi, cb, texture.data());
+ rhi->endOffscreenFrame();
+
+ image.setDevicePixelRatio(window->devicePixelRatio());
+
+ // Called from gui/main thread on no onscreen rendering initialized
+ if (!currentRhi) {
+ wd->cleanupNodesOnShutdown();
+ wd->context->invalidate();
+
+ window->setRenderTarget(QQuickRenderTarget());
+ wd->rhi = nullptr;
+ } else {
+ // Called from rendering thread for protected content
+ // Restore to original Rhi, RenderTarget and Context
+ window->setRenderTarget(currentRenderTarget);
+ wd->rhi = currentRhi;
+ params.rhi = currentRhi;
+ wd->context->initialize(&params);
+ }
+
+ return image;
}
+#endif
-void QSGRhiProfileConnection::initialize(QRhi *rhi)
+void QSGRhiSupport::applySwapChainFormat(QRhiSwapChain *scWithWindowSet, QQuickWindow *window)
{
-#ifdef RHI_REMOTE_PROFILER
- const QString profHost = qEnvironmentVariable("QSG_RHI_PROFILE_HOST");
- if (!profHost.isEmpty()) {
- if (!QQmlEnginePrivate::qml_debugging_enabled) {
- qWarning("RHI profiling cannot be enabled without QML debugging, for security reasons. "
- "Set CONFIG+=qml_debug in the application project.");
- return;
+ Q_ASSERT(scWithWindowSet->window() == window);
+
+ QRhiSwapChain::Format swapChainFormat = QRhiSwapChain::SDR;
+
+ QByteArray hdrRequest = qgetenv("QSG_RHI_HDR");
+ if (hdrRequest.isEmpty())
+ hdrRequest = window->property("_qt_sg_hdr_format").toByteArray();
+
+ if (!hdrRequest.isEmpty()) {
+ hdrRequest = hdrRequest.toLower();
+ if (hdrRequest == QByteArrayLiteral("scrgb") || hdrRequest == QByteArrayLiteral("extendedsrgblinear"))
+ swapChainFormat = QRhiSwapChain::HDRExtendedSrgbLinear;
+ else if (hdrRequest == QByteArrayLiteral("hdr10"))
+ swapChainFormat = QRhiSwapChain::HDR10;
+ else if (hdrRequest == QByteArrayLiteral("p3"))
+ swapChainFormat = QRhiSwapChain::HDRExtendedDisplayP3Linear;
+ }
+
+ const char *fmtStr = "unknown";
+ switch (swapChainFormat) {
+ case QRhiSwapChain::SDR:
+ fmtStr = "SDR";
+ break;
+ case QRhiSwapChain::HDRExtendedSrgbLinear:
+ fmtStr = "scRGB";
+ break;
+ case QRhiSwapChain::HDR10:
+ fmtStr = "HDR10";
+ break;
+ case QRhiSwapChain::HDRExtendedDisplayP3Linear:
+ fmtStr = "Extended Linear Display P3";
+ break;
+ default:
+ break;
+ }
+
+ if (!scWithWindowSet->isFormatSupported(swapChainFormat)) {
+ if (swapChainFormat != QRhiSwapChain::SDR) {
+ qCDebug(QSG_LOG_INFO, "Requested a %s swapchain but it is reported to be unsupported with the current display(s). "
+ "In multi-screen configurations make sure the window is located on a HDR-enabled screen. "
+ "Request ignored, using SDR swapchain.", fmtStr);
}
- int profPort = qEnvironmentVariableIntValue("QSG_RHI_PROFILE_PORT");
- if (!profPort)
- profPort = 30667;
- qCDebug(QSG_LOG_INFO, "Sending RHI profiling output to %s:%d", qPrintable(profHost), profPort);
- m_profConn.reset(new QTcpSocket);
- QObject::connect(m_profConn.data(), &QAbstractSocket::errorOccurred, m_profConn.data(),
- [this](QAbstractSocket::SocketError socketError) { qWarning(" RHI profiler error: %d (%s)",
- socketError, qPrintable(m_profConn->errorString())); });
- m_profConn->connectToHost(profHost, profPort);
- m_profConn->waitForConnected(); // blocking wait because we want to send stuff already from the init below
- rhi->profiler()->setDevice(m_profConn.data());
- m_lastMemStatWrite.start();
+ return;
+ }
+
+ scWithWindowSet->setFormat(swapChainFormat);
+
+ if (swapChainFormat != QRhiSwapChain::SDR) {
+ qCDebug(QSG_LOG_INFO, "Creating %s swapchain", fmtStr);
+ qCDebug(QSG_LOG_INFO) << "HDR output info:" << scWithWindowSet->hdrInfo();
}
-#else
- Q_UNUSED(rhi);
-#endif
}
-void QSGRhiProfileConnection::cleanup()
+QRhiTexture::Format QSGRhiSupport::toRhiTextureFormat(uint nativeFormat, QRhiTexture::Flags *flags) const
{
-#ifdef RHI_REMOTE_PROFILER
- m_profConn.reset();
+ switch (m_rhiBackend) {
+#if QT_CONFIG(vulkan)
+ case QRhi::Vulkan:
+ return toRhiTextureFormatFromVulkan(nativeFormat, flags);
#endif
+#if QT_CONFIG(opengl)
+ case QRhi::OpenGLES2:
+ Q_UNUSED(flags);
+ return toRhiTextureFormatFromGL(nativeFormat, flags);
+#endif
+#ifdef Q_OS_WIN
+ case QRhi::D3D11:
+ case QRhi::D3D12:
+ return toRhiTextureFormatFromDXGI(nativeFormat, flags);
+#endif
+#if QT_CONFIG(metal)
+ case QRhi::Metal:
+ return toRhiTextureFormatFromMetal(nativeFormat, flags);
+#endif
+ default:
+ return QRhiTexture::UnknownFormat;
+ }
+ Q_UNUSED(nativeFormat)
+ Q_UNUSED(flags)
}
-void QSGRhiProfileConnection::send(QRhi *rhi)
+bool QSGRhiSupport::attemptReinitWithSwRastUponFail() const
{
-#ifdef RHI_REMOTE_PROFILER
- if (m_profConn) {
- // do this every 5 sec at most
- if (m_lastMemStatWrite.elapsed() >= 5000) {
- rhi->profiler()->addVMemAllocatorStats();
- m_lastMemStatWrite.restart();
- }
- }
-#else
- Q_UNUSED(rhi);
-#endif
+ const QRhi::Implementation backend = rhiBackend();
+
+ // On Windows it makes sense to retry using a software adapter whenever
+ // device creation or swapchain creation fails, as WARP is usually available
+ // (built in to the OS) and is good quality. This helps a lot in particular
+ // when running in a VM that cripples proper 3D graphics.
+ if (backend == QRhi::D3D11 || backend == QRhi::D3D12)
+ return true;
+
+ return false;
}
QT_END_NAMESPACE