Coverage Report

Created: 2026-05-31 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/qtbase/src/gui/rhi/qrhi_p.h
Line
Count
Source
1
// Copyright (C) 2023 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
#ifndef QRHI_P_H
6
#define QRHI_P_H
7
8
//
9
//  W A R N I N G
10
//  -------------
11
//
12
// This file is not part of the Qt API.  It exists purely as an
13
// implementation detail.  This header file may change from version to
14
// version without notice, or even be removed.
15
//
16
// We mean it.
17
//
18
19
#include <rhi/qrhi.h>
20
#include <QBitArray>
21
#include <QAtomicInt>
22
#include <QElapsedTimer>
23
#include <QLoggingCategory>
24
#include <QtCore/qset.h>
25
#include <QtCore/qvarlengtharray.h>
26
#include <QtCore/private/qflatmap_p.h>
27
28
QT_BEGIN_NAMESPACE
29
30
0
#define QRHI_RES(t, x) static_cast<t *>(x)
31
0
#define QRHI_RES_RHI(t) t *rhiD = static_cast<t *>(m_rhi)
32
33
Q_DECLARE_LOGGING_CATEGORY(QRHI_LOG_INFO)
34
Q_DECLARE_LOGGING_CATEGORY(QRHI_LOG_RUB)
35
36
class QRhiImplementation
37
{
38
public:
39
    virtual ~QRhiImplementation();
40
41
    virtual bool create(QRhi::Flags flags) = 0;
42
    virtual void destroy() = 0;
43
    virtual QRhi::AdapterList enumerateAdaptersBeforeCreate(QRhiNativeHandles *nativeHandles) const;
44
45
    virtual QRhiGraphicsPipeline *createGraphicsPipeline() = 0;
46
    virtual QRhiComputePipeline *createComputePipeline() = 0;
47
    virtual QRhiShaderResourceBindings *createShaderResourceBindings() = 0;
48
    virtual QRhiBuffer *createBuffer(QRhiBuffer::Type type,
49
                                     QRhiBuffer::UsageFlags usage,
50
                                     quint32 size) = 0;
51
    virtual QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type,
52
                                                 const QSize &pixelSize,
53
                                                 int sampleCount,
54
                                                 QRhiRenderBuffer::Flags flags,
55
                                                 QRhiTexture::Format backingFormatHint) = 0;
56
    virtual QRhiTexture *createTexture(QRhiTexture::Format format,
57
                                       const QSize &pixelSize,
58
                                       int depth,
59
                                       int arraySize,
60
                                       int sampleCount,
61
                                       QRhiTexture::Flags flags) = 0;
62
    virtual QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
63
                                       QRhiSampler::Filter minFilter,
64
                                       QRhiSampler::Filter mipmapMode,
65
                                       QRhiSampler:: AddressMode u,
66
                                       QRhiSampler::AddressMode v,
67
                                       QRhiSampler::AddressMode w) = 0;
68
69
    virtual QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
70
                                                               QRhiTextureRenderTarget::Flags flags) = 0;
71
72
    virtual QRhiShadingRateMap *createShadingRateMap() = 0;
73
74
    virtual QRhiSwapChain *createSwapChain() = 0;
75
    virtual QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) = 0;
76
    virtual QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) = 0;
77
    virtual QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags) = 0;
78
    virtual QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) = 0;
79
    virtual QRhi::FrameOpResult finish() = 0;
80
81
    virtual void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) = 0;
82
83
    virtual void beginPass(QRhiCommandBuffer *cb,
84
                           QRhiRenderTarget *rt,
85
                           const QColor &colorClearValue,
86
                           const QRhiDepthStencilClearValue &depthStencilClearValue,
87
                           QRhiResourceUpdateBatch *resourceUpdates,
88
                           QRhiCommandBuffer::BeginPassFlags flags) = 0;
89
    virtual void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) = 0;
90
91
    virtual void setGraphicsPipeline(QRhiCommandBuffer *cb,
92
                                     QRhiGraphicsPipeline *ps) = 0;
93
94
    virtual void setShaderResources(QRhiCommandBuffer *cb,
95
                                    QRhiShaderResourceBindings *srb,
96
                                    int dynamicOffsetCount,
97
                                    const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) = 0;
98
99
    virtual void setVertexInput(QRhiCommandBuffer *cb,
100
                                int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
101
                                QRhiBuffer *indexBuf, quint32 indexOffset,
102
                                QRhiCommandBuffer::IndexFormat indexFormat) = 0;
103
104
    virtual void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) = 0;
105
    virtual void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) = 0;
106
    virtual void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) = 0;
107
    virtual void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) = 0;
108
    virtual void setShadingRate(QRhiCommandBuffer *cb, const QSize &coarsePixelSize) = 0;
109
110
    virtual void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
111
                      quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) = 0;
112
    virtual void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
113
                             quint32 instanceCount, quint32 firstIndex,
114
                             qint32 vertexOffset, quint32 firstInstance) = 0;
115
    virtual void drawIndirect(QRhiCommandBuffer *cb, QRhiBuffer *indirectBuffer,
116
                              quint32 offset, quint32 drawCount, quint32 stride) = 0;
117
    virtual void drawIndexedIndirect(QRhiCommandBuffer *cb, QRhiBuffer *indirectBuffer,
118
                                     quint32 offset, quint32 drawCount, quint32 stride) = 0;
119
120
    virtual void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) = 0;
121
    virtual void debugMarkEnd(QRhiCommandBuffer *cb) = 0;
122
    virtual void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) = 0;
123
124
    virtual void beginComputePass(QRhiCommandBuffer *cb,
125
                                  QRhiResourceUpdateBatch *resourceUpdates,
126
                                  QRhiCommandBuffer::BeginPassFlags flags) = 0;
127
    virtual void endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) = 0;
128
    virtual void setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps) = 0;
129
    virtual void dispatch(QRhiCommandBuffer *cb, int x, int y, int z) = 0;
130
131
    virtual const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) = 0;
132
    virtual void beginExternal(QRhiCommandBuffer *cb) = 0;
133
    virtual void endExternal(QRhiCommandBuffer *cb) = 0;
134
    virtual double lastCompletedGpuTime(QRhiCommandBuffer *cb) = 0;
135
136
    virtual QList<int> supportedSampleCounts() const = 0;
137
    virtual int ubufAlignment() const = 0;
138
    virtual QList<QSize> supportedShadingRates(int sampleCount) const = 0;
139
    virtual bool isYUpInFramebuffer() const = 0;
140
    virtual bool isYUpInNDC() const = 0;
141
    virtual bool isClipDepthZeroToOne() const = 0;
142
    virtual QMatrix4x4 clipSpaceCorrMatrix() const = 0;
143
    virtual bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const = 0;
144
    virtual bool isFeatureSupported(QRhi::Feature feature) const = 0;
145
    virtual int resourceLimit(QRhi::ResourceLimit limit) const = 0;
146
    virtual const QRhiNativeHandles *nativeHandles() = 0;
147
    virtual QRhiDriverInfo driverInfo() const = 0;
148
    virtual QRhiStats statistics() = 0;
149
    virtual bool makeThreadLocalNativeContextCurrent() = 0;
150
    virtual void setQueueSubmitParams(QRhiNativeHandles *params) = 0;
151
    virtual void releaseCachedResources() = 0;
152
    virtual bool isDeviceLost() const = 0;
153
154
    virtual QByteArray pipelineCacheData() = 0;
155
    virtual void setPipelineCacheData(const QByteArray &data) = 0;
156
157
    static QRhiImplementation *newInstance(QRhi::Implementation impl, QRhiInitParams *params, QRhiNativeHandles *importDevice);
158
    void prepareForCreate(QRhi *rhi, QRhi::Implementation impl, QRhi::Flags flags, QRhiAdapter *adapter);
159
160
    bool isCompressedFormat(QRhiTexture::Format format) const;
161
    void compressedFormatInfo(QRhiTexture::Format format, const QSize &size,
162
                              quint32 *bpl, quint32 *byteSize,
163
                              QSize *blockDim) const;
164
    void textureFormatInfo(QRhiTexture::Format format, const QSize &size,
165
                           quint32 *bpl, quint32 *byteSize, quint32 *bytesPerPixel) const;
166
    bool isStencilSupportingFormat(QRhiTexture::Format format) const;
167
168
    void registerResource(QRhiResource *res, bool ownsNativeResources = true)
169
0
    {
170
        // The ownsNativeResources is relevant for the (graphics resource) leak
171
        // check in ~QRhiImplementation; when false, the registration's sole
172
        // purpose is to automatically null out the resource's m_rhi pointer in
173
        // case the rhi goes away first. (which should not happen in
174
        // well-written applications but we try to be graceful)
175
0
        resources.insert(res, ownsNativeResources);
176
0
    }
177
178
    void unregisterResource(QRhiResource *res)
179
0
    {
180
0
        resources.remove(res);
181
0
    }
182
183
    void addDeleteLater(QRhiResource *res)
184
0
    {
185
0
        if (inFrame)
186
0
            pendingDeleteResources.insert(res);
187
0
        else
188
0
            delete res;
189
0
    }
190
191
    void addCleanupCallback(const QRhi::CleanupCallback &callback)
192
0
    {
193
0
        cleanupCallbacks.append(callback);
194
0
    }
195
196
    void addCleanupCallback(const void *key, const QRhi::CleanupCallback &callback)
197
0
    {
198
0
        keyedCleanupCallbacks[key] = callback;
199
0
    }
200
201
    void removeCleanupCallback(const void *key)
202
0
    {
203
0
        keyedCleanupCallbacks.remove(key);
204
0
    }
205
206
    bool sanityCheckGraphicsPipeline(QRhiGraphicsPipeline *ps);
207
    bool sanityCheckShaderResourceBindings(QRhiShaderResourceBindings *srb);
208
    bool sanityCheckResourceOwnership(QRhiResource *maybeResource);
209
    void updateLayoutDesc(QRhiShaderResourceBindings *srb);
210
211
    quint32 pipelineCacheRhiId() const
212
0
    {
213
0
        const quint32 ver = (QT_VERSION_MAJOR << 16) | (QT_VERSION_MINOR << 8) | (QT_VERSION_PATCH);
214
0
        return (quint32(implType) << 24) | ver;
215
0
    }
216
217
    void pipelineCreationStart()
218
0
    {
219
0
        pipelineCreationTimer.start();
220
0
    }
221
222
    void pipelineCreationEnd()
223
0
    {
224
0
        accumulatedPipelineCreationTime += pipelineCreationTimer.elapsed();
225
0
    }
226
227
    qint64 totalPipelineCreationTime() const
228
0
    {
229
0
        return accumulatedPipelineCreationTime;
230
0
    }
231
232
    QRhiVertexInputAttribute::Format shaderDescVariableFormatToVertexInputFormat(QShaderDescription::VariableType type) const;
233
    quint32 byteSizePerVertexForVertexInputFormat(QRhiVertexInputAttribute::Format format) const;
234
235
    static const QRhiShaderResourceBinding::Data *shaderResourceBindingData(const QRhiShaderResourceBinding &binding)
236
0
    {
237
0
        return &binding.d;
238
0
    }
239
240
    static QRhiShaderResourceBinding::Data *shaderResourceBindingData(QRhiShaderResourceBinding &binding)
241
0
    {
242
0
        return &binding.d;
243
0
    }
244
245
    static bool sortedBindingLessThan(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b)
246
0
    {
247
0
        return a.d.binding < b.d.binding;
248
0
    }
249
250
    int effectiveSampleCount(int sampleCount) const;
251
    QSize clampedSubResourceUploadSize(QSize size, QPoint dstPos, int level, QSize textureSizeAtLevelZero, bool warn = true);
252
253
    void runCleanup();
254
255
    QRhi *q;
256
257
    static const int MAX_SHADER_CACHE_ENTRIES = 128;
258
259
    bool debugMarkers = false;
260
    int currentFrameSlot = 0; // for vk, mtl, and similar. unused by gl and d3d11.
261
    bool inFrame = false;
262
263
    QRhiAdapter *requestedRhiAdapter = nullptr;
264
265
private:
266
    QRhi::Implementation implType;
267
    QThread *implThread;
268
    QVarLengthArray<QRhiResourceUpdateBatch *, 4> resUpdPool;
269
    quint64 resUpdPoolMap = 0;
270
    int lastResUpdIdx = -1;
271
    QHash<QRhiResource *, bool> resources;
272
    QSet<QRhiResource *> pendingDeleteResources;
273
    QVarLengthArray<QRhi::CleanupCallback, 4> cleanupCallbacks;
274
    QHash<const void *, QRhi::CleanupCallback> keyedCleanupCallbacks;
275
    QElapsedTimer pipelineCreationTimer;
276
    qint64 accumulatedPipelineCreationTime = 0;
277
278
    friend class QRhi;
279
    friend class QRhiResourceUpdateBatchPrivate;
280
    friend class QRhiBufferData;
281
};
282
283
enum QRhiTargetRectBoundMode
284
{
285
    UnBounded,
286
    Bounded
287
};
288
289
template<QRhiTargetRectBoundMode boundingMode, typename T, size_t N>
290
bool qrhi_toTopLeftRenderTargetRect(const QSize &outputSize, const std::array<T, N> &r,
291
                                    T *x, T *y, T *w, T *h)
292
{
293
    // x,y are bottom-left in QRhiScissor and QRhiViewport but top-left in
294
    // Vulkan/Metal/D3D. Our input is an OpenGL-style scissor rect where both
295
    // negative x or y, and partly or completely out of bounds rects are
296
    // allowed. The only thing the input here cannot have is a negative width
297
    // or height. We must handle all other input gracefully, clamping to a zero
298
    // width or height rect in the worst case, and ensuring the resulting rect
299
    // is inside the rendertarget's bounds because some APIs' validation/debug
300
    // layers are allergic to out of bounds scissor rects.
301
302
    const T outputWidth = outputSize.width();
303
    const T outputHeight = outputSize.height();
304
    const T inputWidth = r[2];
305
    const T inputHeight = r[3];
306
307
    if (inputWidth < 0 || inputHeight < 0)
308
        return false;
309
310
    *x = r[0];
311
    *y = outputHeight - (r[1] + inputHeight);
312
    *w = inputWidth;
313
    *h = inputHeight;
314
315
    if (boundingMode == Bounded) {
316
        const T widthOffset = *x < 0 ? -*x : 0;
317
        const T heightOffset = *y < 0 ? -*y : 0;
318
        *w = *x < outputWidth ? qMax<T>(0, inputWidth - widthOffset) : 0;
319
        *h = *y < outputHeight ? qMax<T>(0, inputHeight - heightOffset) : 0;
320
321
        if (outputWidth > 0)
322
            *x = qBound<T>(0, *x, outputWidth - 1);
323
        if (outputHeight > 0)
324
            *y = qBound<T>(0, *y, outputHeight - 1);
325
326
        if (*x + *w > outputWidth)
327
            *w = qMax<T>(0, outputWidth - *x);
328
        if (*y + *h > outputHeight)
329
            *h = qMax<T>(0, outputHeight - *y);
330
    }
331
    return true;
332
}
333
334
struct QRhiBufferDataPrivate
335
{
336
    Q_DISABLE_COPY_MOVE(QRhiBufferDataPrivate)
337
0
    QRhiBufferDataPrivate() { } // don't value-initialize smallData
338
    int ref = 1;
339
    quint32 size = 0;
340
    QByteArray largeData;
341
    static constexpr quint32 SMALL_DATA_SIZE = 1024;
342
    char smallData[SMALL_DATA_SIZE];
343
};
344
345
// no detach-with-contents, no atomic refcount, no shrink
346
class QRhiBufferData
347
{
348
public:
349
0
    QRhiBufferData() = default;
350
    ~QRhiBufferData()
351
0
    {
352
0
        if (d && !--d->ref)
353
0
            delete d;
354
0
    }
355
    QRhiBufferData(const QRhiBufferData &other)
356
0
        : d(other.d)
357
0
    {
358
0
        if (d)
359
0
            d->ref += 1;
360
0
    }
361
    QRhiBufferData &operator=(const QRhiBufferData &other)
362
0
    {
363
0
        if (d == other.d)
364
0
            return *this;
365
0
        if (other.d)
366
0
            other.d->ref += 1;
367
0
        if (d && !--d->ref)
368
0
            delete d;
369
0
        d = other.d;
370
0
        return *this;
371
0
    }
372
    const char *constData() const
373
0
    {
374
0
        return d ? (d->size <= QRhiBufferDataPrivate::SMALL_DATA_SIZE ? d->smallData : d->largeData.constData()) : nullptr;
375
0
    }
376
    quint32 size() const
377
0
    {
378
0
        return d ? d->size : 0;
379
0
    }
380
    quint32 largeAlloc() const
381
0
    {
382
0
        return d ? d->largeData.size() : 0;
383
0
    }
384
    void assign(const char *s, quint32 size)
385
0
    {
386
0
        if (!d) {
387
0
            d = new QRhiBufferDataPrivate;
388
0
        } else if (d->ref != 1) {
389
0
            if (QRHI_LOG_RUB().isDebugEnabled())
390
0
                qDebug("[rub] QRhiBufferData %p/%p new backing due to no-copy detach, ref was %d", this, d, d->ref);
391
0
            d->ref -= 1;
392
0
            d = new QRhiBufferDataPrivate;
393
0
        }
394
0
        d->size = size;
395
0
        if (size <= QRhiBufferDataPrivate::SMALL_DATA_SIZE) {
396
0
            memcpy(d->smallData, s, size);
397
0
        } else {
398
0
            if (QRHI_LOG_RUB().isDebugEnabled() && largeAlloc() < size)
399
0
                qDebug("[rub] QRhiBufferData %p/%p new large data allocation %u -> %u", this, d, largeAlloc(), size);
400
0
            d->largeData.assign(QByteArrayView(s, size)); // keeps capacity
401
0
        }
402
0
    }
403
    void assign(QByteArray data)
404
0
    {
405
0
        if (!d) {
406
0
            d = new QRhiBufferDataPrivate;
407
0
        } else if (d->ref != 1) {
408
0
            if (QRHI_LOG_RUB().isDebugEnabled())
409
0
                qDebug("[rub] QRhiBufferData %p/%p new backing due to no-copy detach, ref was %d", this, d, d->ref);
410
0
            d->ref -= 1;
411
0
            d = new QRhiBufferDataPrivate;
412
0
        }
413
0
        d->size = data.size();
414
0
        if (d->size <= QRhiBufferDataPrivate::SMALL_DATA_SIZE) {
415
0
            memcpy(d->smallData, data.constData(), data.size());
416
0
        } else {
417
0
            d->largeData = std::move(data);
418
0
        }
419
0
    }
420
private:
421
    QRhiBufferDataPrivate *d = nullptr;
422
};
423
424
Q_DECLARE_TYPEINFO(QRhiBufferData, Q_RELOCATABLE_TYPE);
425
426
class QRhiResourceUpdateBatchPrivate
427
{
428
public:
429
    struct BufferOp {
430
        enum Type {
431
            DynamicUpdate,
432
            StaticUpload,
433
            Read
434
        };
435
        Type type;
436
        QRhiBuffer *buf;
437
        quint32 offset;
438
        QRhiBufferData data;
439
        quint32 readSize;
440
        QRhiReadbackResult *result;
441
442
        static BufferOp dynamicUpdate(QRhiBuffer *buf, quint32 offset, quint32 size, const void *data)
443
0
        {
444
0
            BufferOp op = {};
445
0
            changeToDynamicUpdate(&op, buf, offset, size, data);
446
0
            return op;
447
0
        }
448
449
        static void changeToDynamicUpdate(BufferOp *op, QRhiBuffer *buf, quint32 offset, quint32 size, const void *data)
450
0
        {
451
0
            op->type = DynamicUpdate;
452
0
            op->buf = buf;
453
0
            op->offset = offset;
454
0
            const int effectiveSize = size ? size : buf->size();
455
0
            op->data.assign(reinterpret_cast<const char *>(data), effectiveSize);
456
0
        }
457
458
        static BufferOp dynamicUpdate(QRhiBuffer *buf, quint32 offset, QByteArray data)
459
0
        {
460
0
            BufferOp op = {};
461
0
            changeToDynamicUpdate(&op, buf, offset, std::move(data));
462
0
            return op;
463
0
        }
464
465
        static void changeToDynamicUpdate(BufferOp *op, QRhiBuffer *buf, quint32 offset, QByteArray data)
466
0
        {
467
0
            op->type = DynamicUpdate;
468
0
            op->buf = buf;
469
0
            op->offset = offset;
470
0
            op->data.assign(std::move(data));
471
0
        }
472
473
        static BufferOp staticUpload(QRhiBuffer *buf, quint32 offset, quint32 size, const void *data)
474
0
        {
475
0
            BufferOp op = {};
476
0
            changeToStaticUpload(&op, buf, offset, size, data);
477
0
            return op;
478
0
        }
479
480
        static void changeToStaticUpload(BufferOp *op, QRhiBuffer *buf, quint32 offset, quint32 size, const void *data)
481
0
        {
482
0
            op->type = StaticUpload;
483
0
            op->buf = buf;
484
0
            op->offset = offset;
485
0
            const int effectiveSize = size ? size : buf->size();
486
0
            op->data.assign(reinterpret_cast<const char *>(data), effectiveSize);
487
0
        }
488
489
        static BufferOp staticUpload(QRhiBuffer *buf, quint32 offset, QByteArray data)
490
0
        {
491
0
            BufferOp op = {};
492
0
            changeToStaticUpload(&op, buf, offset, std::move(data));
493
0
            return op;
494
0
        }
495
496
        static void changeToStaticUpload(BufferOp *op, QRhiBuffer *buf, quint32 offset, QByteArray data)
497
0
        {
498
0
            op->type = StaticUpload;
499
0
            op->buf = buf;
500
0
            op->offset = offset;
501
0
            op->data.assign(std::move(data));
502
0
        }
503
504
        static BufferOp read(QRhiBuffer *buf, quint32 offset, quint32 size, QRhiReadbackResult *result)
505
0
        {
506
0
            BufferOp op = {};
507
0
            op.type = Read;
508
0
            op.buf = buf;
509
0
            op.offset = offset;
510
0
            op.readSize = size;
511
0
            op.result = result;
512
0
            return op;
513
0
        }
514
    };
515
516
    struct TextureOp {
517
        enum Type {
518
            Upload,
519
            Copy,
520
            Read,
521
            GenMips
522
        };
523
        Type type;
524
        QRhiTexture *dst;
525
        // Specifying multiple uploads for a subresource must be supported.
526
        // In the backend this can then end up, where applicable, as a
527
        // single, batched copy operation with only one set of barriers.
528
        // This helps when doing for example glyph cache fills.
529
        using MipLevelUploadList = std::array<QVector<QRhiTextureSubresourceUploadDescription>, QRhi::MAX_MIP_LEVELS>;
530
        QVarLengthArray<MipLevelUploadList, 6> subresDesc;
531
        QRhiTexture *src;
532
        QRhiTextureCopyDescription desc;
533
        QRhiReadbackDescription rb;
534
        QRhiReadbackResult *result;
535
536
        static TextureOp upload(QRhiTexture *tex, const QRhiTextureUploadDescription &desc)
537
0
        {
538
0
            TextureOp op = {};
539
0
            op.type = Upload;
540
0
            op.dst = tex;
541
0
            int maxLayer = -1;
542
0
            for (auto it = desc.cbeginEntries(), itEnd = desc.cendEntries(); it != itEnd; ++it) {
543
0
                if (it->layer() > maxLayer)
544
0
                    maxLayer = it->layer();
545
0
            }
546
0
            op.subresDesc.resize(maxLayer + 1);
547
0
            for (auto it = desc.cbeginEntries(), itEnd = desc.cendEntries(); it != itEnd; ++it)
548
0
                op.subresDesc[it->layer()][it->level()].append(it->description());
549
0
            return op;
550
0
        }
551
552
        static TextureOp copy(QRhiTexture *dst, QRhiTexture *src, const QRhiTextureCopyDescription &desc)
553
0
        {
554
0
            TextureOp op = {};
555
0
            op.type = Copy;
556
0
            op.dst = dst;
557
0
            op.src = src;
558
0
            op.desc = desc;
559
0
            return op;
560
0
        }
561
562
        static TextureOp read(const QRhiReadbackDescription &rb, QRhiReadbackResult *result)
563
0
        {
564
0
            TextureOp op = {};
565
0
            op.type = Read;
566
0
            op.rb = rb;
567
0
            op.result = result;
568
0
            return op;
569
0
        }
570
571
        static TextureOp genMips(QRhiTexture *tex)
572
0
        {
573
0
            TextureOp op = {};
574
0
            op.type = GenMips;
575
0
            op.dst = tex;
576
0
            return op;
577
0
        }
578
    };
579
580
    int activeBufferOpCount = 0; // this is the real number of used elements in bufferOps, not bufferOps.count()
581
    static const int BUFFER_OPS_STATIC_ALLOC = 64;
582
    QVarLengthArray<BufferOp, BUFFER_OPS_STATIC_ALLOC> bufferOps;
583
584
    int activeTextureOpCount = 0; // this is the real number of used elements in textureOps, not textureOps.count()
585
    static const int TEXTURE_OPS_STATIC_ALLOC = 32;
586
    QVarLengthArray<TextureOp, TEXTURE_OPS_STATIC_ALLOC> textureOps;
587
588
    QRhiResourceUpdateBatch *q = nullptr;
589
    QRhiImplementation *rhi = nullptr;
590
    int poolIndex = -1;
591
592
    void free();
593
    void merge(QRhiResourceUpdateBatchPrivate *other);
594
    bool hasOptimalCapacity() const;
595
    void trimOpLists();
596
597
0
    static QRhiResourceUpdateBatchPrivate *get(QRhiResourceUpdateBatch *b) { return b->d; }
598
};
599
600
template<typename T>
601
struct QRhiBatchedBindings
602
{
603
    void feed(int binding, T resource) { // binding must be strictly increasing
604
        if (curBinding == -1 || binding > curBinding + 1) {
605
            finish();
606
            curBatch.startBinding = binding;
607
            curBatch.resources.clear();
608
            curBatch.resources.append(resource);
609
        } else {
610
            Q_ASSERT(binding == curBinding + 1);
611
            curBatch.resources.append(resource);
612
        }
613
        curBinding = binding;
614
    }
615
616
    bool finish() {
617
        if (!curBatch.resources.isEmpty())
618
            batches.append(curBatch);
619
        return !batches.isEmpty();
620
    }
621
622
    void clear() {
623
        batches.clear();
624
        curBatch.resources.clear();
625
        curBinding = -1;
626
    }
627
628
    struct Batch {
629
        uint startBinding;
630
        QVector<T> resources;
631
632
        bool operator==(const Batch &other) const
633
        {
634
            return startBinding == other.startBinding && resources == other.resources;
635
        }
636
637
        bool operator!=(const Batch &other) const
638
        {
639
            return !operator==(other);
640
        }
641
    };
642
643
    // some backends make copies of QRhiBatchedBindings -> implicit sharing and
644
    // not having a (possibly wasted) prealloc are beneficial -> use QVector
645
    // instead of QVLA
646
    QVector<Batch> batches; // sorted by startBinding
647
648
    bool operator==(const QRhiBatchedBindings<T> &other) const
649
    {
650
        return batches == other.batches;
651
    }
652
653
    bool operator!=(const QRhiBatchedBindings<T> &other) const
654
    {
655
        return !operator==(other);
656
    }
657
658
private:
659
    Batch curBatch;
660
    int curBinding = -1;
661
};
662
663
class QRhiGlobalObjectIdGenerator
664
{
665
public:
666
#ifdef Q_ATOMIC_INT64_IS_SUPPORTED
667
    using Type = quint64;
668
#else
669
    using Type = quint32;
670
#endif
671
    static Type newId();
672
};
673
674
class QRhiPassResourceTracker
675
{
676
public:
677
    bool isEmpty() const;
678
    void reset();
679
680
    struct UsageState {
681
        int layout;
682
        int access;
683
        int stage;
684
    };
685
686
    enum BufferStage {
687
        BufIndirectDrawStage,
688
        BufVertexInputStage,
689
        BufVertexStage,
690
        BufTCStage,
691
        BufTEStage,
692
        BufFragmentStage,
693
        BufComputeStage,
694
        BufGeometryStage
695
    };
696
697
    enum BufferAccess {
698
        BufIndirectDraw,
699
        BufVertexInput,
700
        BufIndexRead,
701
        BufUniformRead,
702
        BufStorageLoad,
703
        BufStorageStore,
704
        BufStorageLoadStore
705
    };
706
707
    void registerBuffer(QRhiBuffer *buf, int slot, BufferAccess *access, BufferStage *stage,
708
                        const UsageState &state);
709
710
    enum TextureStage {
711
        TexVertexStage,
712
        TexTCStage,
713
        TexTEStage,
714
        TexFragmentStage,
715
        TexColorOutputStage,
716
        TexDepthOutputStage,
717
        TexComputeStage,
718
        TexGeometryStage
719
    };
720
721
    enum TextureAccess {
722
        TexSample,
723
        TexColorOutput,
724
        TexDepthOutput,
725
        TexStorageLoad,
726
        TexStorageStore,
727
        TexStorageLoadStore,
728
        TexShadingRate
729
    };
730
731
    void registerTexture(QRhiTexture *tex, TextureAccess *access, TextureStage *stage,
732
                         const UsageState &state);
733
734
    struct Buffer {
735
        int slot;
736
        BufferAccess access;
737
        BufferStage stage;
738
        UsageState stateAtPassBegin;
739
    };
740
741
0
    const QVarLengthFlatMap<QRhiBuffer *, Buffer, 12> &buffers() const { return m_buffers; }
742
743
    struct Texture {
744
        TextureAccess access;
745
        TextureStage stage;
746
        UsageState stateAtPassBegin;
747
    };
748
749
0
    const QVarLengthFlatMap<QRhiTexture *, Texture, 12> &textures() const { return m_textures; }
750
751
    static BufferStage toPassTrackerBufferStage(QRhiShaderResourceBinding::StageFlags stages);
752
    static TextureStage toPassTrackerTextureStage(QRhiShaderResourceBinding::StageFlags stages);
753
754
private:
755
    QVarLengthFlatMap<QRhiBuffer *, Buffer, 12> m_buffers;
756
    QVarLengthFlatMap<QRhiTexture *, Texture, 12> m_textures;
757
};
758
759
Q_DECLARE_TYPEINFO(QRhiPassResourceTracker::Buffer, Q_RELOCATABLE_TYPE);
760
Q_DECLARE_TYPEINFO(QRhiPassResourceTracker::Texture, Q_RELOCATABLE_TYPE);
761
762
template<typename T, int GROW = 1024>
763
class QRhiBackendCommandList
764
{
765
public:
766
    QRhiBackendCommandList() = default;
767
    ~QRhiBackendCommandList() { delete[] v; }
768
    inline void reset() { p = 0; }
769
    inline bool isEmpty() const { return p == 0; }
770
    inline T &get() {
771
        if (p == a) {
772
            a += GROW;
773
            T *nv = new T[a];
774
            if (v) {
775
                memcpy(nv, v, p * sizeof(T));
776
                delete[] v;
777
            }
778
            v = nv;
779
        }
780
        return v[p++];
781
    }
782
    inline void unget() { --p; }
783
    inline T *cbegin() const { return v; }
784
    inline T *cend() const { return v + p; }
785
    inline T *begin() { return v; }
786
    inline T *end() { return v + p; }
787
private:
788
    Q_DISABLE_COPY(QRhiBackendCommandList)
789
    T *v = nullptr;
790
    int a = 0;
791
    int p = 0;
792
};
793
794
struct QRhiRenderTargetAttachmentTracker
795
{
796
    struct ResId { quint64 id; uint generation; };
797
    using ResIdList = QVarLengthArray<ResId, 8 * 2 + 1>; // color, resolve, ds
798
799
    template<typename TexType, typename RenderBufferType>
800
    static void updateResIdList(const QRhiTextureRenderTargetDescription &desc, ResIdList *dst);
801
802
    template<typename TexType, typename RenderBufferType>
803
    static bool isUpToDate(const QRhiTextureRenderTargetDescription &desc, const ResIdList &currentResIdList);
804
};
805
806
inline bool operator==(const QRhiRenderTargetAttachmentTracker::ResId &a, const QRhiRenderTargetAttachmentTracker::ResId &b)
807
0
{
808
0
    return a.id == b.id && a.generation == b.generation;
809
0
}
810
811
inline bool operator!=(const QRhiRenderTargetAttachmentTracker::ResId &a, const QRhiRenderTargetAttachmentTracker::ResId &b)
812
0
{
813
0
    return !(a == b);
814
0
}
815
816
template<typename TexType, typename RenderBufferType>
817
void QRhiRenderTargetAttachmentTracker::updateResIdList(const QRhiTextureRenderTargetDescription &desc, ResIdList *dst)
818
0
{
819
0
    const bool hasDepthStencil = desc.depthStencilBuffer() || desc.depthTexture();
820
0
    dst->resize(desc.colorAttachmentCount() * 2 + (hasDepthStencil ? 1 : 0));
821
0
    int n = 0;
822
0
    for (auto it = desc.cbeginColorAttachments(), itEnd = desc.cendColorAttachments(); it != itEnd; ++it, ++n) {
823
0
        const QRhiColorAttachment &colorAtt(*it);
824
0
        if (colorAtt.texture()) {
825
0
            TexType *texD = QRHI_RES(TexType, colorAtt.texture());
826
0
            (*dst)[n] = { texD->globalResourceId(), texD->generation };
827
0
        } else if (colorAtt.renderBuffer()) {
828
0
            RenderBufferType *rbD = QRHI_RES(RenderBufferType, colorAtt.renderBuffer());
829
0
            (*dst)[n] = { rbD->globalResourceId(), rbD->generation };
830
0
        } else {
831
0
            (*dst)[n] = { 0, 0 };
832
0
        }
833
0
        ++n;
834
0
        if (colorAtt.resolveTexture()) {
835
0
            TexType *texD = QRHI_RES(TexType, colorAtt.resolveTexture());
836
0
            (*dst)[n] = { texD->globalResourceId(), texD->generation };
837
0
        } else {
838
0
            (*dst)[n] = { 0, 0 };
839
0
        }
840
0
    }
841
0
    if (hasDepthStencil) {
842
0
        if (desc.depthTexture()) {
843
0
            TexType *depthTexD = QRHI_RES(TexType, desc.depthTexture());
844
0
            (*dst)[n] = { depthTexD->globalResourceId(), depthTexD->generation };
845
0
        } else if (desc.depthStencilBuffer()) {
846
0
            RenderBufferType *depthRbD = QRHI_RES(RenderBufferType, desc.depthStencilBuffer());
847
0
            (*dst)[n] = { depthRbD->globalResourceId(), depthRbD->generation };
848
0
        } else {
849
0
            (*dst)[n] = { 0, 0 };
850
0
        }
851
0
    }
852
0
}
853
854
template<typename TexType, typename RenderBufferType>
855
bool QRhiRenderTargetAttachmentTracker::isUpToDate(const QRhiTextureRenderTargetDescription &desc, const ResIdList &currentResIdList)
856
0
{
857
    // Just as setShaderResources() recognizes if an srb's referenced
858
    // resources have been rebuilt (got a create() since the srb's
859
    // create()), we should do the same for the textures and renderbuffers
860
    // referenced from the rendertarget. It is not uncommon that a texture
861
    // or ds buffer gets resized due to following a window size in some
862
    // form, which involves a create() on them. It is then nice if the
863
    // render target auto-rebuilds in beginPass().
864
865
0
    ResIdList resIdList;
866
0
    updateResIdList<TexType, RenderBufferType>(desc, &resIdList);
867
0
    return resIdList == currentResIdList;
868
0
}
869
870
template<typename T>
871
inline T *qrhi_objectFromProxyData(QRhiSwapChainProxyData *pd, QWindow *window, QRhi::Implementation impl, uint objectIndex)
872
{
873
    Q_ASSERT(objectIndex < std::size(pd->reserved));
874
    if (!pd->reserved[objectIndex]) // // was not set, no other choice, do it here, whatever thread this is
875
        *pd = QRhi::updateSwapChainProxyData(impl, window);
876
    return static_cast<T *>(pd->reserved[objectIndex]);
877
}
878
879
QT_END_NAMESPACE
880
881
#endif