rhi: vulkan: make it easier to hunt VMA leaks

When leaking a RHI resource, the VMA asserts (VMA_ASSERT_LEAK) before
the RHI has a chance to list the leaks.

Plug the VMA leak output to the QDebug infrastructure, and give names to
the VmaAllocations so that we know exactly what leaked. This is only
enabled for debug builds or when QT_RHI_LEAK_CHECK is set.

Change-Id: Ief0e46cceaefb2e736542f9764264c29b0c28c64
Reviewed-by: Laszlo Agocs <laszlo.agocs@qt.io>
This commit is contained in:
Aurélien Brooke 2025-03-29 08:56:22 +01:00
parent 70e75851bf
commit 433360c6cd
2 changed files with 40 additions and 0 deletions

View File

@ -9,9 +9,29 @@
#define VMA_STATIC_VULKAN_FUNCTIONS 0
#define VMA_RECORDING_ENABLED 0
#define VMA_DEDICATED_ALLOCATION 0
QT_BEGIN_NAMESPACE
Q_STATIC_LOGGING_CATEGORY(QRHI_LOG_VMA, "qt.rhi.vma")
QT_END_NAMESPACE
#define VMA_ASSERT(expr) Q_ASSERT(expr)
#ifdef QT_DEBUG
#define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1
#define VMA_DEBUG_LOG(str) QT_PREPEND_NAMESPACE(qDebug)(QT_PREPEND_NAMESPACE(QRHI_LOG_VMA), (str))
#define VMA_DEBUG_LOG_FORMAT(format, ...) QT_PREPEND_NAMESPACE(qDebug)(QT_PREPEND_NAMESPACE(QRHI_LOG_VMA), format, __VA_ARGS__)
#endif
template<typename... Args>
static void debugVmaLeak(const char *format, Args&&... args)
{
#ifndef QT_NO_DEBUG
// debug builds: just do it always
static bool leakCheck = true;
#else
// release builds: opt-in
static bool leakCheck = QT_PREPEND_NAMESPACE(qEnvironmentVariableIntValue)("QT_RHI_LEAK_CHECK");
#endif
if (leakCheck)
QT_PREPEND_NAMESPACE(qWarning)(QT_PREPEND_NAMESPACE(QRHI_LOG_VMA), format, std::forward<Args>(args)...);
}
#define VMA_LEAK_LOG_FORMAT(format, ...) debugVmaLeak(format, __VA_ARGS__)
QT_WARNING_PUSH
QT_WARNING_DISABLE_GCC("-Wsuggest-override")
QT_WARNING_DISABLE_GCC("-Wundef")
@ -4075,6 +4095,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
&bufD->stagingBuffers[currentFrameSlot], &allocation, nullptr);
if (err == VK_SUCCESS) {
bufD->stagingAllocations[currentFrameSlot] = allocation;
setAllocationName(allocation, bufD->name());
} else {
qWarning("Failed to create staging buffer of size %u: %d", bufD->m_size, err);
printExtraErrorInfo(err);
@ -4161,6 +4182,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo, &readback.stagingBuf, &allocation, nullptr);
if (err == VK_SUCCESS) {
readback.stagingAlloc = allocation;
setAllocationName(allocation, bufD->name());
} else {
qWarning("Failed to create readback buffer of size %u: %d", readback.byteSize, err);
printExtraErrorInfo(err);
@ -4217,6 +4239,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
continue;
}
utexD->stagingAllocations[currentFrameSlot] = allocation;
setAllocationName(allocation, utexD->name());
BufferImageCopyList copyInfos;
size_t curOfs = 0;
@ -4374,6 +4397,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo, &readback.stagingBuf, &allocation, nullptr);
if (err == VK_SUCCESS) {
readback.stagingAlloc = allocation;
setAllocationName(allocation, texD ? texD->name() : swapChainD->name());
} else {
qWarning("Failed to create readback buffer of size %u: %d", readback.byteSize, err);
printExtraErrorInfo(err);
@ -6284,6 +6308,19 @@ double QRhiVulkan::lastCompletedGpuTime(QRhiCommandBuffer *cb)
return cbD->lastGpuTime;
}
void QRhiVulkan::setAllocationName(QVkAlloc allocation, const QByteArray &name, int slot)
{
if (!debugMarkers || name.isEmpty())
return;
QByteArray decoratedName = name;
if (slot >= 0) {
decoratedName += '/';
decoratedName += QByteArray::number(slot);
}
vmaSetAllocationName(toVmaAllocator(allocator), toVmaAllocation(allocation), decoratedName.constData());
}
void QRhiVulkan::setObjectName(uint64_t object, VkObjectType type, const QByteArray &name, int slot)
{
#ifdef VK_EXT_debug_utils
@ -6803,6 +6840,7 @@ bool QVkBuffer::create()
if (err != VK_SUCCESS)
break;
allocations[i] = allocation;
rhiD->setAllocationName(allocation, m_objectName, m_type == Dynamic ? i : -1);
rhiD->setObjectName(uint64_t(buffers[i]), VK_OBJECT_TYPE_BUFFER, m_objectName,
m_type == Dynamic ? i : -1);
}
@ -7250,6 +7288,7 @@ bool QVkTexture::create()
return false;
}
imageAlloc = allocation;
rhiD->setAllocationName(allocation, m_objectName);
if (!finishCreate())
return false;

View File

@ -857,6 +857,7 @@ public:
void executeDeferredReleases(bool forced = false);
void finishActiveReadbacks(bool forced = false);
void setAllocationName(QVkAlloc allocation, const QByteArray &name, int slot = -1);
void setObjectName(uint64_t object, VkObjectType type, const QByteArray &name, int slot = -1);
void trackedBufferBarrier(QVkCommandBuffer *cbD, QVkBuffer *bufD, int slot,
VkAccessFlags access, VkPipelineStageFlags stage);