summaryrefslogtreecommitdiffstats
path: root/src/gui/rhi
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2020-10-09 13:34:37 +0200
committerLaszlo Agocs <laszlo.agocs@qt.io>2020-10-11 10:58:37 +0200
commit2ac2809ec36af238b3755f176ef45427c1d32467 (patch)
tree1567c3bba1db68c7e249d5a345f14f42ccc45804 /src/gui/rhi
parentcd0b5bba9a1a76e03d4b9b5105c4d16d1e1dfd1e (diff)
rhi: Add support for full, direct buffer updates
Change-Id: I02c1f8c32c08d39cde9845d20ba8b02541d9d325 Reviewed-by: Andy Nichols <andy.nichols@qt.io>
Diffstat (limited to 'src/gui/rhi')
-rw-r--r--src/gui/rhi/qrhi.cpp49
-rw-r--r--src/gui/rhi/qrhi_p.h3
-rw-r--r--src/gui/rhi/qrhi_p_p.h1
-rw-r--r--src/gui/rhi/qrhid3d11.cpp25
-rw-r--r--src/gui/rhi/qrhid3d11_p_p.h2
-rw-r--r--src/gui/rhi/qrhigles2.cpp6
-rw-r--r--src/gui/rhi/qrhigles2_p_p.h1
-rw-r--r--src/gui/rhi/qrhimetal.mm26
-rw-r--r--src/gui/rhi/qrhimetal_p_p.h2
-rw-r--r--src/gui/rhi/qrhinull.cpp16
-rw-r--r--src/gui/rhi/qrhinull_p_p.h3
-rw-r--r--src/gui/rhi/qrhivulkan.cpp30
-rw-r--r--src/gui/rhi/qrhivulkan_p_p.h2
13 files changed, 160 insertions, 6 deletions
diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp
index 1e158f36ef..ead50628f6 100644
--- a/src/gui/rhi/qrhi.cpp
+++ b/src/gui/rhi/qrhi.cpp
@@ -2113,6 +2113,55 @@ QRhiBuffer::NativeBuffer QRhiBuffer::nativeBuffer()
}
/*!
+ \return a pointer to a memory block with the host visible buffer data.
+
+ This is a shortcut for medium-to-large dynamic uniform buffers that have
+ their \b entire contents (or at least all regions that are read by the
+ shaders in the current frame) changed \b{in every frame} and the
+ QRhiResourceUpdateBatch-based update mechanism is seen too heavy due to the
+ amount of data copying involved.
+
+ The call to this function must be eventually followed by a call to
+ endFullDynamicUniformBufferUpdateForCurrentFrame(), before recording any
+ render or compute pass that relies on this buffer.
+
+ \warning Updating data via this method is not compatible with
+ QRhiResourceUpdateBatch-based updates and readbacks. Unexpected behavior
+ may occur when attempting to combine the two update models for the same
+ buffer. Similarly, the data updated this direct way may not be visible to
+ \l{QRhiResourceUpdateBatch::readBackBuffer()}{readBackBuffer operations},
+ depending on the backend.
+
+ \warning When updating buffer data via this method, the update must be done
+ in every frame, otherwise backends that perform double or tripple buffering
+ of resources may end up in unexpected behavior.
+
+ \warning Partial updates are not possible with this approach since some
+ backends may choose a strategy where the previous contents of the buffer is
+ lost upon calling this function. Data must be written to all regions that
+ are read by shaders in the frame currently being prepared.
+
+ \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.
+ */
+char *QRhiBuffer::beginFullDynamicUniformBufferUpdateForCurrentFrame()
+{
+ return nullptr;
+}
+
+/*!
+ To be called when the entire contents of the buffer data has been updated
+ in the memory block returned from
+ beginFullDynamicUniformBufferUpdateForCurrentFrame().
+ */
+void QRhiBuffer::endFullDynamicUniformBufferUpdateForCurrentFrame()
+{
+}
+
+/*!
\class QRhiRenderBuffer
\internal
\inmodule QtGui
diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h
index adbb1b61ff..fa58718ace 100644
--- a/src/gui/rhi/qrhi_p.h
+++ b/src/gui/rhi/qrhi_p.h
@@ -717,6 +717,9 @@ public:
virtual NativeBuffer nativeBuffer();
+ virtual char *beginFullDynamicUniformBufferUpdateForCurrentFrame();
+ virtual void endFullDynamicUniformBufferUpdateForCurrentFrame();
+
protected:
QRhiBuffer(QRhiImplementation *rhi, Type type_, UsageFlags usage_, int size_);
Type m_type;
diff --git a/src/gui/rhi/qrhi_p_p.h b/src/gui/rhi/qrhi_p_p.h
index 3a76e9b97b..862d68f5a5 100644
--- a/src/gui/rhi/qrhi_p_p.h
+++ b/src/gui/rhi/qrhi_p_p.h
@@ -220,7 +220,6 @@ public:
static const int MAX_SHADER_CACHE_ENTRIES = 128;
-protected:
bool debugMarkers = false;
int currentFrameSlot = 0; // for vk, mtl, and similar. unused by gl and d3d11.
bool inFrame = false;
diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp
index 4d00f11c84..7d8339252b 100644
--- a/src/gui/rhi/qrhid3d11.cpp
+++ b/src/gui/rhi/qrhid3d11.cpp
@@ -2692,6 +2692,31 @@ QRhiBuffer::NativeBuffer QD3D11Buffer::nativeBuffer()
return { { &buffer }, 1 };
}
+char *QD3D11Buffer::beginFullDynamicUniformBufferUpdateForCurrentFrame()
+{
+ // 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
+ // every frame.
+ Q_ASSERT(m_type == Dynamic && m_usage.testFlag(UniformBuffer));
+ D3D11_MAPPED_SUBRESOURCE mp;
+ QRHI_RES_RHI(QRhiD3D11);
+ HRESULT hr = rhiD->context->Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mp);
+ if (FAILED(hr)) {
+ qWarning("Failed to map buffer: %s", qPrintable(comErrorMessage(hr)));
+ return nullptr;
+ }
+ return static_cast<char *>(mp.pData);
+}
+
+void QD3D11Buffer::endFullDynamicUniformBufferUpdateForCurrentFrame()
+{
+ QRHI_RES_RHI(QRhiD3D11);
+ rhiD->context->Unmap(buffer, 0);
+}
+
ID3D11UnorderedAccessView *QD3D11Buffer::unorderedAccessView()
{
if (uav)
diff --git a/src/gui/rhi/qrhid3d11_p_p.h b/src/gui/rhi/qrhid3d11_p_p.h
index bec263e210..4de193530a 100644
--- a/src/gui/rhi/qrhid3d11_p_p.h
+++ b/src/gui/rhi/qrhid3d11_p_p.h
@@ -65,6 +65,8 @@ struct QD3D11Buffer : public QRhiBuffer
void destroy() override;
bool create() override;
QRhiBuffer::NativeBuffer nativeBuffer() override;
+ char *beginFullDynamicUniformBufferUpdateForCurrentFrame() override;
+ void endFullDynamicUniformBufferUpdateForCurrentFrame() override;
ID3D11UnorderedAccessView *unorderedAccessView();
diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp
index 3d47a71d31..0ec2ff9c35 100644
--- a/src/gui/rhi/qrhigles2.cpp
+++ b/src/gui/rhi/qrhigles2.cpp
@@ -3897,6 +3897,12 @@ QRhiBuffer::NativeBuffer QGles2Buffer::nativeBuffer()
return { { &buffer }, 1 };
}
+char *QGles2Buffer::beginFullDynamicUniformBufferUpdateForCurrentFrame()
+{
+ Q_ASSERT(m_type == Dynamic && m_usage.testFlag(UniformBuffer));
+ return ubuf;
+}
+
QGles2RenderBuffer::QGles2RenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
int sampleCount, QRhiRenderBuffer::Flags flags,
QRhiTexture::Format backingFormatHint)
diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h
index 79f38dffce..2023585b6b 100644
--- a/src/gui/rhi/qrhigles2_p_p.h
+++ b/src/gui/rhi/qrhigles2_p_p.h
@@ -65,6 +65,7 @@ struct QGles2Buffer : public QRhiBuffer
void destroy() override;
bool create() override;
QRhiBuffer::NativeBuffer nativeBuffer() override;
+ char *beginFullDynamicUniformBufferUpdateForCurrentFrame() override;
GLuint buffer = 0;
GLenum targetForDataOps;
diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm
index 4caaca4a3a..5bbcd50047 100644
--- a/src/gui/rhi/qrhimetal.mm
+++ b/src/gui/rhi/qrhimetal.mm
@@ -2254,6 +2254,32 @@ QRhiBuffer::NativeBuffer QMetalBuffer::nativeBuffer()
return { { &d->buf[0] }, 1 };
}
+char *QMetalBuffer::beginFullDynamicUniformBufferUpdateForCurrentFrame()
+{
+ // 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
+ // content changed in every frame.
+ Q_ASSERT(m_type == Dynamic && m_usage.testFlag(UniformBuffer));
+ QRHI_RES_RHI(QRhiMetal);
+ Q_ASSERT(rhiD->inFrame);
+ const int slot = rhiD->currentFrameSlot;
+ void *p = [d->buf[slot] contents];
+ return static_cast<char *>(p);
+}
+
+void QMetalBuffer::endFullDynamicUniformBufferUpdateForCurrentFrame()
+{
+#ifdef Q_OS_MACOS
+ if (d->managed) {
+ QRHI_RES_RHI(QRhiMetal);
+ const int slot = rhiD->currentFrameSlot;
+ [d->buf[slot] didModifyRange: NSMakeRange(0, NSUInteger(m_size))];
+ }
+#endif
+}
+
static inline MTLPixelFormat toMetalTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags, const QRhiMetalData *d)
{
const bool srgb = flags.testFlag(QRhiTexture::sRGB);
diff --git a/src/gui/rhi/qrhimetal_p_p.h b/src/gui/rhi/qrhimetal_p_p.h
index a8c342da25..1691b03ec6 100644
--- a/src/gui/rhi/qrhimetal_p_p.h
+++ b/src/gui/rhi/qrhimetal_p_p.h
@@ -66,6 +66,8 @@ struct QMetalBuffer : public QRhiBuffer
void destroy() override;
bool create() override;
QRhiBuffer::NativeBuffer nativeBuffer() override;
+ char *beginFullDynamicUniformBufferUpdateForCurrentFrame() override;
+ void endFullDynamicUniformBufferUpdateForCurrentFrame() override;
QMetalBufferData *d;
uint generation = 0;
diff --git a/src/gui/rhi/qrhinull.cpp b/src/gui/rhi/qrhinull.cpp
index 86beb243c6..737e5fb48b 100644
--- a/src/gui/rhi/qrhinull.cpp
+++ b/src/gui/rhi/qrhinull.cpp
@@ -465,12 +465,12 @@ void QRhiNull::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *re
|| 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()));
+ memcpy(bufD->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));
+ memcpy(result->data.data(), bufD->data + u.offset, size_t(u.readSize));
if (result->completed)
result->completed();
}
@@ -566,7 +566,8 @@ QNullBuffer::~QNullBuffer()
void QNullBuffer::destroy()
{
- data.clear();
+ delete[] data;
+ data = nullptr;
QRHI_PROF;
QRHI_PROF_F(releaseBuffer(this));
@@ -574,13 +575,20 @@ void QNullBuffer::destroy()
bool QNullBuffer::create()
{
- data.fill('\0', m_size);
+ data = new char[m_size];
+ memset(data, 0, m_size);
QRHI_PROF;
QRHI_PROF_F(newBuffer(this, uint(m_size), 1, 0));
return true;
}
+char *QNullBuffer::beginFullDynamicUniformBufferUpdateForCurrentFrame()
+{
+ Q_ASSERT(m_type == Dynamic && m_usage.testFlag(UniformBuffer));
+ return data;
+}
+
QNullRenderBuffer::QNullRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
int sampleCount, QRhiRenderBuffer::Flags flags,
QRhiTexture::Format backingFormatHint)
diff --git a/src/gui/rhi/qrhinull_p_p.h b/src/gui/rhi/qrhinull_p_p.h
index 567b672b3f..e0e796f7ca 100644
--- a/src/gui/rhi/qrhinull_p_p.h
+++ b/src/gui/rhi/qrhinull_p_p.h
@@ -59,8 +59,9 @@ struct QNullBuffer : public QRhiBuffer
~QNullBuffer();
void destroy() override;
bool create() override;
+ char *beginFullDynamicUniformBufferUpdateForCurrentFrame() override;
- QByteArray data;
+ char *data = nullptr;
};
struct QNullRenderBuffer : public QRhiRenderBuffer
diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp
index 4e23f1622c..d0e98fb751 100644
--- a/src/gui/rhi/qrhivulkan.cpp
+++ b/src/gui/rhi/qrhivulkan.cpp
@@ -5373,6 +5373,36 @@ QRhiBuffer::NativeBuffer QVkBuffer::nativeBuffer()
return { { &buffers[0] }, 1 };
}
+char *QVkBuffer::beginFullDynamicUniformBufferUpdateForCurrentFrame()
+{
+ // 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
+ // content changed in every frame.
+ Q_ASSERT(m_type == Dynamic && m_usage.testFlag(UniformBuffer));
+ QRHI_RES_RHI(QRhiVulkan);
+ Q_ASSERT(rhiD->inFrame);
+ const int slot = rhiD->currentFrameSlot;
+ void *p = nullptr;
+ VmaAllocation a = toVmaAllocation(allocations[slot]);
+ VkResult err = vmaMapMemory(toVmaAllocator(rhiD->allocator), a, &p);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to map buffer: %d", err);
+ return nullptr;
+ }
+ return static_cast<char *>(p);
+}
+
+void QVkBuffer::endFullDynamicUniformBufferUpdateForCurrentFrame()
+{
+ QRHI_RES_RHI(QRhiVulkan);
+ const int slot = rhiD->currentFrameSlot;
+ VmaAllocation a = toVmaAllocation(allocations[slot]);
+ vmaUnmapMemory(toVmaAllocator(rhiD->allocator), a);
+ vmaFlushAllocation(toVmaAllocator(rhiD->allocator), a, 0, m_size);
+}
+
QVkRenderBuffer::QVkRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
int sampleCount, Flags flags,
QRhiTexture::Format backingFormatHint)
diff --git a/src/gui/rhi/qrhivulkan_p_p.h b/src/gui/rhi/qrhivulkan_p_p.h
index 0a1c43f538..5a20e6a757 100644
--- a/src/gui/rhi/qrhivulkan_p_p.h
+++ b/src/gui/rhi/qrhivulkan_p_p.h
@@ -77,6 +77,8 @@ struct QVkBuffer : public QRhiBuffer
void destroy() override;
bool create() override;
QRhiBuffer::NativeBuffer nativeBuffer() override;
+ char *beginFullDynamicUniformBufferUpdateForCurrentFrame() override;
+ void endFullDynamicUniformBufferUpdateForCurrentFrame() override;
VkBuffer buffers[QVK_FRAMES_IN_FLIGHT];
QVkAlloc allocations[QVK_FRAMES_IN_FLIGHT];