Coverage Report

Created: 2026-07-30 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/qtbase/src/gui/rhi/qrhinull.cpp
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
#include "qrhinull_p.h"
6
#include <qmath.h>
7
#include <QPainter>
8
9
QT_BEGIN_NAMESPACE
10
11
/*!
12
    \class QRhiNullInitParams
13
    \inmodule QtGuiPrivate
14
    \inheaderfile rhi/qrhi.h
15
    \since 6.6
16
    \brief Null backend specific initialization parameters.
17
18
    \note This is a RHI API with limited compatibility guarantees, see \l QRhi
19
    for details.
20
21
    A Null QRhi needs no special parameters for initialization.
22
23
    \badcode
24
        QRhiNullInitParams params;
25
        rhi = QRhi::create(QRhi::Null, &params);
26
    \endcode
27
28
    The Null backend does not issue any graphics calls and creates no
29
    resources. All QRhi operations will succeed as normal so applications can
30
    still be run, albeit potentially at an unthrottled speed, depending on
31
    their frame rendering strategy.
32
 */
33
34
/*!
35
    \class QRhiNullNativeHandles
36
    \inmodule QtGuiPrivate
37
    \inheaderfile rhi/qrhi.h
38
    \since 6.6
39
    \brief Empty.
40
41
    \note This is a RHI API with limited compatibility guarantees, see \l QRhi
42
    for details.
43
 */
44
45
QRhiNull::QRhiNull(QRhiNullInitParams *params)
46
0
    : offscreenCommandBuffer(this)
47
0
{
48
0
    Q_UNUSED(params);
49
0
}
50
51
bool QRhiNull::create(QRhi::Flags flags)
52
0
{
53
0
    Q_UNUSED(flags);
54
0
    return true;
55
0
}
56
57
void QRhiNull::destroy()
58
0
{
59
0
}
60
61
QList<int> QRhiNull::supportedSampleCounts() const
62
0
{
63
0
    return { 1 };
64
0
}
65
66
QList<QSize> QRhiNull::supportedShadingRates(int sampleCount) const
67
0
{
68
0
    Q_UNUSED(sampleCount);
69
0
    return { QSize(1, 1) };
70
0
}
71
72
QRhiSwapChain *QRhiNull::createSwapChain()
73
0
{
74
0
    return new QNullSwapChain(this);
75
0
}
76
77
QRhiBuffer *QRhiNull::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size)
78
0
{
79
0
    return new QNullBuffer(this, type, usage, size);
80
0
}
81
82
int QRhiNull::ubufAlignment() const
83
0
{
84
0
    return 256;
85
0
}
86
87
bool QRhiNull::isYUpInFramebuffer() const
88
0
{
89
0
    return false;
90
0
}
91
92
bool QRhiNull::isYUpInNDC() const
93
0
{
94
0
    return true;
95
0
}
96
97
bool QRhiNull::isClipDepthZeroToOne() const
98
0
{
99
0
    return true;
100
0
}
101
102
QMatrix4x4 QRhiNull::clipSpaceCorrMatrix() const
103
0
{
104
0
    return QMatrix4x4(); // identity
105
0
}
106
107
bool QRhiNull::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const
108
0
{
109
0
    Q_UNUSED(format);
110
0
    Q_UNUSED(flags);
111
0
    return true;
112
0
}
113
114
bool QRhiNull::isFeatureSupported(QRhi::Feature feature) const
115
0
{
116
0
    Q_UNUSED(feature);
117
0
    return true;
118
0
}
119
120
int QRhiNull::resourceLimit(QRhi::ResourceLimit limit) const
121
0
{
122
0
    switch (limit) {
123
0
    case QRhi::TextureSizeMin:
124
0
        return 1;
125
0
    case QRhi::TextureSizeMax:
126
0
        return 16384;
127
0
    case QRhi::MaxColorAttachments:
128
0
        return 8;
129
0
    case QRhi::FramesInFlight:
130
0
        return 1;
131
0
    case QRhi::MaxAsyncReadbackFrames:
132
0
        return 1;
133
0
    case QRhi::MaxThreadGroupsPerDimension:
134
0
        return 0;
135
0
    case QRhi::MaxThreadsPerThreadGroup:
136
0
        return 0;
137
0
    case QRhi::MaxThreadGroupX:
138
0
        return 0;
139
0
    case QRhi::MaxThreadGroupY:
140
0
        return 0;
141
0
    case QRhi::MaxThreadGroupZ:
142
0
        return 0;
143
0
    case QRhi::TextureArraySizeMax:
144
0
        return 2048;
145
0
    case QRhi::MaxUniformBufferRange:
146
0
        return 65536;
147
0
    case QRhi::MaxVertexInputs:
148
0
        return 32;
149
0
    case QRhi::MaxVertexOutputs:
150
0
        return 32;
151
0
    case QRhi::ShadingRateImageTileSize:
152
0
        return 0;
153
0
    }
154
155
0
    Q_UNREACHABLE_RETURN(0);
156
0
}
157
158
const QRhiNativeHandles *QRhiNull::nativeHandles()
159
0
{
160
0
    return &nativeHandlesStruct;
161
0
}
162
163
QRhiDriverInfo QRhiNull::driverInfo() const
164
0
{
165
0
    QRhiDriverInfo info;
166
0
    info.deviceName = QByteArrayLiteral("Null");
167
0
    return info;
168
0
}
169
170
QRhiStats QRhiNull::statistics()
171
0
{
172
0
    return {};
173
0
}
174
175
bool QRhiNull::makeThreadLocalNativeContextCurrent()
176
0
{
177
    // not applicable
178
0
    return false;
179
0
}
180
181
void QRhiNull::setQueueSubmitParams(QRhiNativeHandles *)
182
0
{
183
    // not applicable
184
0
}
185
186
void QRhiNull::releaseCachedResources()
187
0
{
188
    // nothing to do here
189
0
}
190
191
bool QRhiNull::isDeviceLost() const
192
0
{
193
0
    return false;
194
0
}
195
196
QByteArray QRhiNull::pipelineCacheData()
197
0
{
198
0
    return QByteArray();
199
0
}
200
201
void QRhiNull::setPipelineCacheData(const QByteArray &data)
202
0
{
203
0
    Q_UNUSED(data);
204
0
}
205
206
QRhiRenderBuffer *QRhiNull::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
207
                                               int sampleCount, QRhiRenderBuffer::Flags flags,
208
                                               QRhiTexture::Format backingFormatHint)
209
0
{
210
0
    return new QNullRenderBuffer(this, type, pixelSize, sampleCount, flags, backingFormatHint);
211
0
}
212
213
QRhiTexture *QRhiNull::createTexture(QRhiTexture::Format format,
214
                                     const QSize &pixelSize, int depth, int arraySize,
215
                                     int sampleCount, QRhiTexture::Flags flags)
216
0
{
217
0
    return new QNullTexture(this, format, pixelSize, depth, arraySize, sampleCount, flags);
218
0
}
219
220
QRhiSampler *QRhiNull::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
221
                                     QRhiSampler::Filter mipmapMode,
222
                                     QRhiSampler::AddressMode u, QRhiSampler::AddressMode v, QRhiSampler::AddressMode w)
223
0
{
224
0
    return new QNullSampler(this, magFilter, minFilter, mipmapMode, u, v, w);
225
0
}
226
227
QRhiTextureRenderTarget *QRhiNull::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
228
                                                             QRhiTextureRenderTarget::Flags flags)
229
0
{
230
0
    return new QNullTextureRenderTarget(this, desc, flags);
231
0
}
232
233
QRhiShadingRateMap *QRhiNull::createShadingRateMap()
234
0
{
235
0
    return nullptr;
236
0
}
237
238
QRhiGraphicsPipeline *QRhiNull::createGraphicsPipeline()
239
0
{
240
0
    return new QNullGraphicsPipeline(this);
241
0
}
242
243
QRhiComputePipeline *QRhiNull::createComputePipeline()
244
0
{
245
0
    return new QNullComputePipeline(this);
246
0
}
247
248
QRhiShaderResourceBindings *QRhiNull::createShaderResourceBindings()
249
0
{
250
0
    return new QNullShaderResourceBindings(this);
251
0
}
252
253
void QRhiNull::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps)
254
0
{
255
0
    Q_UNUSED(cb);
256
0
    Q_UNUSED(ps);
257
0
}
258
259
void QRhiNull::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb,
260
                                  int dynamicOffsetCount,
261
                                  const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
262
0
{
263
0
    Q_UNUSED(cb);
264
0
    Q_UNUSED(srb);
265
0
    Q_UNUSED(dynamicOffsetCount);
266
0
    Q_UNUSED(dynamicOffsets);
267
0
}
268
269
void QRhiNull::setVertexInput(QRhiCommandBuffer *cb,
270
                              int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
271
                              QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
272
0
{
273
0
    Q_UNUSED(cb);
274
0
    Q_UNUSED(startBinding);
275
0
    Q_UNUSED(bindingCount);
276
0
    Q_UNUSED(bindings);
277
0
    Q_UNUSED(indexBuf);
278
0
    Q_UNUSED(indexOffset);
279
0
    Q_UNUSED(indexFormat);
280
0
}
281
282
void QRhiNull::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport)
283
0
{
284
0
    Q_UNUSED(cb);
285
0
    Q_UNUSED(viewport);
286
0
}
287
288
void QRhiNull::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor)
289
0
{
290
0
    Q_UNUSED(cb);
291
0
    Q_UNUSED(scissor);
292
0
}
293
294
void QRhiNull::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c)
295
0
{
296
0
    Q_UNUSED(cb);
297
0
    Q_UNUSED(c);
298
0
}
299
300
void QRhiNull::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
301
0
{
302
0
    Q_UNUSED(cb);
303
0
    Q_UNUSED(refValue);
304
0
}
305
306
void QRhiNull::setShadingRate(QRhiCommandBuffer *cb, const QSize &coarsePixelSize)
307
0
{
308
0
    Q_UNUSED(cb);
309
0
    Q_UNUSED(coarsePixelSize);
310
0
}
311
312
void QRhiNull::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
313
                    quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
314
0
{
315
0
    Q_UNUSED(cb);
316
0
    Q_UNUSED(vertexCount);
317
0
    Q_UNUSED(instanceCount);
318
0
    Q_UNUSED(firstVertex);
319
0
    Q_UNUSED(firstInstance);
320
0
}
321
322
void QRhiNull::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
323
                           quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
324
0
{
325
0
    Q_UNUSED(cb);
326
0
    Q_UNUSED(indexCount);
327
0
    Q_UNUSED(instanceCount);
328
0
    Q_UNUSED(firstIndex);
329
0
    Q_UNUSED(vertexOffset);
330
0
    Q_UNUSED(firstInstance);
331
0
}
332
333
void QRhiNull::drawIndirect(QRhiCommandBuffer *cb, QRhiBuffer *indirectBuffer,
334
                            quint32 indirectBufferOffset, quint32 drawCount, quint32 stride)
335
0
{
336
0
    Q_UNUSED(cb);
337
0
    Q_UNUSED(indirectBuffer);
338
0
    Q_UNUSED(indirectBufferOffset);
339
0
    Q_UNUSED(drawCount);
340
0
    Q_UNUSED(stride);
341
0
}
342
343
void QRhiNull::drawIndexedIndirect(QRhiCommandBuffer *cb, QRhiBuffer *indirectBuffer,
344
                                   quint32 indirectBufferOffset, quint32 drawCount, quint32 stride)
345
0
{
346
0
    Q_UNUSED(cb);
347
0
    Q_UNUSED(indirectBuffer);
348
0
    Q_UNUSED(indirectBufferOffset);
349
0
    Q_UNUSED(drawCount);
350
0
    Q_UNUSED(stride);
351
0
}
352
353
void QRhiNull::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
354
0
{
355
0
    Q_UNUSED(cb);
356
0
    Q_UNUSED(name);
357
0
}
358
359
void QRhiNull::debugMarkEnd(QRhiCommandBuffer *cb)
360
0
{
361
0
    Q_UNUSED(cb);
362
0
}
363
364
void QRhiNull::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
365
0
{
366
0
    Q_UNUSED(cb);
367
0
    Q_UNUSED(msg);
368
0
}
369
370
void QRhiNull::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps)
371
0
{
372
0
    Q_UNUSED(cb);
373
0
    Q_UNUSED(ps);
374
0
}
375
376
void QRhiNull::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
377
0
{
378
0
    Q_UNUSED(cb);
379
0
    Q_UNUSED(x);
380
0
    Q_UNUSED(y);
381
0
    Q_UNUSED(z);
382
0
}
383
384
const QRhiNativeHandles *QRhiNull::nativeHandles(QRhiCommandBuffer *cb)
385
0
{
386
0
    Q_UNUSED(cb);
387
0
    return nullptr;
388
0
}
389
390
void QRhiNull::beginExternal(QRhiCommandBuffer *cb)
391
0
{
392
0
    Q_UNUSED(cb);
393
0
}
394
395
void QRhiNull::endExternal(QRhiCommandBuffer *cb)
396
0
{
397
0
    Q_UNUSED(cb);
398
0
}
399
400
double QRhiNull::lastCompletedGpuTime(QRhiCommandBuffer *cb)
401
0
{
402
0
    Q_UNUSED(cb);
403
0
    return 0;
404
0
}
405
406
QRhi::FrameOpResult QRhiNull::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags)
407
0
{
408
0
    Q_UNUSED(flags);
409
0
    currentSwapChain = swapChain;
410
0
    return QRhi::FrameOpSuccess;
411
0
}
412
413
QRhi::FrameOpResult QRhiNull::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags)
414
0
{
415
0
    Q_UNUSED(flags);
416
0
    QNullSwapChain *swapChainD = QRHI_RES(QNullSwapChain, swapChain);
417
0
    swapChainD->frameCount += 1;
418
0
    currentSwapChain = nullptr;
419
0
    return QRhi::FrameOpSuccess;
420
0
}
421
422
QRhi::FrameOpResult QRhiNull::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags)
423
0
{
424
0
    Q_UNUSED(flags);
425
0
    *cb = &offscreenCommandBuffer;
426
0
    return QRhi::FrameOpSuccess;
427
0
}
428
429
QRhi::FrameOpResult QRhiNull::endOffscreenFrame(QRhi::EndFrameFlags flags)
430
0
{
431
0
    Q_UNUSED(flags);
432
0
    return QRhi::FrameOpSuccess;
433
0
}
434
435
QRhi::FrameOpResult QRhiNull::finish()
436
0
{
437
0
    return QRhi::FrameOpSuccess;
438
0
}
439
440
void QRhiNull::simulateTextureUpload(const QRhiResourceUpdateBatchPrivate::TextureOp &u)
441
0
{
442
0
    QNullTexture *texD = QRHI_RES(QNullTexture, u.dst);
443
0
    for (int layer = 0, maxLayer = u.subresDesc.size(); layer < maxLayer; ++layer) {
444
0
        for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
445
0
            for (const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level])) {
446
0
                if (!subresDesc.image().isNull()) {
447
0
                    const QImage src = subresDesc.image();
448
0
                    QPainter painter(&texD->image[layer][level]);
449
0
                    const QSize srcSize = subresDesc.sourceSize().isEmpty()
450
0
                            ? src.size() : subresDesc.sourceSize();
451
0
                    painter.setCompositionMode(QPainter::CompositionMode_Source);
452
0
                    painter.drawImage(subresDesc.destinationTopLeft(), src,
453
0
                                      QRect(subresDesc.sourceTopLeft(), srcSize));
454
0
                } else if (!subresDesc.data().isEmpty()) {
455
0
                    const QSize subresSize = q->sizeForMipLevel(level, texD->pixelSize());
456
0
                    int w = subresSize.width();
457
0
                    int h = subresSize.height();
458
0
                    if (!subresDesc.sourceSize().isEmpty()) {
459
0
                        w = subresDesc.sourceSize().width();
460
0
                        h = subresDesc.sourceSize().height();
461
0
                    }
462
                    // sourceTopLeft is not supported on this path as per QRhi docs
463
0
                    const QByteArray srcData = subresDesc.data();
464
0
                    const char *src = srcData.constData();
465
0
                    const int srcBpl = w * 4;
466
0
                    int srcStride = srcBpl;
467
0
                    if (subresDesc.dataStride())
468
0
                        srcStride = subresDesc.dataStride();
469
0
                    const QPoint dstOffset = subresDesc.destinationTopLeft();
470
0
                    uchar *dst = texD->image[layer][level].bits();
471
0
                    const int dstBpl = texD->image[layer][level].bytesPerLine();
472
0
                    for (int y = 0; y < h; ++y) {
473
0
                        memcpy(dst + dstOffset.x() * 4 + (y + dstOffset.y()) * dstBpl,
474
0
                               src + y * srcStride,
475
0
                               size_t(srcBpl));
476
0
                    }
477
0
                }
478
0
            }
479
0
        }
480
0
    }
481
0
}
482
483
void QRhiNull::simulateTextureCopy(const QRhiResourceUpdateBatchPrivate::TextureOp &u)
484
0
{
485
0
    QNullTexture *srcD = QRHI_RES(QNullTexture, u.src);
486
0
    QNullTexture *dstD = QRHI_RES(QNullTexture, u.dst);
487
0
    const QImage &srcImage(srcD->image[u.desc.sourceLayer()][u.desc.sourceLevel()]);
488
0
    QImage &dstImage(dstD->image[u.desc.destinationLayer()][u.desc.destinationLevel()]);
489
0
    const QPoint dstPos = u.desc.destinationTopLeft();
490
0
    const QSize size = u.desc.pixelSize().isEmpty() ? srcD->pixelSize() : u.desc.pixelSize();
491
0
    const QPoint srcPos = u.desc.sourceTopLeft();
492
493
0
    QPainter painter(&dstImage);
494
0
    painter.setCompositionMode(QPainter::CompositionMode_Source);
495
0
    painter.drawImage(QRect(dstPos, size), srcImage, QRect(srcPos, size));
496
0
}
497
498
void QRhiNull::simulateTextureGenMips(const QRhiResourceUpdateBatchPrivate::TextureOp &u)
499
0
{
500
0
    QNullTexture *texD = QRHI_RES(QNullTexture, u.dst);
501
0
    const QSize baseSize = texD->pixelSize();
502
0
    const int levelCount = q->mipLevelsForSize(baseSize);
503
0
    for (int level = 1; level < levelCount; ++level)
504
0
        texD->image[0][level] = texD->image[0][0].scaled(q->sizeForMipLevel(level, baseSize));
505
0
}
506
507
void QRhiNull::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
508
0
{
509
0
    Q_UNUSED(cb);
510
0
    QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates);
511
0
    for (int opIdx = 0; opIdx < ud->activeBufferOpCount; ++opIdx) {
512
0
        const QRhiResourceUpdateBatchPrivate::BufferOp &u(ud->bufferOps[opIdx]);
513
0
        if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::DynamicUpdate
514
0
                || u.type == QRhiResourceUpdateBatchPrivate::BufferOp::StaticUpload)
515
0
        {
516
0
            QNullBuffer *bufD = QRHI_RES(QNullBuffer, u.buf);
517
0
            memcpy(bufD->data + u.offset, u.data.constData(), size_t(u.data.size()));
518
0
        } else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::Read) {
519
0
            QRhiReadbackResult *result = u.result;
520
0
            result->data.resize(u.readSize);
521
0
            QNullBuffer *bufD = QRHI_RES(QNullBuffer, u.buf);
522
0
            memcpy(result->data.data(), bufD->data + u.offset, size_t(u.readSize));
523
0
            if (result->completed)
524
0
                result->completed();
525
0
        }
526
0
    }
527
0
    for (int opIdx = 0; opIdx < ud->activeTextureOpCount; ++opIdx) {
528
0
        const QRhiResourceUpdateBatchPrivate::TextureOp &u(ud->textureOps[opIdx]);
529
0
        if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) {
530
0
            if (u.dst->format() == QRhiTexture::RGBA8)
531
0
                simulateTextureUpload(u);
532
0
        } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) {
533
0
            if (u.src->format() == QRhiTexture::RGBA8 && u.dst->format() == QRhiTexture::RGBA8)
534
0
                simulateTextureCopy(u);
535
0
        } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) {
536
0
            QRhiReadbackResult *result = u.result;
537
0
            QNullTexture *texD = QRHI_RES(QNullTexture, u.rb.texture());
538
0
            if (texD) {
539
0
                result->format = texD->format();
540
0
                if (u.rb.rect().isValid())
541
0
                    result->pixelSize = u.rb.rect().size();
542
0
                else
543
0
                    result->pixelSize = q->sizeForMipLevel(u.rb.level(), texD->pixelSize());
544
0
            } else {
545
0
                Q_ASSERT(currentSwapChain);
546
0
                result->format = QRhiTexture::RGBA8;
547
0
                if (u.rb.rect().isValid())
548
0
                    result->pixelSize = u.rb.rect().size();
549
0
                else
550
0
                    result->pixelSize = currentSwapChain->currentPixelSize();
551
0
            }
552
0
            quint32 bytesPerLine = 0;
553
0
            quint32 byteSize = 0;
554
0
            textureFormatInfo(result->format, result->pixelSize, &bytesPerLine, &byteSize, nullptr);
555
0
            if (texD && texD->format() == QRhiTexture::RGBA8) {
556
0
                result->data.resize(int(byteSize));
557
0
                const QImage &src(texD->image[u.rb.layer()][u.rb.level()]);
558
0
                char *dst = result->data.data();
559
0
                for (int y = 0, h = src.height(); y < h; ++y) {
560
0
                    memcpy(dst, src.constScanLine(y), bytesPerLine);
561
0
                    dst += bytesPerLine;
562
0
                }
563
0
            } else {
564
0
                result->data.fill(0, int(byteSize));
565
0
            }
566
0
            if (result->completed)
567
0
                result->completed();
568
0
        } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::GenMips) {
569
0
            if (u.dst->format() == QRhiTexture::RGBA8)
570
0
                simulateTextureGenMips(u);
571
0
        }
572
0
    }
573
0
    ud->free();
574
0
}
575
576
void QRhiNull::beginPass(QRhiCommandBuffer *cb,
577
                         QRhiRenderTarget *rt,
578
                         const QColor &colorClearValue,
579
                         const QRhiDepthStencilClearValue &depthStencilClearValue,
580
                         QRhiResourceUpdateBatch *resourceUpdates,
581
                         QRhiCommandBuffer::BeginPassFlags flags)
582
0
{
583
0
    Q_UNUSED(colorClearValue);
584
0
    Q_UNUSED(depthStencilClearValue);
585
0
    Q_UNUSED(flags);
586
587
0
    if (resourceUpdates)
588
0
        resourceUpdate(cb, resourceUpdates);
589
590
0
    if (rt->resourceType() == QRhiRenderTarget::TextureRenderTarget) {
591
0
        QNullTextureRenderTarget *rtTex = QRHI_RES(QNullTextureRenderTarget, rt);
592
0
        if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QNullTexture, QNullRenderBuffer>(rtTex->description(), rtTex->d.currentResIdList))
593
0
            rtTex->create();
594
0
    }
595
0
}
596
597
void QRhiNull::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
598
0
{
599
0
    if (resourceUpdates)
600
0
        resourceUpdate(cb, resourceUpdates);
601
0
}
602
603
void QRhiNull::beginComputePass(QRhiCommandBuffer *cb,
604
                                QRhiResourceUpdateBatch *resourceUpdates,
605
                                QRhiCommandBuffer::BeginPassFlags flags)
606
0
{
607
0
    Q_UNUSED(flags);
608
0
    if (resourceUpdates)
609
0
        resourceUpdate(cb, resourceUpdates);
610
0
}
611
612
void QRhiNull::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
613
0
{
614
0
    if (resourceUpdates)
615
0
        resourceUpdate(cb, resourceUpdates);
616
0
}
617
618
QNullBuffer::QNullBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, quint32 size)
619
0
    : QRhiBuffer(rhi, type, usage, size)
620
0
{
621
0
}
622
623
QNullBuffer::~QNullBuffer()
624
0
{
625
0
    destroy();
626
0
}
627
628
void QNullBuffer::destroy()
629
0
{
630
0
    delete[] data;
631
0
    data = nullptr;
632
633
0
    QRHI_RES_RHI(QRhiNull);
634
0
    if (rhiD)
635
0
        rhiD->unregisterResource(this);
636
0
}
637
638
bool QNullBuffer::create()
639
0
{
640
0
    if (data)
641
0
        destroy();
642
643
0
    data = new char[m_size];
644
0
    memset(data, 0, m_size);
645
646
0
    QRHI_RES_RHI(QRhiNull);
647
0
    rhiD->registerResource(this);
648
649
0
    return true;
650
0
}
651
652
char *QNullBuffer::beginFullDynamicBufferUpdateForCurrentFrame()
653
0
{
654
0
    Q_ASSERT(m_type == Dynamic);
655
0
    return data;
656
0
}
657
658
QNullRenderBuffer::QNullRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
659
                                     int sampleCount, QRhiRenderBuffer::Flags flags,
660
                                     QRhiTexture::Format backingFormatHint)
661
0
    : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags, backingFormatHint)
662
0
{
663
0
}
664
665
QNullRenderBuffer::~QNullRenderBuffer()
666
0
{
667
0
    destroy();
668
0
}
669
670
void QNullRenderBuffer::destroy()
671
0
{
672
0
    valid = false;
673
674
0
    QRHI_RES_RHI(QRhiNull);
675
0
    if (rhiD)
676
0
        rhiD->unregisterResource(this);
677
0
}
678
679
bool QNullRenderBuffer::create()
680
0
{
681
0
    if (valid)
682
0
        destroy();
683
684
0
    valid = true;
685
0
    generation += 1;
686
687
0
    QRHI_RES_RHI(QRhiNull);
688
0
    rhiD->registerResource(this);
689
690
0
    return true;
691
0
}
692
693
QRhiTexture::Format QNullRenderBuffer::backingFormat() const
694
0
{
695
0
    return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
696
0
}
697
698
QNullTexture::QNullTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
699
                           int arraySize, int sampleCount, Flags flags)
700
0
    : QRhiTexture(rhi, format, pixelSize, depth, arraySize, sampleCount, flags)
701
0
{
702
0
}
703
704
QNullTexture::~QNullTexture()
705
0
{
706
0
    destroy();
707
0
}
708
709
void QNullTexture::destroy()
710
0
{
711
0
    valid = false;
712
713
0
    QRHI_RES_RHI(QRhiNull);
714
0
    if (rhiD)
715
0
        rhiD->unregisterResource(this);
716
0
}
717
718
bool QNullTexture::create()
719
0
{
720
0
    if (valid)
721
0
        destroy();
722
723
0
    valid = true;
724
725
0
    QRHI_RES_RHI(QRhiNull);
726
0
    const bool isCube = m_flags.testFlag(CubeMap);
727
0
    const bool is3D = m_flags.testFlag(ThreeDimensional);
728
0
    const bool isArray = m_flags.testFlag(TextureArray);
729
0
    const bool hasMipMaps = m_flags.testFlag(MipMapped);
730
0
    const bool is1D = m_flags.testFlags(OneDimensional);
731
0
    QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
732
0
                      : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
733
0
    const int mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
734
0
    const int layerCount = is3D ? qMax(1, m_depth)
735
0
                                : (isCube ? 6
736
0
                                          : (isArray ? qMax(0, m_arraySize)
737
0
                                                     : 1));
738
739
0
    if (!rhiD->textureFormatInfo(m_format, size, nullptr, nullptr, nullptr)) {
740
0
        valid = false;
741
0
        return false;
742
0
    }
743
744
0
    if (m_format == RGBA8) {
745
0
        image.resize(layerCount);
746
0
        for (int layer = 0; layer < layerCount; ++layer) {
747
0
            for (int level = 0; level < mipLevelCount; ++level) {
748
0
                image[layer][level] = QImage(rhiD->q->sizeForMipLevel(level, size),
749
0
                                             QImage::Format_RGBA8888_Premultiplied);
750
0
                image[layer][level].fill(Qt::yellow);
751
0
            }
752
0
        }
753
0
    }
754
755
0
    generation += 1;
756
757
0
    rhiD->registerResource(this);
758
759
0
    return true;
760
0
}
761
762
bool QNullTexture::createFrom(QRhiTexture::NativeTexture src)
763
0
{
764
0
    Q_UNUSED(src);
765
0
    if (valid)
766
0
        destroy();
767
768
0
    valid = true;
769
770
0
    generation += 1;
771
772
0
    QRHI_RES_RHI(QRhiNull);
773
0
    rhiD->registerResource(this);
774
775
0
    return true;
776
0
}
777
778
QNullSampler::QNullSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
779
                           AddressMode u, AddressMode v, AddressMode w)
780
0
    : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v, w)
781
0
{
782
0
}
783
784
QNullSampler::~QNullSampler()
785
0
{
786
0
    destroy();
787
0
}
788
789
void QNullSampler::destroy()
790
0
{
791
0
    QRHI_RES_RHI(QRhiNull);
792
0
    if (rhiD)
793
0
        rhiD->unregisterResource(this);
794
0
}
795
796
bool QNullSampler::create()
797
0
{
798
0
    QRHI_RES_RHI(QRhiNull);
799
0
    rhiD->registerResource(this);
800
0
    return true;
801
0
}
802
803
QNullRenderPassDescriptor::QNullRenderPassDescriptor(QRhiImplementation *rhi)
804
0
    : QRhiRenderPassDescriptor(rhi)
805
0
{
806
0
}
807
808
QNullRenderPassDescriptor::~QNullRenderPassDescriptor()
809
0
{
810
0
    destroy();
811
0
}
812
813
void QNullRenderPassDescriptor::destroy()
814
0
{
815
0
    QRHI_RES_RHI(QRhiNull);
816
0
    if (rhiD)
817
0
        rhiD->unregisterResource(this);
818
0
}
819
820
bool QNullRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const
821
0
{
822
0
    Q_UNUSED(other);
823
0
    return true;
824
0
}
825
826
QRhiRenderPassDescriptor *QNullRenderPassDescriptor::newCompatibleRenderPassDescriptor() const
827
0
{
828
0
    QNullRenderPassDescriptor *rpD = new QNullRenderPassDescriptor(m_rhi);
829
0
    QRHI_RES_RHI(QRhiNull);
830
0
    rhiD->registerResource(rpD, false);
831
0
    return rpD;
832
0
}
833
834
QVector<quint32> QNullRenderPassDescriptor::serializedFormat() const
835
0
{
836
0
    return {};
837
0
}
838
839
QNullSwapChainRenderTarget::QNullSwapChainRenderTarget(QRhiImplementation *rhi, QRhiSwapChain *swapchain)
840
0
    : QRhiSwapChainRenderTarget(rhi, swapchain),
841
0
      d(rhi)
842
0
{
843
0
}
844
845
QNullSwapChainRenderTarget::~QNullSwapChainRenderTarget()
846
0
{
847
0
    destroy();
848
0
}
849
850
void QNullSwapChainRenderTarget::destroy()
851
0
{
852
0
}
853
854
QSize QNullSwapChainRenderTarget::pixelSize() const
855
0
{
856
0
    return d.pixelSize;
857
0
}
858
859
float QNullSwapChainRenderTarget::devicePixelRatio() const
860
0
{
861
0
    return d.dpr;
862
0
}
863
864
int QNullSwapChainRenderTarget::sampleCount() const
865
0
{
866
0
    return 1;
867
0
}
868
869
QNullTextureRenderTarget::QNullTextureRenderTarget(QRhiImplementation *rhi,
870
                                                   const QRhiTextureRenderTargetDescription &desc,
871
                                                   Flags flags)
872
0
    : QRhiTextureRenderTarget(rhi, desc, flags),
873
0
      d(rhi)
874
0
{
875
0
}
876
877
QNullTextureRenderTarget::~QNullTextureRenderTarget()
878
0
{
879
0
    destroy();
880
0
}
881
882
void QNullTextureRenderTarget::destroy()
883
0
{
884
0
    QRHI_RES_RHI(QRhiNull);
885
0
    if (rhiD)
886
0
        rhiD->unregisterResource(this);
887
0
}
888
889
QRhiRenderPassDescriptor *QNullTextureRenderTarget::newCompatibleRenderPassDescriptor()
890
0
{
891
0
    QNullRenderPassDescriptor *rpD = new QNullRenderPassDescriptor(m_rhi);
892
0
    QRHI_RES_RHI(QRhiNull);
893
0
    rhiD->registerResource(rpD, false);
894
0
    return rpD;
895
0
}
896
897
bool QNullTextureRenderTarget::create()
898
0
{
899
0
    QRHI_RES_RHI(QRhiNull);
900
0
    d.rp = QRHI_RES(QNullRenderPassDescriptor, m_renderPassDesc);
901
0
    if (m_desc.cbeginColorAttachments() != m_desc.cendColorAttachments()) {
902
0
        const QRhiColorAttachment *colorAtt = m_desc.cbeginColorAttachments();
903
0
        QRhiTexture *tex = colorAtt->texture();
904
0
        QRhiRenderBuffer *rb = colorAtt->renderBuffer();
905
0
        d.pixelSize = tex ? rhiD->q->sizeForMipLevel(colorAtt->level(), tex->pixelSize()) : rb->pixelSize();
906
0
    } else if (m_desc.depthStencilBuffer()) {
907
0
        d.pixelSize = m_desc.depthStencilBuffer()->pixelSize();
908
0
    } else if (m_desc.depthTexture()) {
909
0
        d.pixelSize = m_desc.depthTexture()->pixelSize();
910
0
    }
911
0
    QRhiRenderTargetAttachmentTracker::updateResIdList<QNullTexture, QNullRenderBuffer>(m_desc, &d.currentResIdList);
912
0
    rhiD->registerResource(this);
913
0
    return true;
914
0
}
915
916
QSize QNullTextureRenderTarget::pixelSize() const
917
0
{
918
0
    if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QNullTexture, QNullRenderBuffer>(m_desc, d.currentResIdList))
919
0
        const_cast<QNullTextureRenderTarget *>(this)->create();
920
921
0
    return d.pixelSize;
922
0
}
923
924
float QNullTextureRenderTarget::devicePixelRatio() const
925
0
{
926
0
    return d.dpr;
927
0
}
928
929
int QNullTextureRenderTarget::sampleCount() const
930
0
{
931
0
    return 1;
932
0
}
933
934
QNullShaderResourceBindings::QNullShaderResourceBindings(QRhiImplementation *rhi)
935
0
    : QRhiShaderResourceBindings(rhi)
936
0
{
937
0
}
938
939
QNullShaderResourceBindings::~QNullShaderResourceBindings()
940
0
{
941
0
    destroy();
942
0
}
943
944
void QNullShaderResourceBindings::destroy()
945
0
{
946
0
    QRHI_RES_RHI(QRhiNull);
947
0
    if (rhiD)
948
0
        rhiD->unregisterResource(this);
949
0
}
950
951
bool QNullShaderResourceBindings::create()
952
0
{
953
0
    QRHI_RES_RHI(QRhiNull);
954
0
    if (!rhiD->sanityCheckShaderResourceBindings(this))
955
0
        return false;
956
957
0
    rhiD->updateLayoutDesc(this);
958
959
0
    rhiD->registerResource(this, false);
960
0
    return true;
961
0
}
962
963
void QNullShaderResourceBindings::updateResources(UpdateFlags flags)
964
0
{
965
0
    Q_UNUSED(flags);
966
0
}
967
968
QNullGraphicsPipeline::QNullGraphicsPipeline(QRhiImplementation *rhi)
969
0
    : QRhiGraphicsPipeline(rhi)
970
0
{
971
0
}
972
973
QNullGraphicsPipeline::~QNullGraphicsPipeline()
974
0
{
975
0
    destroy();
976
0
}
977
978
void QNullGraphicsPipeline::destroy()
979
0
{
980
0
    QRHI_RES_RHI(QRhiNull);
981
0
    if (rhiD)
982
0
        rhiD->unregisterResource(this);
983
0
}
984
985
bool QNullGraphicsPipeline::create()
986
0
{
987
0
    QRHI_RES_RHI(QRhiNull);
988
0
    if (!rhiD->sanityCheckGraphicsPipeline(this))
989
0
        return false;
990
991
0
    rhiD->registerResource(this);
992
0
    return true;
993
0
}
994
995
QNullComputePipeline::QNullComputePipeline(QRhiImplementation *rhi)
996
0
    : QRhiComputePipeline(rhi)
997
0
{
998
0
}
999
1000
QNullComputePipeline::~QNullComputePipeline()
1001
0
{
1002
0
    destroy();
1003
0
}
1004
1005
void QNullComputePipeline::destroy()
1006
0
{
1007
0
    QRHI_RES_RHI(QRhiNull);
1008
0
    if (rhiD)
1009
0
        rhiD->unregisterResource(this);
1010
0
}
1011
1012
bool QNullComputePipeline::create()
1013
0
{
1014
0
    QRHI_RES_RHI(QRhiNull);
1015
0
    rhiD->registerResource(this);
1016
0
    return true;
1017
0
}
1018
1019
QNullCommandBuffer::QNullCommandBuffer(QRhiImplementation *rhi)
1020
0
    : QRhiCommandBuffer(rhi)
1021
0
{
1022
0
}
1023
1024
QNullCommandBuffer::~QNullCommandBuffer()
1025
0
{
1026
0
    destroy();
1027
0
}
1028
1029
void QNullCommandBuffer::destroy()
1030
0
{
1031
    // nothing to do here
1032
0
}
1033
1034
QNullSwapChain::QNullSwapChain(QRhiImplementation *rhi)
1035
0
    : QRhiSwapChain(rhi),
1036
0
      rt(rhi, this),
1037
0
      cb(rhi)
1038
0
{
1039
0
}
1040
1041
QNullSwapChain::~QNullSwapChain()
1042
0
{
1043
0
    destroy();
1044
0
}
1045
1046
void QNullSwapChain::destroy()
1047
0
{
1048
0
    QRHI_RES_RHI(QRhiNull);
1049
0
    if (rhiD)
1050
0
        rhiD->unregisterResource(this);
1051
0
}
1052
1053
QRhiCommandBuffer *QNullSwapChain::currentFrameCommandBuffer()
1054
0
{
1055
0
    return &cb;
1056
0
}
1057
1058
QRhiRenderTarget *QNullSwapChain::currentFrameRenderTarget()
1059
0
{
1060
0
    return &rt;
1061
0
}
1062
1063
QSize QNullSwapChain::surfacePixelSize()
1064
0
{
1065
0
    return QSize(1280, 720);
1066
0
}
1067
1068
bool QNullSwapChain::isFormatSupported(Format f)
1069
0
{
1070
0
    return f == SDR;
1071
0
}
1072
1073
QRhiRenderPassDescriptor *QNullSwapChain::newCompatibleRenderPassDescriptor()
1074
0
{
1075
0
    QNullRenderPassDescriptor *rpD = new QNullRenderPassDescriptor(m_rhi);
1076
0
    QRHI_RES_RHI(QRhiNull);
1077
0
    rhiD->registerResource(rpD, false);
1078
0
    return rpD;
1079
0
}
1080
1081
bool QNullSwapChain::createOrResize()
1082
0
{
1083
0
    const bool needsRegistration = !window || window != m_window;
1084
0
    if (window && window != m_window)
1085
0
        destroy();
1086
1087
0
    window = m_window;
1088
0
    m_currentPixelSize = surfacePixelSize();
1089
0
    rt.setRenderPassDescriptor(m_renderPassDesc); // for the public getter in QRhiRenderTarget
1090
0
    rt.d.rp = QRHI_RES(QNullRenderPassDescriptor, m_renderPassDesc);
1091
0
    rt.d.pixelSize = m_currentPixelSize;
1092
0
    frameCount = 0;
1093
1094
0
    if (needsRegistration) {
1095
0
        QRHI_RES_RHI(QRhiNull);
1096
0
        rhiD->registerResource(this);
1097
0
    }
1098
1099
0
    return true;
1100
0
}
1101
1102
QT_END_NAMESPACE