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

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