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