Coverage Report

Created: 2026-03-31 07:41

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::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
334
0
{
335
0
    Q_UNUSED(cb);
336
0
    Q_UNUSED(name);
337
0
}
338
339
void QRhiNull::debugMarkEnd(QRhiCommandBuffer *cb)
340
0
{
341
0
    Q_UNUSED(cb);
342
0
}
343
344
void QRhiNull::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
345
0
{
346
0
    Q_UNUSED(cb);
347
0
    Q_UNUSED(msg);
348
0
}
349
350
void QRhiNull::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps)
351
0
{
352
0
    Q_UNUSED(cb);
353
0
    Q_UNUSED(ps);
354
0
}
355
356
void QRhiNull::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
357
0
{
358
0
    Q_UNUSED(cb);
359
0
    Q_UNUSED(x);
360
0
    Q_UNUSED(y);
361
0
    Q_UNUSED(z);
362
0
}
363
364
const QRhiNativeHandles *QRhiNull::nativeHandles(QRhiCommandBuffer *cb)
365
0
{
366
0
    Q_UNUSED(cb);
367
0
    return nullptr;
368
0
}
369
370
void QRhiNull::beginExternal(QRhiCommandBuffer *cb)
371
0
{
372
0
    Q_UNUSED(cb);
373
0
}
374
375
void QRhiNull::endExternal(QRhiCommandBuffer *cb)
376
0
{
377
0
    Q_UNUSED(cb);
378
0
}
379
380
double QRhiNull::lastCompletedGpuTime(QRhiCommandBuffer *cb)
381
0
{
382
0
    Q_UNUSED(cb);
383
0
    return 0;
384
0
}
385
386
QRhi::FrameOpResult QRhiNull::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags)
387
0
{
388
0
    Q_UNUSED(flags);
389
0
    currentSwapChain = swapChain;
390
0
    return QRhi::FrameOpSuccess;
391
0
}
392
393
QRhi::FrameOpResult QRhiNull::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags)
394
0
{
395
0
    Q_UNUSED(flags);
396
0
    QNullSwapChain *swapChainD = QRHI_RES(QNullSwapChain, swapChain);
397
0
    swapChainD->frameCount += 1;
398
0
    currentSwapChain = nullptr;
399
0
    return QRhi::FrameOpSuccess;
400
0
}
401
402
QRhi::FrameOpResult QRhiNull::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags)
403
0
{
404
0
    Q_UNUSED(flags);
405
0
    *cb = &offscreenCommandBuffer;
406
0
    return QRhi::FrameOpSuccess;
407
0
}
408
409
QRhi::FrameOpResult QRhiNull::endOffscreenFrame(QRhi::EndFrameFlags flags)
410
0
{
411
0
    Q_UNUSED(flags);
412
0
    return QRhi::FrameOpSuccess;
413
0
}
414
415
QRhi::FrameOpResult QRhiNull::finish()
416
0
{
417
0
    return QRhi::FrameOpSuccess;
418
0
}
419
420
void QRhiNull::simulateTextureUpload(const QRhiResourceUpdateBatchPrivate::TextureOp &u)
421
0
{
422
0
    QNullTexture *texD = QRHI_RES(QNullTexture, u.dst);
423
0
    for (int layer = 0, maxLayer = u.subresDesc.size(); layer < maxLayer; ++layer) {
424
0
        for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
425
0
            for (const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level])) {
426
0
                if (!subresDesc.image().isNull()) {
427
0
                    const QImage src = subresDesc.image();
428
0
                    QPainter painter(&texD->image[layer][level]);
429
0
                    const QSize srcSize = subresDesc.sourceSize().isEmpty()
430
0
                            ? src.size() : subresDesc.sourceSize();
431
0
                    painter.setCompositionMode(QPainter::CompositionMode_Source);
432
0
                    painter.drawImage(subresDesc.destinationTopLeft(), src,
433
0
                                      QRect(subresDesc.sourceTopLeft(), srcSize));
434
0
                } else if (!subresDesc.data().isEmpty()) {
435
0
                    const QSize subresSize = q->sizeForMipLevel(level, texD->pixelSize());
436
0
                    int w = subresSize.width();
437
0
                    int h = subresSize.height();
438
0
                    if (!subresDesc.sourceSize().isEmpty()) {
439
0
                        w = subresDesc.sourceSize().width();
440
0
                        h = subresDesc.sourceSize().height();
441
0
                    }
442
                    // sourceTopLeft is not supported on this path as per QRhi docs
443
0
                    const char *src = subresDesc.data().constData();
444
0
                    const int srcBpl = w * 4;
445
0
                    int srcStride = srcBpl;
446
0
                    if (subresDesc.dataStride())
447
0
                        srcStride = subresDesc.dataStride();
448
0
                    const QPoint dstOffset = subresDesc.destinationTopLeft();
449
0
                    uchar *dst = texD->image[layer][level].bits();
450
0
                    const int dstBpl = texD->image[layer][level].bytesPerLine();
451
0
                    for (int y = 0; y < h; ++y) {
452
0
                        memcpy(dst + dstOffset.x() * 4 + (y + dstOffset.y()) * dstBpl,
453
0
                               src + y * srcStride,
454
0
                               size_t(srcBpl));
455
0
                    }
456
0
                }
457
0
            }
458
0
        }
459
0
    }
460
0
}
461
462
void QRhiNull::simulateTextureCopy(const QRhiResourceUpdateBatchPrivate::TextureOp &u)
463
0
{
464
0
    QNullTexture *srcD = QRHI_RES(QNullTexture, u.src);
465
0
    QNullTexture *dstD = QRHI_RES(QNullTexture, u.dst);
466
0
    const QImage &srcImage(srcD->image[u.desc.sourceLayer()][u.desc.sourceLevel()]);
467
0
    QImage &dstImage(dstD->image[u.desc.destinationLayer()][u.desc.destinationLevel()]);
468
0
    const QPoint dstPos = u.desc.destinationTopLeft();
469
0
    const QSize size = u.desc.pixelSize().isEmpty() ? srcD->pixelSize() : u.desc.pixelSize();
470
0
    const QPoint srcPos = u.desc.sourceTopLeft();
471
472
0
    QPainter painter(&dstImage);
473
0
    painter.setCompositionMode(QPainter::CompositionMode_Source);
474
0
    painter.drawImage(QRect(dstPos, size), srcImage, QRect(srcPos, size));
475
0
}
476
477
void QRhiNull::simulateTextureGenMips(const QRhiResourceUpdateBatchPrivate::TextureOp &u)
478
0
{
479
0
    QNullTexture *texD = QRHI_RES(QNullTexture, u.dst);
480
0
    const QSize baseSize = texD->pixelSize();
481
0
    const int levelCount = q->mipLevelsForSize(baseSize);
482
0
    for (int level = 1; level < levelCount; ++level)
483
0
        texD->image[0][level] = texD->image[0][0].scaled(q->sizeForMipLevel(level, baseSize));
484
0
}
485
486
void QRhiNull::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
487
0
{
488
0
    Q_UNUSED(cb);
489
0
    QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates);
490
0
    for (int opIdx = 0; opIdx < ud->activeBufferOpCount; ++opIdx) {
491
0
        const QRhiResourceUpdateBatchPrivate::BufferOp &u(ud->bufferOps[opIdx]);
492
0
        if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::DynamicUpdate
493
0
                || u.type == QRhiResourceUpdateBatchPrivate::BufferOp::StaticUpload)
494
0
        {
495
0
            QNullBuffer *bufD = QRHI_RES(QNullBuffer, u.buf);
496
0
            memcpy(bufD->data + u.offset, u.data.constData(), size_t(u.data.size()));
497
0
        } else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::Read) {
498
0
            QRhiReadbackResult *result = u.result;
499
0
            result->data.resize(u.readSize);
500
0
            QNullBuffer *bufD = QRHI_RES(QNullBuffer, u.buf);
501
0
            memcpy(result->data.data(), bufD->data + u.offset, size_t(u.readSize));
502
0
            if (result->completed)
503
0
                result->completed();
504
0
        }
505
0
    }
506
0
    for (int opIdx = 0; opIdx < ud->activeTextureOpCount; ++opIdx) {
507
0
        const QRhiResourceUpdateBatchPrivate::TextureOp &u(ud->textureOps[opIdx]);
508
0
        if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) {
509
0
            if (u.dst->format() == QRhiTexture::RGBA8)
510
0
                simulateTextureUpload(u);
511
0
        } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) {
512
0
            if (u.src->format() == QRhiTexture::RGBA8 && u.dst->format() == QRhiTexture::RGBA8)
513
0
                simulateTextureCopy(u);
514
0
        } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) {
515
0
            QRhiReadbackResult *result = u.result;
516
0
            QNullTexture *texD = QRHI_RES(QNullTexture, u.rb.texture());
517
0
            if (texD) {
518
0
                result->format = texD->format();
519
0
                if (u.rb.rect().isValid())
520
0
                    result->pixelSize = u.rb.rect().size();
521
0
                else
522
0
                    result->pixelSize = q->sizeForMipLevel(u.rb.level(), texD->pixelSize());
523
0
            } else {
524
0
                Q_ASSERT(currentSwapChain);
525
0
                result->format = QRhiTexture::RGBA8;
526
0
                if (u.rb.rect().isValid())
527
0
                    result->pixelSize = u.rb.rect().size();
528
0
                else
529
0
                    result->pixelSize = currentSwapChain->currentPixelSize();
530
0
            }
531
0
            quint32 bytesPerLine = 0;
532
0
            quint32 byteSize = 0;
533
0
            textureFormatInfo(result->format, result->pixelSize, &bytesPerLine, &byteSize, nullptr);
534
0
            if (texD && texD->format() == QRhiTexture::RGBA8) {
535
0
                result->data.resize(int(byteSize));
536
0
                const QImage &src(texD->image[u.rb.layer()][u.rb.level()]);
537
0
                char *dst = result->data.data();
538
0
                for (int y = 0, h = src.height(); y < h; ++y) {
539
0
                    memcpy(dst, src.constScanLine(y), bytesPerLine);
540
0
                    dst += bytesPerLine;
541
0
                }
542
0
            } else {
543
0
                result->data.fill(0, int(byteSize));
544
0
            }
545
0
            if (result->completed)
546
0
                result->completed();
547
0
        } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::GenMips) {
548
0
            if (u.dst->format() == QRhiTexture::RGBA8)
549
0
                simulateTextureGenMips(u);
550
0
        }
551
0
    }
552
0
    ud->free();
553
0
}
554
555
void QRhiNull::beginPass(QRhiCommandBuffer *cb,
556
                         QRhiRenderTarget *rt,
557
                         const QColor &colorClearValue,
558
                         const QRhiDepthStencilClearValue &depthStencilClearValue,
559
                         QRhiResourceUpdateBatch *resourceUpdates,
560
                         QRhiCommandBuffer::BeginPassFlags flags)
561
0
{
562
0
    Q_UNUSED(colorClearValue);
563
0
    Q_UNUSED(depthStencilClearValue);
564
0
    Q_UNUSED(flags);
565
566
0
    if (resourceUpdates)
567
0
        resourceUpdate(cb, resourceUpdates);
568
569
0
    if (rt->resourceType() == QRhiRenderTarget::TextureRenderTarget) {
570
0
        QNullTextureRenderTarget *rtTex = QRHI_RES(QNullTextureRenderTarget, rt);
571
0
        if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QNullTexture, QNullRenderBuffer>(rtTex->description(), rtTex->d.currentResIdList))
572
0
            rtTex->create();
573
0
    }
574
0
}
575
576
void QRhiNull::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
577
0
{
578
0
    if (resourceUpdates)
579
0
        resourceUpdate(cb, resourceUpdates);
580
0
}
581
582
void QRhiNull::beginComputePass(QRhiCommandBuffer *cb,
583
                                QRhiResourceUpdateBatch *resourceUpdates,
584
                                QRhiCommandBuffer::BeginPassFlags flags)
585
0
{
586
0
    Q_UNUSED(flags);
587
0
    if (resourceUpdates)
588
0
        resourceUpdate(cb, resourceUpdates);
589
0
}
590
591
void QRhiNull::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
592
0
{
593
0
    if (resourceUpdates)
594
0
        resourceUpdate(cb, resourceUpdates);
595
0
}
596
597
QNullBuffer::QNullBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, quint32 size)
598
0
    : QRhiBuffer(rhi, type, usage, size)
599
0
{
600
0
}
601
602
QNullBuffer::~QNullBuffer()
603
0
{
604
0
    destroy();
605
0
}
606
607
void QNullBuffer::destroy()
608
0
{
609
0
    delete[] data;
610
0
    data = nullptr;
611
612
0
    QRHI_RES_RHI(QRhiNull);
613
0
    if (rhiD)
614
0
        rhiD->unregisterResource(this);
615
0
}
616
617
bool QNullBuffer::create()
618
0
{
619
0
    if (data)
620
0
        destroy();
621
622
0
    data = new char[m_size];
623
0
    memset(data, 0, m_size);
624
625
0
    QRHI_RES_RHI(QRhiNull);
626
0
    rhiD->registerResource(this);
627
628
0
    return true;
629
0
}
630
631
char *QNullBuffer::beginFullDynamicBufferUpdateForCurrentFrame()
632
0
{
633
0
    Q_ASSERT(m_type == Dynamic);
634
0
    return data;
635
0
}
636
637
QNullRenderBuffer::QNullRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
638
                                     int sampleCount, QRhiRenderBuffer::Flags flags,
639
                                     QRhiTexture::Format backingFormatHint)
640
0
    : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags, backingFormatHint)
641
0
{
642
0
}
643
644
QNullRenderBuffer::~QNullRenderBuffer()
645
0
{
646
0
    destroy();
647
0
}
648
649
void QNullRenderBuffer::destroy()
650
0
{
651
0
    valid = false;
652
653
0
    QRHI_RES_RHI(QRhiNull);
654
0
    if (rhiD)
655
0
        rhiD->unregisterResource(this);
656
0
}
657
658
bool QNullRenderBuffer::create()
659
0
{
660
0
    if (valid)
661
0
        destroy();
662
663
0
    valid = true;
664
0
    generation += 1;
665
666
0
    QRHI_RES_RHI(QRhiNull);
667
0
    rhiD->registerResource(this);
668
669
0
    return true;
670
0
}
671
672
QRhiTexture::Format QNullRenderBuffer::backingFormat() const
673
0
{
674
0
    return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
675
0
}
676
677
QNullTexture::QNullTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
678
                           int arraySize, int sampleCount, Flags flags)
679
0
    : QRhiTexture(rhi, format, pixelSize, depth, arraySize, sampleCount, flags)
680
0
{
681
0
}
682
683
QNullTexture::~QNullTexture()
684
0
{
685
0
    destroy();
686
0
}
687
688
void QNullTexture::destroy()
689
0
{
690
0
    valid = false;
691
692
0
    QRHI_RES_RHI(QRhiNull);
693
0
    if (rhiD)
694
0
        rhiD->unregisterResource(this);
695
0
}
696
697
bool QNullTexture::create()
698
0
{
699
0
    if (valid)
700
0
        destroy();
701
702
0
    valid = true;
703
704
0
    QRHI_RES_RHI(QRhiNull);
705
0
    const bool isCube = m_flags.testFlag(CubeMap);
706
0
    const bool is3D = m_flags.testFlag(ThreeDimensional);
707
0
    const bool isArray = m_flags.testFlag(TextureArray);
708
0
    const bool hasMipMaps = m_flags.testFlag(MipMapped);
709
0
    const bool is1D = m_flags.testFlags(OneDimensional);
710
0
    QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
711
0
                      : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
712
0
    const int mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
713
0
    const int layerCount = is3D ? qMax(1, m_depth)
714
0
                                : (isCube ? 6
715
0
                                          : (isArray ? qMax(0, m_arraySize)
716
0
                                                     : 1));
717
718
0
    if (m_format == RGBA8) {
719
0
        image.resize(layerCount);
720
0
        for (int layer = 0; layer < layerCount; ++layer) {
721
0
            for (int level = 0; level < mipLevelCount; ++level) {
722
0
                image[layer][level] = QImage(rhiD->q->sizeForMipLevel(level, size),
723
0
                                             QImage::Format_RGBA8888_Premultiplied);
724
0
                image[layer][level].fill(Qt::yellow);
725
0
            }
726
0
        }
727
0
    }
728
729
0
    generation += 1;
730
731
0
    rhiD->registerResource(this);
732
733
0
    return true;
734
0
}
735
736
bool QNullTexture::createFrom(QRhiTexture::NativeTexture src)
737
0
{
738
0
    Q_UNUSED(src);
739
0
    if (valid)
740
0
        destroy();
741
742
0
    valid = true;
743
744
0
    generation += 1;
745
746
0
    QRHI_RES_RHI(QRhiNull);
747
0
    rhiD->registerResource(this);
748
749
0
    return true;
750
0
}
751
752
QNullSampler::QNullSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
753
                           AddressMode u, AddressMode v, AddressMode w)
754
0
    : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v, w)
755
0
{
756
0
}
757
758
QNullSampler::~QNullSampler()
759
0
{
760
0
    destroy();
761
0
}
762
763
void QNullSampler::destroy()
764
0
{
765
0
    QRHI_RES_RHI(QRhiNull);
766
0
    if (rhiD)
767
0
        rhiD->unregisterResource(this);
768
0
}
769
770
bool QNullSampler::create()
771
0
{
772
0
    QRHI_RES_RHI(QRhiNull);
773
0
    rhiD->registerResource(this);
774
0
    return true;
775
0
}
776
777
QNullRenderPassDescriptor::QNullRenderPassDescriptor(QRhiImplementation *rhi)
778
0
    : QRhiRenderPassDescriptor(rhi)
779
0
{
780
0
}
781
782
QNullRenderPassDescriptor::~QNullRenderPassDescriptor()
783
0
{
784
0
    destroy();
785
0
}
786
787
void QNullRenderPassDescriptor::destroy()
788
0
{
789
0
    QRHI_RES_RHI(QRhiNull);
790
0
    if (rhiD)
791
0
        rhiD->unregisterResource(this);
792
0
}
793
794
bool QNullRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const
795
0
{
796
0
    Q_UNUSED(other);
797
0
    return true;
798
0
}
799
800
QRhiRenderPassDescriptor *QNullRenderPassDescriptor::newCompatibleRenderPassDescriptor() const
801
0
{
802
0
    QNullRenderPassDescriptor *rpD = new QNullRenderPassDescriptor(m_rhi);
803
0
    QRHI_RES_RHI(QRhiNull);
804
0
    rhiD->registerResource(rpD, false);
805
0
    return rpD;
806
0
}
807
808
QVector<quint32> QNullRenderPassDescriptor::serializedFormat() const
809
0
{
810
0
    return {};
811
0
}
812
813
QNullSwapChainRenderTarget::QNullSwapChainRenderTarget(QRhiImplementation *rhi, QRhiSwapChain *swapchain)
814
0
    : QRhiSwapChainRenderTarget(rhi, swapchain),
815
0
      d(rhi)
816
0
{
817
0
}
818
819
QNullSwapChainRenderTarget::~QNullSwapChainRenderTarget()
820
0
{
821
0
    destroy();
822
0
}
823
824
void QNullSwapChainRenderTarget::destroy()
825
0
{
826
0
}
827
828
QSize QNullSwapChainRenderTarget::pixelSize() const
829
0
{
830
0
    return d.pixelSize;
831
0
}
832
833
float QNullSwapChainRenderTarget::devicePixelRatio() const
834
0
{
835
0
    return d.dpr;
836
0
}
837
838
int QNullSwapChainRenderTarget::sampleCount() const
839
0
{
840
0
    return 1;
841
0
}
842
843
QNullTextureRenderTarget::QNullTextureRenderTarget(QRhiImplementation *rhi,
844
                                                   const QRhiTextureRenderTargetDescription &desc,
845
                                                   Flags flags)
846
0
    : QRhiTextureRenderTarget(rhi, desc, flags),
847
0
      d(rhi)
848
0
{
849
0
}
850
851
QNullTextureRenderTarget::~QNullTextureRenderTarget()
852
0
{
853
0
    destroy();
854
0
}
855
856
void QNullTextureRenderTarget::destroy()
857
0
{
858
0
    QRHI_RES_RHI(QRhiNull);
859
0
    if (rhiD)
860
0
        rhiD->unregisterResource(this);
861
0
}
862
863
QRhiRenderPassDescriptor *QNullTextureRenderTarget::newCompatibleRenderPassDescriptor()
864
0
{
865
0
    QNullRenderPassDescriptor *rpD = new QNullRenderPassDescriptor(m_rhi);
866
0
    QRHI_RES_RHI(QRhiNull);
867
0
    rhiD->registerResource(rpD, false);
868
0
    return rpD;
869
0
}
870
871
bool QNullTextureRenderTarget::create()
872
0
{
873
0
    QRHI_RES_RHI(QRhiNull);
874
0
    d.rp = QRHI_RES(QNullRenderPassDescriptor, m_renderPassDesc);
875
0
    if (m_desc.cbeginColorAttachments() != m_desc.cendColorAttachments()) {
876
0
        const QRhiColorAttachment *colorAtt = m_desc.cbeginColorAttachments();
877
0
        QRhiTexture *tex = colorAtt->texture();
878
0
        QRhiRenderBuffer *rb = colorAtt->renderBuffer();
879
0
        d.pixelSize = tex ? rhiD->q->sizeForMipLevel(colorAtt->level(), tex->pixelSize()) : rb->pixelSize();
880
0
    } else if (m_desc.depthStencilBuffer()) {
881
0
        d.pixelSize = m_desc.depthStencilBuffer()->pixelSize();
882
0
    } else if (m_desc.depthTexture()) {
883
0
        d.pixelSize = m_desc.depthTexture()->pixelSize();
884
0
    }
885
0
    QRhiRenderTargetAttachmentTracker::updateResIdList<QNullTexture, QNullRenderBuffer>(m_desc, &d.currentResIdList);
886
0
    rhiD->registerResource(this);
887
0
    return true;
888
0
}
889
890
QSize QNullTextureRenderTarget::pixelSize() const
891
0
{
892
0
    if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QNullTexture, QNullRenderBuffer>(m_desc, d.currentResIdList))
893
0
        const_cast<QNullTextureRenderTarget *>(this)->create();
894
895
0
    return d.pixelSize;
896
0
}
897
898
float QNullTextureRenderTarget::devicePixelRatio() const
899
0
{
900
0
    return d.dpr;
901
0
}
902
903
int QNullTextureRenderTarget::sampleCount() const
904
0
{
905
0
    return 1;
906
0
}
907
908
QNullShaderResourceBindings::QNullShaderResourceBindings(QRhiImplementation *rhi)
909
0
    : QRhiShaderResourceBindings(rhi)
910
0
{
911
0
}
912
913
QNullShaderResourceBindings::~QNullShaderResourceBindings()
914
0
{
915
0
    destroy();
916
0
}
917
918
void QNullShaderResourceBindings::destroy()
919
0
{
920
0
    QRHI_RES_RHI(QRhiNull);
921
0
    if (rhiD)
922
0
        rhiD->unregisterResource(this);
923
0
}
924
925
bool QNullShaderResourceBindings::create()
926
0
{
927
0
    QRHI_RES_RHI(QRhiNull);
928
0
    if (!rhiD->sanityCheckShaderResourceBindings(this))
929
0
        return false;
930
931
0
    rhiD->updateLayoutDesc(this);
932
933
0
    rhiD->registerResource(this, false);
934
0
    return true;
935
0
}
936
937
void QNullShaderResourceBindings::updateResources(UpdateFlags flags)
938
0
{
939
0
    Q_UNUSED(flags);
940
0
}
941
942
QNullGraphicsPipeline::QNullGraphicsPipeline(QRhiImplementation *rhi)
943
0
    : QRhiGraphicsPipeline(rhi)
944
0
{
945
0
}
946
947
QNullGraphicsPipeline::~QNullGraphicsPipeline()
948
0
{
949
0
    destroy();
950
0
}
951
952
void QNullGraphicsPipeline::destroy()
953
0
{
954
0
    QRHI_RES_RHI(QRhiNull);
955
0
    if (rhiD)
956
0
        rhiD->unregisterResource(this);
957
0
}
958
959
bool QNullGraphicsPipeline::create()
960
0
{
961
0
    QRHI_RES_RHI(QRhiNull);
962
0
    if (!rhiD->sanityCheckGraphicsPipeline(this))
963
0
        return false;
964
965
0
    rhiD->registerResource(this);
966
0
    return true;
967
0
}
968
969
QNullComputePipeline::QNullComputePipeline(QRhiImplementation *rhi)
970
0
    : QRhiComputePipeline(rhi)
971
0
{
972
0
}
973
974
QNullComputePipeline::~QNullComputePipeline()
975
0
{
976
0
    destroy();
977
0
}
978
979
void QNullComputePipeline::destroy()
980
0
{
981
0
    QRHI_RES_RHI(QRhiNull);
982
0
    if (rhiD)
983
0
        rhiD->unregisterResource(this);
984
0
}
985
986
bool QNullComputePipeline::create()
987
0
{
988
0
    QRHI_RES_RHI(QRhiNull);
989
0
    rhiD->registerResource(this);
990
0
    return true;
991
0
}
992
993
QNullCommandBuffer::QNullCommandBuffer(QRhiImplementation *rhi)
994
0
    : QRhiCommandBuffer(rhi)
995
0
{
996
0
}
997
998
QNullCommandBuffer::~QNullCommandBuffer()
999
0
{
1000
0
    destroy();
1001
0
}
1002
1003
void QNullCommandBuffer::destroy()
1004
0
{
1005
    // nothing to do here
1006
0
}
1007
1008
QNullSwapChain::QNullSwapChain(QRhiImplementation *rhi)
1009
0
    : QRhiSwapChain(rhi),
1010
0
      rt(rhi, this),
1011
0
      cb(rhi)
1012
0
{
1013
0
}
1014
1015
QNullSwapChain::~QNullSwapChain()
1016
0
{
1017
0
    destroy();
1018
0
}
1019
1020
void QNullSwapChain::destroy()
1021
0
{
1022
0
    QRHI_RES_RHI(QRhiNull);
1023
0
    if (rhiD)
1024
0
        rhiD->unregisterResource(this);
1025
0
}
1026
1027
QRhiCommandBuffer *QNullSwapChain::currentFrameCommandBuffer()
1028
0
{
1029
0
    return &cb;
1030
0
}
1031
1032
QRhiRenderTarget *QNullSwapChain::currentFrameRenderTarget()
1033
0
{
1034
0
    return &rt;
1035
0
}
1036
1037
QSize QNullSwapChain::surfacePixelSize()
1038
0
{
1039
0
    return QSize(1280, 720);
1040
0
}
1041
1042
bool QNullSwapChain::isFormatSupported(Format f)
1043
0
{
1044
0
    return f == SDR;
1045
0
}
1046
1047
QRhiRenderPassDescriptor *QNullSwapChain::newCompatibleRenderPassDescriptor()
1048
0
{
1049
0
    QNullRenderPassDescriptor *rpD = new QNullRenderPassDescriptor(m_rhi);
1050
0
    QRHI_RES_RHI(QRhiNull);
1051
0
    rhiD->registerResource(rpD, false);
1052
0
    return rpD;
1053
0
}
1054
1055
bool QNullSwapChain::createOrResize()
1056
0
{
1057
0
    const bool needsRegistration = !window || window != m_window;
1058
0
    if (window && window != m_window)
1059
0
        destroy();
1060
1061
0
    window = m_window;
1062
0
    m_currentPixelSize = surfacePixelSize();
1063
0
    rt.setRenderPassDescriptor(m_renderPassDesc); // for the public getter in QRhiRenderTarget
1064
0
    rt.d.rp = QRHI_RES(QNullRenderPassDescriptor, m_renderPassDesc);
1065
0
    rt.d.pixelSize = m_currentPixelSize;
1066
0
    frameCount = 0;
1067
1068
0
    if (needsRegistration) {
1069
0
        QRHI_RES_RHI(QRhiNull);
1070
0
        rhiD->registerResource(this);
1071
0
    }
1072
1073
0
    return true;
1074
0
}
1075
1076
QT_END_NAMESPACE