1/****************************************************************************
2**
3** Copyright (C) 2019 The Qt Company Ltd.
4** Contact: http://www.qt.io/licensing/
5**
6** This file is part of the Qt Gui module
7**
8** $QT_BEGIN_LICENSE:LGPL3$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or later as published by the Free
28** Software Foundation and appearing in the file LICENSE.GPL included in
29** the packaging of this file. Please review the following information to
30** ensure the GNU General Public License version 2.0 requirements will be
31** met: http://www.gnu.org/licenses/gpl-2.0.html.
32**
33** $QT_END_LICENSE$
34**
35****************************************************************************/
36
37#ifndef QRHI_P_H
38#define QRHI_P_H
39
40//
41// W A R N I N G
42// -------------
43//
44// This file is not part of the Qt API. It exists purely as an
45// implementation detail. This header file may change from version to
46// version without notice, or even be removed.
47//
48// We mean it.
49//
50
51#include "qrhi_p.h"
52#include "qrhiprofiler_p_p.h"
53#include <QBitArray>
54#include <QAtomicInt>
55#include <QLoggingCategory>
56
57QT_BEGIN_NAMESPACE
58
59#define QRHI_RES(t, x) static_cast<t *>(x)
60#define QRHI_RES_RHI(t) t *rhiD = static_cast<t *>(m_rhi)
61#define QRHI_PROF QRhiProfilerPrivate *rhiP = m_rhi->profilerPrivateOrNull()
62#define QRHI_PROF_F(f) for (bool qrhip_enabled = rhiP != nullptr; qrhip_enabled; qrhip_enabled = false) rhiP->f
63
64Q_DECLARE_LOGGING_CATEGORY(QRHI_LOG_INFO)
65
66class QRhiImplementation
67{
68public:
69 virtual ~QRhiImplementation();
70
71 virtual bool create(QRhi::Flags flags) = 0;
72 virtual void destroy() = 0;
73
74 virtual QRhiGraphicsPipeline *createGraphicsPipeline() = 0;
75 virtual QRhiComputePipeline *createComputePipeline() = 0;
76 virtual QRhiShaderResourceBindings *createShaderResourceBindings() = 0;
77 virtual QRhiBuffer *createBuffer(QRhiBuffer::Type type,
78 QRhiBuffer::UsageFlags usage,
79 int size) = 0;
80 virtual QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type,
81 const QSize &pixelSize,
82 int sampleCount,
83 QRhiRenderBuffer::Flags flags,
84 QRhiTexture::Format backingFormatHint) = 0;
85 virtual QRhiTexture *createTexture(QRhiTexture::Format format,
86 const QSize &pixelSize,
87 int sampleCount,
88 QRhiTexture::Flags flags) = 0;
89 virtual QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
90 QRhiSampler::Filter minFilter,
91 QRhiSampler::Filter mipmapMode,
92 QRhiSampler:: AddressMode u,
93 QRhiSampler::AddressMode v,
94 QRhiSampler::AddressMode w) = 0;
95
96 virtual QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
97 QRhiTextureRenderTarget::Flags flags) = 0;
98
99 virtual QRhiSwapChain *createSwapChain() = 0;
100 virtual QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) = 0;
101 virtual QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) = 0;
102 virtual QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags) = 0;
103 virtual QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) = 0;
104 virtual QRhi::FrameOpResult finish() = 0;
105
106 virtual void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) = 0;
107
108 virtual void beginPass(QRhiCommandBuffer *cb,
109 QRhiRenderTarget *rt,
110 const QColor &colorClearValue,
111 const QRhiDepthStencilClearValue &depthStencilClearValue,
112 QRhiResourceUpdateBatch *resourceUpdates,
113 QRhiCommandBuffer::BeginPassFlags flags) = 0;
114 virtual void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) = 0;
115
116 virtual void setGraphicsPipeline(QRhiCommandBuffer *cb,
117 QRhiGraphicsPipeline *ps) = 0;
118
119 virtual void setShaderResources(QRhiCommandBuffer *cb,
120 QRhiShaderResourceBindings *srb,
121 int dynamicOffsetCount,
122 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) = 0;
123
124 virtual void setVertexInput(QRhiCommandBuffer *cb,
125 int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
126 QRhiBuffer *indexBuf, quint32 indexOffset,
127 QRhiCommandBuffer::IndexFormat indexFormat) = 0;
128
129 virtual void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) = 0;
130 virtual void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) = 0;
131 virtual void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) = 0;
132 virtual void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) = 0;
133
134 virtual void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
135 quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) = 0;
136 virtual void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
137 quint32 instanceCount, quint32 firstIndex,
138 qint32 vertexOffset, quint32 firstInstance) = 0;
139
140 virtual void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) = 0;
141 virtual void debugMarkEnd(QRhiCommandBuffer *cb) = 0;
142 virtual void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) = 0;
143
144 virtual void beginComputePass(QRhiCommandBuffer *cb,
145 QRhiResourceUpdateBatch *resourceUpdates,
146 QRhiCommandBuffer::BeginPassFlags flags) = 0;
147 virtual void endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) = 0;
148 virtual void setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps) = 0;
149 virtual void dispatch(QRhiCommandBuffer *cb, int x, int y, int z) = 0;
150
151 virtual const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) = 0;
152 virtual void beginExternal(QRhiCommandBuffer *cb) = 0;
153 virtual void endExternal(QRhiCommandBuffer *cb) = 0;
154
155 virtual QList<int> supportedSampleCounts() const = 0;
156 virtual int ubufAlignment() const = 0;
157 virtual bool isYUpInFramebuffer() const = 0;
158 virtual bool isYUpInNDC() const = 0;
159 virtual bool isClipDepthZeroToOne() const = 0;
160 virtual QMatrix4x4 clipSpaceCorrMatrix() const = 0;
161 virtual bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const = 0;
162 virtual bool isFeatureSupported(QRhi::Feature feature) const = 0;
163 virtual int resourceLimit(QRhi::ResourceLimit limit) const = 0;
164 virtual const QRhiNativeHandles *nativeHandles() = 0;
165 virtual void sendVMemStatsToProfiler() = 0;
166 virtual bool makeThreadLocalNativeContextCurrent() = 0;
167 virtual void releaseCachedResources() = 0;
168 virtual bool isDeviceLost() const = 0;
169
170 bool isCompressedFormat(QRhiTexture::Format format) const;
171 void compressedFormatInfo(QRhiTexture::Format format, const QSize &size,
172 quint32 *bpl, quint32 *byteSize,
173 QSize *blockDim) const;
174 void textureFormatInfo(QRhiTexture::Format format, const QSize &size,
175 quint32 *bpl, quint32 *byteSize) const;
176 quint32 approxByteSizeForTexture(QRhiTexture::Format format, const QSize &baseSize,
177 int mipCount, int layerCount);
178
179 QRhiProfilerPrivate *profilerPrivateOrNull()
180 {
181 // return null when QRhi::EnableProfiling was not set
182 QRhiProfilerPrivate *p = QRhiProfilerPrivate::get(&profiler);
183 return p->rhiDWhenEnabled ? p : nullptr;
184 }
185
186 // only really care about resources that own native graphics resources underneath
187 void registerResource(QRhiResource *res)
188 {
189 resources.insert(res);
190 }
191
192 void unregisterResource(QRhiResource *res)
193 {
194 resources.remove(res);
195 }
196
197 QSet<QRhiResource *> activeResources() const
198 {
199 return resources;
200 }
201
202 void addDeleteLater(QRhiResource *res)
203 {
204 if (inFrame)
205 pendingDeleteResources.insert(res);
206 else
207 delete res;
208 }
209
210 void addCleanupCallback(const QRhi::CleanupCallback &callback)
211 {
212 cleanupCallbacks.append(callback);
213 }
214
215 bool sanityCheckGraphicsPipeline(QRhiGraphicsPipeline *ps);
216 bool sanityCheckShaderResourceBindings(QRhiShaderResourceBindings *srb);
217 void updateLayoutDesc(QRhiShaderResourceBindings *srb);
218
219 QRhi *q;
220
221 static const int MAX_SHADER_CACHE_ENTRIES = 128;
222
223 bool debugMarkers = false;
224 int currentFrameSlot = 0; // for vk, mtl, and similar. unused by gl and d3d11.
225 bool inFrame = false;
226
227private:
228 QRhi::Implementation implType;
229 QThread *implThread;
230 QRhiProfiler profiler;
231 QVarLengthArray<QRhiResourceUpdateBatch *, 4> resUpdPool;
232 quint64 resUpdPoolMap = 0;
233 int lastResUpdIdx = -1;
234 QSet<QRhiResource *> resources;
235 QSet<QRhiResource *> pendingDeleteResources;
236 QVarLengthArray<QRhi::CleanupCallback, 4> cleanupCallbacks;
237
238 friend class QRhi;
239 friend class QRhiResourceUpdateBatchPrivate;
240};
241
242template<typename T, size_t N>
243bool qrhi_toTopLeftRenderTargetRect(const QSize &outputSize, const std::array<T, N> &r,
244 T *x, T *y, T *w, T *h)
245{
246 // x,y are bottom-left in QRhiScissor and QRhiViewport but top-left in
247 // Vulkan/Metal/D3D. Our input is an OpenGL-style scissor rect where both
248 // negative x or y, and partly or completely out of bounds rects are
249 // allowed. The only thing the input here cannot have is a negative width
250 // or height. We must handle all other input gracefully, clamping to a zero
251 // width or height rect in the worst case, and ensuring the resulting rect
252 // is inside the rendertarget's bounds because some APIs' validation/debug
253 // layers are allergic to out of bounds scissor or viewport rects.
254
255 const T outputWidth = outputSize.width();
256 const T outputHeight = outputSize.height();
257 const T inputWidth = r[2];
258 const T inputHeight = r[3];
259
260 if (inputWidth < 0 || inputHeight < 0)
261 return false;
262
263 *x = r[0];
264 *y = outputHeight - (r[1] + inputHeight);
265
266 const T widthOffset = *x < 0 ? -*x : 0;
267 const T heightOffset = *y < 0 ? -*y : 0;
268 *w = *x < outputWidth ? qMax<T>(0, inputWidth - widthOffset) : 0;
269 *h = *y < outputHeight ? qMax<T>(0, inputHeight - heightOffset) : 0;
270
271 *x = qBound<T>(0, *x, outputWidth - 1);
272 *y = qBound<T>(0, *y, outputHeight - 1);
273
274 if (*x + *w > outputWidth)
275 *w = qMax<T>(0, outputWidth - *x);
276 if (*y + *h > outputHeight)
277 *h = qMax<T>(0, outputHeight - *y);
278
279 return true;
280}
281
282struct QRhiBufferDataPrivate
283{
284 Q_DISABLE_COPY_MOVE(QRhiBufferDataPrivate)
285 QRhiBufferDataPrivate() { }
286 ~QRhiBufferDataPrivate() { delete[] largeData; }
287 int ref = 1;
288 int size = 0;
289 int largeAlloc = 0;
290 char *largeData = nullptr;
291 static constexpr int SMALL_DATA_SIZE = 1024;
292 char data[SMALL_DATA_SIZE];
293};
294
295// no detach-with-contents, no atomic refcount, no shrink
296class QRhiBufferData
297{
298public:
299 QRhiBufferData() = default;
300 ~QRhiBufferData()
301 {
302 if (d && !--d->ref)
303 delete d;
304 }
305 QRhiBufferData(const QRhiBufferData &other)
306 : d(other.d)
307 {
308 if (d)
309 d->ref += 1;
310 }
311 QRhiBufferData &operator=(const QRhiBufferData &other)
312 {
313 if (d == other.d)
314 return *this;
315 if (other.d)
316 other.d->ref += 1;
317 if (d && !--d->ref)
318 delete d;
319 d = other.d;
320 return *this;
321 }
322 const char *constData() const
323 {
324 return d->size <= QRhiBufferDataPrivate::SMALL_DATA_SIZE ? d->data : d->largeData;
325 }
326 int size() const
327 {
328 return d->size;
329 }
330 void assign(const char *s, int size)
331 {
332 if (!d) {
333 d = new QRhiBufferDataPrivate;
334 } else if (d->ref != 1) {
335 d->ref -= 1;
336 d = new QRhiBufferDataPrivate;
337 }
338 d->size = size;
339 if (size <= QRhiBufferDataPrivate::SMALL_DATA_SIZE) {
340 memcpy(d->data, s, size);
341 } else {
342 if (d->largeAlloc < size) {
343 delete[] d->largeData;
344 d->largeAlloc = size;
345 d->largeData = new char[size];
346 }
347 memcpy(d->largeData, s, size);
348 }
349 }
350private:
351 QRhiBufferDataPrivate *d = nullptr;
352};
353
354Q_DECLARE_TYPEINFO(QRhiBufferData, Q_MOVABLE_TYPE);
355
356class QRhiResourceUpdateBatchPrivate
357{
358public:
359 struct BufferOp {
360 enum Type {
361 DynamicUpdate,
362 StaticUpload,
363 Read
364 };
365 Type type;
366 QRhiBuffer *buf;
367 int offset;
368 QRhiBufferData data;
369 int readSize;
370 QRhiBufferReadbackResult *result;
371
372 static BufferOp dynamicUpdate(QRhiBuffer *buf, int offset, int size, const void *data)
373 {
374 BufferOp op = {};
375 op.type = DynamicUpdate;
376 op.buf = buf;
377 op.offset = offset;
378 const int effectiveSize = size ? size : buf->size();
379 op.data.assign(reinterpret_cast<const char *>(data), effectiveSize);
380 return op;
381 }
382
383 static void changeToDynamicUpdate(BufferOp *op, QRhiBuffer *buf, int offset, int size, const void *data)
384 {
385 op->type = DynamicUpdate;
386 op->buf = buf;
387 op->offset = offset;
388 const int effectiveSize = size ? size : buf->size();
389 op->data.assign(reinterpret_cast<const char *>(data), effectiveSize);
390 }
391
392 static BufferOp staticUpload(QRhiBuffer *buf, int offset, int size, const void *data)
393 {
394 BufferOp op = {};
395 op.type = StaticUpload;
396 op.buf = buf;
397 op.offset = offset;
398 const int effectiveSize = size ? size : buf->size();
399 op.data.assign(reinterpret_cast<const char *>(data), effectiveSize);
400 return op;
401 }
402
403 static void changeToStaticUpload(BufferOp *op, QRhiBuffer *buf, int offset, int size, const void *data)
404 {
405 op->type = StaticUpload;
406 op->buf = buf;
407 op->offset = offset;
408 const int effectiveSize = size ? size : buf->size();
409 op->data.assign(reinterpret_cast<const char *>(data), effectiveSize);
410 }
411
412 static BufferOp read(QRhiBuffer *buf, int offset, int size, QRhiBufferReadbackResult *result)
413 {
414 BufferOp op = {};
415 op.type = Read;
416 op.buf = buf;
417 op.offset = offset;
418 op.readSize = size;
419 op.result = result;
420 return op;
421 }
422 };
423
424 struct TextureOp {
425 enum Type {
426 Upload,
427 Copy,
428 Read,
429 GenMips
430 };
431 Type type;
432 QRhiTexture *dst;
433 // Specifying multiple uploads for a subresource must be supported.
434 // In the backend this can then end up, where applicable, as a
435 // single, batched copy operation with only one set of barriers.
436 // This helps when doing for example glyph cache fills.
437 QList<QRhiTextureSubresourceUploadDescription> subresDesc[QRhi::MAX_LAYERS][QRhi::MAX_LEVELS];
438 QRhiTexture *src;
439 QRhiTextureCopyDescription desc;
440 QRhiReadbackDescription rb;
441 QRhiReadbackResult *result;
442
443 static TextureOp upload(QRhiTexture *tex, const QRhiTextureUploadDescription &desc)
444 {
445 TextureOp op = {};
446 op.type = Upload;
447 op.dst = tex;
448 for (auto it = desc.cbeginEntries(), itEnd = desc.cendEntries(); it != itEnd; ++it)
449 op.subresDesc[it->layer()][it->level()].append(it->description());
450 return op;
451 }
452
453 static TextureOp copy(QRhiTexture *dst, QRhiTexture *src, const QRhiTextureCopyDescription &desc)
454 {
455 TextureOp op = {};
456 op.type = Copy;
457 op.dst = dst;
458 op.src = src;
459 op.desc = desc;
460 return op;
461 }
462
463 static TextureOp read(const QRhiReadbackDescription &rb, QRhiReadbackResult *result)
464 {
465 TextureOp op = {};
466 op.type = Read;
467 op.rb = rb;
468 op.result = result;
469 return op;
470 }
471
472 static TextureOp genMips(QRhiTexture *tex)
473 {
474 TextureOp op = {};
475 op.type = GenMips;
476 op.dst = tex;
477 return op;
478 }
479 };
480
481 int activeBufferOpCount = 0; // this is the real number of used elements in bufferOps, not bufferOps.count()
482 static const int BUFFER_OPS_STATIC_ALLOC = 1024;
483 QVarLengthArray<BufferOp, BUFFER_OPS_STATIC_ALLOC> bufferOps;
484
485 int activeTextureOpCount = 0; // this is the real number of used elements in textureOps, not textureOps.count()
486 static const int TEXTURE_OPS_STATIC_ALLOC = 256;
487 QVarLengthArray<TextureOp, TEXTURE_OPS_STATIC_ALLOC> textureOps;
488
489 QRhiResourceUpdateBatch *q = nullptr;
490 QRhiImplementation *rhi = nullptr;
491 int poolIndex = -1;
492
493 void free();
494 void merge(QRhiResourceUpdateBatchPrivate *other);
495 bool hasOptimalCapacity() const;
496 void trimOpLists();
497
498 static QRhiResourceUpdateBatchPrivate *get(QRhiResourceUpdateBatch *b) { return b->d; }
499};
500
501Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::BufferOp, Q_MOVABLE_TYPE);
502Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::TextureOp, Q_MOVABLE_TYPE);
503
504template<typename T>
505struct QRhiBatchedBindings
506{
507 void feed(int binding, T resource) { // binding must be strictly increasing
508 if (curBinding == -1 || binding > curBinding + 1) {
509 finish();
510 curBatch.startBinding = binding;
511 curBatch.resources.clear();
512 curBatch.resources.append(resource);
513 } else {
514 Q_ASSERT(binding == curBinding + 1);
515 curBatch.resources.append(resource);
516 }
517 curBinding = binding;
518 }
519
520 bool finish() {
521 if (!curBatch.resources.isEmpty())
522 batches.append(curBatch);
523 return !batches.isEmpty();
524 }
525
526 void clear() {
527 batches.clear();
528 curBatch.resources.clear();
529 curBinding = -1;
530 }
531
532 struct Batch {
533 uint startBinding;
534 QVarLengthArray<T, 4> resources;
535
536 bool operator==(const Batch &other) const
537 {
538 return startBinding == other.startBinding && resources == other.resources;
539 }
540
541 bool operator!=(const Batch &other) const
542 {
543 return !operator==(other);
544 }
545 };
546
547 QVarLengthArray<Batch, 4> batches; // sorted by startBinding
548
549 bool operator==(const QRhiBatchedBindings<T> &other) const
550 {
551 return batches == other.batches;
552 }
553
554 bool operator!=(const QRhiBatchedBindings<T> &other) const
555 {
556 return !operator==(other);
557 }
558
559private:
560 Batch curBatch;
561 int curBinding = -1;
562};
563
564class QRhiGlobalObjectIdGenerator
565{
566public:
567#ifdef Q_ATOMIC_INT64_IS_SUPPORTED
568 using Type = quint64;
569#else
570 using Type = quint32;
571#endif
572 static Type newId();
573};
574
575class QRhiPassResourceTracker
576{
577public:
578 bool isEmpty() const;
579 void reset();
580
581 struct UsageState {
582 int layout;
583 int access;
584 int stage;
585 };
586
587 enum BufferStage {
588 BufVertexInputStage,
589 BufVertexStage,
590 BufFragmentStage,
591 BufComputeStage
592 };
593
594 enum BufferAccess {
595 BufVertexInput,
596 BufIndexRead,
597 BufUniformRead,
598 BufStorageLoad,
599 BufStorageStore,
600 BufStorageLoadStore
601 };
602
603 void registerBuffer(QRhiBuffer *buf, int slot, BufferAccess *access, BufferStage *stage,
604 const UsageState &state);
605
606 enum TextureStage {
607 TexVertexStage,
608 TexFragmentStage,
609 TexColorOutputStage,
610 TexDepthOutputStage,
611 TexComputeStage
612 };
613
614 enum TextureAccess {
615 TexSample,
616 TexColorOutput,
617 TexDepthOutput,
618 TexStorageLoad,
619 TexStorageStore,
620 TexStorageLoadStore
621 };
622
623 void registerTexture(QRhiTexture *tex, TextureAccess *access, TextureStage *stage,
624 const UsageState &state);
625
626 struct Buffer {
627 int slot;
628 BufferAccess access;
629 BufferStage stage;
630 UsageState stateAtPassBegin;
631 };
632
633 using BufferIterator = QHash<QRhiBuffer *, Buffer>::const_iterator;
634 BufferIterator cbeginBuffers() const { return m_buffers.cbegin(); }
635 BufferIterator cendBuffers() const { return m_buffers.cend(); }
636
637 struct Texture {
638 TextureAccess access;
639 TextureStage stage;
640 UsageState stateAtPassBegin;
641 };
642
643 using TextureIterator = QHash<QRhiTexture *, Texture>::const_iterator;
644 TextureIterator cbeginTextures() const { return m_textures.cbegin(); }
645 TextureIterator cendTextures() const { return m_textures.cend(); }
646
647 static BufferStage toPassTrackerBufferStage(QRhiShaderResourceBinding::StageFlags stages);
648 static TextureStage toPassTrackerTextureStage(QRhiShaderResourceBinding::StageFlags stages);
649
650private:
651 QHash<QRhiBuffer *, Buffer> m_buffers;
652 QHash<QRhiTexture *, Texture> m_textures;
653};
654
655Q_DECLARE_TYPEINFO(QRhiPassResourceTracker::Buffer, Q_MOVABLE_TYPE);
656Q_DECLARE_TYPEINFO(QRhiPassResourceTracker::Texture, Q_MOVABLE_TYPE);
657
658template<typename T, int GROW = 1024>
659class QRhiBackendCommandList
660{
661public:
662 QRhiBackendCommandList() = default;
663 ~QRhiBackendCommandList() { delete[] v; }
664 inline void reset() { p = 0; }
665 inline bool isEmpty() const { return p == 0; }
666 inline T &get() {
667 if (p == a) {
668 a += GROW;
669 T *nv = new T[a];
670 if (v) {
671 memcpy(nv, v, p * sizeof(T));
672 delete[] v;
673 }
674 v = nv;
675 }
676 return v[p++];
677 }
678 inline void unget() { --p; }
679 inline T *cbegin() const { return v; }
680 inline T *cend() const { return v + p; }
681 inline T *begin() { return v; }
682 inline T *end() { return v + p; }
683private:
684 Q_DISABLE_COPY(QRhiBackendCommandList)
685 T *v = nullptr;
686 int a = 0;
687 int p = 0;
688};
689
690QT_END_NAMESPACE
691
692#endif
693