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 bool isStencilSupportingFormat(QRhiTexture::Format format) const;
153
154 void registerResource(QRhiResource *res, bool ownsNativeResources = true)
155 {
156 // The ownsNativeResources is relevant for the (graphics resource) leak
157 // check in ~QRhiImplementation; when false, the registration's sole
158 // purpose is to automatically null out the resource's m_rhi pointer in
159 // case the rhi goes away first. (which should not happen in
160 // well-written applications but we try to be graceful)
161 resources.insert(key: res, value: ownsNativeResources);
162 }
163
164 void unregisterResource(QRhiResource *res)
165 {
166 resources.remove(key: res);
167 }
168
169 void addDeleteLater(QRhiResource *res)
170 {
171 if (inFrame)
172 pendingDeleteResources.insert(value: res);
173 else
174 delete res;
175 }
176
177 void addCleanupCallback(const QRhi::CleanupCallback &callback)
178 {
179 cleanupCallbacks.append(t: callback);
180 }
181
182 void addCleanupCallback(const void *key, const QRhi::CleanupCallback &callback)
183 {
184 keyedCleanupCallbacks[key] = callback;
185 }
186
187 void removeCleanupCallback(const void *key)
188 {
189 keyedCleanupCallbacks.remove(key);
190 }
191
192 bool sanityCheckGraphicsPipeline(QRhiGraphicsPipeline *ps);
193 bool sanityCheckShaderResourceBindings(QRhiShaderResourceBindings *srb);
194 void updateLayoutDesc(QRhiShaderResourceBindings *srb);
195
196 quint32 pipelineCacheRhiId() const
197 {
198 const quint32 ver = (QT_VERSION_MAJOR << 16) | (QT_VERSION_MINOR << 8) | (QT_VERSION_PATCH);
199 return (quint32(implType) << 24) | ver;
200 }
201
202 void pipelineCreationStart()
203 {
204 pipelineCreationTimer.start();
205 }
206
207 void pipelineCreationEnd()
208 {
209 accumulatedPipelineCreationTime += pipelineCreationTimer.elapsed();
210 }
211
212 qint64 totalPipelineCreationTime() const
213 {
214 return accumulatedPipelineCreationTime;
215 }
216
217 QRhiVertexInputAttribute::Format shaderDescVariableFormatToVertexInputFormat(QShaderDescription::VariableType type) const;
218 quint32 byteSizePerVertexForVertexInputFormat(QRhiVertexInputAttribute::Format format) const;
219
220 static const QRhiShaderResourceBinding::Data *shaderResourceBindingData(const QRhiShaderResourceBinding &binding)
221 {
222 return &binding.d;
223 }
224
225 static QRhiShaderResourceBinding::Data *shaderResourceBindingData(QRhiShaderResourceBinding &binding)
226 {
227 return &binding.d;
228 }
229
230 static bool sortedBindingLessThan(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b)
231 {
232 return a.d.binding < b.d.binding;
233 }
234
235 int effectiveSampleCount(int sampleCount) const;
236
237 QRhi *q;
238
239 static const int MAX_SHADER_CACHE_ENTRIES = 128;
240
241 bool debugMarkers = false;
242 int currentFrameSlot = 0; // for vk, mtl, and similar. unused by gl and d3d11.
243 bool inFrame = false;
244
245private:
246 QRhi::Implementation implType;
247 QThread *implThread;
248 QVarLengthArray<QRhiResourceUpdateBatch *, 4> resUpdPool;
249 quint64 resUpdPoolMap = 0;
250 int lastResUpdIdx = -1;
251 QHash<QRhiResource *, bool> resources;
252 QSet<QRhiResource *> pendingDeleteResources;
253 QVarLengthArray<QRhi::CleanupCallback, 4> cleanupCallbacks;
254 QHash<const void *, QRhi::CleanupCallback> keyedCleanupCallbacks;
255 QElapsedTimer pipelineCreationTimer;
256 qint64 accumulatedPipelineCreationTime = 0;
257 static bool rubLogEnabled;
258
259 friend class QRhi;
260 friend class QRhiResourceUpdateBatchPrivate;
261 friend class QRhiBufferData;
262};
263
264enum QRhiTargetRectBoundMode
265{
266 UnBounded,
267 Bounded
268};
269
270template<QRhiTargetRectBoundMode boundingMode, typename T, size_t N>
271bool qrhi_toTopLeftRenderTargetRect(const QSize &outputSize, const std::array<T, N> &r,
272 T *x, T *y, T *w, T *h)
273{
274 // x,y are bottom-left in QRhiScissor and QRhiViewport but top-left in
275 // Vulkan/Metal/D3D. Our input is an OpenGL-style scissor rect where both
276 // negative x or y, and partly or completely out of bounds rects are
277 // allowed. The only thing the input here cannot have is a negative width
278 // or height. We must handle all other input gracefully, clamping to a zero
279 // width or height rect in the worst case, and ensuring the resulting rect
280 // is inside the rendertarget's bounds because some APIs' validation/debug
281 // layers are allergic to out of bounds scissor rects.
282
283 const T outputWidth = outputSize.width();
284 const T outputHeight = outputSize.height();
285 const T inputWidth = r[2];
286 const T inputHeight = r[3];
287
288 if (inputWidth < 0 || inputHeight < 0)
289 return false;
290
291 *x = r[0];
292 *y = outputHeight - (r[1] + inputHeight);
293 *w = inputWidth;
294 *h = inputHeight;
295
296 if (boundingMode == Bounded) {
297 const T widthOffset = *x < 0 ? -*x : 0;
298 const T heightOffset = *y < 0 ? -*y : 0;
299 *w = *x < outputWidth ? qMax<T>(0, inputWidth - widthOffset) : 0;
300 *h = *y < outputHeight ? qMax<T>(0, inputHeight - heightOffset) : 0;
301
302 if (outputWidth > 0)
303 *x = qBound<T>(0, *x, outputWidth - 1);
304 if (outputHeight > 0)
305 *y = qBound<T>(0, *y, outputHeight - 1);
306
307 if (*x + *w > outputWidth)
308 *w = qMax<T>(0, outputWidth - *x);
309 if (*y + *h > outputHeight)
310 *h = qMax<T>(0, outputHeight - *y);
311 }
312 return true;
313}
314
315struct QRhiBufferDataPrivate
316{
317 Q_DISABLE_COPY_MOVE(QRhiBufferDataPrivate)
318 QRhiBufferDataPrivate() { }
319 ~QRhiBufferDataPrivate() { delete[] largeData; }
320 int ref = 1;
321 quint32 size = 0;
322 quint32 largeAlloc = 0;
323 char *largeData = nullptr;
324 static constexpr quint32 SMALL_DATA_SIZE = 1024;
325 char data[SMALL_DATA_SIZE];
326};
327
328// no detach-with-contents, no atomic refcount, no shrink
329class QRhiBufferData
330{
331public:
332 QRhiBufferData() = default;
333 ~QRhiBufferData()
334 {
335 if (d && !--d->ref)
336 delete d;
337 }
338 QRhiBufferData(const QRhiBufferData &other)
339 : d(other.d)
340 {
341 if (d)
342 d->ref += 1;
343 }
344 QRhiBufferData &operator=(const QRhiBufferData &other)
345 {
346 if (d == other.d)
347 return *this;
348 if (other.d)
349 other.d->ref += 1;
350 if (d && !--d->ref)
351 delete d;
352 d = other.d;
353 return *this;
354 }
355 const char *constData() const
356 {
357 return d ? (d->size <= QRhiBufferDataPrivate::SMALL_DATA_SIZE ? d->data : d->largeData) : nullptr;
358 }
359 quint32 size() const
360 {
361 return d ? d->size : 0;
362 }
363 quint32 largeAlloc() const
364 {
365 return d ? d->largeAlloc : 0;
366 }
367 void assign(const char *s, quint32 size)
368 {
369 if (!d) {
370 d = new QRhiBufferDataPrivate;
371 } else if (d->ref != 1) {
372 if (QRhiImplementation::rubLogEnabled)
373 qDebug(msg: "[rub] QRhiBufferData %p/%p new backing due to no-copy detach, ref was %d", this, d, d->ref);
374 d->ref -= 1;
375 d = new QRhiBufferDataPrivate;
376 }
377 d->size = size;
378 if (size <= QRhiBufferDataPrivate::SMALL_DATA_SIZE) {
379 memcpy(dest: d->data, src: s, n: size);
380 } else {
381 if (d->largeAlloc < size) {
382 if (QRhiImplementation::rubLogEnabled)
383 qDebug(msg: "[rub] QRhiBufferData %p/%p new large data allocation %u -> %u", this, d, d->largeAlloc, size);
384 delete[] d->largeData;
385 d->largeAlloc = size;
386 d->largeData = new char[size];
387 }
388 memcpy(dest: d->largeData, src: s, n: size);
389 }
390 }
391private:
392 QRhiBufferDataPrivate *d = nullptr;
393};
394
395Q_DECLARE_TYPEINFO(QRhiBufferData, Q_RELOCATABLE_TYPE);
396
397class QRhiResourceUpdateBatchPrivate
398{
399public:
400 struct BufferOp {
401 enum Type {
402 DynamicUpdate,
403 StaticUpload,
404 Read
405 };
406 Type type;
407 QRhiBuffer *buf;
408 quint32 offset;
409 QRhiBufferData data;
410 quint32 readSize;
411 QRhiReadbackResult *result;
412
413 static BufferOp dynamicUpdate(QRhiBuffer *buf, quint32 offset, quint32 size, const void *data)
414 {
415 BufferOp op = {};
416 op.type = DynamicUpdate;
417 op.buf = buf;
418 op.offset = offset;
419 const int effectiveSize = size ? size : buf->size();
420 op.data.assign(s: reinterpret_cast<const char *>(data), size: effectiveSize);
421 return op;
422 }
423
424 static void changeToDynamicUpdate(BufferOp *op, QRhiBuffer *buf, quint32 offset, quint32 size, const void *data)
425 {
426 op->type = DynamicUpdate;
427 op->buf = buf;
428 op->offset = offset;
429 const int effectiveSize = size ? size : buf->size();
430 op->data.assign(s: reinterpret_cast<const char *>(data), size: effectiveSize);
431 }
432
433 static BufferOp staticUpload(QRhiBuffer *buf, quint32 offset, quint32 size, const void *data)
434 {
435 BufferOp op = {};
436 op.type = StaticUpload;
437 op.buf = buf;
438 op.offset = offset;
439 const int effectiveSize = size ? size : buf->size();
440 op.data.assign(s: reinterpret_cast<const char *>(data), size: effectiveSize);
441 return op;
442 }
443
444 static void changeToStaticUpload(BufferOp *op, QRhiBuffer *buf, quint32 offset, quint32 size, const void *data)
445 {
446 op->type = StaticUpload;
447 op->buf = buf;
448 op->offset = offset;
449 const int effectiveSize = size ? size : buf->size();
450 op->data.assign(s: reinterpret_cast<const char *>(data), size: effectiveSize);
451 }
452
453 static BufferOp read(QRhiBuffer *buf, quint32 offset, quint32 size, QRhiReadbackResult *result)
454 {
455 BufferOp op = {};
456 op.type = Read;
457 op.buf = buf;
458 op.offset = offset;
459 op.readSize = size;
460 op.result = result;
461 return op;
462 }
463 };
464
465 struct TextureOp {
466 enum Type {
467 Upload,
468 Copy,
469 Read,
470 GenMips
471 };
472 Type type;
473 QRhiTexture *dst;
474 // Specifying multiple uploads for a subresource must be supported.
475 // In the backend this can then end up, where applicable, as a
476 // single, batched copy operation with only one set of barriers.
477 // This helps when doing for example glyph cache fills.
478 using MipLevelUploadList = std::array<QVector<QRhiTextureSubresourceUploadDescription>, QRhi::MAX_MIP_LEVELS>;
479 QVarLengthArray<MipLevelUploadList, 6> subresDesc;
480 QRhiTexture *src;
481 QRhiTextureCopyDescription desc;
482 QRhiReadbackDescription rb;
483 QRhiReadbackResult *result;
484
485 static TextureOp upload(QRhiTexture *tex, const QRhiTextureUploadDescription &desc)
486 {
487 TextureOp op = {};
488 op.type = Upload;
489 op.dst = tex;
490 int maxLayer = -1;
491 for (auto it = desc.cbeginEntries(), itEnd = desc.cendEntries(); it != itEnd; ++it) {
492 if (it->layer() > maxLayer)
493 maxLayer = it->layer();
494 }
495 op.subresDesc.resize(sz: maxLayer + 1);
496 for (auto it = desc.cbeginEntries(), itEnd = desc.cendEntries(); it != itEnd; ++it)
497 op.subresDesc[it->layer()][it->level()].append(t: it->description());
498 return op;
499 }
500
501 static TextureOp copy(QRhiTexture *dst, QRhiTexture *src, const QRhiTextureCopyDescription &desc)
502 {
503 TextureOp op = {};
504 op.type = Copy;
505 op.dst = dst;
506 op.src = src;
507 op.desc = desc;
508 return op;
509 }
510
511 static TextureOp read(const QRhiReadbackDescription &rb, QRhiReadbackResult *result)
512 {
513 TextureOp op = {};
514 op.type = Read;
515 op.rb = rb;
516 op.result = result;
517 return op;
518 }
519
520 static TextureOp genMips(QRhiTexture *tex)
521 {
522 TextureOp op = {};
523 op.type = GenMips;
524 op.dst = tex;
525 return op;
526 }
527 };
528
529 int activeBufferOpCount = 0; // this is the real number of used elements in bufferOps, not bufferOps.count()
530 static const int BUFFER_OPS_STATIC_ALLOC = 64;
531 QVarLengthArray<BufferOp, BUFFER_OPS_STATIC_ALLOC> bufferOps;
532
533 int activeTextureOpCount = 0; // this is the real number of used elements in textureOps, not textureOps.count()
534 static const int TEXTURE_OPS_STATIC_ALLOC = 32;
535 QVarLengthArray<TextureOp, TEXTURE_OPS_STATIC_ALLOC> textureOps;
536
537 QRhiResourceUpdateBatch *q = nullptr;
538 QRhiImplementation *rhi = nullptr;
539 int poolIndex = -1;
540
541 void free();
542 void merge(QRhiResourceUpdateBatchPrivate *other);
543 bool hasOptimalCapacity() const;
544 void trimOpLists();
545
546 static QRhiResourceUpdateBatchPrivate *get(QRhiResourceUpdateBatch *b) { return b->d; }
547};
548
549template<typename T>
550struct QRhiBatchedBindings
551{
552 void feed(int binding, T resource) { // binding must be strictly increasing
553 if (curBinding == -1 || binding > curBinding + 1) {
554 finish();
555 curBatch.startBinding = binding;
556 curBatch.resources.clear();
557 curBatch.resources.append(resource);
558 } else {
559 Q_ASSERT(binding == curBinding + 1);
560 curBatch.resources.append(resource);
561 }
562 curBinding = binding;
563 }
564
565 bool finish() {
566 if (!curBatch.resources.isEmpty())
567 batches.append(curBatch);
568 return !batches.isEmpty();
569 }
570
571 void clear() {
572 batches.clear();
573 curBatch.resources.clear();
574 curBinding = -1;
575 }
576
577 struct Batch {
578 uint startBinding;
579 QVarLengthArray<T, 4> resources;
580
581 bool operator==(const Batch &other) const
582 {
583 return startBinding == other.startBinding && resources == other.resources;
584 }
585
586 bool operator!=(const Batch &other) const
587 {
588 return !operator==(other);
589 }
590 };
591
592 QVarLengthArray<Batch, 4> batches; // sorted by startBinding
593
594 bool operator==(const QRhiBatchedBindings<T> &other) const
595 {
596 return batches == other.batches;
597 }
598
599 bool operator!=(const QRhiBatchedBindings<T> &other) const
600 {
601 return !operator==(other);
602 }
603
604private:
605 Batch curBatch;
606 int curBinding = -1;
607};
608
609class QRhiGlobalObjectIdGenerator
610{
611public:
612#ifdef Q_ATOMIC_INT64_IS_SUPPORTED
613 using Type = quint64;
614#else
615 using Type = quint32;
616#endif
617 static Type newId();
618};
619
620class QRhiPassResourceTracker
621{
622public:
623 bool isEmpty() const;
624 void reset();
625
626 struct UsageState {
627 int layout;
628 int access;
629 int stage;
630 };
631
632 enum BufferStage {
633 BufVertexInputStage,
634 BufVertexStage,
635 BufTCStage,
636 BufTEStage,
637 BufFragmentStage,
638 BufComputeStage,
639 BufGeometryStage
640 };
641
642 enum BufferAccess {
643 BufVertexInput,
644 BufIndexRead,
645 BufUniformRead,
646 BufStorageLoad,
647 BufStorageStore,
648 BufStorageLoadStore
649 };
650
651 void registerBuffer(QRhiBuffer *buf, int slot, BufferAccess *access, BufferStage *stage,
652 const UsageState &state);
653
654 enum TextureStage {
655 TexVertexStage,
656 TexTCStage,
657 TexTEStage,
658 TexFragmentStage,
659 TexColorOutputStage,
660 TexDepthOutputStage,
661 TexComputeStage,
662 TexGeometryStage
663 };
664
665 enum TextureAccess {
666 TexSample,
667 TexColorOutput,
668 TexDepthOutput,
669 TexStorageLoad,
670 TexStorageStore,
671 TexStorageLoadStore
672 };
673
674 void registerTexture(QRhiTexture *tex, TextureAccess *access, TextureStage *stage,
675 const UsageState &state);
676
677 struct Buffer {
678 int slot;
679 BufferAccess access;
680 BufferStage stage;
681 UsageState stateAtPassBegin;
682 };
683
684 using BufferIterator = QHash<QRhiBuffer *, Buffer>::const_iterator;
685 BufferIterator cbeginBuffers() const { return m_buffers.cbegin(); }
686 BufferIterator cendBuffers() const { return m_buffers.cend(); }
687
688 struct Texture {
689 TextureAccess access;
690 TextureStage stage;
691 UsageState stateAtPassBegin;
692 };
693
694 using TextureIterator = QHash<QRhiTexture *, Texture>::const_iterator;
695 TextureIterator cbeginTextures() const { return m_textures.cbegin(); }
696 TextureIterator cendTextures() const { return m_textures.cend(); }
697
698 static BufferStage toPassTrackerBufferStage(QRhiShaderResourceBinding::StageFlags stages);
699 static TextureStage toPassTrackerTextureStage(QRhiShaderResourceBinding::StageFlags stages);
700
701private:
702 QHash<QRhiBuffer *, Buffer> m_buffers;
703 QHash<QRhiTexture *, Texture> m_textures;
704};
705
706Q_DECLARE_TYPEINFO(QRhiPassResourceTracker::Buffer, Q_RELOCATABLE_TYPE);
707Q_DECLARE_TYPEINFO(QRhiPassResourceTracker::Texture, Q_RELOCATABLE_TYPE);
708
709template<typename T, int GROW = 1024>
710class QRhiBackendCommandList
711{
712public:
713 QRhiBackendCommandList() = default;
714 ~QRhiBackendCommandList() { delete[] v; }
715 inline void reset() { p = 0; }
716 inline bool isEmpty() const { return p == 0; }
717 inline T &get() {
718 if (p == a) {
719 a += GROW;
720 T *nv = new T[a];
721 if (v) {
722 memcpy(nv, v, p * sizeof(T));
723 delete[] v;
724 }
725 v = nv;
726 }
727 return v[p++];
728 }
729 inline void unget() { --p; }
730 inline T *cbegin() const { return v; }
731 inline T *cend() const { return v + p; }
732 inline T *begin() { return v; }
733 inline T *end() { return v + p; }
734private:
735 Q_DISABLE_COPY(QRhiBackendCommandList)
736 T *v = nullptr;
737 int a = 0;
738 int p = 0;
739};
740
741struct QRhiRenderTargetAttachmentTracker
742{
743 struct ResId { quint64 id; uint generation; };
744 using ResIdList = QVarLengthArray<ResId, 8 * 2 + 1>; // color, resolve, ds
745
746 template<typename TexType, typename RenderBufferType>
747 static void updateResIdList(const QRhiTextureRenderTargetDescription &desc, ResIdList *dst);
748
749 template<typename TexType, typename RenderBufferType>
750 static bool isUpToDate(const QRhiTextureRenderTargetDescription &desc, const ResIdList &currentResIdList);
751};
752
753inline bool operator==(const QRhiRenderTargetAttachmentTracker::ResId &a, const QRhiRenderTargetAttachmentTracker::ResId &b)
754{
755 return a.id == b.id && a.generation == b.generation;
756}
757
758inline bool operator!=(const QRhiRenderTargetAttachmentTracker::ResId &a, const QRhiRenderTargetAttachmentTracker::ResId &b)
759{
760 return !(a == b);
761}
762
763template<typename TexType, typename RenderBufferType>
764void QRhiRenderTargetAttachmentTracker::updateResIdList(const QRhiTextureRenderTargetDescription &desc, ResIdList *dst)
765{
766 const bool hasDepthStencil = desc.depthStencilBuffer() || desc.depthTexture();
767 dst->resize(sz: desc.colorAttachmentCount() * 2 + (hasDepthStencil ? 1 : 0));
768 int n = 0;
769 for (auto it = desc.cbeginColorAttachments(), itEnd = desc.cendColorAttachments(); it != itEnd; ++it, ++n) {
770 const QRhiColorAttachment &colorAtt(*it);
771 if (colorAtt.texture()) {
772 TexType *texD = QRHI_RES(TexType, colorAtt.texture());
773 (*dst)[n] = { texD->globalResourceId(), texD->generation };
774 } else if (colorAtt.renderBuffer()) {
775 RenderBufferType *rbD = QRHI_RES(RenderBufferType, colorAtt.renderBuffer());
776 (*dst)[n] = { rbD->globalResourceId(), rbD->generation };
777 } else {
778 (*dst)[n] = { .id: 0, .generation: 0 };
779 }
780 ++n;
781 if (colorAtt.resolveTexture()) {
782 TexType *texD = QRHI_RES(TexType, colorAtt.resolveTexture());
783 (*dst)[n] = { texD->globalResourceId(), texD->generation };
784 } else {
785 (*dst)[n] = { .id: 0, .generation: 0 };
786 }
787 }
788 if (hasDepthStencil) {
789 if (desc.depthTexture()) {
790 TexType *depthTexD = QRHI_RES(TexType, desc.depthTexture());
791 (*dst)[n] = { depthTexD->globalResourceId(), depthTexD->generation };
792 } else if (desc.depthStencilBuffer()) {
793 RenderBufferType *depthRbD = QRHI_RES(RenderBufferType, desc.depthStencilBuffer());
794 (*dst)[n] = { depthRbD->globalResourceId(), depthRbD->generation };
795 } else {
796 (*dst)[n] = { .id: 0, .generation: 0 };
797 }
798 }
799}
800
801template<typename TexType, typename RenderBufferType>
802bool QRhiRenderTargetAttachmentTracker::isUpToDate(const QRhiTextureRenderTargetDescription &desc, const ResIdList &currentResIdList)
803{
804 // Just as setShaderResources() recognizes if an srb's referenced
805 // resources have been rebuilt (got a create() since the srb's
806 // create()), we should do the same for the textures and renderbuffers
807 // referenced from the rendertarget. It is not uncommon that a texture
808 // or ds buffer gets resized due to following a window size in some
809 // form, which involves a create() on them. It is then nice if the
810 // render target auto-rebuilds in beginPass().
811
812 ResIdList resIdList;
813 updateResIdList<TexType, RenderBufferType>(desc, &resIdList);
814 return resIdList == currentResIdList;
815}
816
817template<typename T>
818inline T *qrhi_objectFromProxyData(QRhiSwapChainProxyData *pd, QWindow *window, QRhi::Implementation impl, uint objectIndex)
819{
820 Q_ASSERT(objectIndex < std::size(pd->reserved));
821 if (!pd->reserved[objectIndex]) // // was not set, no other choice, do it here, whatever thread this is
822 *pd = QRhi::updateSwapChainProxyData(impl, window);
823 return static_cast<T *>(pd->reserved[objectIndex]);
824}
825
826QT_END_NAMESPACE
827
828#endif
829

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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