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