Skip to content

Commit

Permalink
Fix vulkan error per VUID-VkMemoryAllocateInfo-pNext-00639
Browse files Browse the repository at this point in the history
Per https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkMemoryAllocateInfo.html#VUID-VkMemoryAllocateInfo-pNext-00639, dedicated allocation is required by certain externalMemoryFeatures. This change is to keep dedicated allocation in those scenarios for capture mode, while being assured no dedicated allocation for reading and replaying mode.
  • Loading branch information
listevemeta committed Nov 18, 2024
1 parent 0d8190e commit 7b86a47
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 2 deletions.
4 changes: 3 additions & 1 deletion renderdoc/driver/vulkan/vk_resources.h
Original file line number Diff line number Diff line change
Expand Up @@ -1027,8 +1027,10 @@ struct ResourceInfo
rdcarray<AspectSparseTable> altSparseAspects;

// for external images if we query both external and non-external and the sizes are different, we
// can't allow dedicated memory as it is required to precisely match in size.
// can't allow dedicated memory as it is required to precisely match in size,
// unless it is required by certain VkExportMemoryAllocateInfo::handleTypes.
bool banDedicated = false;
VkExternalMemoryFeatureFlags externalMemoryFeatures = 0;

VkImageAspectFlags sparseAspect;

Expand Down
54 changes: 53 additions & 1 deletion renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,19 @@ VkResult WrappedVulkan::vkAllocateMemory(VkDevice device, const VkMemoryAllocate
VkResourceRecord *imageRecord = GetRecord(dedicated->image);

if(imageRecord->resInfo->banDedicated)
RemoveNextStruct(&unwrapped, VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO);
{
VkExportMemoryAllocateInfo *exportMem = (VkExportMemoryAllocateInfo *)FindNextStruct(
pAllocateInfo, VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO);
if(exportMem && exportMem->handleTypes & imageRecord->resInfo->externalMemoryFeatures)
{
RDCWARN(
"Can't ban dedicated memory per Vulkan spec VUID-VkMemoryAllocateInfo-pNext-00639.");
}
else
{
RemoveNextStruct(&unwrapped, VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO);
}
}
}
}

Expand Down Expand Up @@ -2509,6 +2521,7 @@ VkResult WrappedVulkan::vkCreateImage(VkDevice device, const VkImageCreateInfo *
bool isLinear = (pCreateInfo->tiling == VK_IMAGE_TILING_LINEAR);

bool isExternal = false;
VkExternalMemoryHandleTypeFlags externalHandleTypes = 0;

const VkBaseInStructure *next = (const VkBaseInStructure *)pCreateInfo->pNext;

Expand All @@ -2528,6 +2541,7 @@ VkResult WrappedVulkan::vkCreateImage(VkDevice device, const VkImageCreateInfo *
resInfo.imageInfo.isAHB =
(extCreateInfo->handleTypes &
VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID) != 0;
externalHandleTypes = extCreateInfo->handleTypes;
break;
}
}
Expand Down Expand Up @@ -2617,6 +2631,44 @@ VkResult WrappedVulkan::vkCreateImage(VkDevice device, const VkImageCreateInfo *
"Required size changed on image between external/non-external, banning "
"dedicated memory");
resInfo.banDedicated = true;

// Per Vulkan spec VUID-VkMemoryAllocateInfo-pNext-00639,
// we need to make sure the dedicated memory is not required.
// We query the externalMemoryFeatures here based on the image's properties.
// Then check the memory's handleType upon vkAllocateMemory().
// externalMemoryFeatures need to be checked bit by bit.
for(VkFlags handleType = 0x01; externalHandleTypes;
externalHandleTypes &= ~handleType, handleType <<= 1)
{
if(!(externalHandleTypes & handleType))
continue;

VkPhysicalDeviceExternalImageFormatInfo extImageFormatInfo = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO};
extImageFormatInfo.handleType = (VkExternalMemoryHandleTypeFlagBits)handleType;

VkPhysicalDeviceImageFormatInfo2 imageFormatInfo = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2};
imageFormatInfo.pNext = &extImageFormatInfo;
imageFormatInfo.format = createInfo_adjusted.format;
imageFormatInfo.type = createInfo_adjusted.imageType;
imageFormatInfo.tiling = createInfo_adjusted.tiling;
imageFormatInfo.usage = createInfo_adjusted.usage;
imageFormatInfo.flags = createInfo_adjusted.flags;

VkExternalImageFormatProperties extImageFormatProperties = {
VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES};
VkImageFormatProperties2 imageFormatProperties = {
VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2};
imageFormatProperties.pNext = &extImageFormatProperties;

if(VK_SUCCESS ==
ObjDisp(m_PhysicalDevice)
->GetPhysicalDeviceImageFormatProperties2(
Unwrap(m_PhysicalDevice), &imageFormatInfo, &imageFormatProperties))
resInfo.externalMemoryFeatures |=
extImageFormatProperties.externalMemoryProperties.externalMemoryFeatures;
}
}

resInfo.memreqs.size = RDCMAX(resInfo.memreqs.size, mrq.size);
Expand Down

1 comment on commit 7b86a47

@listevemeta
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even we have to allow the dedicated memory in Capture mode per VUID-VkMemoryAllocateInfo-pNext-00639, we are still safe in ReplayingAndReading mode by

// remove dedicated memory struct if it is not allowed due to changing memory sizes
{
VkMemoryDedicatedAllocateInfo *dedicated = (VkMemoryDedicatedAllocateInfo *)FindNextStruct(
&AllocateInfo, VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO);
if(dedicated && dedicated->image != VK_NULL_HANDLE)
{
VkMemoryRequirements mrq = {};
ObjDisp(device)->GetImageMemoryRequirements(Unwrap(device), Unwrap(dedicated->image), &mrq);
if(mrq.size != AllocateInfo.allocationSize)
{
RDCDEBUG("Removing dedicated allocation for incompatible size");
RemoveNextStruct(&patched, VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO);
}
}
}
, so it won't trigger another vulkan error https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkMemoryDedicatedAllocateInfo.html#VUID-VkMemoryDedicatedAllocateInfo-image-02964.

Test plan:

Tested by a simple Android app with following codes in rendering loop.

  • Without this change,
    vulkan error was raised from App, once it was launched from RenderDoc.
  • With this change,
    capturing/loading/replaying are all good.
    no vulkan error happens..
#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, "TestApp", __VA_ARGS__)
void TestFunc(VkDevice device) {
    VkFlags expMemAllocInfHandleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
    VkExternalMemoryImageCreateInfo expMemAllocInfImgCreateInfo = {VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO};
    expMemAllocInfImgCreateInfo.handleTypes = expMemAllocInfHandleType;

    VkImageCreateInfo imageCreateInfo = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};
    imageCreateInfo.pNext = &expMemAllocInfImgCreateInfo;
    imageCreateInfo.flags = 0;
    imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
    imageCreateInfo.format = VK_FORMAT_R8G8B8A8_SRGB;
    imageCreateInfo.mipLevels = 1;
    imageCreateInfo.arrayLayers = 2;
    imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
    imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
    imageCreateInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
    imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
    imageCreateInfo.queueFamilyIndexCount = 0;
    imageCreateInfo.pQueueFamilyIndices = nullptr;
    imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    imageCreateInfo.extent.width = 57;
    imageCreateInfo.extent.height = 61;
    imageCreateInfo.extent.depth = 1;
    VkImage image;
    VkResult res = vkCreateImage(device, &imageCreateInfo, nullptr, &image);
    if (image == VK_NULL_HANDLE || res != VK_SUCCESS) {
        ALOGE("vkCreateImage Failed res = %d", res);
    }

    VkMemoryDedicatedAllocateInfo memDedAllocInfo = {VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO};
    memDedAllocInfo.image = image;
    VkExportMemoryAllocateInfo expMemAllocInf = {VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO};
    expMemAllocInf.pNext = &memDedAllocInfo;
    expMemAllocInf.handleTypes = expMemAllocInfHandleType;
    VkMemoryRequirements memReq;
    vkGetImageMemoryRequirements(device, image, &memReq);
    VkMemoryAllocateInfo memoryAllocateInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO};
    memoryAllocateInfo.pNext = &expMemAllocInf;
    memoryAllocateInfo.allocationSize = memReq.size;
    memoryAllocateInfo.memoryTypeIndex = 0;

    VkDeviceMemory memory;
    res = vkAllocateMemory(device, &memoryAllocateInfo, nullptr, &memory);
    if (memory == VK_NULL_HANDLE || res != VK_SUCCESS) {
        ALOGE("vkAllocateMemory Failed res = %d", res);
    }
    res = vkBindImageMemory(device, image, memory, 0);
    if (res != VK_SUCCESS) {
        ALOGE("vkBindImageMemory Failed res = %d", res);
    }

    vkDestroyImage(device, image, nullptr);
    vkFreeMemory(device, memory, nullptr);
}

Please sign in to comment.