rhi: Add support for full, direct buffer updates
Change-Id: I02c1f8c32c08d39cde9845d20ba8b02541d9d325 Reviewed-by: Andy Nichols <andy.nichols@qt.io>bb10
parent
cd0b5bba9a
commit
2ac2809ec3
|
|
@ -2112,6 +2112,55 @@ QRhiBuffer::NativeBuffer QRhiBuffer::nativeBuffer()
|
|||
return {};
|
||||
}
|
||||
|
||||
/*!
|
||||
\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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
Loading…
Reference in New Issue