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

source code of qtbase/src/gui/rhi/qrhinull.cpp