Coverage Report

Created: 2026-09-28 08:21

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