From ee9bc61cd906505271bad887664c15b9397906ab Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 3 Oct 2019 08:37:28 +0200 Subject: evdevtouch: Report stationary touchpoints that include pressure changes Task-number: QTBUG-77142 Change-Id: I35446092679573df51891302155c896a3bb6fc1c Reviewed-by: Laszlo Agocs --- src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp b/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp index bf044229ff..2802c9b647 100644 --- a/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp +++ b/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp @@ -577,6 +577,7 @@ void QEvdevTouchScreenData::processInputEvent(input_event *data) m_lastTouchPoints = m_touchPoints; m_touchPoints.clear(); Qt::TouchPointStates combinedStates; + bool hasPressure = false; for (auto i = m_contacts.begin(), end = m_contacts.end(); i != end; /*erasing*/) { auto it = i++; @@ -607,6 +608,9 @@ void QEvdevTouchScreenData::processInputEvent(input_event *data) continue; } + if (contact.pressure) + hasPressure = true; + addTouchPoint(contact, &combinedStates); } @@ -651,7 +655,7 @@ void QEvdevTouchScreenData::processInputEvent(input_event *data) m_contacts.clear(); - if (!m_touchPoints.isEmpty() && combinedStates != Qt::TouchPointStationary) + if (!m_touchPoints.isEmpty() && (hasPressure || combinedStates != Qt::TouchPointStationary)) reportPoints(); } -- cgit v1.2.3 From 00e8f044e1954ea47f1f0c5c5ff35fff97b732be Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Tue, 8 Oct 2019 16:34:49 +0200 Subject: QWidget: document that a widget might only get a mouseDoubleClickEvent If two widgets are on top of each other, and the top widget disappears in response to a press or release that is followed by a double click, then the widget at the bottom only receives the double click event. This is a sequence of events that the application developer that choses to build such a UI has to take care of. Change-Id: I440efd2cac01631de8995abf9a9fb76815de8f9a Fixes: QTBUG-61173 Reviewed-by: Paul Wicking --- src/widgets/kernel/qwidget.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 6889c2e9e5..b70a03b311 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -9218,9 +9218,11 @@ void QWidget::mouseReleaseEvent(QMouseEvent *event) The default implementation calls mousePressEvent(). \note The widget will also receive mouse press and mouse release - events in addition to the double click event. It is up to the - developer to ensure that the application interprets these events - correctly. + events in addition to the double click event. And if another widget + that overlaps this widget disappears in response to press or + release events, then this widget will only receive the double click + event. It is up to the developer to ensure that the application + interprets these events correctly. \sa mousePressEvent(), mouseReleaseEvent(), mouseMoveEvent(), event(), QMouseEvent -- cgit v1.2.3 From 0683de205f1d5f54e7fe62a5be5ad43d6de15a9e Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Tue, 8 Oct 2019 10:13:50 +0200 Subject: QtConcurrent: fix warning about function parameters shadowing class members No functional change. Change-Id: I76aa01e8eb044c794d518ca72e6861cf95060dfc Fixes: QTBUG-79071 Reviewed-by: Thiago Macieira --- src/concurrent/qtconcurrentmapkernel.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/concurrent/qtconcurrentmapkernel.h b/src/concurrent/qtconcurrentmapkernel.h index 87fcf30cf9..7c9538a015 100644 --- a/src/concurrent/qtconcurrentmapkernel.h +++ b/src/concurrent/qtconcurrentmapkernel.h @@ -118,16 +118,16 @@ public: return false; } - bool runIterations(Iterator sequenceBeginIterator, int begin, int end, ReducedResultType *) override + bool runIterations(Iterator sequenceBeginIterator, int beginIndex, int endIndex, ReducedResultType *) override { IntermediateResults results; - results.begin = begin; - results.end = end; - results.vector.reserve(end - begin); + results.begin = beginIndex; + results.end = endIndex; + results.vector.reserve(endIndex - beginIndex); Iterator it = sequenceBeginIterator; - std::advance(it, begin); - for (int i = begin; i < end; ++i) { + std::advance(it, beginIndex); + for (int i = beginIndex; i < endIndex; ++i) { results.vector.append(map(*(it))); std::advance(it, 1); } @@ -176,13 +176,13 @@ public: return true; } - bool runIterations(Iterator sequenceBeginIterator, int begin, int end, T *results) override + bool runIterations(Iterator sequenceBeginIterator, int beginIndex, int endIndex, T *results) override { Iterator it = sequenceBeginIterator; - std::advance(it, begin); - for (int i = begin; i < end; ++i) { - runIteration(it, i, results + (i - begin)); + std::advance(it, beginIndex); + for (int i = beginIndex; i < endIndex; ++i) { + runIteration(it, i, results + (i - beginIndex)); std::advance(it, 1); } -- cgit v1.2.3 From b7a915398a2ee928df4db2c5dd8e308f480c7906 Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Mon, 7 Oct 2019 15:13:57 +0200 Subject: QAuthenticator - use GSS framework, as Apple requires MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit since declarations in gssapi.h are marked as deprecated. Fixes: QTBUG-78810 Change-Id: I241ae4913f362f6e9219438e9bfe7a63dfc91b7c Reviewed-by: Timur Pocheptsov Reviewed-by: MÃ¥rten Nordheim --- src/network/configure.json | 12 +++++++++++- src/network/kernel/qauthenticator.cpp | 7 ++++++- 2 files changed, 17 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/network/configure.json b/src/network/configure.json index a1cb77b6d1..f501465c91 100644 --- a/src/network/configure.json +++ b/src/network/configure.json @@ -102,13 +102,23 @@ "gssapi": { "label": "KRB5 GSSAPI Support", "test": { + "head": [ + "#if defined(__APPLE__) && (defined(__GNUC__) || defined(__xlC__) || defined(__xlc__))", + "# include ", + "# if defined(TARGET_OS_MAC) && TARGET_OS_MAC", + "# include ", + "# endif", + "#else", + "# include ", + "#endif" + ], "main": [ "gss_ctx_id_t ctx;", "gss_context_time(nullptr, ctx, nullptr);" ] }, - "headers": [ "gssapi/gssapi.h" ], "sources": [ + { "libs": "-framework GSS", "condition": "config.darwin" }, { "type": "pkgConfig", "args": "krb5-gssapi" }, "-lgssapi_krb5" ] diff --git a/src/network/kernel/qauthenticator.cpp b/src/network/kernel/qauthenticator.cpp index 4100dfd784..858a5bc2de 100644 --- a/src/network/kernel/qauthenticator.cpp +++ b/src/network/kernel/qauthenticator.cpp @@ -1,3 +1,4 @@ + /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. @@ -59,8 +60,12 @@ #define SECURITY_WIN32 1 #include #elif QT_CONFIG(gssapi) // GSSAPI +#if defined(Q_OS_DARWIN) +#include +#else #include -#endif +#endif // Q_OS_DARWIN +#endif // Q_CONFIG(sspi) QT_BEGIN_NAMESPACE -- cgit v1.2.3 From 89ec1b3618f0f26a8f92049aabfc2df4d84c34c5 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Thu, 3 Oct 2019 14:28:23 +0200 Subject: rhi: Add support for buffer readbacks This also marks the beginnings of significantly extending autotesting of the resource and rendering functionality in QRhi. Also involves fixing up the buffer operation lists like we did for textures before. This is to ensure updates and reads on the same batch execute in the correct order. So just have two lists: one with buffer, one with texture operations. Also simplify the struct layouts. No need for those inner structs with many duplicate members. This reduces the size even, since using a union was never an option here. Also switch to a VLA, the size is around 253 KB per batch. The Null backend now keeps track of the QRhiBuffer data so it can return valid results in readbacks. Task-number: QTBUG-78984 Task-number: QTBUG-78986 Task-number: QTBUG-78971 Task-number: QTBUG-78883 Change-Id: I9694bd7fec523a4e71cf8a5c77c828123ebbb3bd Reviewed-by: Paul Olav Tvete --- src/gui/rhi/qrhi.cpp | 55 ++++-- src/gui/rhi/qrhi_p.h | 10 +- src/gui/rhi/qrhi_p_p.h | 131 ++++++++------- src/gui/rhi/qrhid3d11.cpp | 224 ++++++++++++++++--------- src/gui/rhi/qrhid3d11_p_p.h | 15 +- src/gui/rhi/qrhigles2.cpp | 151 +++++++++++------ src/gui/rhi/qrhigles2_p_p.h | 14 +- src/gui/rhi/qrhimetal.mm | 150 +++++++++-------- src/gui/rhi/qrhinull.cpp | 29 +++- src/gui/rhi/qrhinull_p_p.h | 2 + src/gui/rhi/qrhivulkan.cpp | 388 ++++++++++++++++++++++++++----------------- src/gui/rhi/qrhivulkan_p_p.h | 23 ++- 12 files changed, 752 insertions(+), 440 deletions(-) (limited to 'src') diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp index c8ec8c7410..8ec8ae9208 100644 --- a/src/gui/rhi/qrhi.cpp +++ b/src/gui/rhi/qrhi.cpp @@ -566,6 +566,13 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general") \value TriangleFanTopology Indicates that QRhiGraphicsPipeline::setTopology() supports QRhiGraphicsPipeline::TriangleFan. + + \value ReadBackNonUniformBuffer Indicates that + {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 can be + unsupported, for example, on OpenGL ES 2.0 implementations without the + MapBuffer extension. */ /*! @@ -4175,7 +4182,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 +4196,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 +4206,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 +4240,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 +4265,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 +4317,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 +4329,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 +4378,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 +4387,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..993fc44b58 100644 --- a/src/gui/rhi/qrhi_p.h +++ b/src/gui/rhi/qrhi_p.h @@ -1355,6 +1355,12 @@ struct Q_GUI_EXPORT QRhiReadbackResult QByteArray data; }; // non-movable due to the std::function +struct Q_GUI_EXPORT QRhiBufferReadbackResult +{ + std::function completed = nullptr; + QByteArray data; +}; + class Q_GUI_EXPORT QRhiResourceUpdateBatch { public: @@ -1367,6 +1373,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 +1435,8 @@ public: VertexShaderPointSize, BaseVertex, BaseInstance, - TriangleFanTopology + TriangleFanTopology, + ReadBackNonUniformBuffer }; enum BeginFrameFlag { diff --git a/src/gui/rhi/qrhi_p_p.h b/src/gui/rhi/qrhi_p_p.h index 80a95e2fcc..cc14293580 100644 --- a/src/gui/rhi/qrhi_p_p.h +++ b/src/gui/rhi/qrhi_p_p.h @@ -218,7 +218,7 @@ private: QRhi::Implementation implType; QThread *implThread; QRhiProfiler profiler; - QVector resUpdPool; + QVarLengthArray resUpdPool; QBitArray resUpdPoolMap; QSet resources; QSet pendingReleaseAndDestroyResources; @@ -271,26 +271,49 @@ bool qrhi_toTopLeftRenderTargetRect(const QSize &outputSize, const std::array(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(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(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(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 +321,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 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 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 dynamicBufferUpdates; - QVector staticBufferUploads; - QVector textureOps; + QVarLengthArray bufferOps; + QVarLengthArray textureOps; QRhiResourceUpdateBatch *q = nullptr; QRhiImplementation *rhi = nullptr; @@ -376,8 +388,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 diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index f011d68ada..7c39fe0b11 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -471,6 +471,8 @@ bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const return true; case QRhi::TriangleFanTopology: return false; + case QRhi::ReadBackNonUniformBuffer: + return true; default: Q_UNREACHABLE(); return false; @@ -1323,54 +1325,97 @@ 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 size = u.desc.pixelSize().isEmpty() ? srcD->m_pixelSize : u.desc.pixelSize(); + const QPoint sp = u.desc.sourceTopLeft(); D3D11_BOX srcBox; srcBox.left = UINT(sp.x()); srcBox.top = UINT(sp.y()); @@ -1391,16 +1436,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 +1455,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 = u.rb.level() > 0 ? q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize) : 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 +1480,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 +1502,7 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate } QRHI_PROF_F(newReadbackBuffer(qint64(qintptr(stagingTex)), texD ? static_cast(texD) : static_cast(swapChainD), - bufSize)); + byteSize)); QD3D11CommandBuffer::Command cmd; cmd.cmd = QD3D11CommandBuffer::Command::CopySubRes; @@ -1470,18 +1515,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 +1539,58 @@ void QRhiD3D11::finishActiveReadbacks() QVarLengthArray, 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(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(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) 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 activeReadbacks; + QVector activeTextureReadbacks; + struct BufferReadback { + QRhiBufferReadbackResult *result; + quint32 byteSize; + ID3D11Buffer *stagingBuf; + }; + QVector 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..13dc016fd6 100644 --- a/src/gui/rhi/qrhigles2.cpp +++ b/src/gui/rhi/qrhigles2.cpp @@ -492,6 +492,8 @@ bool QRhiGles2::create(QRhi::Flags flags) else caps.textureCompareMode = true; + caps.mapBuffer = f->hasOpenGLExtension(QOpenGLExtensions::MapBuffer); + if (!caps.gles) { f->glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); f->glEnable(GL_POINT_SPRITE); @@ -734,6 +736,8 @@ 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.mapBuffer; default: Q_UNREACHABLE(); return false; @@ -1417,65 +1421,82 @@ 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 size = u.desc.pixelSize().isEmpty() ? srcD->m_pixelSize : 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,16 +1506,16 @@ 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(); @@ -1505,8 +1526,8 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate } 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; @@ -1516,12 +1537,12 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate 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 +2101,32 @@ 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.mapBuffer) { + void *p = f->glMapBuffer(cmd.args.getBufferSubData.target, GL_READ_ONLY); + if (p) { + result->data.resize(cmd.args.getBufferSubData.size); + memcpy(result->data.data(), + reinterpret_cast(p) + cmd.args.getBufferSubData.offset, + 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; diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h index 646836a699..a4a9c15ce7 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, @@ -401,6 +401,13 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer int size; 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; @@ -744,7 +751,9 @@ public: rgba8Format(false), instancing(false), baseVertex(false), - compute(false) + compute(false), + textureCompareMode(false), + mapBuffer(false) { } int ctxMajor; int ctxMinor; @@ -775,6 +784,7 @@ public: uint baseVertex : 1; uint compute : 1; uint textureCompareMode : 1; + uint mapBuffer : 1; } caps; QGles2SwapChain *currentSwapChain = nullptr; QVector supportedCompressedFormats; diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm index f9d9cdc01a..e55513277b 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 activeReadbacks; + QVector activeTextureReadbacks; API_AVAILABLE(macos(10.13), ios(11.0)) MTLCaptureManager *captureMgr; API_AVAILABLE(macos(10.13), ios(11.0)) id 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 buf[QMTL_FRAMES_IN_FLIGHT]; - QVector pendingUpdates[QMTL_FRAMES_IN_FLIGHT]; + QVarLengthArray pendingUpdates[QMTL_FRAMES_IN_FLIGHT]; }; struct QMetalRenderBufferData @@ -552,6 +552,8 @@ bool QRhiMetal::isFeatureSupported(QRhi::Feature feature) const return true; case QRhi::TriangleFanTopology: return false; + case QRhi::ReadBackNonUniformBuffer: + return true; default: Q_UNREACHABLE(); return false; @@ -1541,21 +1543,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([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 blitEnc = nil; @@ -1569,11 +1583,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 +1602,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 +1617,32 @@ 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 size = u.desc.pixelSize().isEmpty() ? srcD->m_pixelSize : 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) 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 src; QSize srcSize; @@ -1637,17 +1651,17 @@ 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) + readback.pixelSize = u.rb.level() > 0 ? q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize) : texD->m_pixelSize; - aRb.format = texD->m_format; + readback.format = texD->m_format; src = texD->d->tex; srcSize = texD->m_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 +1670,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(texD) : static_cast(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 +1711,13 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate void QRhiMetal::executeBufferHostWritesForCurrentFrame(QMetalBuffer *bufD) { const int idx = bufD->d->slotted ? currentFrameSlot : 0; - QVector &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(p) + u.offset, u.data.constData(), size_t(u.data.size())); if (changeBegin == -1 || u.offset < changeBegin) @@ -1715,7 +1728,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 +1964,22 @@ void QRhiMetal::finishActiveReadbacks(bool forced) QVarLengthArray, 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 +2055,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()]; diff --git a/src/gui/rhi/qrhinull.cpp b/src/gui/rhi/qrhinull.cpp index 487afd3ed1..de6616b677 100644 --- a/src/gui/rhi/qrhinull.cpp +++ b/src/gui/rhi/qrhinull.cpp @@ -389,13 +389,28 @@ void QRhiNull::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *re { 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(); + QRhiReadbackResult *result = u.result; + QRhiTexture *tex = u.rb.texture(); if (tex) { result->format = tex->format(); - result->pixelSize = q->sizeForMipLevel(u.read.rb.level(), tex->pixelSize()); + result->pixelSize = q->sizeForMipLevel(u.rb.level(), tex->pixelSize()); } else { Q_ASSERT(currentSwapChain); result->format = QRhiTexture::RGBA8; @@ -403,7 +418,7 @@ void QRhiNull::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *re } quint32 byteSize = 0; textureFormatInfo(result->format, result->pixelSize, nullptr, &byteSize); - result->data.fill(0, byteSize); + result->data.fill(0, int(byteSize)); if (result->completed) result->completed(); } @@ -454,14 +469,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; } diff --git a/src/gui/rhi/qrhinull_p_p.h b/src/gui/rhi/qrhinull_p_p.h index ee301d247b..93e298481c 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 diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp index 36a6557e04..154908b333 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(); @@ -2645,100 +2650,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(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(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. + + bufD->lastActiveFrameSlot = currentFrameSlot; - trackedBufferBarrier(cbD, bufD, 0, - VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); + 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(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; + } - VkBufferCopy copyInfo; - memset(©Info, 0, sizeof(copyInfo)); - copyInfo.srcOffset = VkDeviceSize(u.offset); - copyInfo.dstOffset = VkDeviceSize(u.offset); - copyInfo.size = VkDeviceSize(u.data.size()); + trackedBufferBarrier(cbD, bufD, 0, VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); - 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); + VkBufferCopy copyInfo; + memset(©Info, 0, sizeof(copyInfo)); + copyInfo.srcOffset = VkDeviceSize(u.offset); + copyInfo.size = VkDeviceSize(u.readSize); - // 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. + 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); - bufD->lastActiveFrameSlot = currentFrameSlot; + bufD->lastActiveFrameSlot = currentFrameSlot; - 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)); + 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); } } @@ -2776,7 +2845,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 &srd(u.upload.subresDesc[layer][level]); + const QVector &srd(u.subresDesc[layer][level]); if (srd.isEmpty()) continue; for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(srd)) { @@ -2817,34 +2886,34 @@ 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(); + const QSize size = u.desc.pixelSize().isEmpty() ? srcD->m_pixelSize : u.desc.pixelSize(); region.extent.width = uint32_t(size.width()); region.extent.height = uint32_t(size.height()); region.extent.depth = 1; @@ -2865,21 +2934,21 @@ 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 = u.rb.level() > 0 ? q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize) + : texD->m_pixelSize; + readback.format = texD->m_format; texD->lastActiveFrameSlot = currentFrameSlot; } else { Q_ASSERT(currentSwapChain); @@ -2888,21 +2957,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; @@ -2910,14 +2979,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(texD) : static_cast(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; } @@ -2926,11 +2995,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) { @@ -2940,7 +3009,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 { @@ -2965,14 +3034,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(); @@ -2989,14 +3058,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); } @@ -3004,7 +3073,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; @@ -3012,7 +3081,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); @@ -3021,7 +3090,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); @@ -3047,13 +3116,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); } @@ -3066,8 +3135,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat void QRhiVulkan::executeBufferHostWritesForCurrentFrame(QVkBuffer *bufD) { - QVector &updates(bufD->pendingDynamicUpdates[currentFrameSlot]); - if (updates.isEmpty()) + if (bufD->pendingDynamicUpdates[currentFrameSlot].isEmpty()) return; Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic); @@ -3083,7 +3151,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(p) + u.offset, u.data.constData(), size_t(u.data.size())); if (changeBegin == -1 || u.offset < changeBegin) @@ -3095,7 +3163,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) @@ -3189,29 +3257,53 @@ void QRhiVulkan::finishActiveReadbacks(bool forced) QVarLengthArray, 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); } } @@ -3729,6 +3821,8 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const return true; case QRhi::TriangleFanTopology: return true; + case QRhi::ReadBackNonUniformBuffer: + return true; default: Q_UNREACHABLE(); return false; @@ -4898,7 +4992,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); @@ -4912,11 +5006,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); } 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 pendingDynamicUpdates[QVK_FRAMES_IN_FLIGHT]; + QVarLengthArray 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 activeReadbacks; + QVector activeTextureReadbacks; + struct BufferReadback { + int activeFrameSlot = -1; + QRhiBufferReadbackResult *result; + int byteSize; + VkBuffer stagingBuf; + QVkAlloc stagingAlloc; + }; + QVector 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 -- cgit v1.2.3 From e22399af826b582e330a2772460d3bc0d98eb558 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Thu, 3 Oct 2019 17:33:23 +0200 Subject: Make QRhi::create() return false when there is no MTLDevice The interesting part here is that sending messages to a null object is valid in Objective-C, so without an explicit check it is not necessarily straightforward to discover that we do not have working rendering. (because the application won't just simply crash) Do the right thing now and return false like other backends do. Task-number: QTBUG-78994 Change-Id: I0d3c4a49a3fc78f9149f8af4fe67d581e74daae7 Reviewed-by: Paul Olav Tvete --- src/gui/rhi/qrhimetal.mm | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm index e55513277b..9c579ebfe1 100644 --- a/src/gui/rhi/qrhimetal.mm +++ b/src/gui/rhi/qrhimetal.mm @@ -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) -- cgit v1.2.3 From 9c466946d0c5b4e319f1c953caeea63f0d453138 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 4 Oct 2019 15:46:49 +0200 Subject: rhi: Autotest basic texture operations ...and make the Null backend able to deal with these, for RGBA8 textures at least. Naturally it is all QImage and QPainter under the hood. Also fix a bug in the OpenGL backend, as discovered by the autotest: the size from the readback did not reflect the mip level. Task-number: QTBUG-78971 Change-Id: Ie424b268bf5feb09021099b67068f4418a9b583e Reviewed-by: Paul Olav Tvete --- src/gui/rhi/qrhigles2.cpp | 5 +- src/gui/rhi/qrhinull.cpp | 116 +++++++++++++++++++++++++++++++++++++++++---- src/gui/rhi/qrhinull_p_p.h | 5 ++ 3 files changed, 114 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp index 13dc016fd6..a6094183fb 100644 --- a/src/gui/rhi/qrhigles2.cpp +++ b/src/gui/rhi/qrhigles2.cpp @@ -1532,8 +1532,9 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate 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; diff --git a/src/gui/rhi/qrhinull.cpp b/src/gui/rhi/qrhinull.cpp index de6616b677..d78b5e20c5 100644 --- a/src/gui/rhi/qrhinull.cpp +++ b/src/gui/rhi/qrhinull.cpp @@ -36,6 +36,7 @@ #include "qrhinull_p_p.h" #include +#include QT_BEGIN_NAMESPACE @@ -385,6 +386,67 @@ 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.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.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); @@ -405,22 +467,42 @@ void QRhiNull::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *re } } for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) { - if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) { + 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; - QRhiTexture *tex = u.rb.texture(); - if (tex) { - result->format = tex->format(); - result->pixelSize = q->sizeForMipLevel(u.rb.level(), tex->pixelSize()); + 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, int(byteSize)); + textureFormatInfo(result->format, result->pixelSize, &bytesPerLine, &byteSize); + if (result->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(); @@ -532,22 +614,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; diff --git a/src/gui/rhi/qrhinull_p_p.h b/src/gui/rhi/qrhinull_p_p.h index 93e298481c..ce517bfa63 100644 --- a/src/gui/rhi/qrhinull_p_p.h +++ b/src/gui/rhi/qrhinull_p_p.h @@ -84,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 @@ -288,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; -- cgit v1.2.3 From 32924110ce69498ab5b05d00728f77affd6817d9 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Sat, 5 Oct 2019 15:31:12 +0200 Subject: rhi: gl: Switch GetBufferSubData emulation to MapBufferRange Use only APIs that are in GLES 3.0. glMapBuffer() is an old OES extension, stop bothering with that. Not the least because ANGLE claims supporting it and then fails the map. (not that we care much about ANGLE, but, for instance, the qrhi autotest is run with ANGLE configurations as well in the CI, so have to still take care of it for the duration of Qt 5.x) Change-Id: I29140402cedffe0430f920ee0c061673257c3aa1 Reviewed-by: Paul Olav Tvete --- src/gui/rhi/qrhi.cpp | 7 +++---- src/gui/rhi/qrhigles2.cpp | 21 ++++++++++++++------- src/gui/rhi/qrhigles2_p_p.h | 4 ++-- 3 files changed, 19 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp index 8ec8ae9208..bea8890234 100644 --- a/src/gui/rhi/qrhi.cpp +++ b/src/gui/rhi/qrhi.cpp @@ -568,11 +568,10 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general") supports QRhiGraphicsPipeline::TriangleFan. \value ReadBackNonUniformBuffer Indicates that - {QRhiResourceUpdateBatch::readBackBuffer()}{reading buffer contents} is + \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 can be - unsupported, for example, on OpenGL ES 2.0 implementations without the - MapBuffer extension. + UniformBuffer. While this is supported in the majority of cases, it will be + unsupported with OpenGL ES older than 3.0. */ /*! diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp index a6094183fb..fa99ee5e25 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,7 +496,9 @@ bool QRhiGles2::create(QRhi::Flags flags) else caps.textureCompareMode = true; - caps.mapBuffer = f->hasOpenGLExtension(QOpenGLExtensions::MapBuffer); + // 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) { f->glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); @@ -737,7 +743,7 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const case QRhi::TriangleFanTopology: return true; case QRhi::ReadBackNonUniformBuffer: - return !caps.gles || caps.mapBuffer; + return !caps.gles || caps.properMapBuffer; default: Q_UNREACHABLE(); return false; @@ -2107,13 +2113,14 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb) QRhiBufferReadbackResult *result = cmd.args.getBufferSubData.result; f->glBindBuffer(cmd.args.getBufferSubData.target, cmd.args.getBufferSubData.buffer); if (caps.gles) { - if (caps.mapBuffer) { - void *p = f->glMapBuffer(cmd.args.getBufferSubData.target, GL_READ_ONLY); + 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(), - reinterpret_cast(p) + cmd.args.getBufferSubData.offset, - size_t(cmd.args.getBufferSubData.size)); + memcpy(result->data.data(), p, size_t(cmd.args.getBufferSubData.size)); f->glUnmapBuffer(cmd.args.getBufferSubData.target); } } diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h index a4a9c15ce7..29cccb0c9c 100644 --- a/src/gui/rhi/qrhigles2_p_p.h +++ b/src/gui/rhi/qrhigles2_p_p.h @@ -753,7 +753,7 @@ public: baseVertex(false), compute(false), textureCompareMode(false), - mapBuffer(false) + properMapBuffer(false) { } int ctxMajor; int ctxMinor; @@ -784,7 +784,7 @@ public: uint baseVertex : 1; uint compute : 1; uint textureCompareMode : 1; - uint mapBuffer : 1; + uint properMapBuffer : 1; } caps; QGles2SwapChain *currentSwapChain = nullptr; QVector supportedCompressedFormats; -- cgit v1.2.3 From 9baf69c765c446b7c8f069ebdd50910877a7d0f8 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Sat, 5 Oct 2019 16:05:06 +0200 Subject: rhi: gl: Add a feature flag for reading back non-zero mip levels The joys of "level - Specifies the mipmap level of the texture image to be attached, which must be 0." for glFramebufferTexture2D in OpenGL ES 2.0. Change-Id: Iaf19502f48d7ba73b26abb72535bfa6696a1e182 Reviewed-by: Paul Olav Tvete --- src/gui/rhi/qrhi.cpp | 8 +++++++- src/gui/rhi/qrhi_p.h | 3 ++- src/gui/rhi/qrhid3d11.cpp | 2 ++ src/gui/rhi/qrhigles2.cpp | 31 +++++++++++++++++++++++-------- src/gui/rhi/qrhigles2_p_p.h | 4 +++- src/gui/rhi/qrhimetal.mm | 2 ++ src/gui/rhi/qrhivulkan.cpp | 2 ++ 7 files changed, 41 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp index bea8890234..57c466678e 100644 --- a/src/gui/rhi/qrhi.cpp +++ b/src/gui/rhi/qrhi.cpp @@ -572,7 +572,13 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general") 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 diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h index 993fc44b58..bfcc1d39f5 100644 --- a/src/gui/rhi/qrhi_p.h +++ b/src/gui/rhi/qrhi_p.h @@ -1436,7 +1436,8 @@ public: BaseVertex, BaseInstance, TriangleFanTopology, - ReadBackNonUniformBuffer + ReadBackNonUniformBuffer, + ReadBackNonBaseMipLevel }; enum BeginFrameFlag { diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index 7c39fe0b11..096a8c2756 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -473,6 +473,8 @@ bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const return false; case QRhi::ReadBackNonUniformBuffer: return true; + case QRhi::ReadBackNonBaseMipLevel: + return true; default: Q_UNREACHABLE(); return false; diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp index fa99ee5e25..ca1c91499d 100644 --- a/src/gui/rhi/qrhigles2.cpp +++ b/src/gui/rhi/qrhigles2.cpp @@ -500,6 +500,11 @@ bool QRhiGles2::create(QRhi::Flags flags) // 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); @@ -744,6 +749,8 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const return true; case QRhi::ReadBackNonUniformBuffer: return !caps.gles || caps.properMapBuffer; + case QRhi::ReadBackNonBaseMipLevel: + return caps.nonBaseLevelFramebufferTexture; default: Q_UNREACHABLE(); return false; @@ -2156,23 +2163,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); diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h index 29cccb0c9c..8814d9c19d 100644 --- a/src/gui/rhi/qrhigles2_p_p.h +++ b/src/gui/rhi/qrhigles2_p_p.h @@ -753,7 +753,8 @@ public: baseVertex(false), compute(false), textureCompareMode(false), - properMapBuffer(false) + properMapBuffer(false), + nonBaseLevelFramebufferTexture(false) { } int ctxMajor; int ctxMinor; @@ -785,6 +786,7 @@ public: uint compute : 1; uint textureCompareMode : 1; uint properMapBuffer : 1; + uint nonBaseLevelFramebufferTexture : 1; } caps; QGles2SwapChain *currentSwapChain = nullptr; QVector supportedCompressedFormats; diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm index 9c579ebfe1..694882ab93 100644 --- a/src/gui/rhi/qrhimetal.mm +++ b/src/gui/rhi/qrhimetal.mm @@ -559,6 +559,8 @@ bool QRhiMetal::isFeatureSupported(QRhi::Feature feature) const return false; case QRhi::ReadBackNonUniformBuffer: return true; + case QRhi::ReadBackNonBaseMipLevel: + return true; default: Q_UNREACHABLE(); return false; diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp index 154908b333..0d364dd9fc 100644 --- a/src/gui/rhi/qrhivulkan.cpp +++ b/src/gui/rhi/qrhivulkan.cpp @@ -3823,6 +3823,8 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const return true; case QRhi::ReadBackNonUniformBuffer: return true; + case QRhi::ReadBackNonBaseMipLevel: + return true; default: Q_UNREACHABLE(); return false; -- cgit v1.2.3 From dd105fab8d8b4bd39654e7e268e6782e53cce2de Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Sun, 6 Oct 2019 17:25:06 +0200 Subject: rhi: Autotest rendering a triangle Also improve (docs and runtime checks) and test the minimum set of required data to create a graphics pipeline. Task-number: QTBUG-78971 Change-Id: If5c14f1ab1ff3cf70f168fde585f05fc9d28ec91 Reviewed-by: Paul Olav Tvete --- src/gui/rhi/qrhi.cpp | 48 +++++++++++++++++++++++++++++++++++++++++++--- src/gui/rhi/qrhi_p.h | 1 - src/gui/rhi/qrhi_p_p.h | 2 ++ src/gui/rhi/qrhid3d11.cpp | 2 ++ src/gui/rhi/qrhigles2.cpp | 3 +++ src/gui/rhi/qrhimetal.mm | 2 ++ src/gui/rhi/qrhinull.cpp | 4 ++++ src/gui/rhi/qrhivulkan.cpp | 3 +++ 8 files changed, 61 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp index 57c466678e..dabad35688 100644 --- a/src/gui/rhi/qrhi.cpp +++ b/src/gui/rhi/qrhi.cpp @@ -3084,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(), @@ -3095,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. @@ -3912,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 */ diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h index bfcc1d39f5..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 { diff --git a/src/gui/rhi/qrhi_p_p.h b/src/gui/rhi/qrhi_p_p.h index cc14293580..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; diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index 096a8c2756..57ebb2909f 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -3491,6 +3491,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/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp index ca1c91499d..4163ab1e79 100644 --- a/src/gui/rhi/qrhigles2.cpp +++ b/src/gui/rhi/qrhigles2.cpp @@ -3690,6 +3690,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/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm index 694882ab93..ef5ab696d7 100644 --- a/src/gui/rhi/qrhimetal.mm +++ b/src/gui/rhi/qrhimetal.mm @@ -3139,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 d78b5e20c5..2eb55e705b 100644 --- a/src/gui/rhi/qrhinull.cpp +++ b/src/gui/rhi/qrhinull.cpp @@ -805,6 +805,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/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp index 0d364dd9fc..fca2125f10 100644 --- a/src/gui/rhi/qrhivulkan.cpp +++ b/src/gui/rhi/qrhivulkan.cpp @@ -5886,6 +5886,9 @@ bool QVkGraphicsPipeline::build() release(); QRHI_RES_RHI(QRhiVulkan); + if (!rhiD->sanityCheckGraphicsPipeline(this)) + return false; + if (!rhiD->ensurePipelineCache()) return false; -- cgit v1.2.3 From 59604405c98cc8d55e924573859dd64d94d5d877 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 7 Oct 2019 10:12:48 +0200 Subject: rhi: Fix non-base level copy and readback wrt source size When the source size is not explicitly specified, we take the entire subresource. However, just using the texture's size is wrong: when the source level in a copy or readback is not 0, the size for the corresponding mip level has to be used instead. This fixes occasional crashes with Metal in the autotest. Change-Id: I99f689feef93ec86dffdc9e82d6bfdaf5c1eb041 Reviewed-by: Paul Olav Tvete --- src/gui/rhi/qrhid3d11.cpp | 9 +++++---- src/gui/rhi/qrhigles2.cpp | 7 ++++--- src/gui/rhi/qrhimetal.mm | 10 +++++----- src/gui/rhi/qrhivulkan.cpp | 10 +++++----- 4 files changed, 19 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index 57ebb2909f..717f3e6d6c 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -1416,15 +1416,16 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate 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 size = u.desc.pixelSize().isEmpty() ? srcD->m_pixelSize : u.desc.pixelSize(); + 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; @@ -1457,7 +1458,7 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate } src = texD->tex; dxgiFormat = texD->dxgiFormat; - pixelSize = u.rb.level() > 0 ? q->sizeForMipLevel(u.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.rb.level()), UINT(u.rb.layer()), texD->mipLevelCount); } else { diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp index 4163ab1e79..e355979626 100644 --- a/src/gui/rhi/qrhigles2.cpp +++ b/src/gui/rhi/qrhigles2.cpp @@ -1506,7 +1506,8 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate trackedImageBarrier(cbD, srcD, QGles2Texture::AccessRead); trackedImageBarrier(cbD, dstD, QGles2Texture::AccessUpdate); - const QSize size = u.desc.pixelSize().isEmpty() ? srcD->m_pixelSize : u.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.desc.sourceTopLeft(); const QPoint dp = u.desc.destinationTopLeft(); @@ -1532,8 +1533,8 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate 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) { diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm index ef5ab696d7..5f14d917b8 100644 --- a/src/gui/rhi/qrhimetal.mm +++ b/src/gui/rhi/qrhimetal.mm @@ -1628,7 +1628,8 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate QMetalTexture *srcD = QRHI_RES(QMetalTexture, u.src); QMetalTexture *dstD = QRHI_RES(QMetalTexture, u.dst); const QPoint dp = u.desc.destinationTopLeft(); - const QSize size = u.desc.pixelSize().isEmpty() ? srcD->m_pixelSize : u.desc.pixelSize(); + 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(); @@ -1636,7 +1637,7 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate 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.desc.destinationLayer()) destinationLevel: NSUInteger(u.desc.destinationLevel()) @@ -1658,11 +1659,10 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate qWarning("Multisample texture cannot be read back"); continue; } - readback.pixelSize = u.rb.level() > 0 ? q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize) - : texD->m_pixelSize; + 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); diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp index fca2125f10..103fea627a 100644 --- a/src/gui/rhi/qrhivulkan.cpp +++ b/src/gui/rhi/qrhivulkan.cpp @@ -2913,9 +2913,10 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat region.dstOffset.x = u.desc.destinationTopLeft().x(); region.dstOffset.y = u.desc.destinationTopLeft().y(); - const QSize size = u.desc.pixelSize().isEmpty() ? srcD->m_pixelSize : u.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, @@ -2946,8 +2947,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat qWarning("Multisample texture cannot be read back"); continue; } - readback.pixelSize = u.rb.level() > 0 ? q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize) - : texD->m_pixelSize; + readback.pixelSize = q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize); readback.format = texD->m_format; texD->lastActiveFrameSlot = currentFrameSlot; } else { -- cgit v1.2.3 From b2de7f8583a9a2e73f0de507534158354a86ce7d Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 7 Oct 2019 17:12:59 +0200 Subject: rhi: Autotest rendering a textured quad Task-number: QTBUG-78971 Change-Id: I0e7e0f3c00f9509031f7b4a8a389e51c915f01c2 Reviewed-by: Paul Olav Tvete --- src/gui/rhi/qrhinull.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/gui/rhi/qrhinull.cpp b/src/gui/rhi/qrhinull.cpp index 2eb55e705b..3027faa397 100644 --- a/src/gui/rhi/qrhinull.cpp +++ b/src/gui/rhi/qrhinull.cpp @@ -397,6 +397,7 @@ void QRhiNull::simulateTextureUpload(const QRhiResourceUpdateBatchPrivate::Textu 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()) { @@ -435,6 +436,7 @@ void QRhiNull::simulateTextureCopy(const QRhiResourceUpdateBatchPrivate::Texture const QPoint srcPos = u.desc.sourceTopLeft(); QPainter painter(&dstImage); + painter.setCompositionMode(QPainter::CompositionMode_Source); painter.drawImage(QRect(dstPos, size), srcImage, QRect(srcPos, size)); } -- cgit v1.2.3 From b62b80706e6b81cd3b5e18d7ffdac2bc630b151e Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 9 Oct 2019 10:23:12 +0200 Subject: rhi: Autotest for rendering a triangle into a window Change-Id: Id1562ff8cf7c6bc7e5bd147bb628f3d9dd57f2b5 Reviewed-by: Paul Olav Tvete --- src/gui/rhi/qrhinull.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/gui/rhi/qrhinull.cpp b/src/gui/rhi/qrhinull.cpp index 3027faa397..fe606f971f 100644 --- a/src/gui/rhi/qrhinull.cpp +++ b/src/gui/rhi/qrhinull.cpp @@ -489,7 +489,7 @@ void QRhiNull::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *re quint32 bytesPerLine = 0; quint32 byteSize = 0; textureFormatInfo(result->format, result->pixelSize, &bytesPerLine, &byteSize); - if (result->format == QRhiTexture::RGBA8) { + 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(); -- cgit v1.2.3 From 5e7365573e703bceb99da525725cd63350987c7a Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Wed, 9 Oct 2019 15:17:05 +0200 Subject: Remove a new-line that was accidentally introduced this week. Change-Id: I0731c7c64e51b40f230b42694603056d89a05712 Reviewed-by: Edward Welbourne --- src/network/kernel/qauthenticator.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/network/kernel/qauthenticator.cpp b/src/network/kernel/qauthenticator.cpp index 858a5bc2de..33a30eb1cd 100644 --- a/src/network/kernel/qauthenticator.cpp +++ b/src/network/kernel/qauthenticator.cpp @@ -1,4 +1,3 @@ - /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. -- cgit v1.2.3 From 18aa8390ce83b4aa9cabe5609b8f830f86e475e5 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 3 Oct 2019 08:58:06 +0200 Subject: Add qt.qpa.input.events logging to evdevtouch The xcb platform plugin uses this category for detailed input event logging, so we might as well be consistent in evdevtouch. When hardware supports pressure sensing, it's likely to need extra debugging. Task-number: QTBUG-77142 Change-Id: I7682bb5d49e669054523f9cf556715e511bcd572 Reviewed-by: Laszlo Agocs --- src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src') diff --git a/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp b/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp index 2802c9b647..c51db59e1f 100644 --- a/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp +++ b/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp @@ -69,6 +69,7 @@ extern "C" { QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(qLcEvdevTouch, "qt.qpa.input") +Q_LOGGING_CATEGORY(qLcEvents, "qt.qpa.input.events") /* android (and perhaps some other linux-derived stuff) don't define everything * in linux/input.h, so we'll need to do that ourselves. @@ -539,6 +540,9 @@ void QEvdevTouchScreenData::processInputEvent(input_event *data) if (m_typeB) m_contacts[m_currentSlot].maj = m_currentData.maj; } else if (data->code == ABS_PRESSURE || data->code == ABS_MT_PRESSURE) { + if (Q_UNLIKELY(qLcEvents().isDebugEnabled())) + qCDebug(qLcEvents, "EV_ABS code 0x%x: pressure %d; bounding to [%d,%d]", + data->code, data->value, hw_pressure_min, hw_pressure_max); m_currentData.pressure = qBound(hw_pressure_min, data->value, hw_pressure_max); if (m_typeB || m_singleTouch) m_contacts[m_currentSlot].pressure = m_currentData.pressure; @@ -781,6 +785,9 @@ void QEvdevTouchScreenData::reportPoints() tp.pressure = tp.state == Qt::TouchPointReleased ? 0 : 1; else tp.pressure = (tp.pressure - hw_pressure_min) / qreal(hw_pressure_max - hw_pressure_min); + + if (Q_UNLIKELY(qLcEvents().isDebugEnabled())) + qCDebug(qLcEvents) << "reporting" << tp; } // Let qguiapp pick the target window. -- cgit v1.2.3