Coverage Report

Created: 2026-08-14 08:24

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