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#ifndef QRHI_P_H
5#define QRHI_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <rhi/qrhi.h>
19#include <QBitArray>
20#include <QAtomicInt>
21#include <QElapsedTimer>
22#include <QLoggingCategory>
23#include <QtCore/qset.h>
24#include <QtCore/qvarlengtharray.h>
25
26QT_BEGIN_NAMESPACE
27
28#define QRHI_RES(t, x) static_cast<t *>(x)
29#define QRHI_RES_RHI(t) t *rhiD = static_cast<t *>(m_rhi)
30
31Q_DECLARE_LOGGING_CATEGORY(QRHI_LOG_INFO)
32
33class QRhiImplementation
34{
35public:
36 virtual ~QRhiImplementation();
37
38 virtual bool create(QRhi::Flags flags) = 0;
39 virtual void destroy() = 0;
40
41 virtual QRhiGraphicsPipeline *createGraphicsPipeline() = 0;
42 virtual QRhiComputePipeline *createComputePipeline() = 0;
43 virtual QRhiShaderResourceBindings *createShaderResourceBindings() = 0;
44 virtual QRhiBuffer *createBuffer(QRhiBuffer::Type type,
45 QRhiBuffer::UsageFlags usage,
46 quint32 size) = 0;
47 virtual QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type,
48 const QSize &pixelSize,
49 int sampleCount,
50 QRhiRenderBuffer::Flags flags,
51 QRhiTexture::Format backingFormatHint) = 0;
52 virtual QRhiTexture *createTexture(QRhiTexture::Format format,
53 const QSize &pixelSize,
54 int depth,
55 int arraySize,
56 int sampleCount,
57 QRhiTexture::Flags flags) = 0;
58 virtual QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
59 QRhiSampler::Filter minFilter,
60 QRhiSampler::Filter mipmapMode,
61 QRhiSampler:: AddressMode u,
62 QRhiSampler::AddressMode v,
63 QRhiSampler::AddressMode w) = 0;
64
65 virtual QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
66 QRhiTextureRenderTarget::Flags flags) = 0;
67
68 virtual QRhiSwapChain *createSwapChain() = 0;
69 virtual QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) = 0;
70 virtual QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) = 0;
71 virtual QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags) = 0;
72 virtual QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) = 0;
73 virtual QRhi::FrameOpResult finish() = 0;
74
75 virtual void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) = 0;
76
77 virtual void beginPass(QRhiCommandBuffer *cb,
78 QRhiRenderTarget *rt,
79 const QColor &colorClearValue,
80 const QRhiDepthStencilClearValue &depthStencilClearValue,
81 QRhiResourceUpdateBatch *resourceUpdates,
82 QRhiCommandBuffer::BeginPassFlags flags) = 0;
83 virtual void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) = 0;
84
85 virtual void setGraphicsPipeline(QRhiCommandBuffer *cb,
86 QRhiGraphicsPipeline *ps) = 0;
87
88 virtual void setShaderResources(QRhiCommandBuffer *cb,
89 QRhiShaderResourceBindings *srb,
90 int dynamicOffsetCount,
91 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) = 0;
92
93 virtual void setVertexInput(QRhiCommandBuffer *cb,
94 int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
95 QRhiBuffer *indexBuf, quint32 indexOffset,
96 QRhiCommandBuffer::IndexFormat indexFormat) = 0;
97
98 virtual void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) = 0;
99 virtual void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) = 0;
100 virtual void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) = 0;
101 virtual void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) = 0;
102
103 virtual void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
104 quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) = 0;
105 virtual void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
106 quint32 instanceCount, quint32 firstIndex,
107 qint32 vertexOffset, quint32 firstInstance) = 0;
108
109 virtual void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) = 0;
110 virtual void debugMarkEnd(QRhiCommandBuffer *cb) = 0;
111 virtual void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) = 0;
112
113 virtual void beginComputePass(QRhiCommandBuffer *cb,
114 QRhiResourceUpdateBatch *resourceUpdates,
115 QRhiCommandBuffer::BeginPassFlags flags) = 0;
116 virtual void endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) = 0;
117 virtual void setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps) = 0;
118 virtual void dispatch(QRhiCommandBuffer *cb, int x, int y, int z) = 0;
119
120 virtual const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) = 0;
121 virtual void beginExternal(QRhiCommandBuffer *cb) = 0;
122 virtual void endExternal(QRhiCommandBuffer *cb) = 0;
123 virtual double lastCompletedGpuTime(QRhiCommandBuffer *cb) = 0;
124
125 virtual QList<int> supportedSampleCounts() const = 0;
126 virtual int ubufAlignment() const = 0;
127 virtual bool isYUpInFramebuffer() const = 0;
128 virtual bool isYUpInNDC() const = 0;
129 virtual bool isClipDepthZeroToOne() const = 0;
130 virtual QMatrix4x4 clipSpaceCorrMatrix() const = 0;
131 virtual bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const = 0;
132 virtual bool isFeatureSupported(QRhi::Feature feature) const = 0;
133 virtual int resourceLimit(QRhi::ResourceLimit limit) const = 0;
134 virtual const QRhiNativeHandles *nativeHandles() = 0;
135 virtual QRhiDriverInfo driverInfo() const = 0;
136 virtual QRhiStats statistics() = 0;
137 virtual bool makeThreadLocalNativeContextCurrent() = 0;
138 virtual void releaseCachedResources() = 0;
139 virtual bool isDeviceLost() const = 0;
140
141 virtual QByteArray pipelineCacheData() = 0;
142 virtual void setPipelineCacheData(const QByteArray &data) = 0;
143
144 void prepareForCreate(QRhi *rhi, QRhi::Implementation impl, QRhi::Flags flags);
145
146 bool isCompressedFormat(QRhiTexture::Format format) const;
147 void compressedFormatInfo(QRhiTexture::Format format, const QSize &size,
148 quint32 *bpl, quint32 *byteSize,
149 QSize *blockDim) const;
150 void textureFormatInfo(QRhiTexture::Format format, const QSize &size,
151 quint32 *bpl, quint32 *byteSize, quint32 *bytesPerPixel) const;
152
153 void registerResource(QRhiResource *res, bool ownsNativeResources = true)
154 {
155 // The ownsNativeResources is relevant for the (graphics resource) leak
156 // check in ~QRhiImplementation; when false, the registration's sole
157 // purpose is to automatically null out the resource's m_rhi pointer in
158 // case the rhi goes away first. (which should not happen in
159 // well-written applications but we try to be graceful)
160 resources.insert(key: res, value: ownsNativeResources);
161 }
162
163 void unregisterResource(QRhiResource *res)
164 {
165 resources.remove(key: res);
166 }
167
168 void addDeleteLater(QRhiResource *res)
169 {
170 if (inFrame)
171 pendingDeleteResources.insert(value: res);
172 else
173 delete res;
174 }
175
176 void addCleanupCallback(const QRhi::CleanupCallback &callback)
177 {
178 cleanupCallbacks.append(t: callback);
179 }
180
181 bool sanityCheckGraphicsPipeline(QRhiGraphicsPipeline *ps);
182 bool sanityCheckShaderResourceBindings(QRhiShaderResourceBindings *srb);
183 void updateLayoutDesc(QRhiShaderResourceBindings *srb);
184
185 quint32 pipelineCacheRhiId() const
186 {
187 const quint32 ver = (QT_VERSION_MAJOR << 16) | (QT_VERSION_MINOR << 8) | (QT_VERSION_PATCH);
188 return (quint32(implType) << 24) | ver;
189 }
190
191 void pipelineCreationStart()
192 {
193 pipelineCreationTimer.start();
194 }
195
196 void pipelineCreationEnd()
197 {
198 accumulatedPipelineCreationTime += pipelineCreationTimer.elapsed();
199 }
200
201 qint64 totalPipelineCreationTime() const
202 {
203 return accumulatedPipelineCreationTime;
204 }
205
206 QRhiVertexInputAttribute::Format shaderDescVariableFormatToVertexInputFormat(QShaderDescription::VariableType type) const;
207 quint32 byteSizePerVertexForVertexInputFormat(QRhiVertexInputAttribute::Format format) const;
208
209 static const QRhiShaderResourceBinding::Data *shaderResourceBindingData(const QRhiShaderResourceBinding &binding)
210 {
211 return &binding.d;
212 }
213
214 static QRhiShaderResourceBinding::Data *shaderResourceBindingData(QRhiShaderResourceBinding &binding)
215 {
216 return &binding.d;
217 }
218
219 static bool sortedBindingLessThan(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b)
220 {
221 return a.d.binding < b.d.binding;
222 }
223
224 QRhi *q;
225
226 static const int MAX_SHADER_CACHE_ENTRIES = 128;
227
228 bool debugMarkers = false;
229 int currentFrameSlot = 0; // for vk, mtl, and similar. unused by gl and d3d11.
230 bool inFrame = false;
231
232private:
233 QRhi::Implementation implType;
234 QThread *implThread;
235 QVarLengthArray<QRhiResourceUpdateBatch *, 4> resUpdPool;
236 quint64 resUpdPoolMap = 0;
237 int lastResUpdIdx = -1;
238 QHash<QRhiResource *, bool> resources;
239 QSet<QRhiResource *> pendingDeleteResources;
240 QVarLengthArray<QRhi::CleanupCallback, 4> cleanupCallbacks;
241 QElapsedTimer pipelineCreationTimer;
242 qint64 accumulatedPipelineCreationTime = 0;
243
244 friend class QRhi;
245 friend class QRhiResourceUpdateBatchPrivate;
246};
247
248enum QRhiTargetRectBoundMode
249{
250 UnBounded,
251 Bounded
252};
253
254template<QRhiTargetRectBoundMode boundingMode, typename T, size_t N>
255bool qrhi_toTopLeftRenderTargetRect(const QSize &outputSize, const std::array<T, N> &r,
256 T *x, T *y, T *w, T *h)
257{
258 // x,y are bottom-left in QRhiScissor and QRhiViewport but top-left in
259 // Vulkan/Metal/D3D. Our input is an OpenGL-style scissor rect where both
260 // negative x or y, and partly or completely out of bounds rects are
261 // allowed. The only thing the input here cannot have is a negative width
262 // or height. We must handle all other input gracefully, clamping to a zero
263 // width or height rect in the worst case, and ensuring the resulting rect
264 // is inside the rendertarget's bounds because some APIs' validation/debug
265 // layers are allergic to out of bounds scissor rects.
266
267 const T outputWidth = outputSize.width();
268 const T outputHeight = outputSize.height();
269 const T inputWidth = r[2];
270 const T inputHeight = r[3];
271
272 if (inputWidth < 0 || inputHeight < 0)
273 return false;
274
275 *x = r[0];
276 *y = outputHeight - (r[1] + inputHeight);
277 *w = inputWidth;
278 *h = inputHeight;
279
280 if (boundingMode == Bounded) {
281 const T widthOffset = *x < 0 ? -*x : 0;
282 const T heightOffset = *y < 0 ? -*y : 0;
283 *w = *x < outputWidth ? qMax<T>(0, inputWidth - widthOffset) : 0;
284 *h = *y < outputHeight ? qMax<T>(0, inputHeight - heightOffset) : 0;
285
286 if (outputWidth > 0)
287 *x = qBound<T>(0, *x, outputWidth - 1);
288 if (outputHeight > 0)
289 *y = qBound<T>(0, *y, outputHeight - 1);
290
291 if (*x + *w > outputWidth)
292 *w = qMax<T>(0, outputWidth - *x);
293 if (*y + *h > outputHeight)
294 *h = qMax<T>(0, outputHeight - *y);
295 }
296 return true;
297}
298
299struct QRhiBufferDataPrivate
300{
301 Q_DISABLE_COPY_MOVE(QRhiBufferDataPrivate)
302 QRhiBufferDataPrivate() { }
303 ~QRhiBufferDataPrivate() { delete[] largeData; }
304 int ref = 1;
305 quint32 size = 0;
306 quint32 largeAlloc = 0;
307 char *largeData = nullptr;
308 static constexpr quint32 SMALL_DATA_SIZE = 1024;
309 char data[SMALL_DATA_SIZE];
310};
311
312// no detach-with-contents, no atomic refcount, no shrink
313class QRhiBufferData
314{
315public:
316 QRhiBufferData() = default;
317 ~QRhiBufferData()
318 {
319 if (d && !--d->ref)
320 delete d;
321 }
322 QRhiBufferData(const QRhiBufferData &other)
323 : d(other.d)
324 {
325 if (d)
326 d->ref += 1;
327 }
328 QRhiBufferData &operator=(const QRhiBufferData &other)
329 {
330 if (d == other.d)
331 return *this;
332 if (other.d)
333 other.d->ref += 1;
334 if (d && !--d->ref)
335 delete d;
336 d = other.d;
337 return *this;
338 }
339 const char *constData() const
340 {
341 return d->size <= QRhiBufferDataPrivate::SMALL_DATA_SIZE ? d->data : d->largeData;
342 }
343 quint32 size() const
344 {
345 return d->size;
346 }
347 void assign(const char *s, quint32 size)
348 {
349 if (!d) {
350 d = new QRhiBufferDataPrivate;
351 } else if (d->ref != 1) {
352 d->ref -= 1;
353 d = new QRhiBufferDataPrivate;
354 }
355 d->size = size;
356 if (size <= QRhiBufferDataPrivate::SMALL_DATA_SIZE) {
357 memcpy(dest: d->data, src: s, n: size);
358 } else {
359 if (d->largeAlloc < size) {
360 delete[] d->largeData;
361 d->largeAlloc = size;
362 d->largeData = new char[size];
363 }
364 memcpy(dest: d->largeData, src: s, n: size);
365 }
366 }
367private:
368 QRhiBufferDataPrivate *d = nullptr;
369};
370
371Q_DECLARE_TYPEINFO(QRhiBufferData, Q_RELOCATABLE_TYPE);
372
373class QRhiResourceUpdateBatchPrivate
374{
375public:
376 struct BufferOp {
377 enum Type {
378 DynamicUpdate,
379 StaticUpload,
380 Read
381 };
382 Type type;
383 QRhiBuffer *buf;
384 quint32 offset;
385 QRhiBufferData data;
386 quint32 readSize;
387 QRhiReadbackResult *result;
388
389 static BufferOp dynamicUpdate(QRhiBuffer *buf, quint32 offset, quint32 size, const void *data)
390 {
391 BufferOp op = {};
392 op.type = DynamicUpdate;
393 op.buf = buf;
394 op.offset = offset;
395 const int effectiveSize = size ? size : buf->size();
396 op.data.assign(s: reinterpret_cast<const char *>(data), size: effectiveSize);
397 return op;
398 }
399
400 static void changeToDynamicUpdate(BufferOp *op, QRhiBuffer *buf, quint32 offset, quint32 size, const void *data)
401 {
402 op->type = DynamicUpdate;
403 op->buf = buf;
404 op->offset = offset;
405 const int effectiveSize = size ? size : buf->size();
406 op->data.assign(s: reinterpret_cast<const char *>(data), size: effectiveSize);
407 }
408
409 static BufferOp staticUpload(QRhiBuffer *buf, quint32 offset, quint32 size, const void *data)
410 {
411 BufferOp op = {};
412 op.type = StaticUpload;
413 op.buf = buf;
414 op.offset = offset;
415 const int effectiveSize = size ? size : buf->size();
416 op.data.assign(s: reinterpret_cast<const char *>(data), size: effectiveSize);
417 return op;
418 }
419
420 static void changeToStaticUpload(BufferOp *op, QRhiBuffer *buf, quint32 offset, quint32 size, const void *data)
421 {
422 op->type = StaticUpload;
423 op->buf = buf;
424 op->offset = offset;
425 const int effectiveSize = size ? size : buf->size();
426 op->data.assign(s: reinterpret_cast<const char *>(data), size: effectiveSize);
427 }
428
429 static BufferOp read(QRhiBuffer *buf, quint32 offset, quint32 size, QRhiReadbackResult *result)
430 {
431 BufferOp op = {};
432 op.type = Read;
433 op.buf = buf;
434 op.offset = offset;
435 op.readSize = size;
436 op.result = result;
437 return op;
438 }
439 };
440
441 struct TextureOp {
442 enum Type {
443 Upload,
444 Copy,
445 Read,
446 GenMips
447 };
448 Type type;
449 QRhiTexture *dst;
450 // Specifying multiple uploads for a subresource must be supported.
451 // In the backend this can then end up, where applicable, as a
452 // single, batched copy operation with only one set of barriers.
453 // This helps when doing for example glyph cache fills.
454 using MipLevelUploadList = std::array<QVector<QRhiTextureSubresourceUploadDescription>, QRhi::MAX_MIP_LEVELS>;
455 QVarLengthArray<MipLevelUploadList, 6> subresDesc;
456 QRhiTexture *src;
457 QRhiTextureCopyDescription desc;
458 QRhiReadbackDescription rb;
459 QRhiReadbackResult *result;
460
461 static TextureOp upload(QRhiTexture *tex, const QRhiTextureUploadDescription &desc)
462 {
463 TextureOp op = {};
464 op.type = Upload;
465 op.dst = tex;
466 int maxLayer = -1;
467 for (auto it = desc.cbeginEntries(), itEnd = desc.cendEntries(); it != itEnd; ++it) {
468 if (it->layer() > maxLayer)
469 maxLayer = it->layer();
470 }
471 op.subresDesc.resize(sz: maxLayer + 1);
472 for (auto it = desc.cbeginEntries(), itEnd = desc.cendEntries(); it != itEnd; ++it)
473 op.subresDesc[it->layer()][it->level()].append(t: it->description());
474 return op;
475 }
476
477 static TextureOp copy(QRhiTexture *dst, QRhiTexture *src, const QRhiTextureCopyDescription &desc)
478 {
479 TextureOp op = {};
480 op.type = Copy;
481 op.dst = dst;
482 op.src = src;
483 op.desc = desc;
484 return op;
485 }
486
487 static TextureOp read(const QRhiReadbackDescription &rb, QRhiReadbackResult *result)
488 {
489 TextureOp op = {};
490 op.type = Read;
491 op.rb = rb;
492 op.result = result;
493 return op;
494 }
495
496 static TextureOp genMips(QRhiTexture *tex)
497 {
498 TextureOp op = {};
499 op.type = GenMips;
500 op.dst = tex;
501 return op;
502 }
503 };
504
505 int activeBufferOpCount = 0; // this is the real number of used elements in bufferOps, not bufferOps.count()
506 static const int BUFFER_OPS_STATIC_ALLOC = 1024;
507 QVarLengthArray<BufferOp, BUFFER_OPS_STATIC_ALLOC> bufferOps;
508
509 int activeTextureOpCount = 0; // this is the real number of used elements in textureOps, not textureOps.count()
510 static const int TEXTURE_OPS_STATIC_ALLOC = 256;
511 QVarLengthArray<TextureOp, TEXTURE_OPS_STATIC_ALLOC> textureOps;
512
513 QRhiResourceUpdateBatch *q = nullptr;
514 QRhiImplementation *rhi = nullptr;
515 int poolIndex = -1;
516
517 void free();
518 void merge(QRhiResourceUpdateBatchPrivate *other);
519 bool hasOptimalCapacity() const;
520 void trimOpLists();
521
522 static QRhiResourceUpdateBatchPrivate *get(QRhiResourceUpdateBatch *b) { return b->d; }
523};
524
525template<typename T>
526struct QRhiBatchedBindings
527{
528 void feed(int binding, T resource) { // binding must be strictly increasing
529 if (curBinding == -1 || binding > curBinding + 1) {
530 finish();
531 curBatch.startBinding = binding;
532 curBatch.resources.clear();
533 curBatch.resources.append(resource);
534 } else {
535 Q_ASSERT(binding == curBinding + 1);
536 curBatch.resources.append(resource);
537 }
538 curBinding = binding;
539 }
540
541 bool finish() {
542 if (!curBatch.resources.isEmpty())
543 batches.append(curBatch);
544 return !batches.isEmpty();
545 }
546
547 void clear() {
548 batches.clear();
549 curBatch.resources.clear();
550 curBinding = -1;
551 }
552
553 struct Batch {
554 uint startBinding;
555 QVarLengthArray<T, 4> resources;
556
557 bool operator==(const Batch &other) const
558 {
559 return startBinding == other.startBinding && resources == other.resources;
560 }
561
562 bool operator!=(const Batch &other) const
563 {
564 return !operator==(other);
565 }
566 };
567
568 QVarLengthArray<Batch, 4> batches; // sorted by startBinding
569
570 bool operator==(const QRhiBatchedBindings<T> &other) const
571 {
572 return batches == other.batches;
573 }
574
575 bool operator!=(const QRhiBatchedBindings<T> &other) const
576 {
577 return !operator==(other);
578 }
579
580private:
581 Batch curBatch;
582 int curBinding = -1;
583};
584
585class QRhiGlobalObjectIdGenerator
586{
587public:
588#ifdef Q_ATOMIC_INT64_IS_SUPPORTED
589 using Type = quint64;
590#else
591 using Type = quint32;
592#endif
593 static Type newId();
594};
595
596class QRhiPassResourceTracker
597{
598public:
599 bool isEmpty() const;
600 void reset();
601
602 struct UsageState {
603 int layout;
604 int access;
605 int stage;
606 };
607
608 enum BufferStage {
609 BufVertexInputStage,
610 BufVertexStage,
611 BufTCStage,
612 BufTEStage,
613 BufFragmentStage,
614 BufComputeStage,
615 BufGeometryStage
616 };
617
618 enum BufferAccess {
619 BufVertexInput,
620 BufIndexRead,
621 BufUniformRead,
622 BufStorageLoad,
623 BufStorageStore,
624 BufStorageLoadStore
625 };
626
627 void registerBuffer(QRhiBuffer *buf, int slot, BufferAccess *access, BufferStage *stage,
628 const UsageState &state);
629
630 enum TextureStage {
631 TexVertexStage,
632 TexTCStage,
633 TexTEStage,
634 TexFragmentStage,
635 TexColorOutputStage,
636 TexDepthOutputStage,
637 TexComputeStage,
638 TexGeometryStage
639 };
640
641 enum TextureAccess {
642 TexSample,
643 TexColorOutput,
644 TexDepthOutput,
645 TexStorageLoad,
646 TexStorageStore,
647 TexStorageLoadStore
648 };
649
650 void registerTexture(QRhiTexture *tex, TextureAccess *access, TextureStage *stage,
651 const UsageState &state);
652
653 struct Buffer {
654 int slot;
655 BufferAccess access;
656 BufferStage stage;
657 UsageState stateAtPassBegin;
658 };
659
660 using BufferIterator = QHash<QRhiBuffer *, Buffer>::const_iterator;
661 BufferIterator cbeginBuffers() const { return m_buffers.cbegin(); }
662 BufferIterator cendBuffers() const { return m_buffers.cend(); }
663
664 struct Texture {
665 TextureAccess access;
666 TextureStage stage;
667 UsageState stateAtPassBegin;
668 };
669
670 using TextureIterator = QHash<QRhiTexture *, Texture>::const_iterator;
671 TextureIterator cbeginTextures() const { return m_textures.cbegin(); }
672 TextureIterator cendTextures() const { return m_textures.cend(); }
673
674 static BufferStage toPassTrackerBufferStage(QRhiShaderResourceBinding::StageFlags stages);
675 static TextureStage toPassTrackerTextureStage(QRhiShaderResourceBinding::StageFlags stages);
676
677private:
678 QHash<QRhiBuffer *, Buffer> m_buffers;
679 QHash<QRhiTexture *, Texture> m_textures;
680};
681
682Q_DECLARE_TYPEINFO(QRhiPassResourceTracker::Buffer, Q_RELOCATABLE_TYPE);
683Q_DECLARE_TYPEINFO(QRhiPassResourceTracker::Texture, Q_RELOCATABLE_TYPE);
684
685template<typename T, int GROW = 1024>
686class QRhiBackendCommandList
687{
688public:
689 QRhiBackendCommandList() = default;
690 ~QRhiBackendCommandList() { delete[] v; }
691 inline void reset() { p = 0; }
692 inline bool isEmpty() const { return p == 0; }
693 inline T &get() {
694 if (p == a) {
695 a += GROW;
696 T *nv = new T[a];
697 if (v) {
698 memcpy(nv, v, p * sizeof(T));
699 delete[] v;
700 }
701 v = nv;
702 }
703 return v[p++];
704 }
705 inline void unget() { --p; }
706 inline T *cbegin() const { return v; }
707 inline T *cend() const { return v + p; }
708 inline T *begin() { return v; }
709 inline T *end() { return v + p; }
710private:
711 Q_DISABLE_COPY(QRhiBackendCommandList)
712 T *v = nullptr;
713 int a = 0;
714 int p = 0;
715};
716
717struct QRhiRenderTargetAttachmentTracker
718{
719 struct ResId { quint64 id; uint generation; };
720 using ResIdList = QVarLengthArray<ResId, 8 * 2 + 1>; // color, resolve, ds
721
722 template<typename TexType, typename RenderBufferType>
723 static void updateResIdList(const QRhiTextureRenderTargetDescription &desc, ResIdList *dst);
724
725 template<typename TexType, typename RenderBufferType>
726 static bool isUpToDate(const QRhiTextureRenderTargetDescription &desc, const ResIdList &currentResIdList);
727};
728
729inline bool operator==(const QRhiRenderTargetAttachmentTracker::ResId &a, const QRhiRenderTargetAttachmentTracker::ResId &b)
730{
731 return a.id == b.id && a.generation == b.generation;
732}
733
734inline bool operator!=(const QRhiRenderTargetAttachmentTracker::ResId &a, const QRhiRenderTargetAttachmentTracker::ResId &b)
735{
736 return !(a == b);
737}
738
739template<typename TexType, typename RenderBufferType>
740void QRhiRenderTargetAttachmentTracker::updateResIdList(const QRhiTextureRenderTargetDescription &desc, ResIdList *dst)
741{
742 const bool hasDepthStencil = desc.depthStencilBuffer() || desc.depthTexture();
743 dst->resize(sz: desc.colorAttachmentCount() * 2 + (hasDepthStencil ? 1 : 0));
744 int n = 0;
745 for (auto it = desc.cbeginColorAttachments(), itEnd = desc.cendColorAttachments(); it != itEnd; ++it, ++n) {
746 const QRhiColorAttachment &colorAtt(*it);
747 if (colorAtt.texture()) {
748 TexType *texD = QRHI_RES(TexType, colorAtt.texture());
749 (*dst)[n] = { texD->globalResourceId(), texD->generation };
750 } else if (colorAtt.renderBuffer()) {
751 RenderBufferType *rbD = QRHI_RES(RenderBufferType, colorAtt.renderBuffer());
752 (*dst)[n] = { rbD->globalResourceId(), rbD->generation };
753 } else {
754 (*dst)[n] = { .id: 0, .generation: 0 };
755 }
756 ++n;
757 if (colorAtt.resolveTexture()) {
758 TexType *texD = QRHI_RES(TexType, colorAtt.resolveTexture());
759 (*dst)[n] = { texD->globalResourceId(), texD->generation };
760 } else {
761 (*dst)[n] = { .id: 0, .generation: 0 };
762 }
763 }
764 if (hasDepthStencil) {
765 if (desc.depthTexture()) {
766 TexType *depthTexD = QRHI_RES(TexType, desc.depthTexture());
767 (*dst)[n] = { depthTexD->globalResourceId(), depthTexD->generation };
768 } else if (desc.depthStencilBuffer()) {
769 RenderBufferType *depthRbD = QRHI_RES(RenderBufferType, desc.depthStencilBuffer());
770 (*dst)[n] = { depthRbD->globalResourceId(), depthRbD->generation };
771 } else {
772 (*dst)[n] = { .id: 0, .generation: 0 };
773 }
774 }
775}
776
777template<typename TexType, typename RenderBufferType>
778bool QRhiRenderTargetAttachmentTracker::isUpToDate(const QRhiTextureRenderTargetDescription &desc, const ResIdList &currentResIdList)
779{
780 // Just as setShaderResources() recognizes if an srb's referenced
781 // resources have been rebuilt (got a create() since the srb's
782 // create()), we should do the same for the textures and renderbuffers
783 // referenced from the rendertarget. It is not uncommon that a texture
784 // or ds buffer gets resized due to following a window size in some
785 // form, which involves a create() on them. It is then nice if the
786 // render target auto-rebuilds in beginPass().
787
788 ResIdList resIdList;
789 updateResIdList<TexType, RenderBufferType>(desc, &resIdList);
790 return resIdList == currentResIdList;
791}
792
793template<typename T>
794inline T *qrhi_objectFromProxyData(QRhiSwapChainProxyData *pd, QWindow *window, QRhi::Implementation impl, uint objectIndex)
795{
796 Q_ASSERT(objectIndex < std::size(pd->reserved));
797 if (!pd->reserved[objectIndex]) // // was not set, no other choice, do it here, whatever thread this is
798 *pd = QRhi::updateSwapChainProxyData(impl, window);
799 return static_cast<T *>(pd->reserved[objectIndex]);
800}
801
802QT_END_NAMESPACE
803
804#endif
805

source code of qtbase/src/gui/rhi/qrhi_p.h