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()
{
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->name = name;
glue->makefile = createMakefileGenerator(project, true);

View File

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

View File

@ -727,6 +727,11 @@ QLibraryInfo::rawLocation(LibraryLocation loc, PathGroup group)
#ifndef QT_BUILD_QMAKE_BOOTSTRAP
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;
if (loc == PrefixPath) {
path = getPrefix(

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -102,13 +102,23 @@
"gssapi": {
"label": "KRB5 GSSAPI Support",
"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": [
"gss_ctx_id_t ctx;",
"gss_context_time(nullptr, ctx, nullptr);"
]
},
"headers": [ "gssapi/gssapi.h" ],
"sources": [
{ "libs": "-framework GSS", "condition": "config.darwin" },
{ "type": "pkgConfig", "args": "krb5-gssapi" },
"-lgssapi_krb5"
]

View File

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

View File

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

View File

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

View File

@ -4593,6 +4593,8 @@ void tst_QString::fromLatin1()
}
#if QT_DEPRECATED_SINCE(5, 0)
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
void tst_QString::fromAscii()
{
QString a;
@ -4613,6 +4615,7 @@ void tst_QString::fromAscii()
a = QString::fromAscii("\0abcd", 5);
QVERIFY(a.size() == 5);
}
QT_WARNING_POP
#endif
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 = 1) in vec2 texcoord;
layout(location = 0) out vec2 v_texcoord;
layout(location = 0) out vec2 uv;
layout(std140, binding = 0) uniform buf {
mat4 mvp;
mat4 matrix;
float opacity;
} ubuf;
out gl_PerVertex { vec4 gl_Position; };
void main()
{
v_texcoord = texcoord;
gl_Position = ubuf.mvp * position;
uv = texcoord;
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 \
networkselftest \
qaccessibility \
# qaccessibilitylinux \ # QTBUG-44434
# qaccessibilitylinux # QTBUG-44434 \
qaccessibilitymac \
qcomplextext \
qfocusevent \

View File

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

View File

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

View File

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

View File

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