diff options
Diffstat (limited to 'src/gui/rhi/qrhi_p_p.h')
-rw-r--r-- | src/gui/rhi/qrhi_p_p.h | 551 |
1 files changed, 551 insertions, 0 deletions
diff --git a/src/gui/rhi/qrhi_p_p.h b/src/gui/rhi/qrhi_p_p.h new file mode 100644 index 0000000000..baffe28202 --- /dev/null +++ b/src/gui/rhi/qrhi_p_p.h @@ -0,0 +1,551 @@ +/**************************************************************************** +** +** 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 <QBitArray> +#include <QAtomicInt> +#include <QLoggingCategory> + +QT_BEGIN_NAMESPACE + +#define QRHI_RES(t, x) static_cast<t *>(x) +#define QRHI_RES_RHI(t) t *rhiD = static_cast<t *>(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<int> 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<QRhiResource *> 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<QRhiResourceUpdateBatch *, 4> resUpdPool; + QBitArray resUpdPoolMap; + QSet<QRhiResource *> resources; + QSet<QRhiResource *> pendingReleaseAndDestroyResources; + QVector<QRhi::CleanupCallback> cleanupCallbacks; + + friend class QRhi; + friend class QRhiResourceUpdateBatchPrivate; +}; + +template<typename T, size_t N> +bool qrhi_toTopLeftRenderTargetRect(const QSize &outputSize, const std::array<T, N> &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<T>(0, *x, outputWidth - 1); + *y = qBound<T>(0, *y, outputHeight - 1); + *w = qMax<T>(0, inputWidth - widthOffset); + *h = qMax<T>(0, inputHeight - heightOffset); + + if (*x + *w > outputWidth) + *w = qMax<T>(0, outputWidth - *x - 1); + if (*y + *h > outputHeight) + *h = qMax<T>(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<const char *>(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<const char *>(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<QRhiTextureSubresourceUploadDescription> 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<BufferOp, 1024> bufferOps; + QVarLengthArray<TextureOp, 256> 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<typename T> +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<T, 4> resources; + + bool operator==(const Batch &other) const + { + return startBinding == other.startBinding && resources == other.resources; + } + + bool operator!=(const Batch &other) const + { + return !operator==(other); + } + }; + + QVarLengthArray<Batch, 4> batches; // sorted by startBinding + + bool operator==(const QRhiBatchedBindings<T> &other) const + { + return batches == other.batches; + } + + bool operator!=(const QRhiBatchedBindings<T> &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<QRhiBuffer *, Buffer>::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<QRhiTexture *, Texture>::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<QRhiBuffer *, Buffer> m_buffers; + QHash<QRhiTexture *, Texture> m_textures; +}; + +Q_DECLARE_TYPEINFO(QRhiPassResourceTracker::Buffer, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(QRhiPassResourceTracker::Texture, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +#endif |