diff options
author | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2019-10-10 09:10:26 +0200 |
---|---|---|
committer | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2019-10-10 09:10:27 +0200 |
commit | 68375b4f3dc9fef5412343ac4496a4af0db43839 (patch) | |
tree | 019e4756c61bba7aead031eed2dc4f4fafd99294 /src/gui/rhi | |
parent | 22891dd897b37be03222ec4881629628fb312442 (diff) | |
parent | 18aa8390ce83b4aa9cabe5609b8f830f86e475e5 (diff) |
Merge remote-tracking branch 'origin/5.14' into 5.15
Change-Id: Iadeca81f499d6b19e86ceae1edd7960db2575e90
Diffstat (limited to 'src/gui/rhi')
-rw-r--r-- | src/gui/rhi/qrhi.cpp | 110 | ||||
-rw-r--r-- | src/gui/rhi/qrhi_p.h | 12 | ||||
-rw-r--r-- | src/gui/rhi/qrhi_p_p.h | 133 | ||||
-rw-r--r-- | src/gui/rhi/qrhid3d11.cpp | 233 | ||||
-rw-r--r-- | src/gui/rhi/qrhid3d11_p_p.h | 15 | ||||
-rw-r--r-- | src/gui/rhi/qrhigles2.cpp | 202 | ||||
-rw-r--r-- | src/gui/rhi/qrhigles2_p_p.h | 16 | ||||
-rw-r--r-- | src/gui/rhi/qrhimetal.mm | 165 | ||||
-rw-r--r-- | src/gui/rhi/qrhinull.cpp | 145 | ||||
-rw-r--r-- | src/gui/rhi/qrhinull_p_p.h | 7 | ||||
-rw-r--r-- | src/gui/rhi/qrhivulkan.cpp | 397 | ||||
-rw-r--r-- | src/gui/rhi/qrhivulkan_p_p.h | 23 |
12 files changed, 987 insertions, 471 deletions
diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp index c8ec8c7410..dabad35688 100644 --- a/src/gui/rhi/qrhi.cpp +++ b/src/gui/rhi/qrhi.cpp @@ -566,7 +566,19 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general") \value TriangleFanTopology Indicates that QRhiGraphicsPipeline::setTopology() supports QRhiGraphicsPipeline::TriangleFan. -*/ + + \value ReadBackNonUniformBuffer Indicates that + \l{QRhiResourceUpdateBatch::readBackBuffer()}{reading buffer contents} is + supported for QRhiBuffer instances with a usage different than + UniformBuffer. While this is supported in the majority of cases, it will be + unsupported with OpenGL ES older than 3.0. + + \value ReadBackNonBaseMipLevel Indicates that specifying a mip level other + than 0 is supported when reading back texture contents. When not supported, + specifying a non-zero level in QRhiReadbackDescription leads to returning + an all-zero image. In practice this feature will be unsupported with OpenGL + ES 2.0, while it will likely be supported everywhere else. + */ /*! \enum QRhi::BeginFrameFlag @@ -3072,9 +3084,13 @@ QDebug operator<<(QDebug dbg, const QRhiShaderResourceBindings &srb) \inmodule QtGui \brief Graphics pipeline state resource. + \note Setting the shader stages is mandatory. There must be at least one + stage, and there must be a vertex stage. + \note Setting the shader resource bindings is mandatory. The referenced QRhiShaderResourceBindings must already be built by the time build() is - called. + called. Associating with a QRhiShaderResourceBindings that has no bindings + is also valid, as long as no shader in any stage expects any resources. \note Setting the render pass descriptor is mandatory. To obtain a QRhiRenderPassDescriptor that can be passed to setRenderPassDescriptor(), @@ -3083,8 +3099,6 @@ QDebug operator<<(QDebug dbg, const QRhiShaderResourceBindings &srb) \note Setting the vertex input layout is mandatory. - \note Setting the shader stages is mandatory. - \note sampleCount() defaults to 1 and must match the sample count of the render target's color and depth stencil attachments. @@ -3900,6 +3914,46 @@ quint32 QRhiImplementation::approxByteSizeForTexture(QRhiTexture::Format format, return approxSize; } +bool QRhiImplementation::sanityCheckGraphicsPipeline(QRhiGraphicsPipeline *ps) +{ + if (ps->cbeginShaderStages() == ps->cendShaderStages()) { + qWarning("Cannot build a graphics pipeline without any stages"); + return false; + } + + bool hasVertexStage = false; + for (auto it = ps->cbeginShaderStages(), itEnd = ps->cendShaderStages(); it != itEnd; ++it) { + if (!it->shader().isValid()) { + qWarning("Empty shader passed to graphics pipeline"); + return false; + } + if (it->type() == QRhiShaderStage::Vertex) { + hasVertexStage = true; + const QRhiVertexInputLayout inputLayout = ps->vertexInputLayout(); + if (inputLayout.cbeginAttributes() == inputLayout.cendAttributes()) { + qWarning("Vertex stage present without any vertex inputs"); + return false; + } + } + } + if (!hasVertexStage) { + qWarning("Cannot build a graphics pipeline without a vertex stage"); + return false; + } + + if (!ps->renderPassDescriptor()) { + qWarning("Cannot build a graphics pipeline without a QRhiRenderPassDescriptor"); + return false; + } + + if (!ps->shaderResourceBindings()) { + qWarning("Cannot build a graphics pipeline without QRhiShaderResourceBindings"); + return false; + } + + return true; +} + /*! \internal */ @@ -4175,7 +4229,7 @@ void QRhiResourceUpdateBatch::merge(QRhiResourceUpdateBatch *other) void QRhiResourceUpdateBatch::updateDynamicBuffer(QRhiBuffer *buf, int offset, int size, const void *data) { if (size > 0) - d->dynamicBufferUpdates.append({ buf, offset, size, data }); + d->bufferOps.append(QRhiResourceUpdateBatchPrivate::BufferOp::dynamicUpdate(buf, offset, size, data)); } /*! @@ -4189,7 +4243,7 @@ void QRhiResourceUpdateBatch::updateDynamicBuffer(QRhiBuffer *buf, int offset, i void QRhiResourceUpdateBatch::uploadStaticBuffer(QRhiBuffer *buf, int offset, int size, const void *data) { if (size > 0) - d->staticBufferUploads.append({ buf, offset, size, data }); + d->bufferOps.append(QRhiResourceUpdateBatchPrivate::BufferOp::staticUpload(buf, offset, size, data)); } /*! @@ -4199,7 +4253,28 @@ void QRhiResourceUpdateBatch::uploadStaticBuffer(QRhiBuffer *buf, int offset, in void QRhiResourceUpdateBatch::uploadStaticBuffer(QRhiBuffer *buf, const void *data) { if (buf->size() > 0) - d->staticBufferUploads.append({ buf, 0, 0, data }); + d->bufferOps.append(QRhiResourceUpdateBatchPrivate::BufferOp::staticUpload(buf, 0, 0, data)); +} + +/*! + Enqueues reading back a region of the QRhiBuffer \a buf. The size of the + region is specified by \a size in bytes, \a offset is the offset in bytes + to start reading from. + + A readback is asynchronous. \a result contains a callback that is invoked + when the operation has completed. The data is provided in + QRhiBufferReadbackResult::data. Upon successful completion that QByteArray + will have a size equal to \a size. On failure the QByteArray will be empty. + + \note Reading buffers with a usage different than QRhiBuffer::UniformBuffer + is supported only when the QRhi::ReadBackNonUniformBuffer feature is + reported as supported. + + \a readBackTexture(), QRhi::isFeatureSupported() + */ +void QRhiResourceUpdateBatch::readBackBuffer(QRhiBuffer *buf, int offset, int size, QRhiBufferReadbackResult *result) +{ + d->bufferOps.append(QRhiResourceUpdateBatchPrivate::BufferOp::read(buf, offset, size, result)); } /*! @@ -4212,7 +4287,7 @@ void QRhiResourceUpdateBatch::uploadStaticBuffer(QRhiBuffer *buf, const void *da void QRhiResourceUpdateBatch::uploadTexture(QRhiTexture *tex, const QRhiTextureUploadDescription &desc) { if (desc.cbeginEntries() != desc.cendEntries()) - d->textureOps.append(QRhiResourceUpdateBatchPrivate::TextureOp::textureUpload(tex, desc)); + d->textureOps.append(QRhiResourceUpdateBatchPrivate::TextureOp::upload(tex, desc)); } /*! @@ -4237,7 +4312,7 @@ void QRhiResourceUpdateBatch::uploadTexture(QRhiTexture *tex, const QImage &imag */ void QRhiResourceUpdateBatch::copyTexture(QRhiTexture *dst, QRhiTexture *src, const QRhiTextureCopyDescription &desc) { - d->textureOps.append(QRhiResourceUpdateBatchPrivate::TextureOp::textureCopy(dst, src, desc)); + d->textureOps.append(QRhiResourceUpdateBatchPrivate::TextureOp::copy(dst, src, desc)); } /*! @@ -4289,7 +4364,7 @@ void QRhiResourceUpdateBatch::copyTexture(QRhiTexture *dst, QRhiTexture *src, co */ void QRhiResourceUpdateBatch::readBackTexture(const QRhiReadbackDescription &rb, QRhiReadbackResult *result) { - d->textureOps.append(QRhiResourceUpdateBatchPrivate::TextureOp::textureRead(rb, result)); + d->textureOps.append(QRhiResourceUpdateBatchPrivate::TextureOp::read(rb, result)); } /*! @@ -4301,7 +4376,7 @@ void QRhiResourceUpdateBatch::readBackTexture(const QRhiReadbackDescription &rb, */ void QRhiResourceUpdateBatch::generateMips(QRhiTexture *tex, int layer) { - d->textureOps.append(QRhiResourceUpdateBatchPrivate::TextureOp::textureMipGen(tex, layer)); + d->textureOps.append(QRhiResourceUpdateBatchPrivate::TextureOp::genMips(tex, layer)); } /*! @@ -4350,8 +4425,7 @@ void QRhiResourceUpdateBatchPrivate::free() { Q_ASSERT(poolIndex >= 0 && rhi->resUpdPool[poolIndex] == q); - dynamicBufferUpdates.clear(); - staticBufferUploads.clear(); + bufferOps.clear(); textureOps.clear(); rhi->resUpdPoolMap.clearBit(poolIndex); @@ -4360,9 +4434,13 @@ void QRhiResourceUpdateBatchPrivate::free() void QRhiResourceUpdateBatchPrivate::merge(QRhiResourceUpdateBatchPrivate *other) { - dynamicBufferUpdates += other->dynamicBufferUpdates; - staticBufferUploads += other->staticBufferUploads; - textureOps += other->textureOps; + bufferOps.reserve(bufferOps.size() + other->bufferOps.size()); + for (const BufferOp &op : qAsConst(other->bufferOps)) + bufferOps.append(op); + + textureOps.reserve(textureOps.size() + other->textureOps.size()); + for (const TextureOp &op : qAsConst(other->textureOps)) + textureOps.append(op); } /*! diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h index 5b371af727..907924c788 100644 --- a/src/gui/rhi/qrhi_p.h +++ b/src/gui/rhi/qrhi_p.h @@ -72,7 +72,6 @@ class QRhiCommandBuffer; class QRhiResourceUpdateBatch; class QRhiResourceUpdateBatchPrivate; class QRhiProfiler; -class QRhiShaderResourceBindingPrivate; class Q_GUI_EXPORT QRhiDepthStencilClearValue { @@ -1355,6 +1354,12 @@ struct Q_GUI_EXPORT QRhiReadbackResult QByteArray data; }; // non-movable due to the std::function +struct Q_GUI_EXPORT QRhiBufferReadbackResult +{ + std::function<void()> completed = nullptr; + QByteArray data; +}; + class Q_GUI_EXPORT QRhiResourceUpdateBatch { public: @@ -1367,6 +1372,7 @@ public: void updateDynamicBuffer(QRhiBuffer *buf, int offset, int size, const void *data); void uploadStaticBuffer(QRhiBuffer *buf, int offset, int size, const void *data); void uploadStaticBuffer(QRhiBuffer *buf, const void *data); + void readBackBuffer(QRhiBuffer *buf, int offset, int size, QRhiBufferReadbackResult *result); void uploadTexture(QRhiTexture *tex, const QRhiTextureUploadDescription &desc); void uploadTexture(QRhiTexture *tex, const QImage &image); void copyTexture(QRhiTexture *dst, QRhiTexture *src, const QRhiTextureCopyDescription &desc = QRhiTextureCopyDescription()); @@ -1428,7 +1434,9 @@ public: VertexShaderPointSize, BaseVertex, BaseInstance, - TriangleFanTopology + TriangleFanTopology, + ReadBackNonUniformBuffer, + ReadBackNonBaseMipLevel }; enum BeginFrameFlag { diff --git a/src/gui/rhi/qrhi_p_p.h b/src/gui/rhi/qrhi_p_p.h index 80a95e2fcc..baffe28202 100644 --- a/src/gui/rhi/qrhi_p_p.h +++ b/src/gui/rhi/qrhi_p_p.h @@ -205,6 +205,8 @@ public: cleanupCallbacks.append(callback); } + bool sanityCheckGraphicsPipeline(QRhiGraphicsPipeline *ps); + QRhi *q; static const int MAX_SHADER_CACHE_ENTRIES = 128; @@ -218,7 +220,7 @@ private: QRhi::Implementation implType; QThread *implThread; QRhiProfiler profiler; - QVector<QRhiResourceUpdateBatch *> resUpdPool; + QVarLengthArray<QRhiResourceUpdateBatch *, 4> resUpdPool; QBitArray resUpdPoolMap; QSet<QRhiResource *> resources; QSet<QRhiResource *> pendingReleaseAndDestroyResources; @@ -271,26 +273,49 @@ bool qrhi_toTopLeftRenderTargetRect(const QSize &outputSize, const std::array<T, class QRhiResourceUpdateBatchPrivate { public: - struct DynamicBufferUpdate { - DynamicBufferUpdate() { } - DynamicBufferUpdate(QRhiBuffer *buf_, int offset_, int size_, const void *data_) - : buf(buf_), offset(offset_), data(reinterpret_cast<const char *>(data_), size_) - { } - - QRhiBuffer *buf = nullptr; - int offset = 0; + struct BufferOp { + enum Type { + DynamicUpdate, + StaticUpload, + Read + }; + Type type; + QRhiBuffer *buf; + int offset; QByteArray data; - }; + int readSize; + QRhiBufferReadbackResult *result; - struct StaticBufferUpload { - StaticBufferUpload() { } - StaticBufferUpload(QRhiBuffer *buf_, int offset_, int size_, const void *data_) - : buf(buf_), offset(offset_), data(reinterpret_cast<const char *>(data_), size_ ? size_ : buf_->size()) - { } + 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; + } - QRhiBuffer *buf = nullptr; - int offset = 0; - QByteArray data; + 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 { @@ -298,73 +323,62 @@ public: Upload, Copy, Read, - MipGen + GenMips }; Type type; - struct SUpload { - QRhiTexture *tex = nullptr; - // 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]; - } upload; - struct SCopy { - QRhiTexture *dst = nullptr; - QRhiTexture *src = nullptr; - QRhiTextureCopyDescription desc; - } copy; - struct SRead { - QRhiReadbackDescription rb; - QRhiReadbackResult *result; - } read; - struct SMipGen { - QRhiTexture *tex = nullptr; - int layer = 0; - } mipgen; - - static TextureOp textureUpload(QRhiTexture *tex, const QRhiTextureUploadDescription &desc) + 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.upload.tex = tex; + op.dst = tex; for (auto it = desc.cbeginEntries(), itEnd = desc.cendEntries(); it != itEnd; ++it) - op.upload.subresDesc[it->layer()][it->level()].append(it->description()); + op.subresDesc[it->layer()][it->level()].append(it->description()); return op; } - static TextureOp textureCopy(QRhiTexture *dst, QRhiTexture *src, const QRhiTextureCopyDescription &desc) + static TextureOp copy(QRhiTexture *dst, QRhiTexture *src, const QRhiTextureCopyDescription &desc) { TextureOp op; op.type = Copy; - op.copy.dst = dst; - op.copy.src = src; - op.copy.desc = desc; + op.dst = dst; + op.src = src; + op.desc = desc; return op; } - static TextureOp textureRead(const QRhiReadbackDescription &rb, QRhiReadbackResult *result) + static TextureOp read(const QRhiReadbackDescription &rb, QRhiReadbackResult *result) { TextureOp op; op.type = Read; - op.read.rb = rb; - op.read.result = result; + op.rb = rb; + op.result = result; return op; } - static TextureOp textureMipGen(QRhiTexture *tex, int layer) + static TextureOp genMips(QRhiTexture *tex, int layer) { TextureOp op; - op.type = MipGen; - op.mipgen.tex = tex; - op.mipgen.layer = layer; + op.type = GenMips; + op.dst = tex; + op.layer = layer; return op; } }; - QVector<DynamicBufferUpdate> dynamicBufferUpdates; - QVector<StaticBufferUpload> staticBufferUploads; - QVector<TextureOp> textureOps; + QVarLengthArray<BufferOp, 1024> bufferOps; + QVarLengthArray<TextureOp, 256> textureOps; QRhiResourceUpdateBatch *q = nullptr; QRhiImplementation *rhi = nullptr; @@ -376,8 +390,7 @@ public: static QRhiResourceUpdateBatchPrivate *get(QRhiResourceUpdateBatch *b) { return b->d; } }; -Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate, Q_MOVABLE_TYPE); -Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::StaticBufferUpload, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::BufferOp, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::TextureOp, Q_MOVABLE_TYPE); template<typename T> diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index f011d68ada..717f3e6d6c 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -471,6 +471,10 @@ bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const return true; case QRhi::TriangleFanTopology: return false; + case QRhi::ReadBackNonUniformBuffer: + return true; + case QRhi::ReadBackNonBaseMipLevel: + return true; default: Q_UNREACHABLE(); return false; @@ -1323,61 +1327,105 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates); QRhiProfilerPrivate *rhiP = profilerPrivateOrNull(); - for (const QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate &u : ud->dynamicBufferUpdates) { - QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, u.buf); - Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic); - memcpy(bufD->dynBuf.data() + u.offset, u.data.constData(), size_t(u.data.size())); - bufD->hasPendingDynamicUpdates = true; - } - - for (const QRhiResourceUpdateBatchPrivate::StaticBufferUpload &u : ud->staticBufferUploads) { - QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, u.buf); - Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic); - Q_ASSERT(u.offset + u.data.size() <= bufD->m_size); - QD3D11CommandBuffer::Command cmd; - cmd.cmd = QD3D11CommandBuffer::Command::UpdateSubRes; - cmd.args.updateSubRes.dst = bufD->buffer; - cmd.args.updateSubRes.dstSubRes = 0; - cmd.args.updateSubRes.src = cbD->retainData(u.data); - cmd.args.updateSubRes.srcRowPitch = 0; - // Specify the region (even when offset is 0 and all data is provided) - // since the ID3D11Buffer's size is rounded up to be a multiple of 256 - // while the data we have has the original size. - D3D11_BOX box; - box.left = UINT(u.offset); - box.top = box.front = 0; - box.back = box.bottom = 1; - box.right = UINT(u.offset + u.data.size()); // no -1: right, bottom, back are exclusive, see D3D11_BOX doc - cmd.args.updateSubRes.hasDstBox = true; - cmd.args.updateSubRes.dstBox = box; - cbD->commands.append(cmd); + for (const QRhiResourceUpdateBatchPrivate::BufferOp &u : ud->bufferOps) { + if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::DynamicUpdate) { + QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, u.buf); + Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic); + memcpy(bufD->dynBuf.data() + u.offset, u.data.constData(), size_t(u.data.size())); + bufD->hasPendingDynamicUpdates = true; + } else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::StaticUpload) { + QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, u.buf); + Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic); + Q_ASSERT(u.offset + u.data.size() <= bufD->m_size); + QD3D11CommandBuffer::Command cmd; + cmd.cmd = QD3D11CommandBuffer::Command::UpdateSubRes; + cmd.args.updateSubRes.dst = bufD->buffer; + cmd.args.updateSubRes.dstSubRes = 0; + cmd.args.updateSubRes.src = cbD->retainData(u.data); + cmd.args.updateSubRes.srcRowPitch = 0; + // Specify the region (even when offset is 0 and all data is provided) + // since the ID3D11Buffer's size is rounded up to be a multiple of 256 + // while the data we have has the original size. + D3D11_BOX box; + box.left = UINT(u.offset); + box.top = box.front = 0; + box.back = box.bottom = 1; + box.right = UINT(u.offset + u.data.size()); // no -1: right, bottom, back are exclusive, see D3D11_BOX doc + cmd.args.updateSubRes.hasDstBox = true; + cmd.args.updateSubRes.dstBox = box; + cbD->commands.append(cmd); + } else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::Read) { + QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, u.buf); + if (bufD->m_type == QRhiBuffer::Dynamic) { + u.result->data.resize(u.readSize); + memcpy(u.result->data.data(), bufD->dynBuf.constData() + u.offset, size_t(u.readSize)); + } else { + BufferReadback readback; + readback.result = u.result; + readback.byteSize = u.readSize; + + D3D11_BUFFER_DESC desc; + memset(&desc, 0, sizeof(desc)); + desc.ByteWidth = readback.byteSize; + desc.Usage = D3D11_USAGE_STAGING; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + HRESULT hr = dev->CreateBuffer(&desc, nullptr, &readback.stagingBuf); + if (FAILED(hr)) { + qWarning("Failed to create buffer: %s", qPrintable(comErrorMessage(hr))); + continue; + } + QRHI_PROF_F(newReadbackBuffer(qint64(qintptr(readback.stagingBuf)), bufD, readback.byteSize)); + + QD3D11CommandBuffer::Command cmd; + cmd.cmd = QD3D11CommandBuffer::Command::CopySubRes; + cmd.args.copySubRes.dst = readback.stagingBuf; + cmd.args.copySubRes.dstSubRes = 0; + cmd.args.copySubRes.dstX = 0; + cmd.args.copySubRes.dstY = 0; + cmd.args.copySubRes.src = bufD->buffer; + cmd.args.copySubRes.srcSubRes = 0; + cmd.args.copySubRes.hasSrcBox = true; + D3D11_BOX box; + box.left = UINT(u.offset); + box.top = box.front = 0; + box.back = box.bottom = 1; + box.right = UINT(u.offset + u.readSize); + cmd.args.copySubRes.srcBox = box; + cbD->commands.append(cmd); + + activeBufferReadbacks.append(readback); + } + if (u.result->completed) + u.result->completed(); + } } for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) { if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) { - QD3D11Texture *texD = QRHI_RES(QD3D11Texture, u.upload.tex); + QD3D11Texture *texD = QRHI_RES(QD3D11Texture, u.dst); for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) { for (int level = 0; level < QRhi::MAX_LEVELS; ++level) { - for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.upload.subresDesc[layer][level])) + for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level])) enqueueSubresUpload(texD, cbD, layer, level, subresDesc); } } } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) { - Q_ASSERT(u.copy.src && u.copy.dst); - QD3D11Texture *srcD = QRHI_RES(QD3D11Texture, u.copy.src); - QD3D11Texture *dstD = QRHI_RES(QD3D11Texture, u.copy.dst); - UINT srcSubRes = D3D11CalcSubresource(UINT(u.copy.desc.sourceLevel()), UINT(u.copy.desc.sourceLayer()), srcD->mipLevelCount); - UINT dstSubRes = D3D11CalcSubresource(UINT(u.copy.desc.destinationLevel()), UINT(u.copy.desc.destinationLayer()), dstD->mipLevelCount); - const QPoint dp = u.copy.desc.destinationTopLeft(); - const QSize size = u.copy.desc.pixelSize().isEmpty() ? srcD->m_pixelSize : u.copy.desc.pixelSize(); - const QPoint sp = u.copy.desc.sourceTopLeft(); + Q_ASSERT(u.src && u.dst); + QD3D11Texture *srcD = QRHI_RES(QD3D11Texture, u.src); + QD3D11Texture *dstD = QRHI_RES(QD3D11Texture, u.dst); + UINT srcSubRes = D3D11CalcSubresource(UINT(u.desc.sourceLevel()), UINT(u.desc.sourceLayer()), srcD->mipLevelCount); + UINT dstSubRes = D3D11CalcSubresource(UINT(u.desc.destinationLevel()), UINT(u.desc.destinationLayer()), dstD->mipLevelCount); + const QPoint dp = u.desc.destinationTopLeft(); + const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize); + const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize(); + const QPoint sp = u.desc.sourceTopLeft(); D3D11_BOX srcBox; srcBox.left = UINT(sp.x()); srcBox.top = UINT(sp.y()); srcBox.front = 0; // back, right, bottom are exclusive - srcBox.right = srcBox.left + UINT(size.width()); - srcBox.bottom = srcBox.top + UINT(size.height()); + srcBox.right = srcBox.left + UINT(copySize.width()); + srcBox.bottom = srcBox.top + UINT(copySize.height()); srcBox.back = 1; QD3D11CommandBuffer::Command cmd; cmd.cmd = QD3D11CommandBuffer::Command::CopySubRes; @@ -1391,16 +1439,16 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate cmd.args.copySubRes.srcBox = srcBox; cbD->commands.append(cmd); } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) { - ActiveReadback aRb; - aRb.desc = u.read.rb; - aRb.result = u.read.result; + TextureReadback readback; + readback.desc = u.rb; + readback.result = u.result; ID3D11Resource *src; DXGI_FORMAT dxgiFormat; QSize pixelSize; QRhiTexture::Format format; UINT subres = 0; - QD3D11Texture *texD = QRHI_RES(QD3D11Texture, u.read.rb.texture()); + QD3D11Texture *texD = QRHI_RES(QD3D11Texture, u.rb.texture()); QD3D11SwapChain *swapChainD = nullptr; if (texD) { @@ -1410,9 +1458,9 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate } src = texD->tex; dxgiFormat = texD->dxgiFormat; - pixelSize = u.read.rb.level() > 0 ? q->sizeForMipLevel(u.read.rb.level(), texD->m_pixelSize) : texD->m_pixelSize; + pixelSize = q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize); format = texD->m_format; - subres = D3D11CalcSubresource(UINT(u.read.rb.level()), UINT(u.read.rb.layer()), texD->mipLevelCount); + subres = D3D11CalcSubresource(UINT(u.rb.level()), UINT(u.rb.layer()), texD->mipLevelCount); } else { Q_ASSERT(contextState.currentSwapChain); swapChainD = QRHI_RES(QD3D11SwapChain, contextState.currentSwapChain); @@ -1435,9 +1483,9 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate if (format == QRhiTexture::UnknownFormat) continue; } - quint32 bufSize = 0; + quint32 byteSize = 0; quint32 bpl = 0; - textureFormatInfo(format, pixelSize, &bpl, &bufSize); + textureFormatInfo(format, pixelSize, &bpl, &byteSize); D3D11_TEXTURE2D_DESC desc; memset(&desc, 0, sizeof(desc)); @@ -1457,7 +1505,7 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate } QRHI_PROF_F(newReadbackBuffer(qint64(qintptr(stagingTex)), texD ? static_cast<QRhiResource *>(texD) : static_cast<QRhiResource *>(swapChainD), - bufSize)); + byteSize)); QD3D11CommandBuffer::Command cmd; cmd.cmd = QD3D11CommandBuffer::Command::CopySubRes; @@ -1470,18 +1518,18 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate cmd.args.copySubRes.hasSrcBox = false; cbD->commands.append(cmd); - aRb.stagingTex = stagingTex; - aRb.bufSize = bufSize; - aRb.bpl = bpl; - aRb.pixelSize = pixelSize; - aRb.format = format; + readback.stagingTex = stagingTex; + readback.byteSize = byteSize; + readback.bpl = bpl; + readback.pixelSize = pixelSize; + readback.format = format; - activeReadbacks.append(aRb); - } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::MipGen) { - Q_ASSERT(u.mipgen.tex->flags().testFlag(QRhiTexture::UsedWithGenerateMips)); + activeTextureReadbacks.append(readback); + } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::GenMips) { + Q_ASSERT(u.dst->flags().testFlag(QRhiTexture::UsedWithGenerateMips)); QD3D11CommandBuffer::Command cmd; cmd.cmd = QD3D11CommandBuffer::Command::GenMip; - cmd.args.genMip.srv = QRHI_RES(QD3D11Texture, u.mipgen.tex)->srv; + cmd.args.genMip.srv = QRHI_RES(QD3D11Texture, u.dst)->srv; cbD->commands.append(cmd); } } @@ -1494,37 +1542,58 @@ void QRhiD3D11::finishActiveReadbacks() QVarLengthArray<std::function<void()>, 4> completedCallbacks; QRhiProfilerPrivate *rhiP = profilerPrivateOrNull(); - for (int i = activeReadbacks.count() - 1; i >= 0; --i) { - const QRhiD3D11::ActiveReadback &aRb(activeReadbacks[i]); - aRb.result->format = aRb.format; - aRb.result->pixelSize = aRb.pixelSize; - aRb.result->data.resize(int(aRb.bufSize)); + for (int i = activeTextureReadbacks.count() - 1; i >= 0; --i) { + const QRhiD3D11::TextureReadback &readback(activeTextureReadbacks[i]); + readback.result->format = readback.format; + readback.result->pixelSize = readback.pixelSize; D3D11_MAPPED_SUBRESOURCE mp; - HRESULT hr = context->Map(aRb.stagingTex, 0, D3D11_MAP_READ, 0, &mp); - if (FAILED(hr)) { + HRESULT hr = context->Map(readback.stagingTex, 0, D3D11_MAP_READ, 0, &mp); + if (SUCCEEDED(hr)) { + readback.result->data.resize(int(readback.byteSize)); + // nothing says the rows are tightly packed in the texture, must take + // the stride into account + char *dst = readback.result->data.data(); + char *src = static_cast<char *>(mp.pData); + for (int y = 0, h = readback.pixelSize.height(); y != h; ++y) { + memcpy(dst, src, readback.bpl); + dst += readback.bpl; + src += mp.RowPitch; + } + context->Unmap(readback.stagingTex, 0); + } else { qWarning("Failed to map readback staging texture: %s", qPrintable(comErrorMessage(hr))); - aRb.stagingTex->Release(); - continue; } - // nothing says the rows are tightly packed in the texture, must take - // the stride into account - char *dst = aRb.result->data.data(); - char *src = static_cast<char *>(mp.pData); - for (int y = 0, h = aRb.pixelSize.height(); y != h; ++y) { - memcpy(dst, src, aRb.bpl); - dst += aRb.bpl; - src += mp.RowPitch; + + readback.stagingTex->Release(); + QRHI_PROF_F(releaseReadbackBuffer(qint64(qintptr(readback.stagingTex)))); + + if (readback.result->completed) + completedCallbacks.append(readback.result->completed); + + activeTextureReadbacks.removeAt(i); + } + + for (int i = activeBufferReadbacks.count() - 1; i >= 0; --i) { + const QRhiD3D11::BufferReadback &readback(activeBufferReadbacks[i]); + + D3D11_MAPPED_SUBRESOURCE mp; + HRESULT hr = context->Map(readback.stagingBuf, 0, D3D11_MAP_READ, 0, &mp); + if (SUCCEEDED(hr)) { + readback.result->data.resize(int(readback.byteSize)); + memcpy(readback.result->data.data(), mp.pData, readback.byteSize); + context->Unmap(readback.stagingBuf, 0); + } else { + qWarning("Failed to map readback staging texture: %s", qPrintable(comErrorMessage(hr))); } - context->Unmap(aRb.stagingTex, 0); - aRb.stagingTex->Release(); - QRHI_PROF_F(releaseReadbackBuffer(qint64(qintptr(aRb.stagingTex)))); + readback.stagingBuf->Release(); + QRHI_PROF_F(releaseReadbackBuffer(qint64(qintptr(readback.stagingBuf)))); - if (aRb.result->completed) - completedCallbacks.append(aRb.result->completed); + if (readback.result->completed) + completedCallbacks.append(readback.result->completed); - activeReadbacks.removeAt(i); + activeBufferReadbacks.removeAt(i); } for (auto f : completedCallbacks) @@ -3423,6 +3492,8 @@ bool QD3D11GraphicsPipeline::build() release(); QRHI_RES_RHI(QRhiD3D11); + if (!rhiD->sanityCheckGraphicsPipeline(this)) + return false; D3D11_RASTERIZER_DESC rastDesc; memset(&rastDesc, 0, sizeof(rastDesc)); diff --git a/src/gui/rhi/qrhid3d11_p_p.h b/src/gui/rhi/qrhid3d11_p_p.h index 6699e7ad64..26de34ae0a 100644 --- a/src/gui/rhi/qrhid3d11_p_p.h +++ b/src/gui/rhi/qrhid3d11_p_p.h @@ -678,16 +678,22 @@ public: QD3D11CommandBuffer cbWrapper; } ofr; - struct ActiveReadback { + struct TextureReadback { QRhiReadbackDescription desc; QRhiReadbackResult *result; ID3D11Texture2D *stagingTex; - quint32 bufSize; + quint32 byteSize; quint32 bpl; QSize pixelSize; QRhiTexture::Format format; }; - QVector<ActiveReadback> activeReadbacks; + QVector<TextureReadback> activeTextureReadbacks; + struct BufferReadback { + QRhiBufferReadbackResult *result; + quint32 byteSize; + ID3D11Buffer *stagingBuf; + }; + QVector<BufferReadback> activeBufferReadbacks; struct Shader { Shader() = default; @@ -711,7 +717,8 @@ public: } deviceCurse; }; -Q_DECLARE_TYPEINFO(QRhiD3D11::ActiveReadback, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(QRhiD3D11::TextureReadback, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(QRhiD3D11::BufferReadback, Q_MOVABLE_TYPE); QT_END_NAMESPACE diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp index 277cf12fd2..e355979626 100644 --- a/src/gui/rhi/qrhigles2.cpp +++ b/src/gui/rhi/qrhigles2.cpp @@ -276,6 +276,10 @@ QT_BEGIN_NAMESPACE #define GL_POINT_SPRITE 0x8861 #endif +#ifndef GL_MAP_READ_BIT +#define GL_MAP_READ_BIT 0x0001 +#endif + Q_DECLARE_LOGGING_CATEGORY(lcOpenGLProgramDiskCache) /*! @@ -492,6 +496,15 @@ bool QRhiGles2::create(QRhi::Flags flags) else caps.textureCompareMode = true; + // proper as in ES 3.0 (glMapBufferRange), not the old glMapBuffer + // extension(s) (which is not in ES 3.0...messy) + caps.properMapBuffer = f->hasOpenGLExtension(QOpenGLExtensions::MapBufferRange); + + if (caps.gles) + caps.nonBaseLevelFramebufferTexture = caps.ctxMajor >= 3; // ES 3.0 + else + caps.nonBaseLevelFramebufferTexture = true; + if (!caps.gles) { f->glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); f->glEnable(GL_POINT_SPRITE); @@ -734,6 +747,10 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const return false; // not in ES 3.2, so won't bother case QRhi::TriangleFanTopology: return true; + case QRhi::ReadBackNonUniformBuffer: + return !caps.gles || caps.properMapBuffer; + case QRhi::ReadBackNonBaseMipLevel: + return caps.nonBaseLevelFramebufferTexture; default: Q_UNREACHABLE(); return false; @@ -1417,65 +1434,83 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates); - for (const QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate &u : ud->dynamicBufferUpdates) { - QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, u.buf); - Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic); - if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) { - memcpy(bufD->ubuf.data() + u.offset, u.data.constData(), size_t(u.data.size())); - } else { - trackedBufferBarrier(cbD, bufD, QGles2Buffer::AccessUpdate); - QGles2CommandBuffer::Command cmd; - cmd.cmd = QGles2CommandBuffer::Command::BufferSubData; - cmd.args.bufferSubData.target = bufD->targetForDataOps; - cmd.args.bufferSubData.buffer = bufD->buffer; - cmd.args.bufferSubData.offset = u.offset; - cmd.args.bufferSubData.size = u.data.size(); - cmd.args.bufferSubData.data = cbD->retainData(u.data); - cbD->commands.append(cmd); - } - } - - for (const QRhiResourceUpdateBatchPrivate::StaticBufferUpload &u : ud->staticBufferUploads) { - QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, u.buf); - Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic); - Q_ASSERT(u.offset + u.data.size() <= bufD->m_size); - if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) { - memcpy(bufD->ubuf.data() + u.offset, u.data.constData(), size_t(u.data.size())); - } else { - trackedBufferBarrier(cbD, bufD, QGles2Buffer::AccessUpdate); - QGles2CommandBuffer::Command cmd; - cmd.cmd = QGles2CommandBuffer::Command::BufferSubData; - cmd.args.bufferSubData.target = bufD->targetForDataOps; - cmd.args.bufferSubData.buffer = bufD->buffer; - cmd.args.bufferSubData.offset = u.offset; - cmd.args.bufferSubData.size = u.data.size(); - cmd.args.bufferSubData.data = cbD->retainData(u.data); - cbD->commands.append(cmd); + for (const QRhiResourceUpdateBatchPrivate::BufferOp &u : ud->bufferOps) { + if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::DynamicUpdate) { + QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, u.buf); + Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic); + if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) { + memcpy(bufD->ubuf.data() + u.offset, u.data.constData(), size_t(u.data.size())); + } else { + trackedBufferBarrier(cbD, bufD, QGles2Buffer::AccessUpdate); + QGles2CommandBuffer::Command cmd; + cmd.cmd = QGles2CommandBuffer::Command::BufferSubData; + cmd.args.bufferSubData.target = bufD->targetForDataOps; + cmd.args.bufferSubData.buffer = bufD->buffer; + cmd.args.bufferSubData.offset = u.offset; + cmd.args.bufferSubData.size = u.data.size(); + cmd.args.bufferSubData.data = cbD->retainData(u.data); + cbD->commands.append(cmd); + } + } else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::StaticUpload) { + QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, u.buf); + Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic); + Q_ASSERT(u.offset + u.data.size() <= bufD->m_size); + if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) { + memcpy(bufD->ubuf.data() + u.offset, u.data.constData(), size_t(u.data.size())); + } else { + trackedBufferBarrier(cbD, bufD, QGles2Buffer::AccessUpdate); + QGles2CommandBuffer::Command cmd; + cmd.cmd = QGles2CommandBuffer::Command::BufferSubData; + cmd.args.bufferSubData.target = bufD->targetForDataOps; + cmd.args.bufferSubData.buffer = bufD->buffer; + cmd.args.bufferSubData.offset = u.offset; + cmd.args.bufferSubData.size = u.data.size(); + cmd.args.bufferSubData.data = cbD->retainData(u.data); + cbD->commands.append(cmd); + } + } else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::Read) { + QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, u.buf); + if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) { + u.result->data.resize(u.readSize); + memcpy(u.result->data.data(), bufD->ubuf.constData() + u.offset, size_t(u.readSize)); + if (u.result->completed) + u.result->completed(); + } else { + QGles2CommandBuffer::Command cmd; + cmd.cmd = QGles2CommandBuffer::Command::GetBufferSubData; + cmd.args.getBufferSubData.result = u.result; + cmd.args.getBufferSubData.target = bufD->targetForDataOps; + cmd.args.getBufferSubData.buffer = bufD->buffer; + cmd.args.getBufferSubData.offset = u.offset; + cmd.args.getBufferSubData.size = u.readSize; + cbD->commands.append(cmd); + } } } for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) { if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) { - QGles2Texture *texD = QRHI_RES(QGles2Texture, u.upload.tex); + QGles2Texture *texD = QRHI_RES(QGles2Texture, u.dst); for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) { for (int level = 0; level < QRhi::MAX_LEVELS; ++level) { - for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.upload.subresDesc[layer][level])) + for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level])) enqueueSubresUpload(texD, cbD, layer, level, subresDesc); } } texD->specified = true; } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) { - Q_ASSERT(u.copy.src && u.copy.dst); - QGles2Texture *srcD = QRHI_RES(QGles2Texture, u.copy.src); - QGles2Texture *dstD = QRHI_RES(QGles2Texture, u.copy.dst); + Q_ASSERT(u.src && u.dst); + QGles2Texture *srcD = QRHI_RES(QGles2Texture, u.src); + QGles2Texture *dstD = QRHI_RES(QGles2Texture, u.dst); trackedImageBarrier(cbD, srcD, QGles2Texture::AccessRead); trackedImageBarrier(cbD, dstD, QGles2Texture::AccessUpdate); - const QSize size = u.copy.desc.pixelSize().isEmpty() ? srcD->m_pixelSize : u.copy.desc.pixelSize(); + const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize); + const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize(); // do not translate coordinates, even if sp is bottom-left from gl's pov - const QPoint sp = u.copy.desc.sourceTopLeft(); - const QPoint dp = u.copy.desc.destinationTopLeft(); + const QPoint sp = u.desc.sourceTopLeft(); + const QPoint dp = u.desc.destinationTopLeft(); const GLenum srcFaceTargetBase = srcD->m_flags.testFlag(QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : srcD->target; @@ -1485,43 +1520,44 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate QGles2CommandBuffer::Command cmd; cmd.cmd = QGles2CommandBuffer::Command::CopyTex; - cmd.args.copyTex.srcFaceTarget = srcFaceTargetBase + uint(u.copy.desc.sourceLayer()); + cmd.args.copyTex.srcFaceTarget = srcFaceTargetBase + uint(u.desc.sourceLayer()); cmd.args.copyTex.srcTexture = srcD->texture; - cmd.args.copyTex.srcLevel = u.copy.desc.sourceLevel(); + cmd.args.copyTex.srcLevel = u.desc.sourceLevel(); cmd.args.copyTex.srcX = sp.x(); cmd.args.copyTex.srcY = sp.y(); cmd.args.copyTex.dstTarget = dstD->target; cmd.args.copyTex.dstTexture = dstD->texture; - cmd.args.copyTex.dstFaceTarget = dstFaceTargetBase + uint(u.copy.desc.destinationLayer()); - cmd.args.copyTex.dstLevel = u.copy.desc.destinationLevel(); + cmd.args.copyTex.dstFaceTarget = dstFaceTargetBase + uint(u.desc.destinationLayer()); + cmd.args.copyTex.dstLevel = u.desc.destinationLevel(); cmd.args.copyTex.dstX = dp.x(); cmd.args.copyTex.dstY = dp.y(); - cmd.args.copyTex.w = size.width(); - cmd.args.copyTex.h = size.height(); + cmd.args.copyTex.w = copySize.width(); + cmd.args.copyTex.h = copySize.height(); cbD->commands.append(cmd); } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) { QGles2CommandBuffer::Command cmd; cmd.cmd = QGles2CommandBuffer::Command::ReadPixels; - cmd.args.readPixels.result = u.read.result; - QGles2Texture *texD = QRHI_RES(QGles2Texture, u.read.rb.texture()); + cmd.args.readPixels.result = u.result; + QGles2Texture *texD = QRHI_RES(QGles2Texture, u.rb.texture()); if (texD) trackedImageBarrier(cbD, texD, QGles2Texture::AccessRead); cmd.args.readPixels.texture = texD ? texD->texture : 0; if (texD) { - cmd.args.readPixels.w = texD->m_pixelSize.width(); - cmd.args.readPixels.h = texD->m_pixelSize.height(); + const QSize readImageSize = q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize); + cmd.args.readPixels.w = readImageSize.width(); + cmd.args.readPixels.h = readImageSize.height(); cmd.args.readPixels.format = texD->m_format; const GLenum faceTargetBase = texD->m_flags.testFlag(QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target; - cmd.args.readPixels.readTarget = faceTargetBase + uint(u.read.rb.layer()); - cmd.args.readPixels.level = u.read.rb.level(); + cmd.args.readPixels.readTarget = faceTargetBase + uint(u.rb.layer()); + cmd.args.readPixels.level = u.rb.level(); } cbD->commands.append(cmd); - } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::MipGen) { - QGles2Texture *texD = QRHI_RES(QGles2Texture, u.mipgen.tex); + } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::GenMips) { + QGles2Texture *texD = QRHI_RES(QGles2Texture, u.dst); trackedImageBarrier(cbD, texD, QGles2Texture::AccessFramebuffer); QGles2CommandBuffer::Command cmd; cmd.cmd = QGles2CommandBuffer::Command::GenMip; @@ -2080,6 +2116,33 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb) f->glBufferSubData(cmd.args.bufferSubData.target, cmd.args.bufferSubData.offset, cmd.args.bufferSubData.size, cmd.args.bufferSubData.data); break; + case QGles2CommandBuffer::Command::GetBufferSubData: + { + QRhiBufferReadbackResult *result = cmd.args.getBufferSubData.result; + f->glBindBuffer(cmd.args.getBufferSubData.target, cmd.args.getBufferSubData.buffer); + if (caps.gles) { + if (caps.properMapBuffer) { + void *p = f->glMapBufferRange(cmd.args.getBufferSubData.target, + cmd.args.getBufferSubData.offset, + cmd.args.getBufferSubData.size, + GL_MAP_READ_BIT); + if (p) { + result->data.resize(cmd.args.getBufferSubData.size); + memcpy(result->data.data(), p, size_t(cmd.args.getBufferSubData.size)); + f->glUnmapBuffer(cmd.args.getBufferSubData.target); + } + } + } else { + result->data.resize(cmd.args.getBufferSubData.size); + f->glGetBufferSubData(cmd.args.getBufferSubData.target, + cmd.args.getBufferSubData.offset, + cmd.args.getBufferSubData.size, + result->data.data()); + } + if (result->completed) + result->completed(); + } + break; case QGles2CommandBuffer::Command::CopyTex: { GLuint fbo; @@ -2101,23 +2164,31 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb) QRhiReadbackResult *result = cmd.args.readPixels.result; GLuint tex = cmd.args.readPixels.texture; GLuint fbo = 0; + int mipLevel = 0; if (tex) { result->pixelSize = QSize(cmd.args.readPixels.w, cmd.args.readPixels.h); result->format = cmd.args.readPixels.format; - f->glGenFramebuffers(1, &fbo); - f->glBindFramebuffer(GL_FRAMEBUFFER, fbo); - f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - cmd.args.readPixels.readTarget, cmd.args.readPixels.texture, cmd.args.readPixels.level); + mipLevel = cmd.args.readPixels.level; + if (mipLevel == 0 || caps.nonBaseLevelFramebufferTexture) { + f->glGenFramebuffers(1, &fbo); + f->glBindFramebuffer(GL_FRAMEBUFFER, fbo); + f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + cmd.args.readPixels.readTarget, cmd.args.readPixels.texture, mipLevel); + } } else { result->pixelSize = currentSwapChain->pixelSize; result->format = QRhiTexture::RGBA8; // readPixels handles multisample resolving implicitly } result->data.resize(result->pixelSize.width() * result->pixelSize.height() * 4); - // With GLES (2.0?) GL_RGBA is the only mandated readback format, so stick with it. - f->glReadPixels(0, 0, result->pixelSize.width(), result->pixelSize.height(), - GL_RGBA, GL_UNSIGNED_BYTE, - result->data.data()); + if (mipLevel == 0 || caps.nonBaseLevelFramebufferTexture) { + // With GLES (2.0?) GL_RGBA is the only mandated readback format, so stick with it. + f->glReadPixels(0, 0, result->pixelSize.width(), result->pixelSize.height(), + GL_RGBA, GL_UNSIGNED_BYTE, + result->data.data()); + } else { + result->data.fill('\0'); + } if (fbo) { f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject()); f->glDeleteFramebuffers(1, &fbo); @@ -3620,6 +3691,9 @@ bool QGles2GraphicsPipeline::build() if (!rhiD->ensureContext()) return false; + if (!rhiD->sanityCheckGraphicsPipeline(this)) + return false; + drawMode = toGlTopology(m_topology); program = rhiD->f->glCreateProgram(); diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h index 646836a699..8814d9c19d 100644 --- a/src/gui/rhi/qrhigles2_p_p.h +++ b/src/gui/rhi/qrhigles2_p_p.h @@ -312,8 +312,8 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer BindShaderResources, BindFramebuffer, Clear, - BufferData, BufferSubData, + GetBufferSubData, CopyTex, ReadPixels, SubImage, @@ -402,6 +402,13 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer const void *data; // must come from retainData() } bufferSubData; struct { + QRhiBufferReadbackResult *result; + GLenum target; + GLuint buffer; + int offset; + int size; + } getBufferSubData; + struct { GLenum srcFaceTarget; GLuint srcTexture; int srcLevel; @@ -744,7 +751,10 @@ public: rgba8Format(false), instancing(false), baseVertex(false), - compute(false) + compute(false), + textureCompareMode(false), + properMapBuffer(false), + nonBaseLevelFramebufferTexture(false) { } int ctxMajor; int ctxMinor; @@ -775,6 +785,8 @@ public: uint baseVertex : 1; uint compute : 1; uint textureCompareMode : 1; + uint properMapBuffer : 1; + uint nonBaseLevelFramebufferTexture : 1; } caps; QGles2SwapChain *currentSwapChain = nullptr; QVector<GLint> supportedCompressedFormats; diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm index f9d9cdc01a..5f14d917b8 100644 --- a/src/gui/rhi/qrhimetal.mm +++ b/src/gui/rhi/qrhimetal.mm @@ -205,7 +205,7 @@ struct QRhiMetalData QMetalCommandBuffer cbWrapper; } ofr; - struct ActiveReadback { + struct TextureReadback { int activeFrameSlot = -1; QRhiReadbackDescription desc; QRhiReadbackResult *result; @@ -214,7 +214,7 @@ struct QRhiMetalData QSize pixelSize; QRhiTexture::Format format; }; - QVector<ActiveReadback> activeReadbacks; + QVector<TextureReadback> activeTextureReadbacks; API_AVAILABLE(macos(10.13), ios(11.0)) MTLCaptureManager *captureMgr; API_AVAILABLE(macos(10.13), ios(11.0)) id<MTLCaptureScope> captureScope = nil; @@ -225,14 +225,14 @@ struct QRhiMetalData }; Q_DECLARE_TYPEINFO(QRhiMetalData::DeferredReleaseEntry, Q_MOVABLE_TYPE); -Q_DECLARE_TYPEINFO(QRhiMetalData::ActiveReadback, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(QRhiMetalData::TextureReadback, Q_MOVABLE_TYPE); struct QMetalBufferData { bool managed; bool slotted; id<MTLBuffer> buf[QMTL_FRAMES_IN_FLIGHT]; - QVector<QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate> pendingUpdates[QMTL_FRAMES_IN_FLIGHT]; + QVarLengthArray<QRhiResourceUpdateBatchPrivate::BufferOp, 16> pendingUpdates[QMTL_FRAMES_IN_FLIGHT]; }; struct QMetalRenderBufferData @@ -367,6 +367,11 @@ bool QRhiMetal::create(QRhi::Flags flags) else d->dev = MTLCreateSystemDefaultDevice(); + if (!d->dev) { + qWarning("No MTLDevice"); + return false; + } + qCDebug(QRHI_LOG_INFO, "Metal device: %s", qPrintable(QString::fromNSString([d->dev name]))); if (importedCmdQueue) @@ -552,6 +557,10 @@ bool QRhiMetal::isFeatureSupported(QRhi::Feature feature) const return true; case QRhi::TriangleFanTopology: return false; + case QRhi::ReadBackNonUniformBuffer: + return true; + case QRhi::ReadBackNonBaseMipLevel: + return true; default: Q_UNREACHABLE(); return false; @@ -1541,21 +1550,33 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates); QRhiProfilerPrivate *rhiP = profilerPrivateOrNull(); - for (const QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate &u : ud->dynamicBufferUpdates) { - QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, u.buf); - Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic); - for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) - bufD->d->pendingUpdates[i].append(u); - } - - // Due to the Metal API the handling of static and dynamic buffers is - // basically the same. So go through the same pendingUpdates machinery. - for (const QRhiResourceUpdateBatchPrivate::StaticBufferUpload &u : ud->staticBufferUploads) { - QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, u.buf); - Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic); - Q_ASSERT(u.offset + u.data.size() <= bufD->m_size); - for (int i = 0, ie = bufD->d->slotted ? QMTL_FRAMES_IN_FLIGHT : 1; i != ie; ++i) - bufD->d->pendingUpdates[i].append({ u.buf, u.offset, u.data.size(), u.data.constData() }); + for (const QRhiResourceUpdateBatchPrivate::BufferOp &u : ud->bufferOps) { + if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::DynamicUpdate) { + QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, u.buf); + Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic); + for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) + bufD->d->pendingUpdates[i].append(u); + } else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::StaticUpload) { + // Due to the Metal API the handling of static and dynamic buffers is + // basically the same. So go through the same pendingUpdates machinery. + QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, u.buf); + Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic); + Q_ASSERT(u.offset + u.data.size() <= bufD->m_size); + for (int i = 0, ie = bufD->d->slotted ? QMTL_FRAMES_IN_FLIGHT : 1; i != ie; ++i) + bufD->d->pendingUpdates[i].append( + QRhiResourceUpdateBatchPrivate::BufferOp::dynamicUpdate(u.buf, u.offset, u.data.size(), u.data.constData())); + } else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::Read) { + QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, u.buf); + executeBufferHostWritesForCurrentFrame(bufD); + const int idx = bufD->d->slotted ? currentFrameSlot : 0; + char *p = reinterpret_cast<char *>([bufD->d->buf[idx] contents]); + if (p) { + u.result->data.resize(u.readSize); + memcpy(u.result->data.data(), p + u.offset, size_t(u.readSize)); + } + if (u.result->completed) + u.result->completed(); + } } id<MTLBlitCommandEncoder> blitEnc = nil; @@ -1569,11 +1590,11 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) { if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) { - QMetalTexture *utexD = QRHI_RES(QMetalTexture, u.upload.tex); + QMetalTexture *utexD = QRHI_RES(QMetalTexture, u.dst); qsizetype stagingSize = 0; for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) { for (int level = 0; level < QRhi::MAX_LEVELS; ++level) { - for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.upload.subresDesc[layer][level])) + for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level])) stagingSize += subresUploadByteSize(subresDesc); } } @@ -1588,7 +1609,7 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate qsizetype curOfs = 0; for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) { for (int level = 0; level < QRhi::MAX_LEVELS; ++level) { - for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.upload.subresDesc[layer][level])) + for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level])) enqueueSubresUpload(utexD, mp, blitEnc, layer, level, subresDesc, &curOfs); } } @@ -1603,32 +1624,33 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate d->releaseQueue.append(e); QRHI_PROF_F(releaseTextureStagingArea(utexD, currentFrameSlot)); } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) { - Q_ASSERT(u.copy.src && u.copy.dst); - QMetalTexture *srcD = QRHI_RES(QMetalTexture, u.copy.src); - QMetalTexture *dstD = QRHI_RES(QMetalTexture, u.copy.dst); - const QPoint dp = u.copy.desc.destinationTopLeft(); - const QSize size = u.copy.desc.pixelSize().isEmpty() ? srcD->m_pixelSize : u.copy.desc.pixelSize(); - const QPoint sp = u.copy.desc.sourceTopLeft(); + Q_ASSERT(u.src && u.dst); + QMetalTexture *srcD = QRHI_RES(QMetalTexture, u.src); + QMetalTexture *dstD = QRHI_RES(QMetalTexture, u.dst); + const QPoint dp = u.desc.destinationTopLeft(); + const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize); + const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize(); + const QPoint sp = u.desc.sourceTopLeft(); ensureBlit(); [blitEnc copyFromTexture: srcD->d->tex - sourceSlice: NSUInteger(u.copy.desc.sourceLayer()) - sourceLevel: NSUInteger(u.copy.desc.sourceLevel()) + sourceSlice: NSUInteger(u.desc.sourceLayer()) + sourceLevel: NSUInteger(u.desc.sourceLevel()) sourceOrigin: MTLOriginMake(NSUInteger(sp.x()), NSUInteger(sp.y()), 0) - sourceSize: MTLSizeMake(NSUInteger(size.width()), NSUInteger(size.height()), 1) + sourceSize: MTLSizeMake(NSUInteger(copySize.width()), NSUInteger(copySize.height()), 1) toTexture: dstD->d->tex - destinationSlice: NSUInteger(u.copy.desc.destinationLayer()) - destinationLevel: NSUInteger(u.copy.desc.destinationLevel()) + destinationSlice: NSUInteger(u.desc.destinationLayer()) + destinationLevel: NSUInteger(u.desc.destinationLevel()) destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), 0)]; srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot; } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) { - QRhiMetalData::ActiveReadback aRb; - aRb.activeFrameSlot = currentFrameSlot; - aRb.desc = u.read.rb; - aRb.result = u.read.result; + QRhiMetalData::TextureReadback readback; + readback.activeFrameSlot = currentFrameSlot; + readback.desc = u.rb; + readback.result = u.result; - QMetalTexture *texD = QRHI_RES(QMetalTexture, u.read.rb.texture()); + QMetalTexture *texD = QRHI_RES(QMetalTexture, u.rb.texture()); QMetalSwapChain *swapChainD = nullptr; id<MTLTexture> src; QSize srcSize; @@ -1637,17 +1659,16 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate qWarning("Multisample texture cannot be read back"); continue; } - aRb.pixelSize = u.read.rb.level() > 0 ? q->sizeForMipLevel(u.read.rb.level(), texD->m_pixelSize) - : texD->m_pixelSize; - aRb.format = texD->m_format; + readback.pixelSize = q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize); + readback.format = texD->m_format; src = texD->d->tex; - srcSize = texD->m_pixelSize; + srcSize = readback.pixelSize; texD->lastActiveFrameSlot = currentFrameSlot; } else { Q_ASSERT(currentSwapChain); swapChainD = QRHI_RES(QMetalSwapChain, currentSwapChain); - aRb.pixelSize = swapChainD->pixelSize; - aRb.format = swapChainD->d->rhiColorFormat; + readback.pixelSize = swapChainD->pixelSize; + readback.format = swapChainD->d->rhiColorFormat; // Multisample swapchains need nothing special since resolving // happens when ending a renderpass. const QMetalRenderTargetData::ColorAtt &colorAtt(swapChainD->rtWrapper.d->fb.colorAtt[0]); @@ -1656,28 +1677,28 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate } quint32 bpl = 0; - textureFormatInfo(aRb.format, aRb.pixelSize, &bpl, &aRb.bufSize); - aRb.buf = [d->dev newBufferWithLength: aRb.bufSize options: MTLResourceStorageModeShared]; + textureFormatInfo(readback.format, readback.pixelSize, &bpl, &readback.bufSize); + readback.buf = [d->dev newBufferWithLength: readback.bufSize options: MTLResourceStorageModeShared]; - QRHI_PROF_F(newReadbackBuffer(qint64(qintptr(aRb.buf)), + QRHI_PROF_F(newReadbackBuffer(qint64(qintptr(readback.buf)), texD ? static_cast<QRhiResource *>(texD) : static_cast<QRhiResource *>(swapChainD), - aRb.bufSize)); + readback.bufSize)); ensureBlit(); [blitEnc copyFromTexture: src - sourceSlice: NSUInteger(u.read.rb.layer()) - sourceLevel: NSUInteger(u.read.rb.level()) + sourceSlice: NSUInteger(u.rb.layer()) + sourceLevel: NSUInteger(u.rb.level()) sourceOrigin: MTLOriginMake(0, 0, 0) sourceSize: MTLSizeMake(NSUInteger(srcSize.width()), NSUInteger(srcSize.height()), 1) - toBuffer: aRb.buf + toBuffer: readback.buf destinationOffset: 0 destinationBytesPerRow: bpl destinationBytesPerImage: 0 options: MTLBlitOptionNone]; - d->activeReadbacks.append(aRb); - } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::MipGen) { - QMetalTexture *utexD = QRHI_RES(QMetalTexture, u.mipgen.tex); + d->activeTextureReadbacks.append(readback); + } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::GenMips) { + QMetalTexture *utexD = QRHI_RES(QMetalTexture, u.dst); ensureBlit(); [blitEnc generateMipmapsForTexture: utexD->d->tex]; utexD->lastActiveFrameSlot = currentFrameSlot; @@ -1697,14 +1718,13 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate void QRhiMetal::executeBufferHostWritesForCurrentFrame(QMetalBuffer *bufD) { const int idx = bufD->d->slotted ? currentFrameSlot : 0; - QVector<QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate> &updates(bufD->d->pendingUpdates[idx]); - if (updates.isEmpty()) + if (bufD->d->pendingUpdates[idx].isEmpty()) return; void *p = [bufD->d->buf[idx] contents]; int changeBegin = -1; int changeEnd = -1; - for (const QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate &u : updates) { + for (const QRhiResourceUpdateBatchPrivate::BufferOp &u : qAsConst(bufD->d->pendingUpdates[idx])) { Q_ASSERT(bufD == QRHI_RES(QMetalBuffer, u.buf)); memcpy(static_cast<char *>(p) + u.offset, u.data.constData(), size_t(u.data.size())); if (changeBegin == -1 || u.offset < changeBegin) @@ -1715,7 +1735,7 @@ void QRhiMetal::executeBufferHostWritesForCurrentFrame(QMetalBuffer *bufD) if (changeBegin >= 0 && bufD->d->managed) [bufD->d->buf[idx] didModifyRange: NSMakeRange(NSUInteger(changeBegin), NSUInteger(changeEnd - changeBegin))]; - updates.clear(); + bufD->d->pendingUpdates[idx].clear(); } void QRhiMetal::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) @@ -1951,22 +1971,22 @@ void QRhiMetal::finishActiveReadbacks(bool forced) QVarLengthArray<std::function<void()>, 4> completedCallbacks; QRhiProfilerPrivate *rhiP = profilerPrivateOrNull(); - for (int i = d->activeReadbacks.count() - 1; i >= 0; --i) { - const QRhiMetalData::ActiveReadback &aRb(d->activeReadbacks[i]); - if (forced || currentFrameSlot == aRb.activeFrameSlot || aRb.activeFrameSlot < 0) { - aRb.result->format = aRb.format; - aRb.result->pixelSize = aRb.pixelSize; - aRb.result->data.resize(int(aRb.bufSize)); - void *p = [aRb.buf contents]; - memcpy(aRb.result->data.data(), p, aRb.bufSize); - [aRb.buf release]; + for (int i = d->activeTextureReadbacks.count() - 1; i >= 0; --i) { + const QRhiMetalData::TextureReadback &readback(d->activeTextureReadbacks[i]); + if (forced || currentFrameSlot == readback.activeFrameSlot || readback.activeFrameSlot < 0) { + readback.result->format = readback.format; + readback.result->pixelSize = readback.pixelSize; + readback.result->data.resize(int(readback.bufSize)); + void *p = [readback.buf contents]; + memcpy(readback.result->data.data(), p, readback.bufSize); + [readback.buf release]; - QRHI_PROF_F(releaseReadbackBuffer(qint64(qintptr(aRb.buf)))); + QRHI_PROF_F(releaseReadbackBuffer(qint64(qintptr(readback.buf)))); - if (aRb.result->completed) - completedCallbacks.append(aRb.result->completed); + if (readback.result->completed) + completedCallbacks.append(readback.result->completed); - d->activeReadbacks.removeAt(i); + d->activeTextureReadbacks.removeAt(i); } } @@ -2042,7 +2062,6 @@ bool QMetalBuffer::build() for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) { if (i == 0 || d->slotted) { d->buf[i] = [rhiD->d->dev newBufferWithLength: roundedSize options: opts]; - d->pendingUpdates[i].reserve(16); if (!m_objectName.isEmpty()) { if (!d->slotted) { d->buf[i].label = [NSString stringWithUTF8String: m_objectName.constData()]; @@ -3120,6 +3139,8 @@ bool QMetalGraphicsPipeline::build() release(); QRHI_RES_RHI(QRhiMetal); + if (!rhiD->sanityCheckGraphicsPipeline(this)) + return false; // same binding space for vertex and constant buffers - work it around const int firstVertexBinding = QRHI_RES(QMetalShaderResourceBindings, m_shaderResourceBindings)->maxBinding + 1; diff --git a/src/gui/rhi/qrhinull.cpp b/src/gui/rhi/qrhinull.cpp index 487afd3ed1..fe606f971f 100644 --- a/src/gui/rhi/qrhinull.cpp +++ b/src/gui/rhi/qrhinull.cpp @@ -36,6 +36,7 @@ #include "qrhinull_p_p.h" #include <qmath.h> +#include <QPainter> QT_BEGIN_NAMESPACE @@ -385,27 +386,125 @@ QRhi::FrameOpResult QRhiNull::finish() return QRhi::FrameOpSuccess; } +void QRhiNull::simulateTextureUpload(const QRhiResourceUpdateBatchPrivate::TextureOp &u) +{ + QNullTexture *texD = QRHI_RES(QNullTexture, u.dst); + for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) { + for (int level = 0; level < QRhi::MAX_LEVELS; ++level) { + for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level])) { + if (!subresDesc.image().isNull()) { + const QImage src = subresDesc.image(); + QPainter painter(&texD->image[layer][level]); + const QSize srcSize = subresDesc.sourceSize().isEmpty() + ? src.size() : subresDesc.sourceSize(); + painter.setCompositionMode(QPainter::CompositionMode_Source); + painter.drawImage(subresDesc.destinationTopLeft(), src, + QRect(subresDesc.sourceTopLeft(), srcSize)); + } else if (!subresDesc.data().isEmpty()) { + const QSize subresSize = q->sizeForMipLevel(level, texD->pixelSize()); + int w = subresSize.width(); + int h = subresSize.height(); + if (!subresDesc.sourceSize().isEmpty()) { + w = subresDesc.sourceSize().width(); + h = subresDesc.sourceSize().height(); + } + // sourceTopLeft is not supported on this path as per QRhi docs + const char *src = subresDesc.data().constData(); + const int srcBpl = w * 4; + const QPoint dstOffset = subresDesc.destinationTopLeft(); + uchar *dst = texD->image[layer][level].bits(); + const int dstBpl = texD->image[layer][level].bytesPerLine(); + for (int y = 0; y < h; ++y) { + memcpy(dst + dstOffset.x() * 4 + (y + dstOffset.y()) * dstBpl, + src + y * srcBpl, + size_t(srcBpl)); + } + } + } + } + } +} + +void QRhiNull::simulateTextureCopy(const QRhiResourceUpdateBatchPrivate::TextureOp &u) +{ + QNullTexture *srcD = QRHI_RES(QNullTexture, u.src); + QNullTexture *dstD = QRHI_RES(QNullTexture, u.dst); + const QImage &srcImage(srcD->image[u.desc.sourceLayer()][u.desc.sourceLevel()]); + QImage &dstImage(dstD->image[u.desc.destinationLayer()][u.desc.destinationLevel()]); + const QPoint dstPos = u.desc.destinationTopLeft(); + const QSize size = u.desc.pixelSize().isEmpty() ? srcD->pixelSize() : u.desc.pixelSize(); + const QPoint srcPos = u.desc.sourceTopLeft(); + + QPainter painter(&dstImage); + painter.setCompositionMode(QPainter::CompositionMode_Source); + painter.drawImage(QRect(dstPos, size), srcImage, QRect(srcPos, size)); +} + +void QRhiNull::simulateTextureGenMips(const QRhiResourceUpdateBatchPrivate::TextureOp &u) +{ + QNullTexture *texD = QRHI_RES(QNullTexture, u.dst); + const QSize baseSize = texD->pixelSize(); + const int levelCount = q->mipLevelsForSize(baseSize); + for (int level = 1; level < levelCount; ++level) + texD->image[0][level] = texD->image[0][0].scaled(q->sizeForMipLevel(level, baseSize)); +} + void QRhiNull::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) { Q_UNUSED(cb); QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates); + for (const QRhiResourceUpdateBatchPrivate::BufferOp &u : ud->bufferOps) { + if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::DynamicUpdate + || u.type == QRhiResourceUpdateBatchPrivate::BufferOp::StaticUpload) + { + QNullBuffer *bufD = QRHI_RES(QNullBuffer, u.buf); + memcpy(bufD->data.data() + u.offset, u.data.constData(), size_t(u.data.size())); + } else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::Read) { + QRhiBufferReadbackResult *result = u.result; + result->data.resize(u.readSize); + QNullBuffer *bufD = QRHI_RES(QNullBuffer, u.buf); + memcpy(result->data.data(), bufD->data.constData() + u.offset, size_t(u.readSize)); + if (result->completed) + result->completed(); + } + } for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) { - if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) { - QRhiReadbackResult *result = u.read.result; - QRhiTexture *tex = u.read.rb.texture(); - if (tex) { - result->format = tex->format(); - result->pixelSize = q->sizeForMipLevel(u.read.rb.level(), tex->pixelSize()); + if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) { + if (u.dst->format() == QRhiTexture::RGBA8) + simulateTextureUpload(u); + } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) { + if (u.src->format() == QRhiTexture::RGBA8 && u.dst->format() == QRhiTexture::RGBA8) + simulateTextureCopy(u); + } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) { + QRhiReadbackResult *result = u.result; + QNullTexture *texD = QRHI_RES(QNullTexture, u.rb.texture()); + if (texD) { + result->format = texD->format(); + result->pixelSize = q->sizeForMipLevel(u.rb.level(), texD->pixelSize()); } else { Q_ASSERT(currentSwapChain); result->format = QRhiTexture::RGBA8; result->pixelSize = currentSwapChain->currentPixelSize(); } + quint32 bytesPerLine = 0; quint32 byteSize = 0; - textureFormatInfo(result->format, result->pixelSize, nullptr, &byteSize); - result->data.fill(0, byteSize); + textureFormatInfo(result->format, result->pixelSize, &bytesPerLine, &byteSize); + if (texD && texD->format() == QRhiTexture::RGBA8) { + result->data.resize(int(byteSize)); + const QImage &src(texD->image[u.rb.layer()][u.rb.level()]); + char *dst = result->data.data(); + for (int y = 0, h = src.height(); y < h; ++y) { + memcpy(dst, src.constScanLine(y), bytesPerLine); + dst += bytesPerLine; + } + } else { + result->data.fill(0, int(byteSize)); + } if (result->completed) result->completed(); + } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::GenMips) { + if (u.dst->format() == QRhiTexture::RGBA8) + simulateTextureGenMips(u); } } ud->free(); @@ -454,14 +553,18 @@ QNullBuffer::~QNullBuffer() void QNullBuffer::release() { + data.clear(); + QRHI_PROF; QRHI_PROF_F(releaseBuffer(this)); } bool QNullBuffer::build() { + data.fill('\0', m_size); + QRHI_PROF; - QRHI_PROF_F(newBuffer(this, m_size, 1, 0)); + QRHI_PROF_F(newBuffer(this, uint(m_size), 1, 0)); return true; } @@ -513,22 +616,36 @@ void QNullTexture::release() bool QNullTexture::build() { + QRHI_RES_RHI(QRhiNull); const bool isCube = m_flags.testFlag(CubeMap); const bool hasMipMaps = m_flags.testFlag(MipMapped); QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize; - const int mipLevelCount = hasMipMaps ? qCeil(log2(qMax(size.width(), size.height()))) + 1 : 1; + const int mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1; + const int layerCount = isCube ? 6 : 1; + + if (m_format == RGBA8) { + for (int layer = 0; layer < layerCount; ++layer) { + for (int level = 0; level < mipLevelCount; ++level) { + image[layer][level] = QImage(rhiD->q->sizeForMipLevel(level, size), + QImage::Format_RGBA8888_Premultiplied); + image[layer][level].fill(Qt::yellow); + } + } + } + QRHI_PROF; - QRHI_PROF_F(newTexture(this, true, mipLevelCount, isCube ? 6 : 1, 1)); + QRHI_PROF_F(newTexture(this, true, mipLevelCount, layerCount, 1)); return true; } bool QNullTexture::buildFrom(const QRhiNativeHandles *src) { Q_UNUSED(src); + QRHI_RES_RHI(QRhiNull); const bool isCube = m_flags.testFlag(CubeMap); const bool hasMipMaps = m_flags.testFlag(MipMapped); QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize; - const int mipLevelCount = hasMipMaps ? qCeil(log2(qMax(size.width(), size.height()))) + 1 : 1; + const int mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1; QRHI_PROF; QRHI_PROF_F(newTexture(this, false, mipLevelCount, isCube ? 6 : 1, 1)); return true; @@ -690,6 +807,10 @@ void QNullGraphicsPipeline::release() bool QNullGraphicsPipeline::build() { + QRHI_RES_RHI(QRhiNull); + if (!rhiD->sanityCheckGraphicsPipeline(this)) + return false; + return true; } diff --git a/src/gui/rhi/qrhinull_p_p.h b/src/gui/rhi/qrhinull_p_p.h index ee301d247b..ce517bfa63 100644 --- a/src/gui/rhi/qrhinull_p_p.h +++ b/src/gui/rhi/qrhinull_p_p.h @@ -59,6 +59,8 @@ struct QNullBuffer : public QRhiBuffer ~QNullBuffer(); void release() override; bool build() override; + + QByteArray data; }; struct QNullRenderBuffer : public QRhiRenderBuffer @@ -82,6 +84,7 @@ struct QNullTexture : public QRhiTexture const QRhiNativeHandles *nativeHandles() override; QRhiNullTextureNativeHandles nativeHandlesStruct; + QImage image[QRhi::MAX_LAYERS][QRhi::MAX_LEVELS]; }; struct QNullSampler : public QRhiSampler @@ -286,6 +289,10 @@ public: void releaseCachedResources() override; bool isDeviceLost() const override; + void simulateTextureUpload(const QRhiResourceUpdateBatchPrivate::TextureOp &u); + void simulateTextureCopy(const QRhiResourceUpdateBatchPrivate::TextureOp &u); + void simulateTextureGenMips(const QRhiResourceUpdateBatchPrivate::TextureOp &u); + QRhiNullNativeHandles nativeHandlesStruct; QRhiSwapChain *currentSwapChain = nullptr; QNullCommandBuffer offscreenCommandBuffer; diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp index d07777d63a..2d69abb36b 100644 --- a/src/gui/rhi/qrhivulkan.cpp +++ b/src/gui/rhi/qrhivulkan.cpp @@ -363,6 +363,11 @@ bool QRhiVulkan::create(QRhi::Flags flags) Q_UNUSED(flags); Q_ASSERT(inst); + if (!inst->isValid()) { + qWarning("Vulkan instance is not valid"); + return false; + } + globalVulkanInstance = inst; // assume this will not change during the lifetime of the entire application f = inst->functions(); @@ -2649,100 +2654,164 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates); QRhiProfilerPrivate *rhiP = profilerPrivateOrNull(); - for (const QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate &u : ud->dynamicBufferUpdates) { - QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf); - Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic); - for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) - bufD->pendingDynamicUpdates[i].append(u); - } + for (const QRhiResourceUpdateBatchPrivate::BufferOp &u : ud->bufferOps) { + if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::DynamicUpdate) { + QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf); + Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic); + for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) + bufD->pendingDynamicUpdates[i].append(u); + } else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::StaticUpload) { + QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf); + Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic); + Q_ASSERT(u.offset + u.data.size() <= bufD->m_size); + + if (!bufD->stagingBuffers[currentFrameSlot]) { + VkBufferCreateInfo bufferInfo; + memset(&bufferInfo, 0, sizeof(bufferInfo)); + bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + // must cover the entire buffer - this way multiple, partial updates per frame + // are supported even when the staging buffer is reused (Static) + bufferInfo.size = VkDeviceSize(bufD->m_size); + bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + + VmaAllocationCreateInfo allocInfo; + memset(&allocInfo, 0, sizeof(allocInfo)); + allocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY; + + VmaAllocation allocation; + VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo, + &bufD->stagingBuffers[currentFrameSlot], &allocation, nullptr); + if (err == VK_SUCCESS) { + bufD->stagingAllocations[currentFrameSlot] = allocation; + QRHI_PROF_F(newBufferStagingArea(bufD, currentFrameSlot, quint32(bufD->m_size))); + } else { + qWarning("Failed to create staging buffer of size %d: %d", bufD->m_size, err); + continue; + } + } - for (const QRhiResourceUpdateBatchPrivate::StaticBufferUpload &u : ud->staticBufferUploads) { - QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf); - Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic); - Q_ASSERT(u.offset + u.data.size() <= bufD->m_size); + void *p = nullptr; + VmaAllocation a = toVmaAllocation(bufD->stagingAllocations[currentFrameSlot]); + VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p); + if (err != VK_SUCCESS) { + qWarning("Failed to map buffer: %d", err); + continue; + } + memcpy(static_cast<uchar *>(p) + u.offset, u.data.constData(), size_t(u.data.size())); + vmaUnmapMemory(toVmaAllocator(allocator), a); + vmaFlushAllocation(toVmaAllocator(allocator), a, VkDeviceSize(u.offset), VkDeviceSize(u.data.size())); - if (!bufD->stagingBuffers[currentFrameSlot]) { - VkBufferCreateInfo bufferInfo; - memset(&bufferInfo, 0, sizeof(bufferInfo)); - bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - // must cover the entire buffer - this way multiple, partial updates per frame - // are supported even when the staging buffer is reused (Static) - bufferInfo.size = VkDeviceSize(bufD->m_size); - bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + trackedBufferBarrier(cbD, bufD, 0, + VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); - VmaAllocationCreateInfo allocInfo; - memset(&allocInfo, 0, sizeof(allocInfo)); - allocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY; + VkBufferCopy copyInfo; + memset(©Info, 0, sizeof(copyInfo)); + copyInfo.srcOffset = VkDeviceSize(u.offset); + copyInfo.dstOffset = VkDeviceSize(u.offset); + copyInfo.size = VkDeviceSize(u.data.size()); - VmaAllocation allocation; - VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo, - &bufD->stagingBuffers[currentFrameSlot], &allocation, nullptr); - if (err == VK_SUCCESS) { - bufD->stagingAllocations[currentFrameSlot] = allocation; - QRHI_PROF_F(newBufferStagingArea(bufD, currentFrameSlot, quint32(bufD->m_size))); - } else { - qWarning("Failed to create staging buffer of size %d: %d", bufD->m_size, err); - continue; - } - } + QVkCommandBuffer::Command cmd; + cmd.cmd = QVkCommandBuffer::Command::CopyBuffer; + cmd.args.copyBuffer.src = bufD->stagingBuffers[currentFrameSlot]; + cmd.args.copyBuffer.dst = bufD->buffers[0]; + cmd.args.copyBuffer.desc = copyInfo; + cbD->commands.append(cmd); - void *p = nullptr; - VmaAllocation a = toVmaAllocation(bufD->stagingAllocations[currentFrameSlot]); - VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p); - if (err != VK_SUCCESS) { - qWarning("Failed to map buffer: %d", err); - continue; - } - memcpy(static_cast<uchar *>(p) + u.offset, u.data.constData(), size_t(u.data.size())); - vmaUnmapMemory(toVmaAllocator(allocator), a); - vmaFlushAllocation(toVmaAllocator(allocator), a, VkDeviceSize(u.offset), VkDeviceSize(u.data.size())); + // Where's the barrier for read-after-write? (assuming the common case + // of binding this buffer as vertex/index, or, less likely, as uniform + // buffer, in a renderpass later on) That is handled by the pass + // resource tracking: the appropriate pipeline barrier will be + // generated and recorded right before the renderpass, that binds this + // buffer in one of its commands, gets its BeginRenderPass recorded. - trackedBufferBarrier(cbD, bufD, 0, - VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); + bufD->lastActiveFrameSlot = currentFrameSlot; - VkBufferCopy copyInfo; - memset(©Info, 0, sizeof(copyInfo)); - copyInfo.srcOffset = VkDeviceSize(u.offset); - copyInfo.dstOffset = VkDeviceSize(u.offset); - copyInfo.size = VkDeviceSize(u.data.size()); + if (bufD->m_type == QRhiBuffer::Immutable) { + QRhiVulkan::DeferredReleaseEntry e; + e.type = QRhiVulkan::DeferredReleaseEntry::StagingBuffer; + e.lastActiveFrameSlot = currentFrameSlot; + e.stagingBuffer.stagingBuffer = bufD->stagingBuffers[currentFrameSlot]; + e.stagingBuffer.stagingAllocation = bufD->stagingAllocations[currentFrameSlot]; + bufD->stagingBuffers[currentFrameSlot] = VK_NULL_HANDLE; + bufD->stagingAllocations[currentFrameSlot] = nullptr; + releaseQueue.append(e); + QRHI_PROF_F(releaseBufferStagingArea(bufD, currentFrameSlot)); + } + } else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::Read) { + QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf); + if (bufD->m_type == QRhiBuffer::Dynamic) { + executeBufferHostWritesForCurrentFrame(bufD); + void *p = nullptr; + VmaAllocation a = toVmaAllocation(bufD->allocations[currentFrameSlot]); + VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p); + if (err == VK_SUCCESS) { + u.result->data.resize(u.readSize); + memcpy(u.result->data.data(), reinterpret_cast<char *>(p) + u.offset, size_t(u.readSize)); + vmaUnmapMemory(toVmaAllocator(allocator), a); + } + if (u.result->completed) + u.result->completed(); + } else { + // Non-Dynamic buffers may not be host visible, so have to + // create a readback buffer, enqueue a copy from + // bufD->buffers[0] to this buffer, and then once the command + // buffer completes, copy the data out of the host visible + // readback buffer. Quite similar to what we do for texture + // readbacks. + BufferReadback readback; + readback.activeFrameSlot = currentFrameSlot; + readback.result = u.result; + readback.byteSize = u.readSize; + + VkBufferCreateInfo bufferInfo; + memset(&bufferInfo, 0, sizeof(bufferInfo)); + bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferInfo.size = VkDeviceSize(readback.byteSize); + bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT; + + VmaAllocationCreateInfo allocInfo; + memset(&allocInfo, 0, sizeof(allocInfo)); + allocInfo.usage = VMA_MEMORY_USAGE_GPU_TO_CPU; + + VmaAllocation allocation; + VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo, &readback.stagingBuf, &allocation, nullptr); + if (err == VK_SUCCESS) { + readback.stagingAlloc = allocation; + QRHI_PROF_F(newReadbackBuffer(qint64(readback.stagingBuf), bufD, uint(readback.byteSize))); + } else { + qWarning("Failed to create readback buffer of size %u: %d", readback.byteSize, err); + continue; + } - QVkCommandBuffer::Command cmd; - cmd.cmd = QVkCommandBuffer::Command::CopyBuffer; - cmd.args.copyBuffer.src = bufD->stagingBuffers[currentFrameSlot]; - cmd.args.copyBuffer.dst = bufD->buffers[0]; - cmd.args.copyBuffer.desc = copyInfo; - cbD->commands.append(cmd); + trackedBufferBarrier(cbD, bufD, 0, VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); - // Where's the barrier for read-after-write? (assuming the common case - // of binding this buffer as vertex/index, or, less likely, as uniform - // buffer, in a renderpass later on) That is handled by the pass - // resource tracking: the appropriate pipeline barrier will be - // generated and recorded right before the renderpass, that binds this - // buffer in one of its commands, gets its BeginRenderPass recorded. + VkBufferCopy copyInfo; + memset(©Info, 0, sizeof(copyInfo)); + copyInfo.srcOffset = VkDeviceSize(u.offset); + copyInfo.size = VkDeviceSize(u.readSize); - bufD->lastActiveFrameSlot = currentFrameSlot; + QVkCommandBuffer::Command cmd; + cmd.cmd = QVkCommandBuffer::Command::CopyBuffer; + cmd.args.copyBuffer.src = bufD->buffers[0]; + cmd.args.copyBuffer.dst = readback.stagingBuf; + cmd.args.copyBuffer.desc = copyInfo; + cbD->commands.append(cmd); - if (bufD->m_type == QRhiBuffer::Immutable) { - QRhiVulkan::DeferredReleaseEntry e; - e.type = QRhiVulkan::DeferredReleaseEntry::StagingBuffer; - e.lastActiveFrameSlot = currentFrameSlot; - e.stagingBuffer.stagingBuffer = bufD->stagingBuffers[currentFrameSlot]; - e.stagingBuffer.stagingAllocation = bufD->stagingAllocations[currentFrameSlot]; - bufD->stagingBuffers[currentFrameSlot] = VK_NULL_HANDLE; - bufD->stagingAllocations[currentFrameSlot] = nullptr; - releaseQueue.append(e); - QRHI_PROF_F(releaseBufferStagingArea(bufD, currentFrameSlot)); + bufD->lastActiveFrameSlot = currentFrameSlot; + + activeBufferReadbacks.append(readback); + } } } for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) { if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) { - QVkTexture *utexD = QRHI_RES(QVkTexture, u.upload.tex); + QVkTexture *utexD = QRHI_RES(QVkTexture, u.dst); // batch into a single staging buffer and a single CopyBufferToImage with multiple copyInfos VkDeviceSize stagingSize = 0; for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) { for (int level = 0; level < QRhi::MAX_LEVELS; ++level) { - for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.upload.subresDesc[layer][level])) + for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level])) stagingSize += subresUploadByteSize(subresDesc); } } @@ -2780,7 +2849,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) { for (int level = 0; level < QRhi::MAX_LEVELS; ++level) { - const QVector<QRhiTextureSubresourceUploadDescription> &srd(u.upload.subresDesc[layer][level]); + const QVector<QRhiTextureSubresourceUploadDescription> &srd(u.subresDesc[layer][level]); if (srd.isEmpty()) continue; for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(srd)) { @@ -2821,36 +2890,37 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat utexD->lastActiveFrameSlot = currentFrameSlot; } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) { - Q_ASSERT(u.copy.src && u.copy.dst); - if (u.copy.src == u.copy.dst) { + Q_ASSERT(u.src && u.dst); + if (u.src == u.dst) { qWarning("Texture copy with matching source and destination is not supported"); continue; } - QVkTexture *srcD = QRHI_RES(QVkTexture, u.copy.src); - QVkTexture *dstD = QRHI_RES(QVkTexture, u.copy.dst); + QVkTexture *srcD = QRHI_RES(QVkTexture, u.src); + QVkTexture *dstD = QRHI_RES(QVkTexture, u.dst); VkImageCopy region; memset(®ion, 0, sizeof(region)); region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - region.srcSubresource.mipLevel = uint32_t(u.copy.desc.sourceLevel()); - region.srcSubresource.baseArrayLayer = uint32_t(u.copy.desc.sourceLayer()); + region.srcSubresource.mipLevel = uint32_t(u.desc.sourceLevel()); + region.srcSubresource.baseArrayLayer = uint32_t(u.desc.sourceLayer()); region.srcSubresource.layerCount = 1; - region.srcOffset.x = u.copy.desc.sourceTopLeft().x(); - region.srcOffset.y = u.copy.desc.sourceTopLeft().y(); + region.srcOffset.x = u.desc.sourceTopLeft().x(); + region.srcOffset.y = u.desc.sourceTopLeft().y(); region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - region.dstSubresource.mipLevel = uint32_t(u.copy.desc.destinationLevel()); - region.dstSubresource.baseArrayLayer = uint32_t(u.copy.desc.destinationLayer()); + region.dstSubresource.mipLevel = uint32_t(u.desc.destinationLevel()); + region.dstSubresource.baseArrayLayer = uint32_t(u.desc.destinationLayer()); region.dstSubresource.layerCount = 1; - region.dstOffset.x = u.copy.desc.destinationTopLeft().x(); - region.dstOffset.y = u.copy.desc.destinationTopLeft().y(); + region.dstOffset.x = u.desc.destinationTopLeft().x(); + region.dstOffset.y = u.desc.destinationTopLeft().y(); - const QSize size = u.copy.desc.pixelSize().isEmpty() ? srcD->m_pixelSize : u.copy.desc.pixelSize(); - region.extent.width = uint32_t(size.width()); - region.extent.height = uint32_t(size.height()); + const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize); + const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize(); + region.extent.width = uint32_t(copySize.width()); + region.extent.height = uint32_t(copySize.height()); region.extent.depth = 1; trackedImageBarrier(cbD, srcD, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, @@ -2869,21 +2939,20 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot; } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) { - ActiveReadback aRb; - aRb.activeFrameSlot = currentFrameSlot; - aRb.desc = u.read.rb; - aRb.result = u.read.result; + TextureReadback readback; + readback.activeFrameSlot = currentFrameSlot; + readback.desc = u.rb; + readback.result = u.result; - QVkTexture *texD = QRHI_RES(QVkTexture, u.read.rb.texture()); + QVkTexture *texD = QRHI_RES(QVkTexture, u.rb.texture()); QVkSwapChain *swapChainD = nullptr; if (texD) { if (texD->samples > VK_SAMPLE_COUNT_1_BIT) { qWarning("Multisample texture cannot be read back"); continue; } - aRb.pixelSize = u.read.rb.level() > 0 ? q->sizeForMipLevel(u.read.rb.level(), texD->m_pixelSize) - : texD->m_pixelSize; - aRb.format = texD->m_format; + readback.pixelSize = q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize); + readback.format = texD->m_format; texD->lastActiveFrameSlot = currentFrameSlot; } else { Q_ASSERT(currentSwapChain); @@ -2892,21 +2961,21 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat qWarning("Swapchain does not support readback"); continue; } - aRb.pixelSize = swapChainD->pixelSize; - aRb.format = colorTextureFormatFromVkFormat(swapChainD->colorFormat, nullptr); - if (aRb.format == QRhiTexture::UnknownFormat) + readback.pixelSize = swapChainD->pixelSize; + readback.format = colorTextureFormatFromVkFormat(swapChainD->colorFormat, nullptr); + if (readback.format == QRhiTexture::UnknownFormat) continue; // Multisample swapchains need nothing special since resolving // happens when ending a renderpass. } - textureFormatInfo(aRb.format, aRb.pixelSize, nullptr, &aRb.bufSize); + textureFormatInfo(readback.format, readback.pixelSize, nullptr, &readback.byteSize); - // Create a host visible buffer. + // Create a host visible readback buffer. VkBufferCreateInfo bufferInfo; memset(&bufferInfo, 0, sizeof(bufferInfo)); bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - bufferInfo.size = aRb.bufSize; + bufferInfo.size = readback.byteSize; bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT; VmaAllocationCreateInfo allocInfo; @@ -2914,14 +2983,14 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat allocInfo.usage = VMA_MEMORY_USAGE_GPU_TO_CPU; VmaAllocation allocation; - VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo, &aRb.buf, &allocation, nullptr); + VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo, &readback.stagingBuf, &allocation, nullptr); if (err == VK_SUCCESS) { - aRb.bufAlloc = allocation; - QRHI_PROF_F(newReadbackBuffer(qint64(aRb.buf), + readback.stagingAlloc = allocation; + QRHI_PROF_F(newReadbackBuffer(qint64(readback.stagingBuf), texD ? static_cast<QRhiResource *>(texD) : static_cast<QRhiResource *>(swapChainD), - aRb.bufSize)); + readback.byteSize)); } else { - qWarning("Failed to create readback buffer of size %u: %d", aRb.bufSize, err); + qWarning("Failed to create readback buffer of size %u: %d", readback.byteSize, err); continue; } @@ -2930,11 +2999,11 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat memset(©Desc, 0, sizeof(copyDesc)); copyDesc.bufferOffset = 0; copyDesc.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - copyDesc.imageSubresource.mipLevel = uint32_t(u.read.rb.level()); - copyDesc.imageSubresource.baseArrayLayer = uint32_t(u.read.rb.layer()); + copyDesc.imageSubresource.mipLevel = uint32_t(u.rb.level()); + copyDesc.imageSubresource.baseArrayLayer = uint32_t(u.rb.layer()); copyDesc.imageSubresource.layerCount = 1; - copyDesc.imageExtent.width = uint32_t(aRb.pixelSize.width()); - copyDesc.imageExtent.height = uint32_t(aRb.pixelSize.height()); + copyDesc.imageExtent.width = uint32_t(readback.pixelSize.width()); + copyDesc.imageExtent.height = uint32_t(readback.pixelSize.height()); copyDesc.imageExtent.depth = 1; if (texD) { @@ -2944,7 +3013,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat cmd.cmd = QVkCommandBuffer::Command::CopyImageToBuffer; cmd.args.copyImageToBuffer.src = texD->image; cmd.args.copyImageToBuffer.srcLayout = texD->usageState.layout; - cmd.args.copyImageToBuffer.dst = aRb.buf; + cmd.args.copyImageToBuffer.dst = readback.stagingBuf; cmd.args.copyImageToBuffer.desc = copyDesc; cbD->commands.append(cmd); } else { @@ -2969,14 +3038,14 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat cmd.cmd = QVkCommandBuffer::Command::CopyImageToBuffer; cmd.args.copyImageToBuffer.src = image; cmd.args.copyImageToBuffer.srcLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - cmd.args.copyImageToBuffer.dst = aRb.buf; + cmd.args.copyImageToBuffer.dst = readback.stagingBuf; cmd.args.copyImageToBuffer.desc = copyDesc; cbD->commands.append(cmd); } - activeReadbacks.append(aRb); - } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::MipGen) { - QVkTexture *utexD = QRHI_RES(QVkTexture, u.mipgen.tex); + activeTextureReadbacks.append(readback); + } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::GenMips) { + QVkTexture *utexD = QRHI_RES(QVkTexture, u.dst); Q_ASSERT(utexD->m_flags.testFlag(QRhiTexture::UsedWithGenerateMips)); int w = utexD->m_pixelSize.width(); int h = utexD->m_pixelSize.height(); @@ -2993,14 +3062,14 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat origLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, origAccess, VK_ACCESS_TRANSFER_READ_BIT, origStage, VK_PIPELINE_STAGE_TRANSFER_BIT, - u.mipgen.layer, 1, + u.layer, 1, level - 1, 1); } else { subresourceBarrier(cbD, utexD->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, - u.mipgen.layer, 1, + u.layer, 1, level - 1, 1); } @@ -3008,7 +3077,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat origLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, origAccess, VK_ACCESS_TRANSFER_WRITE_BIT, origStage, VK_PIPELINE_STAGE_TRANSFER_BIT, - u.mipgen.layer, 1, + u.layer, 1, level, 1); VkImageBlit region; @@ -3016,7 +3085,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.srcSubresource.mipLevel = uint32_t(level) - 1; - region.srcSubresource.baseArrayLayer = uint32_t(u.mipgen.layer); + region.srcSubresource.baseArrayLayer = uint32_t(u.layer); region.srcSubresource.layerCount = 1; region.srcOffsets[1].x = qMax(1, w); @@ -3025,7 +3094,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.dstSubresource.mipLevel = uint32_t(level); - region.dstSubresource.baseArrayLayer = uint32_t(u.mipgen.layer); + region.dstSubresource.baseArrayLayer = uint32_t(u.layer); region.dstSubresource.layerCount = 1; region.dstOffsets[1].x = qMax(1, w >> 1); @@ -3051,13 +3120,13 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, origLayout, VK_ACCESS_TRANSFER_READ_BIT, origAccess, VK_PIPELINE_STAGE_TRANSFER_BIT, origStage, - u.mipgen.layer, 1, + u.layer, 1, 0, int(utexD->mipLevelCount) - 1); subresourceBarrier(cbD, utexD->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, origLayout, VK_ACCESS_TRANSFER_WRITE_BIT, origAccess, VK_PIPELINE_STAGE_TRANSFER_BIT, origStage, - u.mipgen.layer, 1, + u.layer, 1, int(utexD->mipLevelCount) - 1, 1); } @@ -3070,8 +3139,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat void QRhiVulkan::executeBufferHostWritesForCurrentFrame(QVkBuffer *bufD) { - QVector<QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate> &updates(bufD->pendingDynamicUpdates[currentFrameSlot]); - if (updates.isEmpty()) + if (bufD->pendingDynamicUpdates[currentFrameSlot].isEmpty()) return; Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic); @@ -3087,7 +3155,7 @@ void QRhiVulkan::executeBufferHostWritesForCurrentFrame(QVkBuffer *bufD) } int changeBegin = -1; int changeEnd = -1; - for (const QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate &u : updates) { + for (const QRhiResourceUpdateBatchPrivate::BufferOp &u : qAsConst(bufD->pendingDynamicUpdates[currentFrameSlot])) { Q_ASSERT(bufD == QRHI_RES(QVkBuffer, u.buf)); memcpy(static_cast<char *>(p) + u.offset, u.data.constData(), size_t(u.data.size())); if (changeBegin == -1 || u.offset < changeBegin) @@ -3099,7 +3167,7 @@ void QRhiVulkan::executeBufferHostWritesForCurrentFrame(QVkBuffer *bufD) if (changeBegin >= 0) vmaFlushAllocation(toVmaAllocator(allocator), a, VkDeviceSize(changeBegin), VkDeviceSize(changeEnd - changeBegin)); - updates.clear(); + bufD->pendingDynamicUpdates[currentFrameSlot].clear(); } static void qrhivk_releaseBuffer(const QRhiVulkan::DeferredReleaseEntry &e, void *allocator) @@ -3193,29 +3261,53 @@ void QRhiVulkan::finishActiveReadbacks(bool forced) QVarLengthArray<std::function<void()>, 4> completedCallbacks; QRhiProfilerPrivate *rhiP = profilerPrivateOrNull(); - for (int i = activeReadbacks.count() - 1; i >= 0; --i) { - const QRhiVulkan::ActiveReadback &aRb(activeReadbacks[i]); - if (forced || currentFrameSlot == aRb.activeFrameSlot || aRb.activeFrameSlot < 0) { - aRb.result->format = aRb.format; - aRb.result->pixelSize = aRb.pixelSize; - aRb.result->data.resize(int(aRb.bufSize)); + for (int i = activeTextureReadbacks.count() - 1; i >= 0; --i) { + const QRhiVulkan::TextureReadback &readback(activeTextureReadbacks[i]); + if (forced || currentFrameSlot == readback.activeFrameSlot || readback.activeFrameSlot < 0) { + readback.result->format = readback.format; + readback.result->pixelSize = readback.pixelSize; + VmaAllocation a = toVmaAllocation(readback.stagingAlloc); void *p = nullptr; - VmaAllocation a = toVmaAllocation(aRb.bufAlloc); VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p); - if (err != VK_SUCCESS) { - qWarning("Failed to map readback buffer: %d", err); - continue; + if (err == VK_SUCCESS && p) { + readback.result->data.resize(int(readback.byteSize)); + memcpy(readback.result->data.data(), p, readback.byteSize); + vmaUnmapMemory(toVmaAllocator(allocator), a); + } else { + qWarning("Failed to map texture readback buffer of size %u: %d", readback.byteSize, err); } - memcpy(aRb.result->data.data(), p, aRb.bufSize); - vmaUnmapMemory(toVmaAllocator(allocator), a); - vmaDestroyBuffer(toVmaAllocator(allocator), aRb.buf, a); - QRHI_PROF_F(releaseReadbackBuffer(qint64(aRb.buf))); + vmaDestroyBuffer(toVmaAllocator(allocator), readback.stagingBuf, a); + QRHI_PROF_F(releaseReadbackBuffer(qint64(readback.stagingBuf))); - if (aRb.result->completed) - completedCallbacks.append(aRb.result->completed); + if (readback.result->completed) + completedCallbacks.append(readback.result->completed); - activeReadbacks.removeAt(i); + activeTextureReadbacks.removeAt(i); + } + } + + for (int i = activeBufferReadbacks.count() - 1; i >= 0; --i) { + const QRhiVulkan::BufferReadback &readback(activeBufferReadbacks[i]); + if (forced || currentFrameSlot == readback.activeFrameSlot || readback.activeFrameSlot < 0) { + VmaAllocation a = toVmaAllocation(readback.stagingAlloc); + void *p = nullptr; + VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p); + if (err == VK_SUCCESS && p) { + readback.result->data.resize(readback.byteSize); + memcpy(readback.result->data.data(), p, size_t(readback.byteSize)); + vmaUnmapMemory(toVmaAllocator(allocator), a); + } else { + qWarning("Failed to map buffer readback buffer of size %d: %d", readback.byteSize, err); + } + + vmaDestroyBuffer(toVmaAllocator(allocator), readback.stagingBuf, a); + QRHI_PROF_F(releaseReadbackBuffer(qint64(readback.stagingBuf))); + + if (readback.result->completed) + completedCallbacks.append(readback.result->completed); + + activeBufferReadbacks.removeAt(i); } } @@ -3733,6 +3825,10 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const return true; case QRhi::TriangleFanTopology: return true; + case QRhi::ReadBackNonUniformBuffer: + return true; + case QRhi::ReadBackNonBaseMipLevel: + return true; default: Q_UNREACHABLE(); return false; @@ -4902,7 +4998,7 @@ bool QVkBuffer::build() allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU; } else { allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; - bufferInfo.usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; + bufferInfo.usage |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; } QRHI_RES_RHI(QRhiVulkan); @@ -4916,11 +5012,7 @@ bool QVkBuffer::build() err = vmaCreateBuffer(toVmaAllocator(rhiD->allocator), &bufferInfo, &allocInfo, &buffers[i], &allocation, nullptr); if (err != VK_SUCCESS) break; - allocations[i] = allocation; - if (m_type == Dynamic) - pendingDynamicUpdates[i].reserve(16); - rhiD->setObjectName(uint64_t(buffers[i]), VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT, m_objectName, m_type == Dynamic ? i : -1); } @@ -5798,6 +5890,9 @@ bool QVkGraphicsPipeline::build() release(); QRHI_RES_RHI(QRhiVulkan); + if (!rhiD->sanityCheckGraphicsPipeline(this)) + return false; + if (!rhiD->ensurePipelineCache()) return false; diff --git a/src/gui/rhi/qrhivulkan_p_p.h b/src/gui/rhi/qrhivulkan_p_p.h index 7bd20b3671..d0e1e6758b 100644 --- a/src/gui/rhi/qrhivulkan_p_p.h +++ b/src/gui/rhi/qrhivulkan_p_p.h @@ -79,7 +79,7 @@ struct QVkBuffer : public QRhiBuffer VkBuffer buffers[QVK_FRAMES_IN_FLIGHT]; QVkAlloc allocations[QVK_FRAMES_IN_FLIGHT]; - QVector<QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate> pendingDynamicUpdates[QVK_FRAMES_IN_FLIGHT]; + QVarLengthArray<QRhiResourceUpdateBatchPrivate::BufferOp, 16> pendingDynamicUpdates[QVK_FRAMES_IN_FLIGHT]; VkBuffer stagingBuffers[QVK_FRAMES_IN_FLIGHT]; QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT]; struct UsageState { @@ -853,17 +853,25 @@ public: VkFence cmdFence = VK_NULL_HANDLE; } ofr; - struct ActiveReadback { + struct TextureReadback { int activeFrameSlot = -1; QRhiReadbackDescription desc; QRhiReadbackResult *result; - VkBuffer buf; - QVkAlloc bufAlloc; - quint32 bufSize; + VkBuffer stagingBuf; + QVkAlloc stagingAlloc; + quint32 byteSize; QSize pixelSize; QRhiTexture::Format format; }; - QVector<ActiveReadback> activeReadbacks; + QVector<TextureReadback> activeTextureReadbacks; + struct BufferReadback { + int activeFrameSlot = -1; + QRhiBufferReadbackResult *result; + int byteSize; + VkBuffer stagingBuf; + QVkAlloc stagingAlloc; + }; + QVector<BufferReadback> activeBufferReadbacks; struct DeferredReleaseEntry { enum Type { @@ -933,7 +941,8 @@ public: Q_DECLARE_TYPEINFO(QRhiVulkan::DescriptorPoolData, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(QRhiVulkan::DeferredReleaseEntry, Q_MOVABLE_TYPE); -Q_DECLARE_TYPEINFO(QRhiVulkan::ActiveReadback, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(QRhiVulkan::TextureReadback, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(QRhiVulkan::BufferReadback, Q_MOVABLE_TYPE); QT_END_NAMESPACE |