Merge remote-tracking branch 'origin/5.15' into dev

Change-Id: I14f539ccfa4ab6e21188b98c314bdb030f3b9f70
This commit is contained in:
Qt Forward Merge Bot 2019-10-11 01:01:14 +02:00
commit 50d41f14ee
41 changed files with 2643 additions and 507 deletions

View File

@ -141,7 +141,8 @@ bool
BuildsMetaMakefileGenerator::write() BuildsMetaMakefileGenerator::write()
{ {
Build *glue = nullptr; Build *glue = nullptr;
if(!makefiles.isEmpty() && !makefiles.first()->build.isNull()) { if(!makefiles.isEmpty() && !makefiles.first()->build.isNull()
&& Option::qmake_mode != Option::QMAKE_GENERATE_PRL) {
glue = new Build; glue = new Build;
glue->name = name; glue->name = name;
glue->makefile = createMakefileGenerator(project, true); glue->makefile = createMakefileGenerator(project, true);

View File

@ -118,16 +118,16 @@ public:
return false; return false;
} }
bool runIterations(Iterator sequenceBeginIterator, int begin, int end, ReducedResultType *) override bool runIterations(Iterator sequenceBeginIterator, int beginIndex, int endIndex, ReducedResultType *) override
{ {
IntermediateResults<typename MapFunctor::result_type> results; IntermediateResults<typename MapFunctor::result_type> results;
results.begin = begin; results.begin = beginIndex;
results.end = end; results.end = endIndex;
results.vector.reserve(end - begin); results.vector.reserve(endIndex - beginIndex);
Iterator it = sequenceBeginIterator; Iterator it = sequenceBeginIterator;
std::advance(it, begin); std::advance(it, beginIndex);
for (int i = begin; i < end; ++i) { for (int i = beginIndex; i < endIndex; ++i) {
results.vector.append(map(*(it))); results.vector.append(map(*(it)));
std::advance(it, 1); std::advance(it, 1);
} }
@ -176,13 +176,13 @@ public:
return true; 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; Iterator it = sequenceBeginIterator;
std::advance(it, begin); std::advance(it, beginIndex);
for (int i = begin; i < end; ++i) { for (int i = beginIndex; i < endIndex; ++i) {
runIteration(it, i, results + (i - begin)); runIteration(it, i, results + (i - beginIndex));
std::advance(it, 1); std::advance(it, 1);
} }

View File

@ -727,6 +727,11 @@ QLibraryInfo::rawLocation(LibraryLocation loc, PathGroup group)
#ifndef QT_BUILD_QMAKE_BOOTSTRAP #ifndef QT_BUILD_QMAKE_BOOTSTRAP
if (!fromConf) { if (!fromConf) {
// "volatile" here is a hack to prevent compilers from doing a
// compile-time strlen() on "path". The issue is that Qt installers
// will binary-patch the Qt installation paths -- in such scenarios, Qt
// will be built with a dummy path, thus the compile-time result of
// strlen is meaningless.
const char * volatile path = 0; const char * volatile path = 0;
if (loc == PrefixPath) { if (loc == PrefixPath) {
path = getPrefix( path = getPrefix(

View File

@ -566,7 +566,19 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
\value TriangleFanTopology Indicates that QRhiGraphicsPipeline::setTopology() \value TriangleFanTopology Indicates that QRhiGraphicsPipeline::setTopology()
supports QRhiGraphicsPipeline::TriangleFan. supports QRhiGraphicsPipeline::TriangleFan.
*/
\value ReadBackNonUniformBuffer Indicates that
\l{QRhiResourceUpdateBatch::readBackBuffer()}{reading buffer contents} is
supported for QRhiBuffer instances with a usage different than
UniformBuffer. While this is supported in the majority of cases, it will be
unsupported with OpenGL ES older than 3.0.
\value ReadBackNonBaseMipLevel Indicates that specifying a mip level other
than 0 is supported when reading back texture contents. When not supported,
specifying a non-zero level in QRhiReadbackDescription leads to returning
an all-zero image. In practice this feature will be unsupported with OpenGL
ES 2.0, while it will likely be supported everywhere else.
*/
/*! /*!
\enum QRhi::BeginFrameFlag \enum QRhi::BeginFrameFlag
@ -3072,9 +3084,13 @@ QDebug operator<<(QDebug dbg, const QRhiShaderResourceBindings &srb)
\inmodule QtGui \inmodule QtGui
\brief Graphics pipeline state resource. \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 \note Setting the shader resource bindings is mandatory. The referenced
QRhiShaderResourceBindings must already be built by the time build() is 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 \note Setting the render pass descriptor is mandatory. To obtain a
QRhiRenderPassDescriptor that can be passed to setRenderPassDescriptor(), QRhiRenderPassDescriptor that can be passed to setRenderPassDescriptor(),
@ -3083,8 +3099,6 @@ QDebug operator<<(QDebug dbg, const QRhiShaderResourceBindings &srb)
\note Setting the vertex input layout is mandatory. \note Setting the 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 \note sampleCount() defaults to 1 and must match the sample count of the
render target's color and depth stencil attachments. render target's color and depth stencil attachments.
@ -3900,6 +3914,46 @@ quint32 QRhiImplementation::approxByteSizeForTexture(QRhiTexture::Format format,
return approxSize; 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 \internal
*/ */
@ -4175,7 +4229,7 @@ void QRhiResourceUpdateBatch::merge(QRhiResourceUpdateBatch *other)
void QRhiResourceUpdateBatch::updateDynamicBuffer(QRhiBuffer *buf, int offset, int size, const void *data) void QRhiResourceUpdateBatch::updateDynamicBuffer(QRhiBuffer *buf, int offset, int size, const void *data)
{ {
if (size > 0) if (size > 0)
d->dynamicBufferUpdates.append({ buf, offset, size, data }); d->bufferOps.append(QRhiResourceUpdateBatchPrivate::BufferOp::dynamicUpdate(buf, offset, size, data));
} }
/*! /*!
@ -4189,7 +4243,7 @@ void QRhiResourceUpdateBatch::updateDynamicBuffer(QRhiBuffer *buf, int offset, i
void QRhiResourceUpdateBatch::uploadStaticBuffer(QRhiBuffer *buf, int offset, int size, const void *data) void QRhiResourceUpdateBatch::uploadStaticBuffer(QRhiBuffer *buf, int offset, int size, const void *data)
{ {
if (size > 0) if (size > 0)
d->staticBufferUploads.append({ buf, offset, size, data }); d->bufferOps.append(QRhiResourceUpdateBatchPrivate::BufferOp::staticUpload(buf, offset, size, data));
} }
/*! /*!
@ -4199,7 +4253,28 @@ void QRhiResourceUpdateBatch::uploadStaticBuffer(QRhiBuffer *buf, int offset, in
void QRhiResourceUpdateBatch::uploadStaticBuffer(QRhiBuffer *buf, const void *data) void QRhiResourceUpdateBatch::uploadStaticBuffer(QRhiBuffer *buf, const void *data)
{ {
if (buf->size() > 0) if (buf->size() > 0)
d->staticBufferUploads.append({ buf, 0, 0, data }); d->bufferOps.append(QRhiResourceUpdateBatchPrivate::BufferOp::staticUpload(buf, 0, 0, data));
}
/*!
Enqueues reading back a region of the QRhiBuffer \a buf. The size of the
region is specified by \a size in bytes, \a offset is the offset in bytes
to start reading from.
A readback is asynchronous. \a result contains a callback that is invoked
when the operation has completed. The data is provided in
QRhiBufferReadbackResult::data. Upon successful completion that QByteArray
will have a size equal to \a size. On failure the QByteArray will be empty.
\note Reading buffers with a usage different than QRhiBuffer::UniformBuffer
is supported only when the QRhi::ReadBackNonUniformBuffer feature is
reported as supported.
\a readBackTexture(), QRhi::isFeatureSupported()
*/
void QRhiResourceUpdateBatch::readBackBuffer(QRhiBuffer *buf, int offset, int size, QRhiBufferReadbackResult *result)
{
d->bufferOps.append(QRhiResourceUpdateBatchPrivate::BufferOp::read(buf, offset, size, result));
} }
/*! /*!
@ -4212,7 +4287,7 @@ void QRhiResourceUpdateBatch::uploadStaticBuffer(QRhiBuffer *buf, const void *da
void QRhiResourceUpdateBatch::uploadTexture(QRhiTexture *tex, const QRhiTextureUploadDescription &desc) void QRhiResourceUpdateBatch::uploadTexture(QRhiTexture *tex, const QRhiTextureUploadDescription &desc)
{ {
if (desc.cbeginEntries() != desc.cendEntries()) if (desc.cbeginEntries() != desc.cendEntries())
d->textureOps.append(QRhiResourceUpdateBatchPrivate::TextureOp::textureUpload(tex, desc)); d->textureOps.append(QRhiResourceUpdateBatchPrivate::TextureOp::upload(tex, desc));
} }
/*! /*!
@ -4237,7 +4312,7 @@ void QRhiResourceUpdateBatch::uploadTexture(QRhiTexture *tex, const QImage &imag
*/ */
void QRhiResourceUpdateBatch::copyTexture(QRhiTexture *dst, QRhiTexture *src, const QRhiTextureCopyDescription &desc) void QRhiResourceUpdateBatch::copyTexture(QRhiTexture *dst, QRhiTexture *src, const QRhiTextureCopyDescription &desc)
{ {
d->textureOps.append(QRhiResourceUpdateBatchPrivate::TextureOp::textureCopy(dst, src, desc)); d->textureOps.append(QRhiResourceUpdateBatchPrivate::TextureOp::copy(dst, src, desc));
} }
/*! /*!
@ -4289,7 +4364,7 @@ void QRhiResourceUpdateBatch::copyTexture(QRhiTexture *dst, QRhiTexture *src, co
*/ */
void QRhiResourceUpdateBatch::readBackTexture(const QRhiReadbackDescription &rb, QRhiReadbackResult *result) void QRhiResourceUpdateBatch::readBackTexture(const QRhiReadbackDescription &rb, QRhiReadbackResult *result)
{ {
d->textureOps.append(QRhiResourceUpdateBatchPrivate::TextureOp::textureRead(rb, result)); d->textureOps.append(QRhiResourceUpdateBatchPrivate::TextureOp::read(rb, result));
} }
/*! /*!
@ -4301,7 +4376,7 @@ void QRhiResourceUpdateBatch::readBackTexture(const QRhiReadbackDescription &rb,
*/ */
void QRhiResourceUpdateBatch::generateMips(QRhiTexture *tex, int layer) void QRhiResourceUpdateBatch::generateMips(QRhiTexture *tex, int layer)
{ {
d->textureOps.append(QRhiResourceUpdateBatchPrivate::TextureOp::textureMipGen(tex, layer)); d->textureOps.append(QRhiResourceUpdateBatchPrivate::TextureOp::genMips(tex, layer));
} }
/*! /*!
@ -4350,8 +4425,7 @@ void QRhiResourceUpdateBatchPrivate::free()
{ {
Q_ASSERT(poolIndex >= 0 && rhi->resUpdPool[poolIndex] == q); Q_ASSERT(poolIndex >= 0 && rhi->resUpdPool[poolIndex] == q);
dynamicBufferUpdates.clear(); bufferOps.clear();
staticBufferUploads.clear();
textureOps.clear(); textureOps.clear();
rhi->resUpdPoolMap.clearBit(poolIndex); rhi->resUpdPoolMap.clearBit(poolIndex);
@ -4360,9 +4434,13 @@ void QRhiResourceUpdateBatchPrivate::free()
void QRhiResourceUpdateBatchPrivate::merge(QRhiResourceUpdateBatchPrivate *other) void QRhiResourceUpdateBatchPrivate::merge(QRhiResourceUpdateBatchPrivate *other)
{ {
dynamicBufferUpdates += other->dynamicBufferUpdates; bufferOps.reserve(bufferOps.size() + other->bufferOps.size());
staticBufferUploads += other->staticBufferUploads; for (const BufferOp &op : qAsConst(other->bufferOps))
textureOps += other->textureOps; bufferOps.append(op);
textureOps.reserve(textureOps.size() + other->textureOps.size());
for (const TextureOp &op : qAsConst(other->textureOps))
textureOps.append(op);
} }
/*! /*!

View File

@ -72,7 +72,6 @@ class QRhiCommandBuffer;
class QRhiResourceUpdateBatch; class QRhiResourceUpdateBatch;
class QRhiResourceUpdateBatchPrivate; class QRhiResourceUpdateBatchPrivate;
class QRhiProfiler; class QRhiProfiler;
class QRhiShaderResourceBindingPrivate;
class Q_GUI_EXPORT QRhiDepthStencilClearValue class Q_GUI_EXPORT QRhiDepthStencilClearValue
{ {
@ -1355,6 +1354,12 @@ struct Q_GUI_EXPORT QRhiReadbackResult
QByteArray data; QByteArray data;
}; // non-movable due to the std::function }; // non-movable due to the std::function
struct Q_GUI_EXPORT QRhiBufferReadbackResult
{
std::function<void()> completed = nullptr;
QByteArray data;
};
class Q_GUI_EXPORT QRhiResourceUpdateBatch class Q_GUI_EXPORT QRhiResourceUpdateBatch
{ {
public: public:
@ -1367,6 +1372,7 @@ public:
void updateDynamicBuffer(QRhiBuffer *buf, int offset, int size, const void *data); 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, int offset, int size, const void *data);
void uploadStaticBuffer(QRhiBuffer *buf, 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 QRhiTextureUploadDescription &desc);
void uploadTexture(QRhiTexture *tex, const QImage &image); void uploadTexture(QRhiTexture *tex, const QImage &image);
void copyTexture(QRhiTexture *dst, QRhiTexture *src, const QRhiTextureCopyDescription &desc = QRhiTextureCopyDescription()); void copyTexture(QRhiTexture *dst, QRhiTexture *src, const QRhiTextureCopyDescription &desc = QRhiTextureCopyDescription());
@ -1428,7 +1434,9 @@ public:
VertexShaderPointSize, VertexShaderPointSize,
BaseVertex, BaseVertex,
BaseInstance, BaseInstance,
TriangleFanTopology TriangleFanTopology,
ReadBackNonUniformBuffer,
ReadBackNonBaseMipLevel
}; };
enum BeginFrameFlag { enum BeginFrameFlag {

View File

@ -205,6 +205,8 @@ public:
cleanupCallbacks.append(callback); cleanupCallbacks.append(callback);
} }
bool sanityCheckGraphicsPipeline(QRhiGraphicsPipeline *ps);
QRhi *q; QRhi *q;
static const int MAX_SHADER_CACHE_ENTRIES = 128; static const int MAX_SHADER_CACHE_ENTRIES = 128;
@ -218,7 +220,7 @@ private:
QRhi::Implementation implType; QRhi::Implementation implType;
QThread *implThread; QThread *implThread;
QRhiProfiler profiler; QRhiProfiler profiler;
QVector<QRhiResourceUpdateBatch *> resUpdPool; QVarLengthArray<QRhiResourceUpdateBatch *, 4> resUpdPool;
QBitArray resUpdPoolMap; QBitArray resUpdPoolMap;
QSet<QRhiResource *> resources; QSet<QRhiResource *> resources;
QSet<QRhiResource *> pendingReleaseAndDestroyResources; QSet<QRhiResource *> pendingReleaseAndDestroyResources;
@ -271,26 +273,49 @@ bool qrhi_toTopLeftRenderTargetRect(const QSize &outputSize, const std::array<T,
class QRhiResourceUpdateBatchPrivate class QRhiResourceUpdateBatchPrivate
{ {
public: public:
struct DynamicBufferUpdate { struct BufferOp {
DynamicBufferUpdate() { } enum Type {
DynamicBufferUpdate(QRhiBuffer *buf_, int offset_, int size_, const void *data_) DynamicUpdate,
: buf(buf_), offset(offset_), data(reinterpret_cast<const char *>(data_), size_) StaticUpload,
{ } Read
};
QRhiBuffer *buf = nullptr; Type type;
int offset = 0; QRhiBuffer *buf;
int offset;
QByteArray data; QByteArray data;
}; int readSize;
QRhiBufferReadbackResult *result;
struct StaticBufferUpload { static BufferOp dynamicUpdate(QRhiBuffer *buf, int offset, int size, const void *data)
StaticBufferUpload() { } {
StaticBufferUpload(QRhiBuffer *buf_, int offset_, int size_, const void *data_) BufferOp op;
: buf(buf_), offset(offset_), data(reinterpret_cast<const char *>(data_), size_ ? size_ : buf_->size()) op.type = DynamicUpdate;
{ } op.buf = buf;
op.offset = offset;
op.data = QByteArray(reinterpret_cast<const char *>(data), size ? size : buf->size());
return op;
}
QRhiBuffer *buf = nullptr; static BufferOp staticUpload(QRhiBuffer *buf, int offset, int size, const void *data)
int offset = 0; {
QByteArray data; BufferOp op;
op.type = StaticUpload;
op.buf = buf;
op.offset = offset;
op.data = QByteArray(reinterpret_cast<const char *>(data), size ? size : buf->size());
return op;
}
static BufferOp read(QRhiBuffer *buf, int offset, int size, QRhiBufferReadbackResult *result)
{
BufferOp op;
op.type = Read;
op.buf = buf;
op.offset = offset;
op.readSize = size;
op.result = result;
return op;
}
}; };
struct TextureOp { struct TextureOp {
@ -298,73 +323,62 @@ public:
Upload, Upload,
Copy, Copy,
Read, Read,
MipGen GenMips
}; };
Type type; Type type;
struct SUpload { QRhiTexture *dst;
QRhiTexture *tex = nullptr; // Specifying multiple uploads for a subresource must be supported.
// Specifying multiple uploads for a subresource must be supported. // In the backend this can then end up, where applicable, as a
// In the backend this can then end up, where applicable, as a // single, batched copy operation with only one set of barriers.
// single, batched copy operation with only one set of barriers. // This helps when doing for example glyph cache fills.
// This helps when doing for example glyph cache fills. QVector<QRhiTextureSubresourceUploadDescription> subresDesc[QRhi::MAX_LAYERS][QRhi::MAX_LEVELS];
QVector<QRhiTextureSubresourceUploadDescription> subresDesc[QRhi::MAX_LAYERS][QRhi::MAX_LEVELS]; QRhiTexture *src;
} upload; QRhiTextureCopyDescription desc;
struct SCopy { QRhiReadbackDescription rb;
QRhiTexture *dst = nullptr; QRhiReadbackResult *result;
QRhiTexture *src = nullptr; int layer;
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) static TextureOp upload(QRhiTexture *tex, const QRhiTextureUploadDescription &desc)
{ {
TextureOp op; TextureOp op;
op.type = Upload; op.type = Upload;
op.upload.tex = tex; op.dst = tex;
for (auto it = desc.cbeginEntries(), itEnd = desc.cendEntries(); it != itEnd; ++it) 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; return op;
} }
static TextureOp textureCopy(QRhiTexture *dst, QRhiTexture *src, const QRhiTextureCopyDescription &desc) static TextureOp copy(QRhiTexture *dst, QRhiTexture *src, const QRhiTextureCopyDescription &desc)
{ {
TextureOp op; TextureOp op;
op.type = Copy; op.type = Copy;
op.copy.dst = dst; op.dst = dst;
op.copy.src = src; op.src = src;
op.copy.desc = desc; op.desc = desc;
return op; return op;
} }
static TextureOp textureRead(const QRhiReadbackDescription &rb, QRhiReadbackResult *result) static TextureOp read(const QRhiReadbackDescription &rb, QRhiReadbackResult *result)
{ {
TextureOp op; TextureOp op;
op.type = Read; op.type = Read;
op.read.rb = rb; op.rb = rb;
op.read.result = result; op.result = result;
return op; return op;
} }
static TextureOp textureMipGen(QRhiTexture *tex, int layer) static TextureOp genMips(QRhiTexture *tex, int layer)
{ {
TextureOp op; TextureOp op;
op.type = MipGen; op.type = GenMips;
op.mipgen.tex = tex; op.dst = tex;
op.mipgen.layer = layer; op.layer = layer;
return op; return op;
} }
}; };
QVector<DynamicBufferUpdate> dynamicBufferUpdates; QVarLengthArray<BufferOp, 1024> bufferOps;
QVector<StaticBufferUpload> staticBufferUploads; QVarLengthArray<TextureOp, 256> textureOps;
QVector<TextureOp> textureOps;
QRhiResourceUpdateBatch *q = nullptr; QRhiResourceUpdateBatch *q = nullptr;
QRhiImplementation *rhi = nullptr; QRhiImplementation *rhi = nullptr;
@ -376,8 +390,7 @@ public:
static QRhiResourceUpdateBatchPrivate *get(QRhiResourceUpdateBatch *b) { return b->d; } static QRhiResourceUpdateBatchPrivate *get(QRhiResourceUpdateBatch *b) { return b->d; }
}; };
Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::BufferOp, Q_MOVABLE_TYPE);
Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::StaticBufferUpload, Q_MOVABLE_TYPE);
Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::TextureOp, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::TextureOp, Q_MOVABLE_TYPE);
template<typename T> template<typename T>

View File

@ -471,6 +471,10 @@ bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const
return true; return true;
case QRhi::TriangleFanTopology: case QRhi::TriangleFanTopology:
return false; return false;
case QRhi::ReadBackNonUniformBuffer:
return true;
case QRhi::ReadBackNonBaseMipLevel:
return true;
default: default:
Q_UNREACHABLE(); Q_UNREACHABLE();
return false; return false;
@ -1323,61 +1327,105 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates); QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates);
QRhiProfilerPrivate *rhiP = profilerPrivateOrNull(); QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
for (const QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate &u : ud->dynamicBufferUpdates) { for (const QRhiResourceUpdateBatchPrivate::BufferOp &u : ud->bufferOps) {
QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, u.buf); if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::DynamicUpdate) {
Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic); QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, u.buf);
memcpy(bufD->dynBuf.data() + u.offset, u.data.constData(), size_t(u.data.size())); Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
bufD->hasPendingDynamicUpdates = true; 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;
for (const QRhiResourceUpdateBatchPrivate::StaticBufferUpload &u : ud->staticBufferUploads) { D3D11_BUFFER_DESC desc;
QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, u.buf); memset(&desc, 0, sizeof(desc));
Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic); desc.ByteWidth = readback.byteSize;
Q_ASSERT(u.offset + u.data.size() <= bufD->m_size); desc.Usage = D3D11_USAGE_STAGING;
QD3D11CommandBuffer::Command cmd; desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
cmd.cmd = QD3D11CommandBuffer::Command::UpdateSubRes; HRESULT hr = dev->CreateBuffer(&desc, nullptr, &readback.stagingBuf);
cmd.args.updateSubRes.dst = bufD->buffer; if (FAILED(hr)) {
cmd.args.updateSubRes.dstSubRes = 0; qWarning("Failed to create buffer: %s", qPrintable(comErrorMessage(hr)));
cmd.args.updateSubRes.src = cbD->retainData(u.data); continue;
cmd.args.updateSubRes.srcRowPitch = 0; }
// Specify the region (even when offset is 0 and all data is provided) QRHI_PROF_F(newReadbackBuffer(qint64(qintptr(readback.stagingBuf)), bufD, readback.byteSize));
// since the ID3D11Buffer's size is rounded up to be a multiple of 256
// while the data we have has the original size. QD3D11CommandBuffer::Command cmd;
D3D11_BOX box; cmd.cmd = QD3D11CommandBuffer::Command::CopySubRes;
box.left = UINT(u.offset); cmd.args.copySubRes.dst = readback.stagingBuf;
box.top = box.front = 0; cmd.args.copySubRes.dstSubRes = 0;
box.back = box.bottom = 1; cmd.args.copySubRes.dstX = 0;
box.right = UINT(u.offset + u.data.size()); // no -1: right, bottom, back are exclusive, see D3D11_BOX doc cmd.args.copySubRes.dstY = 0;
cmd.args.updateSubRes.hasDstBox = true; cmd.args.copySubRes.src = bufD->buffer;
cmd.args.updateSubRes.dstBox = box; cmd.args.copySubRes.srcSubRes = 0;
cbD->commands.append(cmd); 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) { for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) {
if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) { 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 layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
for (int level = 0; level < QRhi::MAX_LEVELS; ++level) { 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); enqueueSubresUpload(texD, cbD, layer, level, subresDesc);
} }
} }
} else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) { } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) {
Q_ASSERT(u.copy.src && u.copy.dst); Q_ASSERT(u.src && u.dst);
QD3D11Texture *srcD = QRHI_RES(QD3D11Texture, u.copy.src); QD3D11Texture *srcD = QRHI_RES(QD3D11Texture, u.src);
QD3D11Texture *dstD = QRHI_RES(QD3D11Texture, u.copy.dst); QD3D11Texture *dstD = QRHI_RES(QD3D11Texture, u.dst);
UINT srcSubRes = D3D11CalcSubresource(UINT(u.copy.desc.sourceLevel()), UINT(u.copy.desc.sourceLayer()), srcD->mipLevelCount); UINT srcSubRes = D3D11CalcSubresource(UINT(u.desc.sourceLevel()), UINT(u.desc.sourceLayer()), srcD->mipLevelCount);
UINT dstSubRes = D3D11CalcSubresource(UINT(u.copy.desc.destinationLevel()), UINT(u.copy.desc.destinationLayer()), dstD->mipLevelCount); UINT dstSubRes = D3D11CalcSubresource(UINT(u.desc.destinationLevel()), UINT(u.desc.destinationLayer()), dstD->mipLevelCount);
const QPoint dp = u.copy.desc.destinationTopLeft(); const QPoint dp = u.desc.destinationTopLeft();
const QSize size = u.copy.desc.pixelSize().isEmpty() ? srcD->m_pixelSize : u.copy.desc.pixelSize(); const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
const QPoint sp = u.copy.desc.sourceTopLeft(); const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
const QPoint sp = u.desc.sourceTopLeft();
D3D11_BOX srcBox; D3D11_BOX srcBox;
srcBox.left = UINT(sp.x()); srcBox.left = UINT(sp.x());
srcBox.top = UINT(sp.y()); srcBox.top = UINT(sp.y());
srcBox.front = 0; srcBox.front = 0;
// back, right, bottom are exclusive // back, right, bottom are exclusive
srcBox.right = srcBox.left + UINT(size.width()); srcBox.right = srcBox.left + UINT(copySize.width());
srcBox.bottom = srcBox.top + UINT(size.height()); srcBox.bottom = srcBox.top + UINT(copySize.height());
srcBox.back = 1; srcBox.back = 1;
QD3D11CommandBuffer::Command cmd; QD3D11CommandBuffer::Command cmd;
cmd.cmd = QD3D11CommandBuffer::Command::CopySubRes; cmd.cmd = QD3D11CommandBuffer::Command::CopySubRes;
@ -1391,16 +1439,16 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
cmd.args.copySubRes.srcBox = srcBox; cmd.args.copySubRes.srcBox = srcBox;
cbD->commands.append(cmd); cbD->commands.append(cmd);
} else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) { } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) {
ActiveReadback aRb; TextureReadback readback;
aRb.desc = u.read.rb; readback.desc = u.rb;
aRb.result = u.read.result; readback.result = u.result;
ID3D11Resource *src; ID3D11Resource *src;
DXGI_FORMAT dxgiFormat; DXGI_FORMAT dxgiFormat;
QSize pixelSize; QSize pixelSize;
QRhiTexture::Format format; QRhiTexture::Format format;
UINT subres = 0; UINT subres = 0;
QD3D11Texture *texD = QRHI_RES(QD3D11Texture, u.read.rb.texture()); QD3D11Texture *texD = QRHI_RES(QD3D11Texture, u.rb.texture());
QD3D11SwapChain *swapChainD = nullptr; QD3D11SwapChain *swapChainD = nullptr;
if (texD) { if (texD) {
@ -1410,9 +1458,9 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
} }
src = texD->tex; src = texD->tex;
dxgiFormat = texD->dxgiFormat; dxgiFormat = texD->dxgiFormat;
pixelSize = u.read.rb.level() > 0 ? q->sizeForMipLevel(u.read.rb.level(), texD->m_pixelSize) : texD->m_pixelSize; pixelSize = q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize);
format = texD->m_format; 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 { } else {
Q_ASSERT(contextState.currentSwapChain); Q_ASSERT(contextState.currentSwapChain);
swapChainD = QRHI_RES(QD3D11SwapChain, contextState.currentSwapChain); swapChainD = QRHI_RES(QD3D11SwapChain, contextState.currentSwapChain);
@ -1435,9 +1483,9 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
if (format == QRhiTexture::UnknownFormat) if (format == QRhiTexture::UnknownFormat)
continue; continue;
} }
quint32 bufSize = 0; quint32 byteSize = 0;
quint32 bpl = 0; quint32 bpl = 0;
textureFormatInfo(format, pixelSize, &bpl, &bufSize); textureFormatInfo(format, pixelSize, &bpl, &byteSize);
D3D11_TEXTURE2D_DESC desc; D3D11_TEXTURE2D_DESC desc;
memset(&desc, 0, sizeof(desc)); memset(&desc, 0, sizeof(desc));
@ -1457,7 +1505,7 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
} }
QRHI_PROF_F(newReadbackBuffer(qint64(qintptr(stagingTex)), QRHI_PROF_F(newReadbackBuffer(qint64(qintptr(stagingTex)),
texD ? static_cast<QRhiResource *>(texD) : static_cast<QRhiResource *>(swapChainD), texD ? static_cast<QRhiResource *>(texD) : static_cast<QRhiResource *>(swapChainD),
bufSize)); byteSize));
QD3D11CommandBuffer::Command cmd; QD3D11CommandBuffer::Command cmd;
cmd.cmd = QD3D11CommandBuffer::Command::CopySubRes; cmd.cmd = QD3D11CommandBuffer::Command::CopySubRes;
@ -1470,18 +1518,18 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
cmd.args.copySubRes.hasSrcBox = false; cmd.args.copySubRes.hasSrcBox = false;
cbD->commands.append(cmd); cbD->commands.append(cmd);
aRb.stagingTex = stagingTex; readback.stagingTex = stagingTex;
aRb.bufSize = bufSize; readback.byteSize = byteSize;
aRb.bpl = bpl; readback.bpl = bpl;
aRb.pixelSize = pixelSize; readback.pixelSize = pixelSize;
aRb.format = format; readback.format = format;
activeReadbacks.append(aRb); activeTextureReadbacks.append(readback);
} else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::MipGen) { } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::GenMips) {
Q_ASSERT(u.mipgen.tex->flags().testFlag(QRhiTexture::UsedWithGenerateMips)); Q_ASSERT(u.dst->flags().testFlag(QRhiTexture::UsedWithGenerateMips));
QD3D11CommandBuffer::Command cmd; QD3D11CommandBuffer::Command cmd;
cmd.cmd = QD3D11CommandBuffer::Command::GenMip; 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); cbD->commands.append(cmd);
} }
} }
@ -1494,37 +1542,58 @@ void QRhiD3D11::finishActiveReadbacks()
QVarLengthArray<std::function<void()>, 4> completedCallbacks; QVarLengthArray<std::function<void()>, 4> completedCallbacks;
QRhiProfilerPrivate *rhiP = profilerPrivateOrNull(); QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
for (int i = activeReadbacks.count() - 1; i >= 0; --i) { for (int i = activeTextureReadbacks.count() - 1; i >= 0; --i) {
const QRhiD3D11::ActiveReadback &aRb(activeReadbacks[i]); const QRhiD3D11::TextureReadback &readback(activeTextureReadbacks[i]);
aRb.result->format = aRb.format; readback.result->format = readback.format;
aRb.result->pixelSize = aRb.pixelSize; readback.result->pixelSize = readback.pixelSize;
aRb.result->data.resize(int(aRb.bufSize));
D3D11_MAPPED_SUBRESOURCE mp; D3D11_MAPPED_SUBRESOURCE mp;
HRESULT hr = context->Map(aRb.stagingTex, 0, D3D11_MAP_READ, 0, &mp); HRESULT hr = context->Map(readback.stagingTex, 0, D3D11_MAP_READ, 0, &mp);
if (FAILED(hr)) { if (SUCCEEDED(hr)) {
readback.result->data.resize(int(readback.byteSize));
// nothing says the rows are tightly packed in the texture, must take
// the stride into account
char *dst = readback.result->data.data();
char *src = static_cast<char *>(mp.pData);
for (int y = 0, h = readback.pixelSize.height(); y != h; ++y) {
memcpy(dst, src, readback.bpl);
dst += readback.bpl;
src += mp.RowPitch;
}
context->Unmap(readback.stagingTex, 0);
} else {
qWarning("Failed to map readback staging texture: %s", qPrintable(comErrorMessage(hr))); 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 readback.stagingTex->Release();
char *dst = aRb.result->data.data(); QRHI_PROF_F(releaseReadbackBuffer(qint64(qintptr(readback.stagingTex))));
char *src = static_cast<char *>(mp.pData);
for (int y = 0, h = aRb.pixelSize.height(); y != h; ++y) { if (readback.result->completed)
memcpy(dst, src, aRb.bpl); completedCallbacks.append(readback.result->completed);
dst += aRb.bpl;
src += mp.RowPitch; 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(); readback.stagingBuf->Release();
QRHI_PROF_F(releaseReadbackBuffer(qint64(qintptr(aRb.stagingTex)))); QRHI_PROF_F(releaseReadbackBuffer(qint64(qintptr(readback.stagingBuf))));
if (aRb.result->completed) if (readback.result->completed)
completedCallbacks.append(aRb.result->completed); completedCallbacks.append(readback.result->completed);
activeReadbacks.removeAt(i); activeBufferReadbacks.removeAt(i);
} }
for (auto f : completedCallbacks) for (auto f : completedCallbacks)
@ -3423,6 +3492,8 @@ bool QD3D11GraphicsPipeline::build()
release(); release();
QRHI_RES_RHI(QRhiD3D11); QRHI_RES_RHI(QRhiD3D11);
if (!rhiD->sanityCheckGraphicsPipeline(this))
return false;
D3D11_RASTERIZER_DESC rastDesc; D3D11_RASTERIZER_DESC rastDesc;
memset(&rastDesc, 0, sizeof(rastDesc)); memset(&rastDesc, 0, sizeof(rastDesc));

View File

@ -678,16 +678,22 @@ public:
QD3D11CommandBuffer cbWrapper; QD3D11CommandBuffer cbWrapper;
} ofr; } ofr;
struct ActiveReadback { struct TextureReadback {
QRhiReadbackDescription desc; QRhiReadbackDescription desc;
QRhiReadbackResult *result; QRhiReadbackResult *result;
ID3D11Texture2D *stagingTex; ID3D11Texture2D *stagingTex;
quint32 bufSize; quint32 byteSize;
quint32 bpl; quint32 bpl;
QSize pixelSize; QSize pixelSize;
QRhiTexture::Format format; QRhiTexture::Format format;
}; };
QVector<ActiveReadback> activeReadbacks; QVector<TextureReadback> activeTextureReadbacks;
struct BufferReadback {
QRhiBufferReadbackResult *result;
quint32 byteSize;
ID3D11Buffer *stagingBuf;
};
QVector<BufferReadback> activeBufferReadbacks;
struct Shader { struct Shader {
Shader() = default; Shader() = default;
@ -711,7 +717,8 @@ public:
} deviceCurse; } 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 QT_END_NAMESPACE

View File

@ -276,6 +276,10 @@ QT_BEGIN_NAMESPACE
#define GL_POINT_SPRITE 0x8861 #define GL_POINT_SPRITE 0x8861
#endif #endif
#ifndef GL_MAP_READ_BIT
#define GL_MAP_READ_BIT 0x0001
#endif
Q_DECLARE_LOGGING_CATEGORY(lcOpenGLProgramDiskCache) Q_DECLARE_LOGGING_CATEGORY(lcOpenGLProgramDiskCache)
/*! /*!
@ -492,6 +496,15 @@ bool QRhiGles2::create(QRhi::Flags flags)
else else
caps.textureCompareMode = true; caps.textureCompareMode = true;
// proper as in ES 3.0 (glMapBufferRange), not the old glMapBuffer
// extension(s) (which is not in ES 3.0...messy)
caps.properMapBuffer = f->hasOpenGLExtension(QOpenGLExtensions::MapBufferRange);
if (caps.gles)
caps.nonBaseLevelFramebufferTexture = caps.ctxMajor >= 3; // ES 3.0
else
caps.nonBaseLevelFramebufferTexture = true;
if (!caps.gles) { if (!caps.gles) {
f->glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); f->glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
f->glEnable(GL_POINT_SPRITE); f->glEnable(GL_POINT_SPRITE);
@ -734,6 +747,10 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const
return false; // not in ES 3.2, so won't bother return false; // not in ES 3.2, so won't bother
case QRhi::TriangleFanTopology: case QRhi::TriangleFanTopology:
return true; return true;
case QRhi::ReadBackNonUniformBuffer:
return !caps.gles || caps.properMapBuffer;
case QRhi::ReadBackNonBaseMipLevel:
return caps.nonBaseLevelFramebufferTexture;
default: default:
Q_UNREACHABLE(); Q_UNREACHABLE();
return false; return false;
@ -1417,65 +1434,83 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates); QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates);
for (const QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate &u : ud->dynamicBufferUpdates) { for (const QRhiResourceUpdateBatchPrivate::BufferOp &u : ud->bufferOps) {
QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, u.buf); if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::DynamicUpdate) {
Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic); QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, u.buf);
if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) { Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
memcpy(bufD->ubuf.data() + u.offset, u.data.constData(), size_t(u.data.size())); if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) {
} else { memcpy(bufD->ubuf.data() + u.offset, u.data.constData(), size_t(u.data.size()));
trackedBufferBarrier(cbD, bufD, QGles2Buffer::AccessUpdate); } else {
QGles2CommandBuffer::Command cmd; trackedBufferBarrier(cbD, bufD, QGles2Buffer::AccessUpdate);
cmd.cmd = QGles2CommandBuffer::Command::BufferSubData; QGles2CommandBuffer::Command cmd;
cmd.args.bufferSubData.target = bufD->targetForDataOps; cmd.cmd = QGles2CommandBuffer::Command::BufferSubData;
cmd.args.bufferSubData.buffer = bufD->buffer; cmd.args.bufferSubData.target = bufD->targetForDataOps;
cmd.args.bufferSubData.offset = u.offset; cmd.args.bufferSubData.buffer = bufD->buffer;
cmd.args.bufferSubData.size = u.data.size(); cmd.args.bufferSubData.offset = u.offset;
cmd.args.bufferSubData.data = cbD->retainData(u.data); cmd.args.bufferSubData.size = u.data.size();
cbD->commands.append(cmd); cmd.args.bufferSubData.data = cbD->retainData(u.data);
} cbD->commands.append(cmd);
} }
} else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::StaticUpload) {
for (const QRhiResourceUpdateBatchPrivate::StaticBufferUpload &u : ud->staticBufferUploads) { QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, u.buf);
QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, u.buf); Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic); Q_ASSERT(u.offset + u.data.size() <= bufD->m_size);
Q_ASSERT(u.offset + u.data.size() <= bufD->m_size); if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) {
if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) { memcpy(bufD->ubuf.data() + u.offset, u.data.constData(), size_t(u.data.size()));
memcpy(bufD->ubuf.data() + u.offset, u.data.constData(), size_t(u.data.size())); } else {
} else { trackedBufferBarrier(cbD, bufD, QGles2Buffer::AccessUpdate);
trackedBufferBarrier(cbD, bufD, QGles2Buffer::AccessUpdate); QGles2CommandBuffer::Command cmd;
QGles2CommandBuffer::Command cmd; cmd.cmd = QGles2CommandBuffer::Command::BufferSubData;
cmd.cmd = QGles2CommandBuffer::Command::BufferSubData; cmd.args.bufferSubData.target = bufD->targetForDataOps;
cmd.args.bufferSubData.target = bufD->targetForDataOps; cmd.args.bufferSubData.buffer = bufD->buffer;
cmd.args.bufferSubData.buffer = bufD->buffer; cmd.args.bufferSubData.offset = u.offset;
cmd.args.bufferSubData.offset = u.offset; cmd.args.bufferSubData.size = u.data.size();
cmd.args.bufferSubData.size = u.data.size(); cmd.args.bufferSubData.data = cbD->retainData(u.data);
cmd.args.bufferSubData.data = cbD->retainData(u.data); cbD->commands.append(cmd);
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) { for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) {
if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) { 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 layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
for (int level = 0; level < QRhi::MAX_LEVELS; ++level) { 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); enqueueSubresUpload(texD, cbD, layer, level, subresDesc);
} }
} }
texD->specified = true; texD->specified = true;
} else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) { } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) {
Q_ASSERT(u.copy.src && u.copy.dst); Q_ASSERT(u.src && u.dst);
QGles2Texture *srcD = QRHI_RES(QGles2Texture, u.copy.src); QGles2Texture *srcD = QRHI_RES(QGles2Texture, u.src);
QGles2Texture *dstD = QRHI_RES(QGles2Texture, u.copy.dst); QGles2Texture *dstD = QRHI_RES(QGles2Texture, u.dst);
trackedImageBarrier(cbD, srcD, QGles2Texture::AccessRead); trackedImageBarrier(cbD, srcD, QGles2Texture::AccessRead);
trackedImageBarrier(cbD, dstD, QGles2Texture::AccessUpdate); trackedImageBarrier(cbD, dstD, QGles2Texture::AccessUpdate);
const QSize size = u.copy.desc.pixelSize().isEmpty() ? srcD->m_pixelSize : u.copy.desc.pixelSize(); const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
// do not translate coordinates, even if sp is bottom-left from gl's pov // do not translate coordinates, even if sp is bottom-left from gl's pov
const QPoint sp = u.copy.desc.sourceTopLeft(); const QPoint sp = u.desc.sourceTopLeft();
const QPoint dp = u.copy.desc.destinationTopLeft(); const QPoint dp = u.desc.destinationTopLeft();
const GLenum srcFaceTargetBase = srcD->m_flags.testFlag(QRhiTexture::CubeMap) const GLenum srcFaceTargetBase = srcD->m_flags.testFlag(QRhiTexture::CubeMap)
? GL_TEXTURE_CUBE_MAP_POSITIVE_X : srcD->target; ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : srcD->target;
@ -1485,43 +1520,44 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
QGles2CommandBuffer::Command cmd; QGles2CommandBuffer::Command cmd;
cmd.cmd = QGles2CommandBuffer::Command::CopyTex; 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.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.srcX = sp.x();
cmd.args.copyTex.srcY = sp.y(); cmd.args.copyTex.srcY = sp.y();
cmd.args.copyTex.dstTarget = dstD->target; cmd.args.copyTex.dstTarget = dstD->target;
cmd.args.copyTex.dstTexture = dstD->texture; cmd.args.copyTex.dstTexture = dstD->texture;
cmd.args.copyTex.dstFaceTarget = dstFaceTargetBase + uint(u.copy.desc.destinationLayer()); cmd.args.copyTex.dstFaceTarget = dstFaceTargetBase + uint(u.desc.destinationLayer());
cmd.args.copyTex.dstLevel = u.copy.desc.destinationLevel(); cmd.args.copyTex.dstLevel = u.desc.destinationLevel();
cmd.args.copyTex.dstX = dp.x(); cmd.args.copyTex.dstX = dp.x();
cmd.args.copyTex.dstY = dp.y(); cmd.args.copyTex.dstY = dp.y();
cmd.args.copyTex.w = size.width(); cmd.args.copyTex.w = copySize.width();
cmd.args.copyTex.h = size.height(); cmd.args.copyTex.h = copySize.height();
cbD->commands.append(cmd); cbD->commands.append(cmd);
} else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) { } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) {
QGles2CommandBuffer::Command cmd; QGles2CommandBuffer::Command cmd;
cmd.cmd = QGles2CommandBuffer::Command::ReadPixels; cmd.cmd = QGles2CommandBuffer::Command::ReadPixels;
cmd.args.readPixels.result = u.read.result; cmd.args.readPixels.result = u.result;
QGles2Texture *texD = QRHI_RES(QGles2Texture, u.read.rb.texture()); QGles2Texture *texD = QRHI_RES(QGles2Texture, u.rb.texture());
if (texD) if (texD)
trackedImageBarrier(cbD, texD, QGles2Texture::AccessRead); trackedImageBarrier(cbD, texD, QGles2Texture::AccessRead);
cmd.args.readPixels.texture = texD ? texD->texture : 0; cmd.args.readPixels.texture = texD ? texD->texture : 0;
if (texD) { if (texD) {
cmd.args.readPixels.w = texD->m_pixelSize.width(); const QSize readImageSize = q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize);
cmd.args.readPixels.h = texD->m_pixelSize.height(); cmd.args.readPixels.w = readImageSize.width();
cmd.args.readPixels.h = readImageSize.height();
cmd.args.readPixels.format = texD->m_format; cmd.args.readPixels.format = texD->m_format;
const GLenum faceTargetBase = texD->m_flags.testFlag(QRhiTexture::CubeMap) const GLenum faceTargetBase = texD->m_flags.testFlag(QRhiTexture::CubeMap)
? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target; ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
cmd.args.readPixels.readTarget = faceTargetBase + uint(u.read.rb.layer()); cmd.args.readPixels.readTarget = faceTargetBase + uint(u.rb.layer());
cmd.args.readPixels.level = u.read.rb.level(); cmd.args.readPixels.level = u.rb.level();
} }
cbD->commands.append(cmd); cbD->commands.append(cmd);
} else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::MipGen) { } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::GenMips) {
QGles2Texture *texD = QRHI_RES(QGles2Texture, u.mipgen.tex); QGles2Texture *texD = QRHI_RES(QGles2Texture, u.dst);
trackedImageBarrier(cbD, texD, QGles2Texture::AccessFramebuffer); trackedImageBarrier(cbD, texD, QGles2Texture::AccessFramebuffer);
QGles2CommandBuffer::Command cmd; QGles2CommandBuffer::Command cmd;
cmd.cmd = QGles2CommandBuffer::Command::GenMip; cmd.cmd = QGles2CommandBuffer::Command::GenMip;
@ -2080,6 +2116,33 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
f->glBufferSubData(cmd.args.bufferSubData.target, cmd.args.bufferSubData.offset, cmd.args.bufferSubData.size, f->glBufferSubData(cmd.args.bufferSubData.target, cmd.args.bufferSubData.offset, cmd.args.bufferSubData.size,
cmd.args.bufferSubData.data); cmd.args.bufferSubData.data);
break; break;
case QGles2CommandBuffer::Command::GetBufferSubData:
{
QRhiBufferReadbackResult *result = cmd.args.getBufferSubData.result;
f->glBindBuffer(cmd.args.getBufferSubData.target, cmd.args.getBufferSubData.buffer);
if (caps.gles) {
if (caps.properMapBuffer) {
void *p = f->glMapBufferRange(cmd.args.getBufferSubData.target,
cmd.args.getBufferSubData.offset,
cmd.args.getBufferSubData.size,
GL_MAP_READ_BIT);
if (p) {
result->data.resize(cmd.args.getBufferSubData.size);
memcpy(result->data.data(), p, size_t(cmd.args.getBufferSubData.size));
f->glUnmapBuffer(cmd.args.getBufferSubData.target);
}
}
} else {
result->data.resize(cmd.args.getBufferSubData.size);
f->glGetBufferSubData(cmd.args.getBufferSubData.target,
cmd.args.getBufferSubData.offset,
cmd.args.getBufferSubData.size,
result->data.data());
}
if (result->completed)
result->completed();
}
break;
case QGles2CommandBuffer::Command::CopyTex: case QGles2CommandBuffer::Command::CopyTex:
{ {
GLuint fbo; GLuint fbo;
@ -2101,23 +2164,31 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
QRhiReadbackResult *result = cmd.args.readPixels.result; QRhiReadbackResult *result = cmd.args.readPixels.result;
GLuint tex = cmd.args.readPixels.texture; GLuint tex = cmd.args.readPixels.texture;
GLuint fbo = 0; GLuint fbo = 0;
int mipLevel = 0;
if (tex) { if (tex) {
result->pixelSize = QSize(cmd.args.readPixels.w, cmd.args.readPixels.h); result->pixelSize = QSize(cmd.args.readPixels.w, cmd.args.readPixels.h);
result->format = cmd.args.readPixels.format; result->format = cmd.args.readPixels.format;
f->glGenFramebuffers(1, &fbo); mipLevel = cmd.args.readPixels.level;
f->glBindFramebuffer(GL_FRAMEBUFFER, fbo); if (mipLevel == 0 || caps.nonBaseLevelFramebufferTexture) {
f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, f->glGenFramebuffers(1, &fbo);
cmd.args.readPixels.readTarget, cmd.args.readPixels.texture, cmd.args.readPixels.level); f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
cmd.args.readPixels.readTarget, cmd.args.readPixels.texture, mipLevel);
}
} else { } else {
result->pixelSize = currentSwapChain->pixelSize; result->pixelSize = currentSwapChain->pixelSize;
result->format = QRhiTexture::RGBA8; result->format = QRhiTexture::RGBA8;
// readPixels handles multisample resolving implicitly // readPixels handles multisample resolving implicitly
} }
result->data.resize(result->pixelSize.width() * result->pixelSize.height() * 4); 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. if (mipLevel == 0 || caps.nonBaseLevelFramebufferTexture) {
f->glReadPixels(0, 0, result->pixelSize.width(), result->pixelSize.height(), // With GLES (2.0?) GL_RGBA is the only mandated readback format, so stick with it.
GL_RGBA, GL_UNSIGNED_BYTE, f->glReadPixels(0, 0, result->pixelSize.width(), result->pixelSize.height(),
result->data.data()); GL_RGBA, GL_UNSIGNED_BYTE,
result->data.data());
} else {
result->data.fill('\0');
}
if (fbo) { if (fbo) {
f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject()); f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
f->glDeleteFramebuffers(1, &fbo); f->glDeleteFramebuffers(1, &fbo);
@ -3620,6 +3691,9 @@ bool QGles2GraphicsPipeline::build()
if (!rhiD->ensureContext()) if (!rhiD->ensureContext())
return false; return false;
if (!rhiD->sanityCheckGraphicsPipeline(this))
return false;
drawMode = toGlTopology(m_topology); drawMode = toGlTopology(m_topology);
program = rhiD->f->glCreateProgram(); program = rhiD->f->glCreateProgram();

View File

@ -312,8 +312,8 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer
BindShaderResources, BindShaderResources,
BindFramebuffer, BindFramebuffer,
Clear, Clear,
BufferData,
BufferSubData, BufferSubData,
GetBufferSubData,
CopyTex, CopyTex,
ReadPixels, ReadPixels,
SubImage, SubImage,
@ -401,6 +401,13 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer
int size; int size;
const void *data; // must come from retainData() const void *data; // must come from retainData()
} bufferSubData; } bufferSubData;
struct {
QRhiBufferReadbackResult *result;
GLenum target;
GLuint buffer;
int offset;
int size;
} getBufferSubData;
struct { struct {
GLenum srcFaceTarget; GLenum srcFaceTarget;
GLuint srcTexture; GLuint srcTexture;
@ -744,7 +751,10 @@ public:
rgba8Format(false), rgba8Format(false),
instancing(false), instancing(false),
baseVertex(false), baseVertex(false),
compute(false) compute(false),
textureCompareMode(false),
properMapBuffer(false),
nonBaseLevelFramebufferTexture(false)
{ } { }
int ctxMajor; int ctxMajor;
int ctxMinor; int ctxMinor;
@ -775,6 +785,8 @@ public:
uint baseVertex : 1; uint baseVertex : 1;
uint compute : 1; uint compute : 1;
uint textureCompareMode : 1; uint textureCompareMode : 1;
uint properMapBuffer : 1;
uint nonBaseLevelFramebufferTexture : 1;
} caps; } caps;
QGles2SwapChain *currentSwapChain = nullptr; QGles2SwapChain *currentSwapChain = nullptr;
QVector<GLint> supportedCompressedFormats; QVector<GLint> supportedCompressedFormats;

View File

@ -205,7 +205,7 @@ struct QRhiMetalData
QMetalCommandBuffer cbWrapper; QMetalCommandBuffer cbWrapper;
} ofr; } ofr;
struct ActiveReadback { struct TextureReadback {
int activeFrameSlot = -1; int activeFrameSlot = -1;
QRhiReadbackDescription desc; QRhiReadbackDescription desc;
QRhiReadbackResult *result; QRhiReadbackResult *result;
@ -214,7 +214,7 @@ struct QRhiMetalData
QSize pixelSize; QSize pixelSize;
QRhiTexture::Format format; QRhiTexture::Format format;
}; };
QVector<ActiveReadback> activeReadbacks; QVector<TextureReadback> activeTextureReadbacks;
API_AVAILABLE(macos(10.13), ios(11.0)) MTLCaptureManager *captureMgr; API_AVAILABLE(macos(10.13), ios(11.0)) MTLCaptureManager *captureMgr;
API_AVAILABLE(macos(10.13), ios(11.0)) id<MTLCaptureScope> captureScope = nil; API_AVAILABLE(macos(10.13), ios(11.0)) id<MTLCaptureScope> captureScope = nil;
@ -225,14 +225,14 @@ struct QRhiMetalData
}; };
Q_DECLARE_TYPEINFO(QRhiMetalData::DeferredReleaseEntry, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(QRhiMetalData::DeferredReleaseEntry, Q_MOVABLE_TYPE);
Q_DECLARE_TYPEINFO(QRhiMetalData::ActiveReadback, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(QRhiMetalData::TextureReadback, Q_MOVABLE_TYPE);
struct QMetalBufferData struct QMetalBufferData
{ {
bool managed; bool managed;
bool slotted; bool slotted;
id<MTLBuffer> buf[QMTL_FRAMES_IN_FLIGHT]; id<MTLBuffer> buf[QMTL_FRAMES_IN_FLIGHT];
QVector<QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate> pendingUpdates[QMTL_FRAMES_IN_FLIGHT]; QVarLengthArray<QRhiResourceUpdateBatchPrivate::BufferOp, 16> pendingUpdates[QMTL_FRAMES_IN_FLIGHT];
}; };
struct QMetalRenderBufferData struct QMetalRenderBufferData
@ -367,6 +367,11 @@ bool QRhiMetal::create(QRhi::Flags flags)
else else
d->dev = MTLCreateSystemDefaultDevice(); d->dev = MTLCreateSystemDefaultDevice();
if (!d->dev) {
qWarning("No MTLDevice");
return false;
}
qCDebug(QRHI_LOG_INFO, "Metal device: %s", qPrintable(QString::fromNSString([d->dev name]))); qCDebug(QRHI_LOG_INFO, "Metal device: %s", qPrintable(QString::fromNSString([d->dev name])));
if (importedCmdQueue) if (importedCmdQueue)
@ -552,6 +557,10 @@ bool QRhiMetal::isFeatureSupported(QRhi::Feature feature) const
return true; return true;
case QRhi::TriangleFanTopology: case QRhi::TriangleFanTopology:
return false; return false;
case QRhi::ReadBackNonUniformBuffer:
return true;
case QRhi::ReadBackNonBaseMipLevel:
return true;
default: default:
Q_UNREACHABLE(); Q_UNREACHABLE();
return false; return false;
@ -1541,21 +1550,33 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates); QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates);
QRhiProfilerPrivate *rhiP = profilerPrivateOrNull(); QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
for (const QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate &u : ud->dynamicBufferUpdates) { for (const QRhiResourceUpdateBatchPrivate::BufferOp &u : ud->bufferOps) {
QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, u.buf); if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::DynamicUpdate) {
Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic); QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, u.buf);
for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
bufD->d->pendingUpdates[i].append(u); 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 // Due to the Metal API the handling of static and dynamic buffers is
// basically the same. So go through the same pendingUpdates machinery. // basically the same. So go through the same pendingUpdates machinery.
for (const QRhiResourceUpdateBatchPrivate::StaticBufferUpload &u : ud->staticBufferUploads) { QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, u.buf);
QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, u.buf); Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic); Q_ASSERT(u.offset + u.data.size() <= bufD->m_size);
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)
for (int i = 0, ie = bufD->d->slotted ? QMTL_FRAMES_IN_FLIGHT : 1; i != ie; ++i) bufD->d->pendingUpdates[i].append(
bufD->d->pendingUpdates[i].append({ u.buf, u.offset, u.data.size(), u.data.constData() }); QRhiResourceUpdateBatchPrivate::BufferOp::dynamicUpdate(u.buf, u.offset, u.data.size(), u.data.constData()));
} else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::Read) {
QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, u.buf);
executeBufferHostWritesForCurrentFrame(bufD);
const int idx = bufD->d->slotted ? currentFrameSlot : 0;
char *p = reinterpret_cast<char *>([bufD->d->buf[idx] contents]);
if (p) {
u.result->data.resize(u.readSize);
memcpy(u.result->data.data(), p + u.offset, size_t(u.readSize));
}
if (u.result->completed)
u.result->completed();
}
} }
id<MTLBlitCommandEncoder> blitEnc = nil; id<MTLBlitCommandEncoder> blitEnc = nil;
@ -1569,11 +1590,11 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) { for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) {
if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) { if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) {
QMetalTexture *utexD = QRHI_RES(QMetalTexture, u.upload.tex); QMetalTexture *utexD = QRHI_RES(QMetalTexture, u.dst);
qsizetype stagingSize = 0; qsizetype stagingSize = 0;
for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) { for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
for (int level = 0; level < QRhi::MAX_LEVELS; ++level) { 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); stagingSize += subresUploadByteSize(subresDesc);
} }
} }
@ -1588,7 +1609,7 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
qsizetype curOfs = 0; qsizetype curOfs = 0;
for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) { for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
for (int level = 0; level < QRhi::MAX_LEVELS; ++level) { 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); enqueueSubresUpload(utexD, mp, blitEnc, layer, level, subresDesc, &curOfs);
} }
} }
@ -1603,32 +1624,33 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
d->releaseQueue.append(e); d->releaseQueue.append(e);
QRHI_PROF_F(releaseTextureStagingArea(utexD, currentFrameSlot)); QRHI_PROF_F(releaseTextureStagingArea(utexD, currentFrameSlot));
} else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) { } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) {
Q_ASSERT(u.copy.src && u.copy.dst); Q_ASSERT(u.src && u.dst);
QMetalTexture *srcD = QRHI_RES(QMetalTexture, u.copy.src); QMetalTexture *srcD = QRHI_RES(QMetalTexture, u.src);
QMetalTexture *dstD = QRHI_RES(QMetalTexture, u.copy.dst); QMetalTexture *dstD = QRHI_RES(QMetalTexture, u.dst);
const QPoint dp = u.copy.desc.destinationTopLeft(); const QPoint dp = u.desc.destinationTopLeft();
const QSize size = u.copy.desc.pixelSize().isEmpty() ? srcD->m_pixelSize : u.copy.desc.pixelSize(); const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
const QPoint sp = u.copy.desc.sourceTopLeft(); const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
const QPoint sp = u.desc.sourceTopLeft();
ensureBlit(); ensureBlit();
[blitEnc copyFromTexture: srcD->d->tex [blitEnc copyFromTexture: srcD->d->tex
sourceSlice: NSUInteger(u.copy.desc.sourceLayer()) sourceSlice: NSUInteger(u.desc.sourceLayer())
sourceLevel: NSUInteger(u.copy.desc.sourceLevel()) sourceLevel: NSUInteger(u.desc.sourceLevel())
sourceOrigin: MTLOriginMake(NSUInteger(sp.x()), NSUInteger(sp.y()), 0) 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 toTexture: dstD->d->tex
destinationSlice: NSUInteger(u.copy.desc.destinationLayer()) destinationSlice: NSUInteger(u.desc.destinationLayer())
destinationLevel: NSUInteger(u.copy.desc.destinationLevel()) destinationLevel: NSUInteger(u.desc.destinationLevel())
destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), 0)]; destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), 0)];
srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot; srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot;
} else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) { } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) {
QRhiMetalData::ActiveReadback aRb; QRhiMetalData::TextureReadback readback;
aRb.activeFrameSlot = currentFrameSlot; readback.activeFrameSlot = currentFrameSlot;
aRb.desc = u.read.rb; readback.desc = u.rb;
aRb.result = u.read.result; readback.result = u.result;
QMetalTexture *texD = QRHI_RES(QMetalTexture, u.read.rb.texture()); QMetalTexture *texD = QRHI_RES(QMetalTexture, u.rb.texture());
QMetalSwapChain *swapChainD = nullptr; QMetalSwapChain *swapChainD = nullptr;
id<MTLTexture> src; id<MTLTexture> src;
QSize srcSize; QSize srcSize;
@ -1637,17 +1659,16 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
qWarning("Multisample texture cannot be read back"); qWarning("Multisample texture cannot be read back");
continue; continue;
} }
aRb.pixelSize = u.read.rb.level() > 0 ? q->sizeForMipLevel(u.read.rb.level(), texD->m_pixelSize) readback.pixelSize = q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize);
: texD->m_pixelSize; readback.format = texD->m_format;
aRb.format = texD->m_format;
src = texD->d->tex; src = texD->d->tex;
srcSize = texD->m_pixelSize; srcSize = readback.pixelSize;
texD->lastActiveFrameSlot = currentFrameSlot; texD->lastActiveFrameSlot = currentFrameSlot;
} else { } else {
Q_ASSERT(currentSwapChain); Q_ASSERT(currentSwapChain);
swapChainD = QRHI_RES(QMetalSwapChain, currentSwapChain); swapChainD = QRHI_RES(QMetalSwapChain, currentSwapChain);
aRb.pixelSize = swapChainD->pixelSize; readback.pixelSize = swapChainD->pixelSize;
aRb.format = swapChainD->d->rhiColorFormat; readback.format = swapChainD->d->rhiColorFormat;
// Multisample swapchains need nothing special since resolving // Multisample swapchains need nothing special since resolving
// happens when ending a renderpass. // happens when ending a renderpass.
const QMetalRenderTargetData::ColorAtt &colorAtt(swapChainD->rtWrapper.d->fb.colorAtt[0]); const QMetalRenderTargetData::ColorAtt &colorAtt(swapChainD->rtWrapper.d->fb.colorAtt[0]);
@ -1656,28 +1677,28 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
} }
quint32 bpl = 0; quint32 bpl = 0;
textureFormatInfo(aRb.format, aRb.pixelSize, &bpl, &aRb.bufSize); textureFormatInfo(readback.format, readback.pixelSize, &bpl, &readback.bufSize);
aRb.buf = [d->dev newBufferWithLength: aRb.bufSize options: MTLResourceStorageModeShared]; readback.buf = [d->dev newBufferWithLength: readback.bufSize options: MTLResourceStorageModeShared];
QRHI_PROF_F(newReadbackBuffer(qint64(qintptr(aRb.buf)), QRHI_PROF_F(newReadbackBuffer(qint64(qintptr(readback.buf)),
texD ? static_cast<QRhiResource *>(texD) : static_cast<QRhiResource *>(swapChainD), texD ? static_cast<QRhiResource *>(texD) : static_cast<QRhiResource *>(swapChainD),
aRb.bufSize)); readback.bufSize));
ensureBlit(); ensureBlit();
[blitEnc copyFromTexture: src [blitEnc copyFromTexture: src
sourceSlice: NSUInteger(u.read.rb.layer()) sourceSlice: NSUInteger(u.rb.layer())
sourceLevel: NSUInteger(u.read.rb.level()) sourceLevel: NSUInteger(u.rb.level())
sourceOrigin: MTLOriginMake(0, 0, 0) sourceOrigin: MTLOriginMake(0, 0, 0)
sourceSize: MTLSizeMake(NSUInteger(srcSize.width()), NSUInteger(srcSize.height()), 1) sourceSize: MTLSizeMake(NSUInteger(srcSize.width()), NSUInteger(srcSize.height()), 1)
toBuffer: aRb.buf toBuffer: readback.buf
destinationOffset: 0 destinationOffset: 0
destinationBytesPerRow: bpl destinationBytesPerRow: bpl
destinationBytesPerImage: 0 destinationBytesPerImage: 0
options: MTLBlitOptionNone]; options: MTLBlitOptionNone];
d->activeReadbacks.append(aRb); d->activeTextureReadbacks.append(readback);
} else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::MipGen) { } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::GenMips) {
QMetalTexture *utexD = QRHI_RES(QMetalTexture, u.mipgen.tex); QMetalTexture *utexD = QRHI_RES(QMetalTexture, u.dst);
ensureBlit(); ensureBlit();
[blitEnc generateMipmapsForTexture: utexD->d->tex]; [blitEnc generateMipmapsForTexture: utexD->d->tex];
utexD->lastActiveFrameSlot = currentFrameSlot; utexD->lastActiveFrameSlot = currentFrameSlot;
@ -1697,14 +1718,13 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
void QRhiMetal::executeBufferHostWritesForCurrentFrame(QMetalBuffer *bufD) void QRhiMetal::executeBufferHostWritesForCurrentFrame(QMetalBuffer *bufD)
{ {
const int idx = bufD->d->slotted ? currentFrameSlot : 0; const int idx = bufD->d->slotted ? currentFrameSlot : 0;
QVector<QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate> &updates(bufD->d->pendingUpdates[idx]); if (bufD->d->pendingUpdates[idx].isEmpty())
if (updates.isEmpty())
return; return;
void *p = [bufD->d->buf[idx] contents]; void *p = [bufD->d->buf[idx] contents];
int changeBegin = -1; int changeBegin = -1;
int changeEnd = -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)); Q_ASSERT(bufD == QRHI_RES(QMetalBuffer, u.buf));
memcpy(static_cast<char *>(p) + u.offset, u.data.constData(), size_t(u.data.size())); memcpy(static_cast<char *>(p) + u.offset, u.data.constData(), size_t(u.data.size()));
if (changeBegin == -1 || u.offset < changeBegin) if (changeBegin == -1 || u.offset < changeBegin)
@ -1715,7 +1735,7 @@ void QRhiMetal::executeBufferHostWritesForCurrentFrame(QMetalBuffer *bufD)
if (changeBegin >= 0 && bufD->d->managed) if (changeBegin >= 0 && bufD->d->managed)
[bufD->d->buf[idx] didModifyRange: NSMakeRange(NSUInteger(changeBegin), NSUInteger(changeEnd - changeBegin))]; [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) void QRhiMetal::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
@ -1951,22 +1971,22 @@ void QRhiMetal::finishActiveReadbacks(bool forced)
QVarLengthArray<std::function<void()>, 4> completedCallbacks; QVarLengthArray<std::function<void()>, 4> completedCallbacks;
QRhiProfilerPrivate *rhiP = profilerPrivateOrNull(); QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
for (int i = d->activeReadbacks.count() - 1; i >= 0; --i) { for (int i = d->activeTextureReadbacks.count() - 1; i >= 0; --i) {
const QRhiMetalData::ActiveReadback &aRb(d->activeReadbacks[i]); const QRhiMetalData::TextureReadback &readback(d->activeTextureReadbacks[i]);
if (forced || currentFrameSlot == aRb.activeFrameSlot || aRb.activeFrameSlot < 0) { if (forced || currentFrameSlot == readback.activeFrameSlot || readback.activeFrameSlot < 0) {
aRb.result->format = aRb.format; readback.result->format = readback.format;
aRb.result->pixelSize = aRb.pixelSize; readback.result->pixelSize = readback.pixelSize;
aRb.result->data.resize(int(aRb.bufSize)); readback.result->data.resize(int(readback.bufSize));
void *p = [aRb.buf contents]; void *p = [readback.buf contents];
memcpy(aRb.result->data.data(), p, aRb.bufSize); memcpy(readback.result->data.data(), p, readback.bufSize);
[aRb.buf release]; [readback.buf release];
QRHI_PROF_F(releaseReadbackBuffer(qint64(qintptr(aRb.buf)))); QRHI_PROF_F(releaseReadbackBuffer(qint64(qintptr(readback.buf))));
if (aRb.result->completed) if (readback.result->completed)
completedCallbacks.append(aRb.result->completed); completedCallbacks.append(readback.result->completed);
d->activeReadbacks.removeAt(i); d->activeTextureReadbacks.removeAt(i);
} }
} }
@ -2042,7 +2062,6 @@ bool QMetalBuffer::build()
for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) { for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
if (i == 0 || d->slotted) { if (i == 0 || d->slotted) {
d->buf[i] = [rhiD->d->dev newBufferWithLength: roundedSize options: opts]; d->buf[i] = [rhiD->d->dev newBufferWithLength: roundedSize options: opts];
d->pendingUpdates[i].reserve(16);
if (!m_objectName.isEmpty()) { if (!m_objectName.isEmpty()) {
if (!d->slotted) { if (!d->slotted) {
d->buf[i].label = [NSString stringWithUTF8String: m_objectName.constData()]; d->buf[i].label = [NSString stringWithUTF8String: m_objectName.constData()];
@ -3120,6 +3139,8 @@ bool QMetalGraphicsPipeline::build()
release(); release();
QRHI_RES_RHI(QRhiMetal); QRHI_RES_RHI(QRhiMetal);
if (!rhiD->sanityCheckGraphicsPipeline(this))
return false;
// same binding space for vertex and constant buffers - work it around // same binding space for vertex and constant buffers - work it around
const int firstVertexBinding = QRHI_RES(QMetalShaderResourceBindings, m_shaderResourceBindings)->maxBinding + 1; const int firstVertexBinding = QRHI_RES(QMetalShaderResourceBindings, m_shaderResourceBindings)->maxBinding + 1;

View File

@ -36,6 +36,7 @@
#include "qrhinull_p_p.h" #include "qrhinull_p_p.h"
#include <qmath.h> #include <qmath.h>
#include <QPainter>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -385,27 +386,125 @@ QRhi::FrameOpResult QRhiNull::finish()
return QRhi::FrameOpSuccess; return QRhi::FrameOpSuccess;
} }
void QRhiNull::simulateTextureUpload(const QRhiResourceUpdateBatchPrivate::TextureOp &u)
{
QNullTexture *texD = QRHI_RES(QNullTexture, u.dst);
for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level])) {
if (!subresDesc.image().isNull()) {
const QImage src = subresDesc.image();
QPainter painter(&texD->image[layer][level]);
const QSize srcSize = subresDesc.sourceSize().isEmpty()
? src.size() : subresDesc.sourceSize();
painter.setCompositionMode(QPainter::CompositionMode_Source);
painter.drawImage(subresDesc.destinationTopLeft(), src,
QRect(subresDesc.sourceTopLeft(), srcSize));
} else if (!subresDesc.data().isEmpty()) {
const QSize subresSize = q->sizeForMipLevel(level, texD->pixelSize());
int w = subresSize.width();
int h = subresSize.height();
if (!subresDesc.sourceSize().isEmpty()) {
w = subresDesc.sourceSize().width();
h = subresDesc.sourceSize().height();
}
// sourceTopLeft is not supported on this path as per QRhi docs
const char *src = subresDesc.data().constData();
const int srcBpl = w * 4;
const QPoint dstOffset = subresDesc.destinationTopLeft();
uchar *dst = texD->image[layer][level].bits();
const int dstBpl = texD->image[layer][level].bytesPerLine();
for (int y = 0; y < h; ++y) {
memcpy(dst + dstOffset.x() * 4 + (y + dstOffset.y()) * dstBpl,
src + y * srcBpl,
size_t(srcBpl));
}
}
}
}
}
}
void QRhiNull::simulateTextureCopy(const QRhiResourceUpdateBatchPrivate::TextureOp &u)
{
QNullTexture *srcD = QRHI_RES(QNullTexture, u.src);
QNullTexture *dstD = QRHI_RES(QNullTexture, u.dst);
const QImage &srcImage(srcD->image[u.desc.sourceLayer()][u.desc.sourceLevel()]);
QImage &dstImage(dstD->image[u.desc.destinationLayer()][u.desc.destinationLevel()]);
const QPoint dstPos = u.desc.destinationTopLeft();
const QSize size = u.desc.pixelSize().isEmpty() ? srcD->pixelSize() : u.desc.pixelSize();
const QPoint srcPos = u.desc.sourceTopLeft();
QPainter painter(&dstImage);
painter.setCompositionMode(QPainter::CompositionMode_Source);
painter.drawImage(QRect(dstPos, size), srcImage, QRect(srcPos, size));
}
void QRhiNull::simulateTextureGenMips(const QRhiResourceUpdateBatchPrivate::TextureOp &u)
{
QNullTexture *texD = QRHI_RES(QNullTexture, u.dst);
const QSize baseSize = texD->pixelSize();
const int levelCount = q->mipLevelsForSize(baseSize);
for (int level = 1; level < levelCount; ++level)
texD->image[0][level] = texD->image[0][0].scaled(q->sizeForMipLevel(level, baseSize));
}
void QRhiNull::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) void QRhiNull::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
{ {
Q_UNUSED(cb); Q_UNUSED(cb);
QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates); 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) { for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) {
if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) { if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) {
QRhiReadbackResult *result = u.read.result; if (u.dst->format() == QRhiTexture::RGBA8)
QRhiTexture *tex = u.read.rb.texture(); simulateTextureUpload(u);
if (tex) { } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) {
result->format = tex->format(); if (u.src->format() == QRhiTexture::RGBA8 && u.dst->format() == QRhiTexture::RGBA8)
result->pixelSize = q->sizeForMipLevel(u.read.rb.level(), tex->pixelSize()); simulateTextureCopy(u);
} else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) {
QRhiReadbackResult *result = u.result;
QNullTexture *texD = QRHI_RES(QNullTexture, u.rb.texture());
if (texD) {
result->format = texD->format();
result->pixelSize = q->sizeForMipLevel(u.rb.level(), texD->pixelSize());
} else { } else {
Q_ASSERT(currentSwapChain); Q_ASSERT(currentSwapChain);
result->format = QRhiTexture::RGBA8; result->format = QRhiTexture::RGBA8;
result->pixelSize = currentSwapChain->currentPixelSize(); result->pixelSize = currentSwapChain->currentPixelSize();
} }
quint32 bytesPerLine = 0;
quint32 byteSize = 0; quint32 byteSize = 0;
textureFormatInfo(result->format, result->pixelSize, nullptr, &byteSize); textureFormatInfo(result->format, result->pixelSize, &bytesPerLine, &byteSize);
result->data.fill(0, byteSize); if (texD && texD->format() == QRhiTexture::RGBA8) {
result->data.resize(int(byteSize));
const QImage &src(texD->image[u.rb.layer()][u.rb.level()]);
char *dst = result->data.data();
for (int y = 0, h = src.height(); y < h; ++y) {
memcpy(dst, src.constScanLine(y), bytesPerLine);
dst += bytesPerLine;
}
} else {
result->data.fill(0, int(byteSize));
}
if (result->completed) if (result->completed)
result->completed(); result->completed();
} else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::GenMips) {
if (u.dst->format() == QRhiTexture::RGBA8)
simulateTextureGenMips(u);
} }
} }
ud->free(); ud->free();
@ -454,14 +553,18 @@ QNullBuffer::~QNullBuffer()
void QNullBuffer::release() void QNullBuffer::release()
{ {
data.clear();
QRHI_PROF; QRHI_PROF;
QRHI_PROF_F(releaseBuffer(this)); QRHI_PROF_F(releaseBuffer(this));
} }
bool QNullBuffer::build() bool QNullBuffer::build()
{ {
data.fill('\0', m_size);
QRHI_PROF; QRHI_PROF;
QRHI_PROF_F(newBuffer(this, m_size, 1, 0)); QRHI_PROF_F(newBuffer(this, uint(m_size), 1, 0));
return true; return true;
} }
@ -513,22 +616,36 @@ void QNullTexture::release()
bool QNullTexture::build() bool QNullTexture::build()
{ {
QRHI_RES_RHI(QRhiNull);
const bool isCube = m_flags.testFlag(CubeMap); const bool isCube = m_flags.testFlag(CubeMap);
const bool hasMipMaps = m_flags.testFlag(MipMapped); const bool hasMipMaps = m_flags.testFlag(MipMapped);
QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize; 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;
QRHI_PROF_F(newTexture(this, true, mipLevelCount, isCube ? 6 : 1, 1)); QRHI_PROF_F(newTexture(this, true, mipLevelCount, layerCount, 1));
return true; return true;
} }
bool QNullTexture::buildFrom(const QRhiNativeHandles *src) bool QNullTexture::buildFrom(const QRhiNativeHandles *src)
{ {
Q_UNUSED(src); Q_UNUSED(src);
QRHI_RES_RHI(QRhiNull);
const bool isCube = m_flags.testFlag(CubeMap); const bool isCube = m_flags.testFlag(CubeMap);
const bool hasMipMaps = m_flags.testFlag(MipMapped); const bool hasMipMaps = m_flags.testFlag(MipMapped);
QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize; 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;
QRHI_PROF_F(newTexture(this, false, mipLevelCount, isCube ? 6 : 1, 1)); QRHI_PROF_F(newTexture(this, false, mipLevelCount, isCube ? 6 : 1, 1));
return true; return true;
@ -690,6 +807,10 @@ void QNullGraphicsPipeline::release()
bool QNullGraphicsPipeline::build() bool QNullGraphicsPipeline::build()
{ {
QRHI_RES_RHI(QRhiNull);
if (!rhiD->sanityCheckGraphicsPipeline(this))
return false;
return true; return true;
} }

View File

@ -59,6 +59,8 @@ struct QNullBuffer : public QRhiBuffer
~QNullBuffer(); ~QNullBuffer();
void release() override; void release() override;
bool build() override; bool build() override;
QByteArray data;
}; };
struct QNullRenderBuffer : public QRhiRenderBuffer struct QNullRenderBuffer : public QRhiRenderBuffer
@ -82,6 +84,7 @@ struct QNullTexture : public QRhiTexture
const QRhiNativeHandles *nativeHandles() override; const QRhiNativeHandles *nativeHandles() override;
QRhiNullTextureNativeHandles nativeHandlesStruct; QRhiNullTextureNativeHandles nativeHandlesStruct;
QImage image[QRhi::MAX_LAYERS][QRhi::MAX_LEVELS];
}; };
struct QNullSampler : public QRhiSampler struct QNullSampler : public QRhiSampler
@ -286,6 +289,10 @@ public:
void releaseCachedResources() override; void releaseCachedResources() override;
bool isDeviceLost() const 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; QRhiNullNativeHandles nativeHandlesStruct;
QRhiSwapChain *currentSwapChain = nullptr; QRhiSwapChain *currentSwapChain = nullptr;
QNullCommandBuffer offscreenCommandBuffer; QNullCommandBuffer offscreenCommandBuffer;

View File

@ -363,6 +363,11 @@ bool QRhiVulkan::create(QRhi::Flags flags)
Q_UNUSED(flags); Q_UNUSED(flags);
Q_ASSERT(inst); 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 globalVulkanInstance = inst; // assume this will not change during the lifetime of the entire application
f = inst->functions(); f = inst->functions();
@ -2649,100 +2654,164 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates); QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates);
QRhiProfilerPrivate *rhiP = profilerPrivateOrNull(); QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
for (const QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate &u : ud->dynamicBufferUpdates) { for (const QRhiResourceUpdateBatchPrivate::BufferOp &u : ud->bufferOps) {
QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf); if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::DynamicUpdate) {
Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic); QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf);
for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
bufD->pendingDynamicUpdates[i].append(u); 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);
for (const QRhiResourceUpdateBatchPrivate::StaticBufferUpload &u : ud->staticBufferUploads) { if (!bufD->stagingBuffers[currentFrameSlot]) {
QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf); VkBufferCreateInfo bufferInfo;
Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic); memset(&bufferInfo, 0, sizeof(bufferInfo));
Q_ASSERT(u.offset + u.data.size() <= bufD->m_size); 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;
if (!bufD->stagingBuffers[currentFrameSlot]) { VmaAllocationCreateInfo allocInfo;
VkBufferCreateInfo bufferInfo; memset(&allocInfo, 0, sizeof(allocInfo));
memset(&bufferInfo, 0, sizeof(bufferInfo)); allocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
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; VmaAllocation allocation;
memset(&allocInfo, 0, sizeof(allocInfo)); VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo,
allocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY; &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;
}
}
VmaAllocation allocation; void *p = nullptr;
VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo, VmaAllocation a = toVmaAllocation(bufD->stagingAllocations[currentFrameSlot]);
&bufD->stagingBuffers[currentFrameSlot], &allocation, nullptr); VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p);
if (err == VK_SUCCESS) { if (err != VK_SUCCESS) {
bufD->stagingAllocations[currentFrameSlot] = allocation; qWarning("Failed to map buffer: %d", err);
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; continue;
} }
} memcpy(static_cast<uchar *>(p) + u.offset, u.data.constData(), size_t(u.data.size()));
vmaUnmapMemory(toVmaAllocator(allocator), a);
vmaFlushAllocation(toVmaAllocator(allocator), a, VkDeviceSize(u.offset), VkDeviceSize(u.data.size()));
void *p = nullptr; trackedBufferBarrier(cbD, bufD, 0,
VmaAllocation a = toVmaAllocation(bufD->stagingAllocations[currentFrameSlot]); VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p);
if (err != VK_SUCCESS) {
qWarning("Failed to map buffer: %d", err);
continue;
}
memcpy(static_cast<uchar *>(p) + u.offset, u.data.constData(), size_t(u.data.size()));
vmaUnmapMemory(toVmaAllocator(allocator), a);
vmaFlushAllocation(toVmaAllocator(allocator), a, VkDeviceSize(u.offset), VkDeviceSize(u.data.size()));
trackedBufferBarrier(cbD, bufD, 0, VkBufferCopy copyInfo;
VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); memset(&copyInfo, 0, sizeof(copyInfo));
copyInfo.srcOffset = VkDeviceSize(u.offset);
copyInfo.dstOffset = VkDeviceSize(u.offset);
copyInfo.size = VkDeviceSize(u.data.size());
VkBufferCopy copyInfo; QVkCommandBuffer::Command cmd;
memset(&copyInfo, 0, sizeof(copyInfo)); cmd.cmd = QVkCommandBuffer::Command::CopyBuffer;
copyInfo.srcOffset = VkDeviceSize(u.offset); cmd.args.copyBuffer.src = bufD->stagingBuffers[currentFrameSlot];
copyInfo.dstOffset = VkDeviceSize(u.offset); cmd.args.copyBuffer.dst = bufD->buffers[0];
copyInfo.size = VkDeviceSize(u.data.size()); cmd.args.copyBuffer.desc = copyInfo;
cbD->commands.append(cmd);
QVkCommandBuffer::Command cmd; // Where's the barrier for read-after-write? (assuming the common case
cmd.cmd = QVkCommandBuffer::Command::CopyBuffer; // of binding this buffer as vertex/index, or, less likely, as uniform
cmd.args.copyBuffer.src = bufD->stagingBuffers[currentFrameSlot]; // buffer, in a renderpass later on) That is handled by the pass
cmd.args.copyBuffer.dst = bufD->buffers[0]; // resource tracking: the appropriate pipeline barrier will be
cmd.args.copyBuffer.desc = copyInfo; // generated and recorded right before the renderpass, that binds this
cbD->commands.append(cmd); // buffer in one of its commands, gets its BeginRenderPass recorded.
// Where's the barrier for read-after-write? (assuming the common case bufD->lastActiveFrameSlot = currentFrameSlot;
// 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; if (bufD->m_type == QRhiBuffer::Immutable) {
QRhiVulkan::DeferredReleaseEntry e;
e.type = QRhiVulkan::DeferredReleaseEntry::StagingBuffer;
e.lastActiveFrameSlot = currentFrameSlot;
e.stagingBuffer.stagingBuffer = bufD->stagingBuffers[currentFrameSlot];
e.stagingBuffer.stagingAllocation = bufD->stagingAllocations[currentFrameSlot];
bufD->stagingBuffers[currentFrameSlot] = VK_NULL_HANDLE;
bufD->stagingAllocations[currentFrameSlot] = nullptr;
releaseQueue.append(e);
QRHI_PROF_F(releaseBufferStagingArea(bufD, currentFrameSlot));
}
} else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::Read) {
QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf);
if (bufD->m_type == QRhiBuffer::Dynamic) {
executeBufferHostWritesForCurrentFrame(bufD);
void *p = nullptr;
VmaAllocation a = toVmaAllocation(bufD->allocations[currentFrameSlot]);
VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p);
if (err == VK_SUCCESS) {
u.result->data.resize(u.readSize);
memcpy(u.result->data.data(), reinterpret_cast<char *>(p) + u.offset, size_t(u.readSize));
vmaUnmapMemory(toVmaAllocator(allocator), a);
}
if (u.result->completed)
u.result->completed();
} else {
// Non-Dynamic buffers may not be host visible, so have to
// create a readback buffer, enqueue a copy from
// bufD->buffers[0] to this buffer, and then once the command
// buffer completes, copy the data out of the host visible
// readback buffer. Quite similar to what we do for texture
// readbacks.
BufferReadback readback;
readback.activeFrameSlot = currentFrameSlot;
readback.result = u.result;
readback.byteSize = u.readSize;
if (bufD->m_type == QRhiBuffer::Immutable) { VkBufferCreateInfo bufferInfo;
QRhiVulkan::DeferredReleaseEntry e; memset(&bufferInfo, 0, sizeof(bufferInfo));
e.type = QRhiVulkan::DeferredReleaseEntry::StagingBuffer; bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
e.lastActiveFrameSlot = currentFrameSlot; bufferInfo.size = VkDeviceSize(readback.byteSize);
e.stagingBuffer.stagingBuffer = bufD->stagingBuffers[currentFrameSlot]; bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
e.stagingBuffer.stagingAllocation = bufD->stagingAllocations[currentFrameSlot];
bufD->stagingBuffers[currentFrameSlot] = VK_NULL_HANDLE; VmaAllocationCreateInfo allocInfo;
bufD->stagingAllocations[currentFrameSlot] = nullptr; memset(&allocInfo, 0, sizeof(allocInfo));
releaseQueue.append(e); allocInfo.usage = VMA_MEMORY_USAGE_GPU_TO_CPU;
QRHI_PROF_F(releaseBufferStagingArea(bufD, currentFrameSlot));
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;
}
trackedBufferBarrier(cbD, bufD, 0, VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
VkBufferCopy copyInfo;
memset(&copyInfo, 0, sizeof(copyInfo));
copyInfo.srcOffset = VkDeviceSize(u.offset);
copyInfo.size = VkDeviceSize(u.readSize);
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;
activeBufferReadbacks.append(readback);
}
} }
} }
for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) { for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) {
if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) { 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 // batch into a single staging buffer and a single CopyBufferToImage with multiple copyInfos
VkDeviceSize stagingSize = 0; VkDeviceSize stagingSize = 0;
for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) { for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
for (int level = 0; level < QRhi::MAX_LEVELS; ++level) { 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); stagingSize += subresUploadByteSize(subresDesc);
} }
} }
@ -2780,7 +2849,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) { for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
for (int level = 0; level < QRhi::MAX_LEVELS; ++level) { for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
const QVector<QRhiTextureSubresourceUploadDescription> &srd(u.upload.subresDesc[layer][level]); const QVector<QRhiTextureSubresourceUploadDescription> &srd(u.subresDesc[layer][level]);
if (srd.isEmpty()) if (srd.isEmpty())
continue; continue;
for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(srd)) { for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(srd)) {
@ -2821,36 +2890,37 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
utexD->lastActiveFrameSlot = currentFrameSlot; utexD->lastActiveFrameSlot = currentFrameSlot;
} else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) { } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) {
Q_ASSERT(u.copy.src && u.copy.dst); Q_ASSERT(u.src && u.dst);
if (u.copy.src == u.copy.dst) { if (u.src == u.dst) {
qWarning("Texture copy with matching source and destination is not supported"); qWarning("Texture copy with matching source and destination is not supported");
continue; continue;
} }
QVkTexture *srcD = QRHI_RES(QVkTexture, u.copy.src); QVkTexture *srcD = QRHI_RES(QVkTexture, u.src);
QVkTexture *dstD = QRHI_RES(QVkTexture, u.copy.dst); QVkTexture *dstD = QRHI_RES(QVkTexture, u.dst);
VkImageCopy region; VkImageCopy region;
memset(&region, 0, sizeof(region)); memset(&region, 0, sizeof(region));
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.srcSubresource.mipLevel = uint32_t(u.copy.desc.sourceLevel()); region.srcSubresource.mipLevel = uint32_t(u.desc.sourceLevel());
region.srcSubresource.baseArrayLayer = uint32_t(u.copy.desc.sourceLayer()); region.srcSubresource.baseArrayLayer = uint32_t(u.desc.sourceLayer());
region.srcSubresource.layerCount = 1; region.srcSubresource.layerCount = 1;
region.srcOffset.x = u.copy.desc.sourceTopLeft().x(); region.srcOffset.x = u.desc.sourceTopLeft().x();
region.srcOffset.y = u.copy.desc.sourceTopLeft().y(); region.srcOffset.y = u.desc.sourceTopLeft().y();
region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.dstSubresource.mipLevel = uint32_t(u.copy.desc.destinationLevel()); region.dstSubresource.mipLevel = uint32_t(u.desc.destinationLevel());
region.dstSubresource.baseArrayLayer = uint32_t(u.copy.desc.destinationLayer()); region.dstSubresource.baseArrayLayer = uint32_t(u.desc.destinationLayer());
region.dstSubresource.layerCount = 1; region.dstSubresource.layerCount = 1;
region.dstOffset.x = u.copy.desc.destinationTopLeft().x(); region.dstOffset.x = u.desc.destinationTopLeft().x();
region.dstOffset.y = u.copy.desc.destinationTopLeft().y(); region.dstOffset.y = u.desc.destinationTopLeft().y();
const QSize size = u.copy.desc.pixelSize().isEmpty() ? srcD->m_pixelSize : u.copy.desc.pixelSize(); const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
region.extent.width = uint32_t(size.width()); const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
region.extent.height = uint32_t(size.height()); region.extent.width = uint32_t(copySize.width());
region.extent.height = uint32_t(copySize.height());
region.extent.depth = 1; region.extent.depth = 1;
trackedImageBarrier(cbD, srcD, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, trackedImageBarrier(cbD, srcD, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
@ -2869,21 +2939,20 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot; srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot;
} else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) { } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) {
ActiveReadback aRb; TextureReadback readback;
aRb.activeFrameSlot = currentFrameSlot; readback.activeFrameSlot = currentFrameSlot;
aRb.desc = u.read.rb; readback.desc = u.rb;
aRb.result = u.read.result; readback.result = u.result;
QVkTexture *texD = QRHI_RES(QVkTexture, u.read.rb.texture()); QVkTexture *texD = QRHI_RES(QVkTexture, u.rb.texture());
QVkSwapChain *swapChainD = nullptr; QVkSwapChain *swapChainD = nullptr;
if (texD) { if (texD) {
if (texD->samples > VK_SAMPLE_COUNT_1_BIT) { if (texD->samples > VK_SAMPLE_COUNT_1_BIT) {
qWarning("Multisample texture cannot be read back"); qWarning("Multisample texture cannot be read back");
continue; continue;
} }
aRb.pixelSize = u.read.rb.level() > 0 ? q->sizeForMipLevel(u.read.rb.level(), texD->m_pixelSize) readback.pixelSize = q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize);
: texD->m_pixelSize; readback.format = texD->m_format;
aRb.format = texD->m_format;
texD->lastActiveFrameSlot = currentFrameSlot; texD->lastActiveFrameSlot = currentFrameSlot;
} else { } else {
Q_ASSERT(currentSwapChain); Q_ASSERT(currentSwapChain);
@ -2892,21 +2961,21 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
qWarning("Swapchain does not support readback"); qWarning("Swapchain does not support readback");
continue; continue;
} }
aRb.pixelSize = swapChainD->pixelSize; readback.pixelSize = swapChainD->pixelSize;
aRb.format = colorTextureFormatFromVkFormat(swapChainD->colorFormat, nullptr); readback.format = colorTextureFormatFromVkFormat(swapChainD->colorFormat, nullptr);
if (aRb.format == QRhiTexture::UnknownFormat) if (readback.format == QRhiTexture::UnknownFormat)
continue; continue;
// Multisample swapchains need nothing special since resolving // Multisample swapchains need nothing special since resolving
// happens when ending a renderpass. // 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; VkBufferCreateInfo bufferInfo;
memset(&bufferInfo, 0, sizeof(bufferInfo)); memset(&bufferInfo, 0, sizeof(bufferInfo));
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = aRb.bufSize; bufferInfo.size = readback.byteSize;
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT; bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
VmaAllocationCreateInfo allocInfo; VmaAllocationCreateInfo allocInfo;
@ -2914,14 +2983,14 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
allocInfo.usage = VMA_MEMORY_USAGE_GPU_TO_CPU; allocInfo.usage = VMA_MEMORY_USAGE_GPU_TO_CPU;
VmaAllocation allocation; 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) { if (err == VK_SUCCESS) {
aRb.bufAlloc = allocation; readback.stagingAlloc = allocation;
QRHI_PROF_F(newReadbackBuffer(qint64(aRb.buf), QRHI_PROF_F(newReadbackBuffer(qint64(readback.stagingBuf),
texD ? static_cast<QRhiResource *>(texD) : static_cast<QRhiResource *>(swapChainD), texD ? static_cast<QRhiResource *>(texD) : static_cast<QRhiResource *>(swapChainD),
aRb.bufSize)); readback.byteSize));
} else { } 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; continue;
} }
@ -2930,11 +2999,11 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
memset(&copyDesc, 0, sizeof(copyDesc)); memset(&copyDesc, 0, sizeof(copyDesc));
copyDesc.bufferOffset = 0; copyDesc.bufferOffset = 0;
copyDesc.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; copyDesc.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copyDesc.imageSubresource.mipLevel = uint32_t(u.read.rb.level()); copyDesc.imageSubresource.mipLevel = uint32_t(u.rb.level());
copyDesc.imageSubresource.baseArrayLayer = uint32_t(u.read.rb.layer()); copyDesc.imageSubresource.baseArrayLayer = uint32_t(u.rb.layer());
copyDesc.imageSubresource.layerCount = 1; copyDesc.imageSubresource.layerCount = 1;
copyDesc.imageExtent.width = uint32_t(aRb.pixelSize.width()); copyDesc.imageExtent.width = uint32_t(readback.pixelSize.width());
copyDesc.imageExtent.height = uint32_t(aRb.pixelSize.height()); copyDesc.imageExtent.height = uint32_t(readback.pixelSize.height());
copyDesc.imageExtent.depth = 1; copyDesc.imageExtent.depth = 1;
if (texD) { if (texD) {
@ -2944,7 +3013,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
cmd.cmd = QVkCommandBuffer::Command::CopyImageToBuffer; cmd.cmd = QVkCommandBuffer::Command::CopyImageToBuffer;
cmd.args.copyImageToBuffer.src = texD->image; cmd.args.copyImageToBuffer.src = texD->image;
cmd.args.copyImageToBuffer.srcLayout = texD->usageState.layout; cmd.args.copyImageToBuffer.srcLayout = texD->usageState.layout;
cmd.args.copyImageToBuffer.dst = aRb.buf; cmd.args.copyImageToBuffer.dst = readback.stagingBuf;
cmd.args.copyImageToBuffer.desc = copyDesc; cmd.args.copyImageToBuffer.desc = copyDesc;
cbD->commands.append(cmd); cbD->commands.append(cmd);
} else { } else {
@ -2969,14 +3038,14 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
cmd.cmd = QVkCommandBuffer::Command::CopyImageToBuffer; cmd.cmd = QVkCommandBuffer::Command::CopyImageToBuffer;
cmd.args.copyImageToBuffer.src = image; cmd.args.copyImageToBuffer.src = image;
cmd.args.copyImageToBuffer.srcLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; 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; cmd.args.copyImageToBuffer.desc = copyDesc;
cbD->commands.append(cmd); cbD->commands.append(cmd);
} }
activeReadbacks.append(aRb); activeTextureReadbacks.append(readback);
} else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::MipGen) { } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::GenMips) {
QVkTexture *utexD = QRHI_RES(QVkTexture, u.mipgen.tex); QVkTexture *utexD = QRHI_RES(QVkTexture, u.dst);
Q_ASSERT(utexD->m_flags.testFlag(QRhiTexture::UsedWithGenerateMips)); Q_ASSERT(utexD->m_flags.testFlag(QRhiTexture::UsedWithGenerateMips));
int w = utexD->m_pixelSize.width(); int w = utexD->m_pixelSize.width();
int h = utexD->m_pixelSize.height(); int h = utexD->m_pixelSize.height();
@ -2993,14 +3062,14 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
origLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, origLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
origAccess, VK_ACCESS_TRANSFER_READ_BIT, origAccess, VK_ACCESS_TRANSFER_READ_BIT,
origStage, VK_PIPELINE_STAGE_TRANSFER_BIT, origStage, VK_PIPELINE_STAGE_TRANSFER_BIT,
u.mipgen.layer, 1, u.layer, 1,
level - 1, 1); level - 1, 1);
} else { } else {
subresourceBarrier(cbD, utexD->image, subresourceBarrier(cbD, utexD->image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
u.mipgen.layer, 1, u.layer, 1,
level - 1, 1); level - 1, 1);
} }
@ -3008,7 +3077,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
origLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, origLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
origAccess, VK_ACCESS_TRANSFER_WRITE_BIT, origAccess, VK_ACCESS_TRANSFER_WRITE_BIT,
origStage, VK_PIPELINE_STAGE_TRANSFER_BIT, origStage, VK_PIPELINE_STAGE_TRANSFER_BIT,
u.mipgen.layer, 1, u.layer, 1,
level, 1); level, 1);
VkImageBlit region; VkImageBlit region;
@ -3016,7 +3085,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.srcSubresource.mipLevel = uint32_t(level) - 1; 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.srcSubresource.layerCount = 1;
region.srcOffsets[1].x = qMax(1, w); region.srcOffsets[1].x = qMax(1, w);
@ -3025,7 +3094,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.dstSubresource.mipLevel = uint32_t(level); 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.dstSubresource.layerCount = 1;
region.dstOffsets[1].x = qMax(1, w >> 1); region.dstOffsets[1].x = qMax(1, w >> 1);
@ -3051,13 +3120,13 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, origLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, origLayout,
VK_ACCESS_TRANSFER_READ_BIT, origAccess, VK_ACCESS_TRANSFER_READ_BIT, origAccess,
VK_PIPELINE_STAGE_TRANSFER_BIT, origStage, VK_PIPELINE_STAGE_TRANSFER_BIT, origStage,
u.mipgen.layer, 1, u.layer, 1,
0, int(utexD->mipLevelCount) - 1); 0, int(utexD->mipLevelCount) - 1);
subresourceBarrier(cbD, utexD->image, subresourceBarrier(cbD, utexD->image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, origLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, origLayout,
VK_ACCESS_TRANSFER_WRITE_BIT, origAccess, VK_ACCESS_TRANSFER_WRITE_BIT, origAccess,
VK_PIPELINE_STAGE_TRANSFER_BIT, origStage, VK_PIPELINE_STAGE_TRANSFER_BIT, origStage,
u.mipgen.layer, 1, u.layer, 1,
int(utexD->mipLevelCount) - 1, 1); int(utexD->mipLevelCount) - 1, 1);
} }
@ -3070,8 +3139,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
void QRhiVulkan::executeBufferHostWritesForCurrentFrame(QVkBuffer *bufD) void QRhiVulkan::executeBufferHostWritesForCurrentFrame(QVkBuffer *bufD)
{ {
QVector<QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate> &updates(bufD->pendingDynamicUpdates[currentFrameSlot]); if (bufD->pendingDynamicUpdates[currentFrameSlot].isEmpty())
if (updates.isEmpty())
return; return;
Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic); Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
@ -3087,7 +3155,7 @@ void QRhiVulkan::executeBufferHostWritesForCurrentFrame(QVkBuffer *bufD)
} }
int changeBegin = -1; int changeBegin = -1;
int changeEnd = -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)); Q_ASSERT(bufD == QRHI_RES(QVkBuffer, u.buf));
memcpy(static_cast<char *>(p) + u.offset, u.data.constData(), size_t(u.data.size())); memcpy(static_cast<char *>(p) + u.offset, u.data.constData(), size_t(u.data.size()));
if (changeBegin == -1 || u.offset < changeBegin) if (changeBegin == -1 || u.offset < changeBegin)
@ -3099,7 +3167,7 @@ void QRhiVulkan::executeBufferHostWritesForCurrentFrame(QVkBuffer *bufD)
if (changeBegin >= 0) if (changeBegin >= 0)
vmaFlushAllocation(toVmaAllocator(allocator), a, VkDeviceSize(changeBegin), VkDeviceSize(changeEnd - changeBegin)); 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) static void qrhivk_releaseBuffer(const QRhiVulkan::DeferredReleaseEntry &e, void *allocator)
@ -3193,29 +3261,53 @@ void QRhiVulkan::finishActiveReadbacks(bool forced)
QVarLengthArray<std::function<void()>, 4> completedCallbacks; QVarLengthArray<std::function<void()>, 4> completedCallbacks;
QRhiProfilerPrivate *rhiP = profilerPrivateOrNull(); QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
for (int i = activeReadbacks.count() - 1; i >= 0; --i) { for (int i = activeTextureReadbacks.count() - 1; i >= 0; --i) {
const QRhiVulkan::ActiveReadback &aRb(activeReadbacks[i]); const QRhiVulkan::TextureReadback &readback(activeTextureReadbacks[i]);
if (forced || currentFrameSlot == aRb.activeFrameSlot || aRb.activeFrameSlot < 0) { if (forced || currentFrameSlot == readback.activeFrameSlot || readback.activeFrameSlot < 0) {
aRb.result->format = aRb.format; readback.result->format = readback.format;
aRb.result->pixelSize = aRb.pixelSize; readback.result->pixelSize = readback.pixelSize;
aRb.result->data.resize(int(aRb.bufSize)); VmaAllocation a = toVmaAllocation(readback.stagingAlloc);
void *p = nullptr; void *p = nullptr;
VmaAllocation a = toVmaAllocation(aRb.bufAlloc);
VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p); VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p);
if (err != VK_SUCCESS) { if (err == VK_SUCCESS && p) {
qWarning("Failed to map readback buffer: %d", err); readback.result->data.resize(int(readback.byteSize));
continue; 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); vmaDestroyBuffer(toVmaAllocator(allocator), readback.stagingBuf, a);
QRHI_PROF_F(releaseReadbackBuffer(qint64(aRb.buf))); QRHI_PROF_F(releaseReadbackBuffer(qint64(readback.stagingBuf)));
if (aRb.result->completed) if (readback.result->completed)
completedCallbacks.append(aRb.result->completed); completedCallbacks.append(readback.result->completed);
activeReadbacks.removeAt(i); activeTextureReadbacks.removeAt(i);
}
}
for (int i = activeBufferReadbacks.count() - 1; i >= 0; --i) {
const QRhiVulkan::BufferReadback &readback(activeBufferReadbacks[i]);
if (forced || currentFrameSlot == readback.activeFrameSlot || readback.activeFrameSlot < 0) {
VmaAllocation a = toVmaAllocation(readback.stagingAlloc);
void *p = nullptr;
VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p);
if (err == VK_SUCCESS && p) {
readback.result->data.resize(readback.byteSize);
memcpy(readback.result->data.data(), p, size_t(readback.byteSize));
vmaUnmapMemory(toVmaAllocator(allocator), a);
} else {
qWarning("Failed to map buffer readback buffer of size %d: %d", readback.byteSize, err);
}
vmaDestroyBuffer(toVmaAllocator(allocator), readback.stagingBuf, a);
QRHI_PROF_F(releaseReadbackBuffer(qint64(readback.stagingBuf)));
if (readback.result->completed)
completedCallbacks.append(readback.result->completed);
activeBufferReadbacks.removeAt(i);
} }
} }
@ -3733,6 +3825,10 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const
return true; return true;
case QRhi::TriangleFanTopology: case QRhi::TriangleFanTopology:
return true; return true;
case QRhi::ReadBackNonUniformBuffer:
return true;
case QRhi::ReadBackNonBaseMipLevel:
return true;
default: default:
Q_UNREACHABLE(); Q_UNREACHABLE();
return false; return false;
@ -4902,7 +4998,7 @@ bool QVkBuffer::build()
allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU; allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
} else { } else {
allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; 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); QRHI_RES_RHI(QRhiVulkan);
@ -4916,11 +5012,7 @@ bool QVkBuffer::build()
err = vmaCreateBuffer(toVmaAllocator(rhiD->allocator), &bufferInfo, &allocInfo, &buffers[i], &allocation, nullptr); err = vmaCreateBuffer(toVmaAllocator(rhiD->allocator), &bufferInfo, &allocInfo, &buffers[i], &allocation, nullptr);
if (err != VK_SUCCESS) if (err != VK_SUCCESS)
break; break;
allocations[i] = allocation; 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, rhiD->setObjectName(uint64_t(buffers[i]), VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT, m_objectName,
m_type == Dynamic ? i : -1); m_type == Dynamic ? i : -1);
} }
@ -5798,6 +5890,9 @@ bool QVkGraphicsPipeline::build()
release(); release();
QRHI_RES_RHI(QRhiVulkan); QRHI_RES_RHI(QRhiVulkan);
if (!rhiD->sanityCheckGraphicsPipeline(this))
return false;
if (!rhiD->ensurePipelineCache()) if (!rhiD->ensurePipelineCache())
return false; return false;

View File

@ -79,7 +79,7 @@ struct QVkBuffer : public QRhiBuffer
VkBuffer buffers[QVK_FRAMES_IN_FLIGHT]; VkBuffer buffers[QVK_FRAMES_IN_FLIGHT];
QVkAlloc allocations[QVK_FRAMES_IN_FLIGHT]; QVkAlloc allocations[QVK_FRAMES_IN_FLIGHT];
QVector<QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate> pendingDynamicUpdates[QVK_FRAMES_IN_FLIGHT]; QVarLengthArray<QRhiResourceUpdateBatchPrivate::BufferOp, 16> pendingDynamicUpdates[QVK_FRAMES_IN_FLIGHT];
VkBuffer stagingBuffers[QVK_FRAMES_IN_FLIGHT]; VkBuffer stagingBuffers[QVK_FRAMES_IN_FLIGHT];
QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT]; QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT];
struct UsageState { struct UsageState {
@ -853,17 +853,25 @@ public:
VkFence cmdFence = VK_NULL_HANDLE; VkFence cmdFence = VK_NULL_HANDLE;
} ofr; } ofr;
struct ActiveReadback { struct TextureReadback {
int activeFrameSlot = -1; int activeFrameSlot = -1;
QRhiReadbackDescription desc; QRhiReadbackDescription desc;
QRhiReadbackResult *result; QRhiReadbackResult *result;
VkBuffer buf; VkBuffer stagingBuf;
QVkAlloc bufAlloc; QVkAlloc stagingAlloc;
quint32 bufSize; quint32 byteSize;
QSize pixelSize; QSize pixelSize;
QRhiTexture::Format format; QRhiTexture::Format format;
}; };
QVector<ActiveReadback> activeReadbacks; QVector<TextureReadback> activeTextureReadbacks;
struct BufferReadback {
int activeFrameSlot = -1;
QRhiBufferReadbackResult *result;
int byteSize;
VkBuffer stagingBuf;
QVkAlloc stagingAlloc;
};
QVector<BufferReadback> activeBufferReadbacks;
struct DeferredReleaseEntry { struct DeferredReleaseEntry {
enum Type { enum Type {
@ -933,7 +941,8 @@ public:
Q_DECLARE_TYPEINFO(QRhiVulkan::DescriptorPoolData, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(QRhiVulkan::DescriptorPoolData, Q_MOVABLE_TYPE);
Q_DECLARE_TYPEINFO(QRhiVulkan::DeferredReleaseEntry, 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 QT_END_NAMESPACE

View File

@ -102,13 +102,23 @@
"gssapi": { "gssapi": {
"label": "KRB5 GSSAPI Support", "label": "KRB5 GSSAPI Support",
"test": { "test": {
"head": [
"#if defined(__APPLE__) && (defined(__GNUC__) || defined(__xlC__) || defined(__xlc__))",
"# include <TargetConditionals.h>",
"# if defined(TARGET_OS_MAC) && TARGET_OS_MAC",
"# include <GSS/GSS.h>",
"# endif",
"#else",
"# include <gssapi/gssapi.h>",
"#endif"
],
"main": [ "main": [
"gss_ctx_id_t ctx;", "gss_ctx_id_t ctx;",
"gss_context_time(nullptr, ctx, nullptr);" "gss_context_time(nullptr, ctx, nullptr);"
] ]
}, },
"headers": [ "gssapi/gssapi.h" ],
"sources": [ "sources": [
{ "libs": "-framework GSS", "condition": "config.darwin" },
{ "type": "pkgConfig", "args": "krb5-gssapi" }, { "type": "pkgConfig", "args": "krb5-gssapi" },
"-lgssapi_krb5" "-lgssapi_krb5"
] ]

View File

@ -59,8 +59,12 @@
#define SECURITY_WIN32 1 #define SECURITY_WIN32 1
#include <security.h> #include <security.h>
#elif QT_CONFIG(gssapi) // GSSAPI #elif QT_CONFIG(gssapi) // GSSAPI
#if defined(Q_OS_DARWIN)
#include <GSS/GSS.h>
#else
#include <gssapi/gssapi.h> #include <gssapi/gssapi.h>
#endif #endif // Q_OS_DARWIN
#endif // Q_CONFIG(sspi)
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE

View File

@ -69,6 +69,7 @@ extern "C" {
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(qLcEvdevTouch, "qt.qpa.input") 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 /* android (and perhaps some other linux-derived stuff) don't define everything
* in linux/input.h, so we'll need to do that ourselves. * 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) if (m_typeB)
m_contacts[m_currentSlot].maj = m_currentData.maj; m_contacts[m_currentSlot].maj = m_currentData.maj;
} else if (data->code == ABS_PRESSURE || data->code == ABS_MT_PRESSURE) { } 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); m_currentData.pressure = qBound(hw_pressure_min, data->value, hw_pressure_max);
if (m_typeB || m_singleTouch) if (m_typeB || m_singleTouch)
m_contacts[m_currentSlot].pressure = m_currentData.pressure; m_contacts[m_currentSlot].pressure = m_currentData.pressure;
@ -577,6 +581,7 @@ void QEvdevTouchScreenData::processInputEvent(input_event *data)
m_lastTouchPoints = m_touchPoints; m_lastTouchPoints = m_touchPoints;
m_touchPoints.clear(); m_touchPoints.clear();
Qt::TouchPointStates combinedStates; Qt::TouchPointStates combinedStates;
bool hasPressure = false;
for (auto i = m_contacts.begin(), end = m_contacts.end(); i != end; /*erasing*/) { for (auto i = m_contacts.begin(), end = m_contacts.end(); i != end; /*erasing*/) {
auto it = i++; auto it = i++;
@ -607,6 +612,9 @@ void QEvdevTouchScreenData::processInputEvent(input_event *data)
continue; continue;
} }
if (contact.pressure)
hasPressure = true;
addTouchPoint(contact, &combinedStates); addTouchPoint(contact, &combinedStates);
} }
@ -651,7 +659,7 @@ void QEvdevTouchScreenData::processInputEvent(input_event *data)
m_contacts.clear(); m_contacts.clear();
if (!m_touchPoints.isEmpty() && combinedStates != Qt::TouchPointStationary) if (!m_touchPoints.isEmpty() && (hasPressure || combinedStates != Qt::TouchPointStationary))
reportPoints(); reportPoints();
} }
@ -777,6 +785,9 @@ void QEvdevTouchScreenData::reportPoints()
tp.pressure = tp.state == Qt::TouchPointReleased ? 0 : 1; tp.pressure = tp.state == Qt::TouchPointReleased ? 0 : 1;
else else
tp.pressure = (tp.pressure - hw_pressure_min) / qreal(hw_pressure_max - hw_pressure_min); 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. // Let qguiapp pick the target window.

View File

@ -9213,9 +9213,11 @@ void QWidget::mouseReleaseEvent(QMouseEvent *event)
The default implementation calls mousePressEvent(). The default implementation calls mousePressEvent().
\note The widget will also receive mouse press and mouse release \note The widget will also receive mouse press and mouse release
events in addition to the double click event. It is up to the events in addition to the double click event. And if another widget
developer to ensure that the application interprets these events that overlaps this widget disappears in response to press or
correctly. 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(), \sa mousePressEvent(), mouseReleaseEvent(), mouseMoveEvent(),
event(), QMouseEvent event(), QMouseEvent

View File

@ -4593,6 +4593,8 @@ void tst_QString::fromLatin1()
} }
#if QT_DEPRECATED_SINCE(5, 0) #if QT_DEPRECATED_SINCE(5, 0)
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
void tst_QString::fromAscii() void tst_QString::fromAscii()
{ {
QString a; QString a;
@ -4613,6 +4615,7 @@ void tst_QString::fromAscii()
a = QString::fromAscii("\0abcd", 5); a = QString::fromAscii("\0abcd", 5);
QVERIFY(a.size() == 5); QVERIFY(a.size() == 5);
} }
QT_WARNING_POP
#endif #endif
void tst_QString::fromUcs4() void tst_QString::fromUcs4()

View File

@ -0,0 +1,48 @@
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: Copyright (C) 2019 The Qt Company Ltd.
:: Contact: https://www.qt.io/licensing/
::
:: This file is part of the QtQuick module of the Qt Toolkit.
::
:: $QT_BEGIN_LICENSE:LGPL$
:: Commercial License Usage
:: Licensees holding valid commercial Qt licenses may use this file in
:: accordance with the commercial license agreement provided with the
:: Software or, alternatively, in accordance with the terms contained in
:: a written agreement between you and The Qt Company. For licensing terms
:: and conditions see https://www.qt.io/terms-conditions. For further
:: information use the contact form at https://www.qt.io/contact-us.
::
:: GNU Lesser General Public License Usage
:: Alternatively, this file may be used under the terms of the GNU Lesser
:: General Public License version 3 as published by the Free Software
:: Foundation and appearing in the file LICENSE.LGPL3 included in the
:: packaging of this file. Please review the following information to
:: ensure the GNU Lesser General Public License version 3 requirements
:: will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
::
:: GNU General Public License Usage
:: Alternatively, this file may be used under the terms of the GNU
:: General Public License version 2.0 or (at your option) the GNU General
:: Public license version 3 or any later version approved by the KDE Free
:: Qt Foundation. The licenses are as published by the Free Software
:: Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
:: included in the packaging of this file. Please review the following
:: information to ensure the GNU General Public License requirements will
:: be met: https://www.gnu.org/licenses/gpl-2.0.html and
:: https://www.gnu.org/licenses/gpl-3.0.html.
::
:: $QT_END_LICENSE$
::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Note the -c argument: we do not want runtime HLSL compilation since that is
:: not an option on UWP (WinRT). This means that running qsb must happen on Windows.
qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o simple.vert.qsb simple.vert
qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o simple.frag.qsb simple.frag
qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o simpletextured.vert.qsb simpletextured.vert
qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o simpletextured.frag.qsb simpletextured.frag
qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o textured.vert.qsb textured.vert
qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o textured.frag.qsb textured.frag

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -0,0 +1,8 @@
#version 440
layout(location = 0) out vec4 fragColor;
void main()
{
fragColor = vec4(1.0, 0.0, 0.0, 1.0);
}

Binary file not shown.

View File

@ -0,0 +1,10 @@
#version 440
layout(location = 0) in vec4 position;
out gl_PerVertex { vec4 gl_Position; };
void main()
{
gl_Position = position;
}

Binary file not shown.

View File

@ -0,0 +1,13 @@
#version 440
layout(location = 0) in vec2 uv;
layout(location = 0) out vec4 fragColor;
layout(binding = 0) uniform sampler2D tex;
void main()
{
vec4 c = texture(tex, uv);
c.rgb *= c.a;
fragColor = c;
}

Binary file not shown.

View File

@ -0,0 +1,14 @@
#version 440
layout(location = 0) in vec4 position;
layout(location = 1) in vec2 texcoord;
layout(location = 0) out vec2 uv;
out gl_PerVertex { vec4 gl_Position; };
void main()
{
uv = texcoord;
gl_Position = position;
}

Binary file not shown.

View File

@ -1,12 +0,0 @@
#version 440
layout(location = 0) in vec2 v_texcoord;
layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D tex;
void main()
{
fragColor = texture(tex, v_texcoord);
}

View File

@ -0,0 +1,19 @@
#version 440
layout(location = 0) in vec2 uv;
layout(location = 0) out vec4 fragColor;
layout(std140, binding = 0) uniform buf {
mat4 matrix;
float opacity;
} ubuf;
layout(binding = 1) uniform sampler2D tex;
void main()
{
vec4 c = texture(tex, uv);
c.a *= ubuf.opacity;
c.rgb *= c.a;
fragColor = c;
}

Binary file not shown.

View File

@ -3,16 +3,17 @@
layout(location = 0) in vec4 position; layout(location = 0) in vec4 position;
layout(location = 1) in vec2 texcoord; layout(location = 1) in vec2 texcoord;
layout(location = 0) out vec2 v_texcoord; layout(location = 0) out vec2 uv;
layout(std140, binding = 0) uniform buf { layout(std140, binding = 0) uniform buf {
mat4 mvp; mat4 matrix;
float opacity;
} ubuf; } ubuf;
out gl_PerVertex { vec4 gl_Position; }; out gl_PerVertex { vec4 gl_Position; };
void main() void main()
{ {
v_texcoord = texcoord; uv = texcoord;
gl_Position = ubuf.mvp * position; gl_Position = ubuf.matrix * position;
} }

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,7 @@ SUBDIRS=\
macplist \ macplist \
networkselftest \ networkselftest \
qaccessibility \ qaccessibility \
# qaccessibilitylinux \ # QTBUG-44434 # qaccessibilitylinux # QTBUG-44434 \
qaccessibilitymac \ qaccessibilitymac \
qcomplextext \ qcomplextext \
qfocusevent \ qfocusevent \

View File

@ -29,7 +29,7 @@
#include <QXmlStreamReader> #include <QXmlStreamReader>
extern "C" int LLVMFuzzerTestOneInput(const char *Data, size_t Size) { extern "C" int LLVMFuzzerTestOneInput(const char *Data, size_t Size) {
QXmlStreamReader reader(QByteArray(Data, Size)); QXmlStreamReader reader(QByteArray::fromRawData(Data, Size));
while (!reader.atEnd()) while (!reader.atEnd())
reader.readNext(); reader.readNext();
return 0; return 0;

View File

@ -32,6 +32,6 @@
extern "C" int LLVMFuzzerTestOneInput(const char *data, size_t size) { extern "C" int LLVMFuzzerTestOneInput(const char *data, size_t size) {
static int c = 0; static int c = 0;
static QGuiApplication a(c, nullptr); static QGuiApplication a(c, nullptr);
QColorSpace cs = QColorSpace::fromIccProfile(QByteArray(data, size)); QColorSpace cs = QColorSpace::fromIccProfile(QByteArray::fromRawData(data, size));
return 0; return 0;
} }

View File

@ -32,6 +32,6 @@
extern "C" int LLVMFuzzerTestOneInput(const char *Data, size_t Size) { extern "C" int LLVMFuzzerTestOneInput(const char *Data, size_t Size) {
static int c = 0; static int c = 0;
static QApplication a(c, nullptr); static QApplication a(c, nullptr);
QTextDocument().setHtml(QByteArray(Data, Size)); QTextDocument().setHtml(QByteArray::fromRawData(Data, Size));
return 0; return 0;
} }

View File

@ -29,6 +29,6 @@
#include <QTextDocument> #include <QTextDocument>
extern "C" int LLVMFuzzerTestOneInput(const char *Data, size_t Size) { extern "C" int LLVMFuzzerTestOneInput(const char *Data, size_t Size) {
QTextDocument().setMarkdown(QByteArray(Data, Size)); QTextDocument().setMarkdown(QByteArray::fromRawData(Data, Size));
return 0; return 0;
} }