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
57 QT_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
Q_DECLARE_LOGGING_CATEGORY(QRHI_LOG_INFO)64 Q_DECLARE_LOGGING_CATEGORY(QRHI_LOG_INFO)
65
66 class QRhiImplementation
67 {
68 public:
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) = 0;
84 virtual QRhiTexture *createTexture(QRhiTexture::Format format,
85 const QSize &pixelSize,
86 int sampleCount,
87 QRhiTexture::Flags flags) = 0;
88 virtual QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
89 QRhiSampler::Filter minFilter,
90 QRhiSampler::Filter mipmapMode,
91 QRhiSampler:: AddressMode u,
92 QRhiSampler::AddressMode v,
93 QRhiSampler::AddressMode w) = 0;
94
95 virtual QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
96 QRhiTextureRenderTarget::Flags flags) = 0;
97
98 virtual QRhiSwapChain *createSwapChain() = 0;
99 virtual QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) = 0;
100 virtual QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) = 0;
101 virtual QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags) = 0;
102 virtual QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) = 0;
103 virtual QRhi::FrameOpResult finish() = 0;
104
105 virtual void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) = 0;
106
107 virtual void beginPass(QRhiCommandBuffer *cb,
108 QRhiRenderTarget *rt,
109 const QColor &colorClearValue,
110 const QRhiDepthStencilClearValue &depthStencilClearValue,
111 QRhiResourceUpdateBatch *resourceUpdates) = 0;
112 virtual void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) = 0;
113
114 virtual void setGraphicsPipeline(QRhiCommandBuffer *cb,
115 QRhiGraphicsPipeline *ps) = 0;
116
117 virtual void setShaderResources(QRhiCommandBuffer *cb,
118 QRhiShaderResourceBindings *srb,
119 int dynamicOffsetCount,
120 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) = 0;
121
122 virtual void setVertexInput(QRhiCommandBuffer *cb,
123 int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
124 QRhiBuffer *indexBuf, quint32 indexOffset,
125 QRhiCommandBuffer::IndexFormat indexFormat) = 0;
126
127 virtual void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) = 0;
128 virtual void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) = 0;
129 virtual void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) = 0;
130 virtual void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) = 0;
131
132 virtual void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
133 quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) = 0;
134 virtual void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
135 quint32 instanceCount, quint32 firstIndex,
136 qint32 vertexOffset, quint32 firstInstance) = 0;
137
138 virtual void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) = 0;
139 virtual void debugMarkEnd(QRhiCommandBuffer *cb) = 0;
140 virtual void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) = 0;
141
142 virtual void beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) = 0;
143 virtual void endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) = 0;
144 virtual void setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps) = 0;
145 virtual void dispatch(QRhiCommandBuffer *cb, int x, int y, int z) = 0;
146
147 virtual const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) = 0;
148 virtual void beginExternal(QRhiCommandBuffer *cb) = 0;
149 virtual void endExternal(QRhiCommandBuffer *cb) = 0;
150
151 virtual QVector<int> supportedSampleCounts() const = 0;
152 virtual int ubufAlignment() const = 0;
153 virtual bool isYUpInFramebuffer() const = 0;
154 virtual bool isYUpInNDC() const = 0;
155 virtual bool isClipDepthZeroToOne() const = 0;
156 virtual QMatrix4x4 clipSpaceCorrMatrix() const = 0;
157 virtual bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const = 0;
158 virtual bool isFeatureSupported(QRhi::Feature feature) const = 0;
159 virtual int resourceLimit(QRhi::ResourceLimit limit) const = 0;
160 virtual const QRhiNativeHandles *nativeHandles() = 0;
161 virtual void sendVMemStatsToProfiler() = 0;
162 virtual bool makeThreadLocalNativeContextCurrent() = 0;
163 virtual void releaseCachedResources() = 0;
164 virtual bool isDeviceLost() const = 0;
165
166 bool isCompressedFormat(QRhiTexture::Format format) const;
167 void compressedFormatInfo(QRhiTexture::Format format, const QSize &size,
168 quint32 *bpl, quint32 *byteSize,
169 QSize *blockDim) const;
170 void textureFormatInfo(QRhiTexture::Format format, const QSize &size,
171 quint32 *bpl, quint32 *byteSize) const;
172 quint32 approxByteSizeForTexture(QRhiTexture::Format format, const QSize &baseSize,
173 int mipCount, int layerCount);
174
175 QRhiProfilerPrivate *profilerPrivateOrNull()
176 {
177 // return null when QRhi::EnableProfiling was not set
178 QRhiProfilerPrivate *p = QRhiProfilerPrivate::get(&profiler);
179 return p->rhiDWhenEnabled ? p : nullptr;
180 }
181
182 // only really care about resources that own native graphics resources underneath
183 void registerResource(QRhiResource *res)
184 {
185 resources.insert(res);
186 }
187
188 void unregisterResource(QRhiResource *res)
189 {
190 resources.remove(res);
191 }
192
193 QSet<QRhiResource *> activeResources() const
194 {
195 return resources;
196 }
197
198 void addReleaseAndDestroyLater(QRhiResource *res)
199 {
200 if (inFrame)
201 pendingReleaseAndDestroyResources.insert(res);
202 else
203 delete res;
204 }
205
206 void addCleanupCallback(const QRhi::CleanupCallback &callback)
207 {
208 cleanupCallbacks.append(callback);
209 }
210
211 bool sanityCheckGraphicsPipeline(QRhiGraphicsPipeline *ps);
212
213 QRhi *q;
214
215 static const int MAX_SHADER_CACHE_ENTRIES = 128;
216
217 protected:
218 bool debugMarkers = false;
219 int currentFrameSlot = 0; // for vk, mtl, and similar. unused by gl and d3d11.
220 bool inFrame = false;
221
222 private:
223 QRhi::Implementation implType;
224 QThread *implThread;
225 QRhiProfiler profiler;
226 QVarLengthArray<QRhiResourceUpdateBatch *, 4> resUpdPool;
227 QBitArray resUpdPoolMap;
228 QSet<QRhiResource *> resources;
229 QSet<QRhiResource *> pendingReleaseAndDestroyResources;
230 QVector<QRhi::CleanupCallback> cleanupCallbacks;
231
232 friend class QRhi;
233 friend class QRhiResourceUpdateBatchPrivate;
234 };
235
236 template<typename T, size_t N>
qrhi_toTopLeftRenderTargetRect(const QSize & outputSize,const std::array<T,N> & r,T * x,T * y,T * w,T * h)237 bool qrhi_toTopLeftRenderTargetRect(const QSize &outputSize, const std::array<T, N> &r,
238 T *x, T *y, T *w, T *h)
239 {
240 // x,y are bottom-left in QRhiScissor and QRhiViewport but top-left in
241 // Vulkan/Metal/D3D. Our input is an OpenGL-style scissor rect where both
242 // negative x or y, and partly or completely out of bounds rects are
243 // allowed. The only thing the input here cannot have is a negative width
244 // or height. We must handle all other input gracefully, clamping to a zero
245 // width or height rect in the worst case, and ensuring the resulting rect
246 // is inside the rendertarget's bounds because some APIs' validation/debug
247 // layers are allergic to out of bounds scissor or viewport rects.
248
249 const T outputWidth = outputSize.width();
250 const T outputHeight = outputSize.height();
251 const T inputWidth = r[2];
252 const T inputHeight = r[3];
253
254 if (inputWidth < 0 || inputHeight < 0)
255 return false;
256
257 *x = r[0];
258 *y = outputHeight - (r[1] + inputHeight);
259
260 const T widthOffset = *x < 0 ? -*x : 0;
261 const T heightOffset = *y < 0 ? -*y : 0;
262
263 *x = qBound<T>(0, *x, outputWidth - 1);
264 *y = qBound<T>(0, *y, outputHeight - 1);
265 *w = qMax<T>(0, inputWidth - widthOffset);
266 *h = qMax<T>(0, inputHeight - heightOffset);
267
268 if (*x + *w > outputWidth)
269 *w = qMax<T>(0, outputWidth - *x - 1);
270 if (*y + *h > outputHeight)
271 *h = qMax<T>(0, outputHeight - *y - 1);
272
273 return true;
274 }
275
276 class QRhiResourceUpdateBatchPrivate
277 {
278 public:
279 struct BufferOp {
280 enum Type {
281 DynamicUpdate,
282 StaticUpload,
283 Read
284 };
285 Type type;
286 QRhiBuffer *buf;
287 int offset;
288 QByteArray data;
289 int readSize;
290 QRhiBufferReadbackResult *result;
291
dynamicUpdateBufferOp292 static BufferOp dynamicUpdate(QRhiBuffer *buf, int offset, int size, const void *data)
293 {
294 BufferOp op;
295 op.type = DynamicUpdate;
296 op.buf = buf;
297 op.offset = offset;
298 op.data = QByteArray(reinterpret_cast<const char *>(data), size ? size : buf->size());
299 op.readSize = 0;
300 op.result = nullptr;
301 return op;
302 }
303
staticUploadBufferOp304 static BufferOp staticUpload(QRhiBuffer *buf, int offset, int size, const void *data)
305 {
306 BufferOp op;
307 op.type = StaticUpload;
308 op.buf = buf;
309 op.offset = offset;
310 op.data = QByteArray(reinterpret_cast<const char *>(data), size ? size : buf->size());
311 op.readSize = 0;
312 op.result = nullptr;
313 return op;
314 }
315
readBufferOp316 static BufferOp read(QRhiBuffer *buf, int offset, int size, QRhiBufferReadbackResult *result)
317 {
318 BufferOp op;
319 op.type = Read;
320 op.buf = buf;
321 op.offset = offset;
322 op.readSize = size;
323 op.result = result;
324 return op;
325 }
326 };
327
328 struct TextureOp {
329 enum Type {
330 Upload,
331 Copy,
332 Read,
333 GenMips
334 };
335 Type type;
336 QRhiTexture *dst;
337 // Specifying multiple uploads for a subresource must be supported.
338 // In the backend this can then end up, where applicable, as a
339 // single, batched copy operation with only one set of barriers.
340 // This helps when doing for example glyph cache fills.
341 QVector<QRhiTextureSubresourceUploadDescription> subresDesc[QRhi::MAX_LAYERS][QRhi::MAX_LEVELS];
342 QRhiTexture *src;
343 QRhiTextureCopyDescription desc;
344 QRhiReadbackDescription rb;
345 QRhiReadbackResult *result;
346 int layer;
347
uploadTextureOp348 static TextureOp upload(QRhiTexture *tex, const QRhiTextureUploadDescription &desc)
349 {
350 TextureOp op;
351 op.type = Upload;
352 op.dst = tex;
353 for (auto it = desc.cbeginEntries(), itEnd = desc.cendEntries(); it != itEnd; ++it)
354 op.subresDesc[it->layer()][it->level()].append(it->description());
355 op.src = nullptr;
356 op.result = nullptr;
357 op.layer = 0;
358 return op;
359 }
360
copyTextureOp361 static TextureOp copy(QRhiTexture *dst, QRhiTexture *src, const QRhiTextureCopyDescription &desc)
362 {
363 TextureOp op;
364 op.type = Copy;
365 op.dst = dst;
366 op.src = src;
367 op.desc = desc;
368 op.result = nullptr;
369 op.layer = 0;
370 return op;
371 }
372
readTextureOp373 static TextureOp read(const QRhiReadbackDescription &rb, QRhiReadbackResult *result)
374 {
375 TextureOp op;
376 op.type = Read;
377 op.dst = nullptr;
378 op.src = nullptr;
379 op.rb = rb;
380 op.result = result;
381 op.layer = 0;
382 return op;
383 }
384
genMipsTextureOp385 static TextureOp genMips(QRhiTexture *tex, int layer)
386 {
387 TextureOp op;
388 op.type = GenMips;
389 op.dst = tex;
390 op.src = nullptr;
391 op.result = nullptr;
392 op.layer = layer;
393 return op;
394 }
395 };
396
397 QVarLengthArray<BufferOp, 1024> bufferOps;
398 QVarLengthArray<TextureOp, 256> textureOps;
399
400 QRhiResourceUpdateBatch *q = nullptr;
401 QRhiImplementation *rhi = nullptr;
402 int poolIndex = -1;
403
404 void free();
405 void merge(QRhiResourceUpdateBatchPrivate *other);
406
get(QRhiResourceUpdateBatch * b)407 static QRhiResourceUpdateBatchPrivate *get(QRhiResourceUpdateBatch *b) { return b->d; }
408 };
409
410 Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::BufferOp, Q_MOVABLE_TYPE);
411 Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::TextureOp, Q_MOVABLE_TYPE);
412
413 template<typename T>
414 struct QRhiBatchedBindings
415 {
feedQRhiBatchedBindings416 void feed(int binding, T resource) { // binding must be strictly increasing
417 if (curBinding == -1 || binding > curBinding + 1) {
418 finish();
419 curBatch.startBinding = binding;
420 curBatch.resources.clear();
421 curBatch.resources.append(resource);
422 } else {
423 Q_ASSERT(binding == curBinding + 1);
424 curBatch.resources.append(resource);
425 }
426 curBinding = binding;
427 }
428
finishQRhiBatchedBindings429 void finish() {
430 if (!curBatch.resources.isEmpty())
431 batches.append(curBatch);
432 }
433
clearQRhiBatchedBindings434 void clear() {
435 batches.clear();
436 curBatch.resources.clear();
437 curBinding = -1;
438 }
439
440 struct Batch {
441 uint startBinding;
442 QVarLengthArray<T, 4> resources;
443
444 bool operator==(const Batch &other) const
445 {
446 return startBinding == other.startBinding && resources == other.resources;
447 }
448
449 bool operator!=(const Batch &other) const
450 {
451 return !operator==(other);
452 }
453 };
454
455 QVarLengthArray<Batch, 4> batches; // sorted by startBinding
456
457 bool operator==(const QRhiBatchedBindings<T> &other) const
458 {
459 return batches == other.batches;
460 }
461
462 bool operator!=(const QRhiBatchedBindings<T> &other) const
463 {
464 return !operator==(other);
465 }
466
467 private:
468 Batch curBatch;
469 int curBinding = -1;
470 };
471
472 class QRhiGlobalObjectIdGenerator
473 {
474 public:
475 #ifdef Q_ATOMIC_INT64_IS_SUPPORTED
476 using Type = quint64;
477 #else
478 using Type = quint32;
479 #endif
480 static Type newId();
481 };
482
483 class QRhiPassResourceTracker
484 {
485 public:
486 bool isEmpty() const;
487 void reset();
488
489 struct UsageState {
490 int layout;
491 int access;
492 int stage;
493 };
494
495 enum BufferStage {
496 BufVertexInputStage,
497 BufVertexStage,
498 BufFragmentStage,
499 BufComputeStage
500 };
501
502 enum BufferAccess {
503 BufVertexInput,
504 BufIndexRead,
505 BufUniformRead,
506 BufStorageLoad,
507 BufStorageStore,
508 BufStorageLoadStore
509 };
510
511 void registerBuffer(QRhiBuffer *buf, int slot, BufferAccess *access, BufferStage *stage,
512 const UsageState &state);
513
514 enum TextureStage {
515 TexVertexStage,
516 TexFragmentStage,
517 TexColorOutputStage,
518 TexDepthOutputStage,
519 TexComputeStage
520 };
521
522 enum TextureAccess {
523 TexSample,
524 TexColorOutput,
525 TexDepthOutput,
526 TexStorageLoad,
527 TexStorageStore,
528 TexStorageLoadStore
529 };
530
531 void registerTexture(QRhiTexture *tex, TextureAccess *access, TextureStage *stage,
532 const UsageState &state);
533
534 struct Buffer {
535 int slot;
536 BufferAccess access;
537 BufferStage stage;
538 UsageState stateAtPassBegin;
539 };
540
541 using BufferIterator = QHash<QRhiBuffer *, Buffer>::const_iterator;
cbeginBuffers()542 BufferIterator cbeginBuffers() const { return m_buffers.cbegin(); }
cendBuffers()543 BufferIterator cendBuffers() const { return m_buffers.cend(); }
544
545 struct Texture {
546 TextureAccess access;
547 TextureStage stage;
548 UsageState stateAtPassBegin;
549 };
550
551 using TextureIterator = QHash<QRhiTexture *, Texture>::const_iterator;
cbeginTextures()552 TextureIterator cbeginTextures() const { return m_textures.cbegin(); }
cendTextures()553 TextureIterator cendTextures() const { return m_textures.cend(); }
554
555 static BufferStage toPassTrackerBufferStage(QRhiShaderResourceBinding::StageFlags stages);
556 static TextureStage toPassTrackerTextureStage(QRhiShaderResourceBinding::StageFlags stages);
557
558 private:
559 QHash<QRhiBuffer *, Buffer> m_buffers;
560 QHash<QRhiTexture *, Texture> m_textures;
561 };
562
563 Q_DECLARE_TYPEINFO(QRhiPassResourceTracker::Buffer, Q_MOVABLE_TYPE);
564 Q_DECLARE_TYPEINFO(QRhiPassResourceTracker::Texture, Q_MOVABLE_TYPE);
565
566 QT_END_NAMESPACE
567
568 #endif
569