summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2020-10-11 22:02:43 +0200
committerLaszlo Agocs <laszlo.agocs@qt.io>2020-10-12 07:55:37 +0200
commit2189e0f2fc2aadf84ed53a7ae6210e2bbe04b45e (patch)
tree5cdc23693256752f6b7ad624efa1d270fd7bd247
parente7c456d3395826a427b863c0cfc8c60aa4ab7d6c (diff)
rhi: Make the new direct buffer update available for non-uniform buffers
The original restriction to UniformBuffer was due to the GL backend where there is no GL buffer object for QRhiBuffers with usage UniformBuffer. However, we can still implement this for cases when there is a true GL buffer object underneath. With other backends it should all work as-is already. This becomes useful when one has buffers with usage Vertex that need full updates every frame. (f.ex. instance data) Unfortunately this involves renaming the function. But while at it, add an autotest case as well. Change-Id: Iff59e4509a8bae06654cc92fe8428bd79eb012fb Reviewed-by: Andy Nichols <andy.nichols@qt.io>
-rw-r--r--src/gui/rhi/qrhi.cpp9
-rw-r--r--src/gui/rhi/qrhi_p.h4
-rw-r--r--src/gui/rhi/qrhid3d11.cpp8
-rw-r--r--src/gui/rhi/qrhid3d11_p_p.h4
-rw-r--r--src/gui/rhi/qrhigles2.cpp49
-rw-r--r--src/gui/rhi/qrhigles2_p_p.h6
-rw-r--r--src/gui/rhi/qrhimetal.mm8
-rw-r--r--src/gui/rhi/qrhimetal_p_p.h4
-rw-r--r--src/gui/rhi/qrhinull.cpp4
-rw-r--r--src/gui/rhi/qrhinull_p_p.h2
-rw-r--r--src/gui/rhi/qrhivulkan.cpp8
-rw-r--r--src/gui/rhi/qrhivulkan_p_p.h4
-rw-r--r--tests/auto/gui/rhi/qrhi/tst_qrhi.cpp216
-rw-r--r--tests/manual/rhi/noninstanced/noninstanced.cpp4
14 files changed, 287 insertions, 43 deletions
diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp
index c67659478a..06781686fa 100644
--- a/src/gui/rhi/qrhi.cpp
+++ b/src/gui/rhi/qrhi.cpp
@@ -2144,10 +2144,9 @@ QRhiBuffer::NativeBuffer QRhiBuffer::nativeBuffer()
\warning This function can only be called when recording a frame, so
between QRhi::beginFrame() and QRhi::endFrame().
- \warning This function can only be called on Dynamic buffers with the
- UniformBuffer usage flag.
+ \warning This function can only be called on Dynamic buffers.
*/
-char *QRhiBuffer::beginFullDynamicUniformBufferUpdateForCurrentFrame()
+char *QRhiBuffer::beginFullDynamicBufferUpdateForCurrentFrame()
{
return nullptr;
}
@@ -2155,9 +2154,9 @@ char *QRhiBuffer::beginFullDynamicUniformBufferUpdateForCurrentFrame()
/*!
To be called when the entire contents of the buffer data has been updated
in the memory block returned from
- beginFullDynamicUniformBufferUpdateForCurrentFrame().
+ beginFullDynamicBufferUpdateForCurrentFrame().
*/
-void QRhiBuffer::endFullDynamicUniformBufferUpdateForCurrentFrame()
+void QRhiBuffer::endFullDynamicBufferUpdateForCurrentFrame()
{
}
diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h
index fa58718ace..977b68cd8e 100644
--- a/src/gui/rhi/qrhi_p.h
+++ b/src/gui/rhi/qrhi_p.h
@@ -717,8 +717,8 @@ public:
virtual NativeBuffer nativeBuffer();
- virtual char *beginFullDynamicUniformBufferUpdateForCurrentFrame();
- virtual void endFullDynamicUniformBufferUpdateForCurrentFrame();
+ virtual char *beginFullDynamicBufferUpdateForCurrentFrame();
+ virtual void endFullDynamicBufferUpdateForCurrentFrame();
protected:
QRhiBuffer(QRhiImplementation *rhi, Type type_, UsageFlags usage_, int size_);
diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp
index 9fccff7b5c..48e626106c 100644
--- a/src/gui/rhi/qrhid3d11.cpp
+++ b/src/gui/rhi/qrhid3d11.cpp
@@ -2708,15 +2708,15 @@ QRhiBuffer::NativeBuffer QD3D11Buffer::nativeBuffer()
return { { &buffer }, 1 };
}
-char *QD3D11Buffer::beginFullDynamicUniformBufferUpdateForCurrentFrame()
+char *QD3D11Buffer::beginFullDynamicBufferUpdateForCurrentFrame()
{
// Shortcut the entire buffer update mechanism and allow the client to do
// the host writes directly to the buffer. This will lead to unexpected
// results when combined with QRhiResourceUpdateBatch-based updates for the
// buffer, since dynBuf is left untouched and out of sync, but provides a
- // fast path for uniform buffers that have all their content changed in
+ // fast path for dynamic buffers that have all their content changed in
// every frame.
- Q_ASSERT(m_type == Dynamic && m_usage.testFlag(UniformBuffer));
+ Q_ASSERT(m_type == Dynamic);
D3D11_MAPPED_SUBRESOURCE mp;
QRHI_RES_RHI(QRhiD3D11);
HRESULT hr = rhiD->context->Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mp);
@@ -2727,7 +2727,7 @@ char *QD3D11Buffer::beginFullDynamicUniformBufferUpdateForCurrentFrame()
return static_cast<char *>(mp.pData);
}
-void QD3D11Buffer::endFullDynamicUniformBufferUpdateForCurrentFrame()
+void QD3D11Buffer::endFullDynamicBufferUpdateForCurrentFrame()
{
QRHI_RES_RHI(QRhiD3D11);
rhiD->context->Unmap(buffer, 0);
diff --git a/src/gui/rhi/qrhid3d11_p_p.h b/src/gui/rhi/qrhid3d11_p_p.h
index 6de899310c..1572751e6c 100644
--- a/src/gui/rhi/qrhid3d11_p_p.h
+++ b/src/gui/rhi/qrhid3d11_p_p.h
@@ -65,8 +65,8 @@ struct QD3D11Buffer : public QRhiBuffer
void destroy() override;
bool create() override;
QRhiBuffer::NativeBuffer nativeBuffer() override;
- char *beginFullDynamicUniformBufferUpdateForCurrentFrame() override;
- void endFullDynamicUniformBufferUpdateForCurrentFrame() override;
+ char *beginFullDynamicBufferUpdateForCurrentFrame() override;
+ void endFullDynamicBufferUpdateForCurrentFrame() override;
ID3D11UnorderedAccessView *unorderedAccessView();
diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp
index 5411d779e1..39f03a784f 100644
--- a/src/gui/rhi/qrhigles2.cpp
+++ b/src/gui/rhi/qrhigles2.cpp
@@ -297,6 +297,10 @@ QT_BEGIN_NAMESPACE
#define GL_MAP_READ_BIT 0x0001
#endif
+#ifndef GL_MAP_WRITE_BIT
+#define GL_MAP_WRITE_BIT 0x0002
+#endif
+
#ifndef GL_TEXTURE_2D_MULTISAMPLE
#define GL_TEXTURE_2D_MULTISAMPLE 0x9100
#endif
@@ -1697,7 +1701,7 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, u.buf);
Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) {
- memcpy(bufD->ubuf + u.offset, u.data.constData(), size_t(u.data.size()));
+ memcpy(bufD->data + u.offset, u.data.constData(), size_t(u.data.size()));
} else {
trackedBufferBarrier(cbD, bufD, QGles2Buffer::AccessUpdate);
QGles2CommandBuffer::Command cmd;
@@ -1714,7 +1718,7 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
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 + u.offset, u.data.constData(), size_t(u.data.size()));
+ memcpy(bufD->data + u.offset, u.data.constData(), size_t(u.data.size()));
} else {
trackedBufferBarrier(cbD, bufD, QGles2Buffer::AccessUpdate);
QGles2CommandBuffer::Command cmd;
@@ -1730,7 +1734,7 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
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 + u.offset, size_t(u.readSize));
+ memcpy(u.result->data.data(), bufD->data + u.offset, size_t(u.readSize));
if (u.result->completed)
u.result->completed();
} else {
@@ -2867,7 +2871,7 @@ void QRhiGles2::bindShaderResources(QGles2CommandBuffer *cbD,
}
}
QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, b->u.ubuf.buf);
- const char *bufView = bufD->ubuf + viewOffset;
+ const char *bufView = bufD->data + viewOffset;
for (const QGles2UniformDescription &uniform : qAsConst(uniforms)) {
if (uniform.binding == b->binding) {
// in a uniform buffer everything is at least 4 byte aligned
@@ -3836,8 +3840,8 @@ void QGles2Buffer::destroy()
e.buffer.buffer = buffer;
buffer = 0;
- delete[] ubuf;
- ubuf = nullptr;
+ delete[] data;
+ data = nullptr;
QRHI_RES_RHI(QRhiGles2);
rhiD->releaseQueue.append(e);
@@ -3854,14 +3858,14 @@ bool QGles2Buffer::create()
QRHI_RES_RHI(QRhiGles2);
QRHI_PROF;
- const int nonZeroSize = m_size <= 0 ? 256 : m_size;
+ nonZeroSize = m_size <= 0 ? 256 : m_size;
if (m_usage.testFlag(QRhiBuffer::UniformBuffer)) {
if (int(m_usage) != QRhiBuffer::UniformBuffer) {
qWarning("Uniform buffer: multiple usages specified, this is not supported by the OpenGL backend");
return false;
}
- ubuf = new char[nonZeroSize];
+ data = new char[nonZeroSize];
QRHI_PROF_F(newBuffer(this, uint(nonZeroSize), 0, 1));
return true;
}
@@ -3894,10 +3898,33 @@ QRhiBuffer::NativeBuffer QGles2Buffer::nativeBuffer()
return { { &buffer }, 1 };
}
-char *QGles2Buffer::beginFullDynamicUniformBufferUpdateForCurrentFrame()
+char *QGles2Buffer::beginFullDynamicBufferUpdateForCurrentFrame()
+{
+ Q_ASSERT(m_type == Dynamic);
+ if (!m_usage.testFlag(UniformBuffer)) {
+ QRHI_RES_RHI(QRhiGles2);
+ rhiD->f->glBindBuffer(targetForDataOps, buffer);
+ if (rhiD->caps.properMapBuffer) {
+ return static_cast<char *>(rhiD->f->glMapBufferRange(targetForDataOps, 0, nonZeroSize,
+ GL_MAP_READ_BIT | GL_MAP_WRITE_BIT));
+ } else {
+ // Need some storage for the data, use the otherwise unused 'data' member.
+ if (!data)
+ data = new char[nonZeroSize];
+ }
+ }
+ return data;
+}
+
+void QGles2Buffer::endFullDynamicBufferUpdateForCurrentFrame()
{
- Q_ASSERT(m_type == Dynamic && m_usage.testFlag(UniformBuffer));
- return ubuf;
+ if (!m_usage.testFlag(UniformBuffer)) {
+ QRHI_RES_RHI(QRhiGles2);
+ if (rhiD->caps.properMapBuffer)
+ rhiD->f->glUnmapBuffer(targetForDataOps);
+ else
+ rhiD->f->glBufferSubData(targetForDataOps, 0, nonZeroSize, data);
+ }
}
QGles2RenderBuffer::QGles2RenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h
index b7577ce51b..6ce3786445 100644
--- a/src/gui/rhi/qrhigles2_p_p.h
+++ b/src/gui/rhi/qrhigles2_p_p.h
@@ -65,11 +65,13 @@ struct QGles2Buffer : public QRhiBuffer
void destroy() override;
bool create() override;
QRhiBuffer::NativeBuffer nativeBuffer() override;
- char *beginFullDynamicUniformBufferUpdateForCurrentFrame() override;
+ char *beginFullDynamicBufferUpdateForCurrentFrame() override;
+ void endFullDynamicBufferUpdateForCurrentFrame() override;
+ int nonZeroSize = 0;
GLuint buffer = 0;
GLenum targetForDataOps;
- char *ubuf = nullptr;
+ char *data = nullptr;
enum Access {
AccessNone,
AccessVertex,
diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm
index 5bbcd50047..9108ad825f 100644
--- a/src/gui/rhi/qrhimetal.mm
+++ b/src/gui/rhi/qrhimetal.mm
@@ -2254,14 +2254,14 @@ QRhiBuffer::NativeBuffer QMetalBuffer::nativeBuffer()
return { { &d->buf[0] }, 1 };
}
-char *QMetalBuffer::beginFullDynamicUniformBufferUpdateForCurrentFrame()
+char *QMetalBuffer::beginFullDynamicBufferUpdateForCurrentFrame()
{
// Shortcut the entire buffer update mechanism and allow the client to do
// the host writes directly to the buffer. This will lead to unexpected
// results when combined with QRhiResourceUpdateBatch-based updates for the
- // buffer, but provides a fast path for uniform buffers that have all their
+ // buffer, but provides a fast path for dynamic buffers that have all their
// content changed in every frame.
- Q_ASSERT(m_type == Dynamic && m_usage.testFlag(UniformBuffer));
+ Q_ASSERT(m_type == Dynamic);
QRHI_RES_RHI(QRhiMetal);
Q_ASSERT(rhiD->inFrame);
const int slot = rhiD->currentFrameSlot;
@@ -2269,7 +2269,7 @@ char *QMetalBuffer::beginFullDynamicUniformBufferUpdateForCurrentFrame()
return static_cast<char *>(p);
}
-void QMetalBuffer::endFullDynamicUniformBufferUpdateForCurrentFrame()
+void QMetalBuffer::endFullDynamicBufferUpdateForCurrentFrame()
{
#ifdef Q_OS_MACOS
if (d->managed) {
diff --git a/src/gui/rhi/qrhimetal_p_p.h b/src/gui/rhi/qrhimetal_p_p.h
index 1691b03ec6..d2082d04b6 100644
--- a/src/gui/rhi/qrhimetal_p_p.h
+++ b/src/gui/rhi/qrhimetal_p_p.h
@@ -66,8 +66,8 @@ struct QMetalBuffer : public QRhiBuffer
void destroy() override;
bool create() override;
QRhiBuffer::NativeBuffer nativeBuffer() override;
- char *beginFullDynamicUniformBufferUpdateForCurrentFrame() override;
- void endFullDynamicUniformBufferUpdateForCurrentFrame() override;
+ char *beginFullDynamicBufferUpdateForCurrentFrame() override;
+ void endFullDynamicBufferUpdateForCurrentFrame() override;
QMetalBufferData *d;
uint generation = 0;
diff --git a/src/gui/rhi/qrhinull.cpp b/src/gui/rhi/qrhinull.cpp
index 737e5fb48b..9666f4c688 100644
--- a/src/gui/rhi/qrhinull.cpp
+++ b/src/gui/rhi/qrhinull.cpp
@@ -583,9 +583,9 @@ bool QNullBuffer::create()
return true;
}
-char *QNullBuffer::beginFullDynamicUniformBufferUpdateForCurrentFrame()
+char *QNullBuffer::beginFullDynamicBufferUpdateForCurrentFrame()
{
- Q_ASSERT(m_type == Dynamic && m_usage.testFlag(UniformBuffer));
+ Q_ASSERT(m_type == Dynamic);
return data;
}
diff --git a/src/gui/rhi/qrhinull_p_p.h b/src/gui/rhi/qrhinull_p_p.h
index e0e796f7ca..eda30614a0 100644
--- a/src/gui/rhi/qrhinull_p_p.h
+++ b/src/gui/rhi/qrhinull_p_p.h
@@ -59,7 +59,7 @@ struct QNullBuffer : public QRhiBuffer
~QNullBuffer();
void destroy() override;
bool create() override;
- char *beginFullDynamicUniformBufferUpdateForCurrentFrame() override;
+ char *beginFullDynamicBufferUpdateForCurrentFrame() override;
char *data = nullptr;
};
diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp
index 05b7afae75..fc539c03a9 100644
--- a/src/gui/rhi/qrhivulkan.cpp
+++ b/src/gui/rhi/qrhivulkan.cpp
@@ -5370,14 +5370,14 @@ QRhiBuffer::NativeBuffer QVkBuffer::nativeBuffer()
return { { &buffers[0] }, 1 };
}
-char *QVkBuffer::beginFullDynamicUniformBufferUpdateForCurrentFrame()
+char *QVkBuffer::beginFullDynamicBufferUpdateForCurrentFrame()
{
// Shortcut the entire buffer update mechanism and allow the client to do
// the host writes directly to the buffer. This will lead to unexpected
// results when combined with QRhiResourceUpdateBatch-based updates for the
- // buffer, but provides a fast path for uniform buffers that have all their
+ // buffer, but provides a fast path for dynamic buffers that have all their
// content changed in every frame.
- Q_ASSERT(m_type == Dynamic && m_usage.testFlag(UniformBuffer));
+ Q_ASSERT(m_type == Dynamic);
QRHI_RES_RHI(QRhiVulkan);
Q_ASSERT(rhiD->inFrame);
const int slot = rhiD->currentFrameSlot;
@@ -5391,7 +5391,7 @@ char *QVkBuffer::beginFullDynamicUniformBufferUpdateForCurrentFrame()
return static_cast<char *>(p);
}
-void QVkBuffer::endFullDynamicUniformBufferUpdateForCurrentFrame()
+void QVkBuffer::endFullDynamicBufferUpdateForCurrentFrame()
{
QRHI_RES_RHI(QRhiVulkan);
const int slot = rhiD->currentFrameSlot;
diff --git a/src/gui/rhi/qrhivulkan_p_p.h b/src/gui/rhi/qrhivulkan_p_p.h
index c16a990894..5819e303c3 100644
--- a/src/gui/rhi/qrhivulkan_p_p.h
+++ b/src/gui/rhi/qrhivulkan_p_p.h
@@ -77,8 +77,8 @@ struct QVkBuffer : public QRhiBuffer
void destroy() override;
bool create() override;
QRhiBuffer::NativeBuffer nativeBuffer() override;
- char *beginFullDynamicUniformBufferUpdateForCurrentFrame() override;
- void endFullDynamicUniformBufferUpdateForCurrentFrame() override;
+ char *beginFullDynamicBufferUpdateForCurrentFrame() override;
+ void endFullDynamicBufferUpdateForCurrentFrame() override;
VkBuffer buffers[QVK_FRAMES_IN_FLIGHT];
QVkAlloc allocations[QVK_FRAMES_IN_FLIGHT];
diff --git a/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp b/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp
index 74a197808b..08a9bb3f95 100644
--- a/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp
+++ b/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp
@@ -110,6 +110,8 @@ private slots:
void renderToTextureArrayOfTexturedQuad();
void renderToTextureTexturedQuadAndUniformBuffer_data();
void renderToTextureTexturedQuadAndUniformBuffer();
+ void renderToTextureTexturedQuadAllDynamicBuffers_data();
+ void renderToTextureTexturedQuadAllDynamicBuffers();
void renderToTextureDeferredSrb_data();
void renderToTextureDeferredSrb();
void renderToTextureMultipleUniformBuffersAndDynamicOffset_data();
@@ -2222,6 +2224,220 @@ void tst_QRhi::renderToTextureTexturedQuadAndUniformBuffer()
QCOMPARE(result1.pixel(28, 178), empty);
}
+void tst_QRhi::renderToTextureTexturedQuadAllDynamicBuffers_data()
+{
+ rhiTestData();
+}
+
+void tst_QRhi::renderToTextureTexturedQuadAllDynamicBuffers()
+{
+ QFETCH(QRhi::Implementation, impl);
+ QFETCH(QRhiInitParams *, initParams);
+
+ QScopedPointer<QRhi> rhi(QRhi::create(impl, initParams, QRhi::Flags(), nullptr));
+ if (!rhi)
+ QSKIP("QRhi could not be created, skipping testing rendering");
+
+ QImage inputImage;
+ inputImage.load(QLatin1String(":/data/qt256.png"));
+ QVERIFY(!inputImage.isNull());
+
+ QScopedPointer<QRhiTexture> texture(rhi->newTexture(QRhiTexture::RGBA8, inputImage.size(), 1,
+ QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource));
+ QVERIFY(texture->create());
+
+ QScopedPointer<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget({ texture.data() }));
+ QScopedPointer<QRhiRenderPassDescriptor> rpDesc(rt->newCompatibleRenderPassDescriptor());
+ rt->setRenderPassDescriptor(rpDesc.data());
+ QVERIFY(rt->create());
+
+ QRhiCommandBuffer *cb = nullptr;
+ QVERIFY(rhi->beginOffscreenFrame(&cb) == QRhi::FrameOpSuccess);
+ QVERIFY(cb);
+
+ static const float verticesUvs[] = {
+ -1.0f, -1.0f, 0.0f, 0.0f,
+ 1.0f, -1.0f, 1.0f, 0.0f,
+ -1.0f, 1.0f, 0.0f, 1.0f,
+ 1.0f, 1.0f, 1.0f, 1.0f
+ };
+
+ // Do like renderToTextureTexturedQuadAndUniformBuffer but only use Dynamic
+ // buffers, and do updates with the direct beginFullDynamicBufferUpdate
+ // function. (for some backend this is different for UniformBuffer and
+ // others, hence useful exercising it also on a VertexBuffer)
+
+ QScopedPointer<QRhiBuffer> vbuf(rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::VertexBuffer, sizeof(verticesUvs)));
+ QVERIFY(vbuf->create());
+ char *p = vbuf->beginFullDynamicBufferUpdateForCurrentFrame();
+ QVERIFY(p);
+ memcpy(p, verticesUvs, sizeof(verticesUvs));
+ vbuf->endFullDynamicBufferUpdateForCurrentFrame();
+
+ const int UNIFORM_BLOCK_SIZE = 64 + 4; // matrix + opacity
+ const int secondUbufOffset = rhi->ubufAligned(UNIFORM_BLOCK_SIZE);
+ const int UBUF_SIZE = secondUbufOffset + UNIFORM_BLOCK_SIZE;
+
+ QScopedPointer<QRhiBuffer> ubuf(rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, UBUF_SIZE));
+ QVERIFY(ubuf->create());
+
+ p = ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
+ QVERIFY(p);
+
+ QMatrix4x4 matrix;
+ memcpy(p, matrix.constData(), 64);
+ float opacity = 0.5f;
+ memcpy(p + 64, &opacity, 4);
+
+ // rotation by 45 degrees around the Z axis
+ matrix.rotate(45, 0, 0, 1);
+ memcpy(p + secondUbufOffset, matrix.constData(), 64);
+ memcpy(p + secondUbufOffset + 64, &opacity, 4);
+
+ ubuf->endFullDynamicBufferUpdateForCurrentFrame();
+
+ QRhiResourceUpdateBatch *updates = rhi->nextResourceUpdateBatch();
+ QScopedPointer<QRhiTexture> inputTexture(rhi->newTexture(QRhiTexture::RGBA8, inputImage.size()));
+ QVERIFY(inputTexture->create());
+ updates->uploadTexture(inputTexture.data(), inputImage);
+ cb->resourceUpdate(updates);
+
+ QScopedPointer<QRhiSampler> sampler(rhi->newSampler(QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
+ QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge));
+ QVERIFY(sampler->create());
+
+ const QRhiShaderResourceBinding::StageFlags commonVisibility = QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage;
+ QScopedPointer<QRhiShaderResourceBindings> srb0(rhi->newShaderResourceBindings());
+ srb0->setBindings({
+ QRhiShaderResourceBinding::uniformBuffer(0, commonVisibility, ubuf.data(), 0, UNIFORM_BLOCK_SIZE),
+ QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, inputTexture.data(), sampler.data())
+ });
+ QVERIFY(srb0->create());
+
+ QScopedPointer<QRhiShaderResourceBindings> srb1(rhi->newShaderResourceBindings());
+ srb1->setBindings({
+ QRhiShaderResourceBinding::uniformBuffer(0, commonVisibility, ubuf.data(), secondUbufOffset, UNIFORM_BLOCK_SIZE),
+ QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, inputTexture.data(), sampler.data())
+ });
+ QVERIFY(srb1->create());
+ QVERIFY(srb1->isLayoutCompatible(srb0.data())); // hence no need for a second pipeline
+
+ QScopedPointer<QRhiGraphicsPipeline> pipeline(rhi->newGraphicsPipeline());
+ pipeline->setTopology(QRhiGraphicsPipeline::TriangleStrip);
+ QShader vs = loadShader(":/data/textured.vert.qsb");
+ QVERIFY(vs.isValid());
+ QShaderDescription shaderDesc = vs.description();
+ QVERIFY(!shaderDesc.uniformBlocks().isEmpty());
+ QCOMPARE(shaderDesc.uniformBlocks().first().size, UNIFORM_BLOCK_SIZE);
+
+ QShader fs = loadShader(":/data/textured.frag.qsb");
+ QVERIFY(fs.isValid());
+ shaderDesc = fs.description();
+ QVERIFY(!shaderDesc.uniformBlocks().isEmpty());
+ QCOMPARE(shaderDesc.uniformBlocks().first().size, UNIFORM_BLOCK_SIZE);
+
+ pipeline->setShaderStages({ { QRhiShaderStage::Vertex, vs }, { QRhiShaderStage::Fragment, fs } });
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({ { 4 * sizeof(float) } });
+ inputLayout.setAttributes({
+ { 0, 0, QRhiVertexInputAttribute::Float2, 0 },
+ { 0, 1, QRhiVertexInputAttribute::Float2, 2 * sizeof(float) }
+ });
+ pipeline->setVertexInputLayout(inputLayout);
+ pipeline->setShaderResourceBindings(srb0.data());
+ pipeline->setRenderPassDescriptor(rpDesc.data());
+
+ QVERIFY(pipeline->create());
+
+ cb->beginPass(rt.data(), Qt::black, { 1.0f, 0 });
+ cb->setGraphicsPipeline(pipeline.data());
+ cb->setShaderResources();
+ cb->setViewport({ 0, 0, float(texture->pixelSize().width()), float(texture->pixelSize().height()) });
+ QRhiCommandBuffer::VertexInput vbindings(vbuf.data(), 0);
+ cb->setVertexInput(0, 1, &vbindings);
+ cb->draw(4);
+
+ QRhiReadbackResult readResult0;
+ QImage result0;
+ readResult0.completed = [&readResult0, &result0] {
+ result0 = QImage(reinterpret_cast<const uchar *>(readResult0.data.constData()),
+ readResult0.pixelSize.width(), readResult0.pixelSize.height(),
+ QImage::Format_RGBA8888_Premultiplied);
+ };
+ QRhiResourceUpdateBatch *readbackBatch = rhi->nextResourceUpdateBatch();
+ readbackBatch->readBackTexture({ texture.data() }, &readResult0);
+ cb->endPass(readbackBatch);
+
+ // second pass (rotated)
+ cb->beginPass(rt.data(), Qt::black, { 1.0f, 0 });
+ cb->setGraphicsPipeline(pipeline.data());
+ cb->setShaderResources(srb1.data()); // sources data from a different offset in ubuf
+ cb->setViewport({ 0, 0, float(texture->pixelSize().width()), float(texture->pixelSize().height()) });
+ cb->setVertexInput(0, 1, &vbindings);
+ cb->draw(4);
+
+ QRhiReadbackResult readResult1;
+ QImage result1;
+ readResult1.completed = [&readResult1, &result1] {
+ result1 = QImage(reinterpret_cast<const uchar *>(readResult1.data.constData()),
+ readResult1.pixelSize.width(), readResult1.pixelSize.height(),
+ QImage::Format_RGBA8888_Premultiplied);
+ };
+ readbackBatch = rhi->nextResourceUpdateBatch();
+ readbackBatch->readBackTexture({ texture.data() }, &readResult1);
+ cb->endPass(readbackBatch);
+
+ rhi->endOffscreenFrame();
+
+ QVERIFY(!result0.isNull());
+ QVERIFY(!result1.isNull());
+
+ if (rhi->isYUpInFramebuffer() != rhi->isYUpInNDC()) {
+ result0 = std::move(result0).mirrored();
+ result1 = std::move(result1).mirrored();
+ }
+
+ if (impl == QRhi::Null)
+ return;
+
+ // opacity 0.5 (premultiplied)
+ static const auto checkSemiWhite = [](const QRgb &c) {
+ QRgb semiWhite127 = qPremultiply(qRgba(255, 255, 255, 127));
+ QRgb semiWhite128 = qPremultiply(qRgba(255, 255, 255, 128));
+ return c == semiWhite127 || c == semiWhite128;
+ };
+ QVERIFY(checkSemiWhite(result0.pixel(79, 77)));
+ QVERIFY(checkSemiWhite(result0.pixel(124, 81)));
+ QVERIFY(checkSemiWhite(result0.pixel(128, 149)));
+ QVERIFY(checkSemiWhite(result0.pixel(120, 189)));
+ QVERIFY(checkSemiWhite(result0.pixel(116, 185)));
+ QVERIFY(checkSemiWhite(result0.pixel(191, 172)));
+
+ QRgb empty = qRgba(0, 0, 0, 0);
+ QCOMPARE(result0.pixel(11, 45), empty);
+ QCOMPARE(result0.pixel(246, 202), empty);
+ QCOMPARE(result0.pixel(130, 18), empty);
+ QCOMPARE(result0.pixel(4, 227), empty);
+
+ // also rotated 45 degrees around Z
+ QRgb black = qRgba(0, 0, 0, 255);
+ QCOMPARE(result1.pixel(20, 23), black);
+ QCOMPARE(result1.pixel(47, 5), black);
+ QCOMPARE(result1.pixel(238, 22), black);
+ QCOMPARE(result1.pixel(250, 203), black);
+ QCOMPARE(result1.pixel(224, 237), black);
+ QCOMPARE(result1.pixel(12, 221), black);
+
+ QVERIFY(checkSemiWhite(result1.pixel(142, 67)));
+ QVERIFY(checkSemiWhite(result1.pixel(81, 79)));
+ QVERIFY(checkSemiWhite(result1.pixel(79, 168)));
+ QVERIFY(checkSemiWhite(result1.pixel(146, 204)));
+ QVERIFY(checkSemiWhite(result1.pixel(186, 156)));
+
+ QCOMPARE(result1.pixel(204, 45), empty);
+ QCOMPARE(result1.pixel(28, 178), empty);
+}
+
void tst_QRhi::renderToTextureDeferredSrb_data()
{
rhiTestData();
diff --git a/tests/manual/rhi/noninstanced/noninstanced.cpp b/tests/manual/rhi/noninstanced/noninstanced.cpp
index 31ea6e24a2..e014e302e0 100644
--- a/tests/manual/rhi/noninstanced/noninstanced.cpp
+++ b/tests/manual/rhi/noninstanced/noninstanced.cpp
@@ -149,7 +149,7 @@ void Window::customRender()
}
for (int i = 0; i < INSTANCE_COUNT; ++i) {
- char *p = d.ubuf[i]->beginFullDynamicUniformBufferUpdateForCurrentFrame();
+ char *p = d.ubuf[i]->beginFullDynamicBufferUpdateForCurrentFrame();
QMatrix4x4 mvp = m_proj;
mvp.rotate(d.rot, 0, 1, 0);
mvp.scale(0.05f);
@@ -171,7 +171,7 @@ void Window::customRender()
memcpy(p + 84, &d.instData[i].g, 4);
memcpy(p + 88, &d.instData[i].b, 4);
- d.ubuf[i]->endFullDynamicUniformBufferUpdateForCurrentFrame();
+ d.ubuf[i]->endFullDynamicBufferUpdateForCurrentFrame();
}
cb->beginPass(m_sc->currentFrameRenderTarget(), m_clearColor, { 1.0f, 0 }, u, QRhiCommandBuffer::DoNotTrackResourcesForCompute);