Coverage Report

Created: 2026-06-30 07:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/qtbase/src/gui/painting/qbackingstorerhisupport.cpp
Line
Count
Source
1
// Copyright (C) 2021 The Qt Company Ltd.
2
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
// Qt-Security score:significant reason:default
4
5
#include "qbackingstorerhisupport_p.h"
6
#include <qpa/qplatformintegration.h>
7
#include <private/qguiapplication_p.h>
8
9
#if QT_CONFIG(opengl)
10
#include <QtGui/qoffscreensurface.h>
11
#include <QtGui/private/qopenglcontext_p.h>
12
#endif
13
14
#if QT_CONFIG(vulkan)
15
#include <QtGui/private/qvulkandefaultinstance_p.h>
16
#endif
17
18
QT_BEGIN_NAMESPACE
19
20
QBackingStoreRhiSupport::~QBackingStoreRhiSupport()
21
0
{
22
0
    reset();
23
0
}
24
25
void QBackingStoreRhiSupport::SwapchainData::reset()
26
0
{
27
0
    delete swapchain;
28
0
    delete renderPassDescriptor;
29
0
    delete windowWatcher;
30
0
    *this = {};
31
0
}
32
33
void QBackingStoreRhiSupport::reset()
34
0
{
35
0
    for (SwapchainData &d : m_swapchains)
36
0
        d.reset();
37
38
0
    m_swapchains.clear();
39
40
0
    delete m_rhi;
41
0
    m_rhi = nullptr;
42
43
0
    delete m_openGLFallbackSurface;
44
0
    m_openGLFallbackSurface = nullptr;
45
0
}
46
47
bool QBackingStoreRhiSupport::create()
48
0
{
49
0
    if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::RhiBasedRendering))
50
0
        return false;
51
52
    // note: m_window may be null (special case for fully offscreen rendering)
53
54
0
    QRhi *rhi = nullptr;
55
0
    QOffscreenSurface *surface = nullptr;
56
0
    QRhi::Flags flags;
57
58
    // These must be the same env.vars Qt Quick uses (as documented), in order
59
    // to ensure symmetry in the behavior between a QQuickWindow and a
60
    // (QRhi-based) widget top-level window.
61
0
    if (qEnvironmentVariableIntValue("QSG_RHI_PREFER_SOFTWARE_RENDERER"))
62
0
        flags |= QRhi::PreferSoftwareRenderer;
63
0
    if (qEnvironmentVariableIntValue("QSG_RHI_PROFILE"))
64
0
        flags |= QRhi::EnableDebugMarkers | QRhi::EnableTimestamps;
65
66
0
    if (m_config.api() == QPlatformBackingStoreRhiConfig::Null) {
67
0
        QRhiNullInitParams params;
68
0
        rhi = QRhi::create(QRhi::Null, &params, flags);
69
0
    }
70
71
#if QT_CONFIG(opengl)
72
    if (!rhi && m_config.api() == QPlatformBackingStoreRhiConfig::OpenGL) {
73
        surface = QRhiGles2InitParams::newFallbackSurface(m_format);
74
        QRhiGles2InitParams params;
75
        params.fallbackSurface = surface;
76
        params.window = m_window;
77
        params.format = m_format;
78
        params.shareContext = QOpenGLContext::globalShareContext();
79
        rhi = QRhi::create(QRhi::OpenGLES2, &params, flags);
80
    }
81
#endif
82
83
#ifdef Q_OS_WIN
84
    if (!rhi) {
85
        if (m_config.api() == QPlatformBackingStoreRhiConfig::D3D11) {
86
            QRhiD3D11InitParams params;
87
            params.enableDebugLayer = m_config.isDebugLayerEnabled();
88
            rhi = QRhi::create(QRhi::D3D11, &params, flags);
89
            if (!rhi && !flags.testFlag(QRhi::PreferSoftwareRenderer)) {
90
                qCDebug(lcQpaBackingStore, "Failed to create a D3D11 device with default settings; "
91
                                           "attempting to get a software rasterizer backed device instead");
92
                flags |= QRhi::PreferSoftwareRenderer;
93
                rhi = QRhi::create(QRhi::D3D11, &params, flags);
94
            }
95
        } else if (m_config.api() == QPlatformBackingStoreRhiConfig::D3D12) {
96
            QRhiD3D12InitParams params;
97
            params.enableDebugLayer = m_config.isDebugLayerEnabled();
98
            rhi = QRhi::create(QRhi::D3D12, &params, flags);
99
            if (!rhi && !flags.testFlag(QRhi::PreferSoftwareRenderer)) {
100
                qCDebug(lcQpaBackingStore, "Failed to create a D3D12 device with default settings; "
101
                                           "attempting to get a software rasterizer backed device instead");
102
                flags |= QRhi::PreferSoftwareRenderer;
103
                rhi = QRhi::create(QRhi::D3D12, &params, flags);
104
            }
105
        }
106
    }
107
#endif
108
109
#if QT_CONFIG(metal)
110
    if (!rhi && m_config.api() == QPlatformBackingStoreRhiConfig::Metal) {
111
        QRhiMetalInitParams params;
112
        // For parity with Qt Quick, fall back to OpenGL when there is no Metal (f.ex. in macOS virtual machines).
113
        if (QRhi::probe(QRhi::Metal, &params)) {
114
            rhi = QRhi::create(QRhi::Metal, &params, flags);
115
        } else {
116
            qCDebug(lcQpaBackingStore, "Metal does not seem to be supported");
117
            return false;
118
        }
119
    }
120
#endif
121
122
#if QT_CONFIG(vulkan)
123
    if (!rhi && m_config.api() == QPlatformBackingStoreRhiConfig::Vulkan) {
124
        if (m_config.isDebugLayerEnabled())
125
            QVulkanDefaultInstance::setFlag(QVulkanDefaultInstance::EnableValidation);
126
        QRhiVulkanInitParams params;
127
        if (m_window) {
128
            if (!m_window->vulkanInstance())
129
                m_window->setVulkanInstance(QVulkanDefaultInstance::instance());
130
            params.inst = m_window->vulkanInstance();
131
        } else {
132
            params.inst = QVulkanDefaultInstance::instance();
133
        }
134
        if (!params.inst) {
135
            qWarning("No QVulkanInstance set for the top-level window, this is wrong.");
136
            return false;
137
        }
138
        params.window = m_window;
139
        rhi = QRhi::create(QRhi::Vulkan, &params, flags);
140
    }
141
#endif
142
143
0
    if (!rhi) {
144
0
        qWarning("Failed to create QRhi for QBackingStoreRhiSupport");
145
0
        delete surface;
146
0
        return false;
147
0
    }
148
149
0
    m_rhi = rhi;
150
0
    m_openGLFallbackSurface = surface;
151
0
    return true;
152
0
}
153
154
QRhiSwapChain *QBackingStoreRhiSupport::swapChainForWindow(QWindow *window)
155
0
{
156
0
    auto it = m_swapchains.constFind(window);
157
0
    if (it != m_swapchains.constEnd())
158
0
        return it.value().swapchain;
159
160
0
    QRhiSwapChain *swapchain = nullptr;
161
0
    QRhiRenderPassDescriptor *rp = nullptr;
162
0
    if (window && m_rhi) {
163
0
        QRhiSwapChain::Flags flags;
164
0
        const QSurfaceFormat format = window->requestedFormat();
165
0
        if (format.swapInterval() == 0)
166
0
            flags |= QRhiSwapChain::NoVSync;
167
0
        if (format.alphaBufferSize() > 0)
168
0
            flags |= QRhiSwapChain::SurfaceHasNonPreMulAlpha;
169
#if QT_CONFIG(vulkan)
170
        if (m_config.api() == QPlatformBackingStoreRhiConfig::Vulkan && !window->vulkanInstance())
171
            window->setVulkanInstance(QVulkanDefaultInstance::instance());
172
#endif
173
0
        qCDebug(lcQpaBackingStore) << "Creating swapchain for window" << window;
174
0
        swapchain = m_rhi->newSwapChain();
175
0
        swapchain->setWindow(window);
176
0
        swapchain->setFlags(flags);
177
0
        rp = swapchain->newCompatibleRenderPassDescriptor();
178
0
        swapchain->setRenderPassDescriptor(rp);
179
0
        if (!swapchain->createOrResize()) {
180
0
            qWarning("Failed to create swapchain for window flushed with an RHI-enabled backingstore");
181
0
            delete rp;
182
0
            return nullptr;
183
0
        }
184
0
    }
185
0
    if (swapchain) {
186
0
        SwapchainData d;
187
0
        d.swapchain = swapchain;
188
0
        d.renderPassDescriptor = rp;
189
0
        d.windowWatcher = new QBackingStoreRhiSupportWindowWatcher(this);
190
0
        m_swapchains.insert(window, d);
191
0
        window->installEventFilter(d.windowWatcher);
192
0
    }
193
0
    return swapchain;
194
0
}
195
196
bool QBackingStoreRhiSupportWindowWatcher::eventFilter(QObject *obj, QEvent *event)
197
0
{
198
0
    if (event->type() == QEvent::WindowAboutToChangeInternal
199
0
        || (event->type() == QEvent::PlatformSurface
200
0
            && static_cast<QPlatformSurfaceEvent *>(event)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed))
201
0
    {
202
0
        QWindow *window = qobject_cast<QWindow *>(obj);
203
0
        auto it = m_rhiSupport->m_swapchains.find(window);
204
0
        if (it != m_rhiSupport->m_swapchains.end()) {
205
0
            qCDebug(lcQpaBackingStore) << event << "received for" << window << "- cleaning up swapchain";
206
0
            auto data = *it;
207
0
            m_rhiSupport->m_swapchains.erase(it);
208
0
            data.reset(); // deletes 'this'
209
0
        }
210
0
    }
211
0
    return false;
212
0
}
213
214
QSurface::SurfaceType QBackingStoreRhiSupport::surfaceTypeForConfig(const QPlatformBackingStoreRhiConfig &config)
215
0
{
216
0
    QSurface::SurfaceType type = QSurface::RasterSurface;
217
0
    switch (config.api()) {
218
0
    case QPlatformBackingStoreRhiConfig::D3D11:
219
0
    case QPlatformBackingStoreRhiConfig::D3D12:
220
0
        type = QSurface::Direct3DSurface;
221
0
        break;
222
0
    case QPlatformBackingStoreRhiConfig::Vulkan:
223
0
        type = QSurface::VulkanSurface;
224
0
        break;
225
0
    case QPlatformBackingStoreRhiConfig::Metal:
226
0
        type = QSurface::MetalSurface;
227
0
        break;
228
0
    case QPlatformBackingStoreRhiConfig::OpenGL:
229
0
        type = QSurface::OpenGLSurface;
230
0
        break;
231
0
    default:
232
0
        break;
233
0
    }
234
0
    return type;
235
0
}
236
237
QRhi::Implementation QBackingStoreRhiSupport::apiToRhiBackend(QPlatformBackingStoreRhiConfig::Api api)
238
0
{
239
0
    switch (api) {
240
0
    case QPlatformBackingStoreRhiConfig::OpenGL:
241
0
        return QRhi::OpenGLES2;
242
0
    case QPlatformBackingStoreRhiConfig::Metal:
243
0
        return QRhi::Metal;
244
0
    case QPlatformBackingStoreRhiConfig::Vulkan:
245
0
        return QRhi::Vulkan;
246
0
    case QPlatformBackingStoreRhiConfig::D3D11:
247
0
        return QRhi::D3D11;
248
0
    case QPlatformBackingStoreRhiConfig::D3D12:
249
0
        return QRhi::D3D12;
250
0
    case QPlatformBackingStoreRhiConfig::Null:
251
0
        return QRhi::Null;
252
0
    default:
253
0
        break;
254
0
    }
255
0
    return QRhi::Null;
256
0
}
257
258
bool QBackingStoreRhiSupport::checkForceRhi(QPlatformBackingStoreRhiConfig *outConfig, QSurface::SurfaceType *outType)
259
0
{
260
0
    static QPlatformBackingStoreRhiConfig config;
261
0
    static bool checked = false;
262
263
0
    if (!checked) {
264
0
        checked = true;
265
266
0
        const bool alwaysRhi = qEnvironmentVariableIntValue("QT_WIDGETS_RHI");
267
0
        const bool highdpiDownscale = qEnvironmentVariableIntValue("QT_WIDGETS_HIGHDPI_DOWNSCALE");
268
0
        if (highdpiDownscale)
269
0
            qCDebug(lcQpaBackingStore) << "Enabling QT_WIDGETS_RHI due to QT_WIDGETS_HIGHDPI_DOWNSCALE";
270
0
        if (alwaysRhi || highdpiDownscale)
271
0
            config.setEnabled(true);
272
273
        // if enabled, choose an api
274
0
        if (config.isEnabled()) {
275
#if defined(Q_OS_WIN)
276
            config.setApi(QPlatformBackingStoreRhiConfig::D3D11);
277
#elif QT_CONFIG(metal)
278
            config.setApi(QPlatformBackingStoreRhiConfig::Metal);
279
#elif QT_CONFIG(opengl)
280
            config.setApi(QPlatformBackingStoreRhiConfig::OpenGL);
281
#elif QT_CONFIG(vulkan)
282
            config.setApi(QPlatformBackingStoreRhiConfig::Vulkan);
283
#else
284
0
            qWarning("QT_WIDGETS_RHI is set but no backend is available; ignoring");
285
0
            return false;
286
0
#endif
287
288
            // the env.var. will always override
289
0
            if (qEnvironmentVariableIsSet("QT_WIDGETS_RHI_BACKEND")) {
290
0
                const QString backend = qEnvironmentVariable("QT_WIDGETS_RHI_BACKEND");
291
#ifdef Q_OS_WIN
292
                if (backend == QStringLiteral("d3d11") || backend == QStringLiteral("d3d"))
293
                    config.setApi(QPlatformBackingStoreRhiConfig::D3D11);
294
                if (backend == QStringLiteral("d3d12"))
295
                    config.setApi(QPlatformBackingStoreRhiConfig::D3D12);
296
#endif
297
#if QT_CONFIG(metal)
298
                if (backend == QStringLiteral("metal"))
299
                    config.setApi(QPlatformBackingStoreRhiConfig::Metal);
300
#endif
301
#if QT_CONFIG(opengl)
302
                if (backend == QStringLiteral("opengl") || backend == QStringLiteral("gl"))
303
                    config.setApi(QPlatformBackingStoreRhiConfig::OpenGL);
304
#endif
305
#if QT_CONFIG(vulkan)
306
                if (backend == QStringLiteral("vulkan"))
307
                    config.setApi(QPlatformBackingStoreRhiConfig::Vulkan);
308
#endif
309
0
            }
310
311
0
            if (qEnvironmentVariableIntValue("QT_WIDGETS_RHI_DEBUG_LAYER"))
312
0
                config.setDebugLayer(true);
313
0
        }
314
315
0
        qCDebug(lcQpaBackingStore) << "Check for forced use of QRhi resulted in enable"
316
0
                                   << config.isEnabled() << "with api" << QRhi::backendName(apiToRhiBackend(config.api()));
317
0
    }
318
319
0
    if (config.isEnabled()) {
320
0
        if (outConfig)
321
0
            *outConfig = config;
322
0
        if (outType)
323
0
            *outType = surfaceTypeForConfig(config);
324
0
        return true;
325
0
    }
326
0
    return false;
327
0
}
328
329
QT_END_NAMESPACE