/**************************************************************************** ** ** Copyright (C) 2019 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Gui module ** ** $QT_BEGIN_LICENSE:LGPL3$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPLv3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or later as published by the Free ** Software Foundation and appearing in the file LICENSE.GPL included in ** the packaging of this file. Please review the following information to ** ensure the GNU General Public License version 2.0 requirements will be ** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QRHI_P_H #define QRHI_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include "qrhi_p.h" #include "qrhiprofiler_p_p.h" #include #include #include QT_BEGIN_NAMESPACE #define QRHI_RES(t, x) static_cast(x) #define QRHI_RES_RHI(t) t *rhiD = static_cast(m_rhi) #define QRHI_PROF QRhiProfilerPrivate *rhiP = m_rhi->profilerPrivateOrNull() #define QRHI_PROF_F(f) for (bool qrhip_enabled = rhiP != nullptr; qrhip_enabled; qrhip_enabled = false) rhiP->f Q_DECLARE_LOGGING_CATEGORY(QRHI_LOG_INFO) class QRhiImplementation { public: virtual ~QRhiImplementation(); virtual bool create(QRhi::Flags flags) = 0; virtual void destroy() = 0; virtual QRhiGraphicsPipeline *createGraphicsPipeline() = 0; virtual QRhiComputePipeline *createComputePipeline() = 0; virtual QRhiShaderResourceBindings *createShaderResourceBindings() = 0; virtual QRhiBuffer *createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, int size) = 0; virtual QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize, int sampleCount, QRhiRenderBuffer::Flags flags) = 0; virtual QRhiTexture *createTexture(QRhiTexture::Format format, const QSize &pixelSize, int sampleCount, QRhiTexture::Flags flags) = 0; virtual QRhiSampler *createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, QRhiSampler::Filter mipmapMode, QRhiSampler:: AddressMode u, QRhiSampler::AddressMode v) = 0; virtual QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc, QRhiTextureRenderTarget::Flags flags) = 0; virtual QRhiSwapChain *createSwapChain() = 0; virtual QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) = 0; virtual QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) = 0; virtual QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags) = 0; virtual QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) = 0; virtual QRhi::FrameOpResult finish() = 0; virtual void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) = 0; virtual void beginPass(QRhiCommandBuffer *cb, QRhiRenderTarget *rt, const QColor &colorClearValue, const QRhiDepthStencilClearValue &depthStencilClearValue, QRhiResourceUpdateBatch *resourceUpdates) = 0; virtual void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) = 0; virtual void setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps) = 0; virtual void setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb, int dynamicOffsetCount, const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) = 0; virtual void setVertexInput(QRhiCommandBuffer *cb, int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings, QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat) = 0; virtual void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) = 0; virtual void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) = 0; virtual void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) = 0; virtual void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) = 0; virtual void draw(QRhiCommandBuffer *cb, quint32 vertexCount, quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) = 0; virtual void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance) = 0; virtual void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) = 0; virtual void debugMarkEnd(QRhiCommandBuffer *cb) = 0; virtual void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) = 0; virtual void beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) = 0; virtual void endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) = 0; virtual void setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps) = 0; virtual void dispatch(QRhiCommandBuffer *cb, int x, int y, int z) = 0; virtual const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) = 0; virtual void beginExternal(QRhiCommandBuffer *cb) = 0; virtual void endExternal(QRhiCommandBuffer *cb) = 0; virtual QVector supportedSampleCounts() const = 0; virtual int ubufAlignment() const = 0; virtual bool isYUpInFramebuffer() const = 0; virtual bool isYUpInNDC() const = 0; virtual bool isClipDepthZeroToOne() const = 0; virtual QMatrix4x4 clipSpaceCorrMatrix() const = 0; virtual bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const = 0; virtual bool isFeatureSupported(QRhi::Feature feature) const = 0; virtual int resourceLimit(QRhi::ResourceLimit limit) const = 0; virtual const QRhiNativeHandles *nativeHandles() = 0; virtual void sendVMemStatsToProfiler() = 0; virtual bool makeThreadLocalNativeContextCurrent() = 0; virtual void releaseCachedResources() = 0; virtual bool isDeviceLost() const = 0; bool isCompressedFormat(QRhiTexture::Format format) const; void compressedFormatInfo(QRhiTexture::Format format, const QSize &size, quint32 *bpl, quint32 *byteSize, QSize *blockDim) const; void textureFormatInfo(QRhiTexture::Format format, const QSize &size, quint32 *bpl, quint32 *byteSize) const; quint32 approxByteSizeForTexture(QRhiTexture::Format format, const QSize &baseSize, int mipCount, int layerCount); QRhiProfilerPrivate *profilerPrivateOrNull() { // return null when QRhi::EnableProfiling was not set QRhiProfilerPrivate *p = QRhiProfilerPrivate::get(&profiler); return p->rhiDWhenEnabled ? p : nullptr; } // only really care about resources that own native graphics resources underneath void registerResource(QRhiResource *res) { resources.insert(res); } void unregisterResource(QRhiResource *res) { resources.remove(res); } QSet activeResources() const { return resources; } void addReleaseAndDestroyLater(QRhiResource *res) { if (inFrame) pendingReleaseAndDestroyResources.insert(res); else delete res; } void addCleanupCallback(const QRhi::CleanupCallback &callback) { cleanupCallbacks.append(callback); } bool sanityCheckGraphicsPipeline(QRhiGraphicsPipeline *ps); QRhi *q; static const int MAX_SHADER_CACHE_ENTRIES = 128; protected: bool debugMarkers = false; int currentFrameSlot = 0; // for vk, mtl, and similar. unused by gl and d3d11. bool inFrame = false; private: QRhi::Implementation implType; QThread *implThread; QRhiProfiler profiler; QVarLengthArray resUpdPool; QBitArray resUpdPoolMap; QSet resources; QSet pendingReleaseAndDestroyResources; QVector cleanupCallbacks; friend class QRhi; friend class QRhiResourceUpdateBatchPrivate; }; template bool qrhi_toTopLeftRenderTargetRect(const QSize &outputSize, const std::array &r, T *x, T *y, T *w, T *h) { // x,y are bottom-left in QRhiScissor and QRhiViewport but top-left in // Vulkan/Metal/D3D. Our input is an OpenGL-style scissor rect where both // negative x or y, and partly or completely out of bounds rects are // allowed. The only thing the input here cannot have is a negative width // or height. We must handle all other input gracefully, clamping to a zero // width or height rect in the worst case, and ensuring the resulting rect // is inside the rendertarget's bounds because some APIs' validation/debug // layers are allergic to out of bounds scissor or viewport rects. const T outputWidth = outputSize.width(); const T outputHeight = outputSize.height(); const T inputWidth = r[2]; const T inputHeight = r[3]; if (inputWidth < 0 || inputHeight < 0) return false; *x = r[0]; *y = outputHeight - (r[1] + inputHeight); const T widthOffset = *x < 0 ? -*x : 0; const T heightOffset = *y < 0 ? -*y : 0; *x = qBound(0, *x, outputWidth - 1); *y = qBound(0, *y, outputHeight - 1); *w = qMax(0, inputWidth - widthOffset); *h = qMax(0, inputHeight - heightOffset); if (*x + *w > outputWidth) *w = qMax(0, outputWidth - *x - 1); if (*y + *h > outputHeight) *h = qMax(0, outputHeight - *y - 1); return true; } class QRhiResourceUpdateBatchPrivate { public: struct BufferOp { enum Type { DynamicUpdate, StaticUpload, Read }; Type type; QRhiBuffer *buf; int offset; QByteArray data; int readSize; QRhiBufferReadbackResult *result; static BufferOp dynamicUpdate(QRhiBuffer *buf, int offset, int size, const void *data) { BufferOp op; op.type = DynamicUpdate; op.buf = buf; op.offset = offset; op.data = QByteArray(reinterpret_cast(data), size ? size : buf->size()); return op; } static BufferOp staticUpload(QRhiBuffer *buf, int offset, int size, const void *data) { BufferOp op; op.type = StaticUpload; op.buf = buf; op.offset = offset; op.data = QByteArray(reinterpret_cast(data), size ? size : buf->size()); return op; } static BufferOp read(QRhiBuffer *buf, int offset, int size, QRhiBufferReadbackResult *result) { BufferOp op; op.type = Read; op.buf = buf; op.offset = offset; op.readSize = size; op.result = result; return op; } }; struct TextureOp { enum Type { Upload, Copy, Read, GenMips }; Type type; QRhiTexture *dst; // Specifying multiple uploads for a subresource must be supported. // In the backend this can then end up, where applicable, as a // single, batched copy operation with only one set of barriers. // This helps when doing for example glyph cache fills. QVector subresDesc[QRhi::MAX_LAYERS][QRhi::MAX_LEVELS]; QRhiTexture *src; QRhiTextureCopyDescription desc; QRhiReadbackDescription rb; QRhiReadbackResult *result; int layer; static TextureOp upload(QRhiTexture *tex, const QRhiTextureUploadDescription &desc) { TextureOp op; op.type = Upload; op.dst = tex; for (auto it = desc.cbeginEntries(), itEnd = desc.cendEntries(); it != itEnd; ++it) op.subresDesc[it->layer()][it->level()].append(it->description()); return op; } static TextureOp copy(QRhiTexture *dst, QRhiTexture *src, const QRhiTextureCopyDescription &desc) { TextureOp op; op.type = Copy; op.dst = dst; op.src = src; op.desc = desc; return op; } static TextureOp read(const QRhiReadbackDescription &rb, QRhiReadbackResult *result) { TextureOp op; op.type = Read; op.rb = rb; op.result = result; return op; } static TextureOp genMips(QRhiTexture *tex, int layer) { TextureOp op; op.type = GenMips; op.dst = tex; op.layer = layer; return op; } }; QVarLengthArray bufferOps; QVarLengthArray textureOps; QRhiResourceUpdateBatch *q = nullptr; QRhiImplementation *rhi = nullptr; int poolIndex = -1; void free(); void merge(QRhiResourceUpdateBatchPrivate *other); static QRhiResourceUpdateBatchPrivate *get(QRhiResourceUpdateBatch *b) { return b->d; } }; Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::BufferOp, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::TextureOp, Q_MOVABLE_TYPE); template struct QRhiBatchedBindings { void feed(int binding, T resource) { // binding must be strictly increasing if (curBinding == -1 || binding > curBinding + 1) { finish(); curBatch.startBinding = binding; curBatch.resources.clear(); curBatch.resources.append(resource); } else { Q_ASSERT(binding == curBinding + 1); curBatch.resources.append(resource); } curBinding = binding; } void finish() { if (!curBatch.resources.isEmpty()) batches.append(curBatch); } void clear() { batches.clear(); curBatch.resources.clear(); curBinding = -1; } struct Batch { uint startBinding; QVarLengthArray resources; bool operator==(const Batch &other) const { return startBinding == other.startBinding && resources == other.resources; } bool operator!=(const Batch &other) const { return !operator==(other); } }; QVarLengthArray batches; // sorted by startBinding bool operator==(const QRhiBatchedBindings &other) const { return batches == other.batches; } bool operator!=(const QRhiBatchedBindings &other) const { return !operator==(other); } private: Batch curBatch; int curBinding = -1; }; class QRhiGlobalObjectIdGenerator { public: #ifdef Q_ATOMIC_INT64_IS_SUPPORTED using Type = quint64; #else using Type = quint32; #endif static Type newId(); }; class QRhiPassResourceTracker { public: bool isEmpty() const; void reset(); struct UsageState { int layout; int access; int stage; }; enum BufferStage { BufVertexInputStage, BufVertexStage, BufFragmentStage, BufComputeStage }; enum BufferAccess { BufVertexInput, BufIndexRead, BufUniformRead, BufStorageLoad, BufStorageStore, BufStorageLoadStore }; void registerBuffer(QRhiBuffer *buf, int slot, BufferAccess *access, BufferStage *stage, const UsageState &state); enum TextureStage { TexVertexStage, TexFragmentStage, TexColorOutputStage, TexDepthOutputStage, TexComputeStage }; enum TextureAccess { TexSample, TexColorOutput, TexDepthOutput, TexStorageLoad, TexStorageStore, TexStorageLoadStore }; void registerTexture(QRhiTexture *tex, TextureAccess *access, TextureStage *stage, const UsageState &state); struct Buffer { int slot; BufferAccess access; BufferStage stage; UsageState stateAtPassBegin; }; using BufferIterator = QHash::const_iterator; BufferIterator cbeginBuffers() const { return m_buffers.cbegin(); } BufferIterator cendBuffers() const { return m_buffers.cend(); } struct Texture { TextureAccess access; TextureStage stage; UsageState stateAtPassBegin; }; using TextureIterator = QHash::const_iterator; TextureIterator cbeginTextures() const { return m_textures.cbegin(); } TextureIterator cendTextures() const { return m_textures.cend(); } static BufferStage toPassTrackerBufferStage(QRhiShaderResourceBinding::StageFlags stages); static TextureStage toPassTrackerTextureStage(QRhiShaderResourceBinding::StageFlags stages); private: QHash m_buffers; QHash m_textures; }; Q_DECLARE_TYPEINFO(QRhiPassResourceTracker::Buffer, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(QRhiPassResourceTracker::Texture, Q_MOVABLE_TYPE); QT_END_NAMESPACE #endif