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