radv+vaapi: Conflicting/impossible image requirements for drm layers?
Trying to get vaapi+vulkan working with mpv, I ran into the following roadblock. The tl;dr is that the driver both requires us to use dedicated memory imports, which the vulkan spec says require an offset of 0, but then also simultaneously gives us a non-zero offset for one of the layers.
This is what the VADRMPRIMESurfaceDescriptor
looks like:
(gdb) print p->desc
$3 = {
fourcc = 842094158,
width = 128,
height = 128,
num_objects = 2,
objects = {{
fd = 16,
size = 0,
drm_format_modifier = 0
}, {
fd = 17,
size = 0,
drm_format_modifier = 0
}, {
fd = 0,
size = 0,
drm_format_modifier = 0
}, {
fd = 0,
size = 0,
drm_format_modifier = 0
}},
num_layers = 2,
layers = {{
drm_format = 538982482,
num_planes = 1,
object_index = {0, 0, 0, 0},
offset = {0, 0, 0, 0},
pitch = {256, 0, 0, 0}
}, {
drm_format = 943215175,
num_planes = 1,
object_index = {1, 0, 0, 0},
offset = {32768, 0, 0, 0},
pitch = {256, 0, 0, 0}
}, {
drm_format = 0,
num_planes = 0,
object_index = {0, 0, 0, 0},
offset = {0, 0, 0, 0},
pitch = {0, 0, 0, 0}
}, {
drm_format = 0,
num_planes = 0,
object_index = {0, 0, 0, 0},
offset = {0, 0, 0, 0},
pitch = {0, 0, 0, 0}
}}
}
And for reference, this is the invocation that fills it:
status = vaExportSurfaceHandle(display, va_surface_id(mapper->src),
VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
VA_EXPORT_SURFACE_READ_ONLY |
VA_EXPORT_SURFACE_SEPARATE_LAYERS,
&p->desc);
As you can see, we are given two layers, each with their own fd
, and the second of which has an offset
of 32768
.
Now, fast forward to the vulkan land, where we need to import this memory and bind it to a VkImage. After calling vkGetPhysicalDeviceImageFormatProperties2KHR
with a VkExternalImageFormatPropertiesKHR
in the pNext
chain (which is required for importing memory), we get returned a struct that looks like this:
(gdb) print ext_props
$5 = {
sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES,
pNext = 0x0,
externalMemoryProperties = {
externalMemoryFeatures = 7,
exportFromImportedHandleTypes = 513,
compatibleHandleTypes = 513
}
}
The externalMemoryFeatures
includes bit 0x1, i.e. VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT
, meaning we must use the "dedicated memory allocation" set of functions in order to successfully import this memory. This is reflected in the output of vkGetImageMemoryRequirements2KHR
, which gives us the following VkMemoryDedicatedRequirementsKHR
:
(gdb) print ded_reqs
$6 = {
sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS,
pNext = 0x0,
prefersDedicatedAllocation = 1,
requiresDedicatedAllocation = 1
}
This import does succeed, but the issue is that we can no longer sanely bind the memory to the image, because vkBindImageMemory
has the following parameter requirements on it:
- If image requires a dedicated allocation (as reported by vkGetImageMemoryRequirements2 in VkMemoryDedicatedRequirements::requiresDedicatedAllocation for image), memory must have been created with VkMemoryDedicatedAllocateInfo::image equal to image
- If the dedicated allocation image aliasing feature is not enabled, and the VkMemoryAllocateInfo provided when memory was allocated included a VkMemoryDedicatedAllocateInfo structure in its pNext chain, and VkMemoryDedicatedAllocateInfo::image was not VK_NULL_HANDLE, then image must equal VkMemoryDedicatedAllocateInfo::image and memoryOffset must be zero.
- If the dedicated allocation image aliasing feature is enabled, and the VkMemoryAllocateInfo provided when memory was allocated included a VkMemoryDedicatedAllocateInfo structure in its pNext chain, and VkMemoryDedicatedAllocateInfo::image was not VK_NULL_HANDLE, then memoryOffset must be zero, and image must be either equal to VkMemoryDedicatedAllocateInfo::image or an image that was created using the same parameters in VkImageCreateInfo, with the exception that extent and arrayLayers may differ subject to the following restrictions: every dimension in the extent parameter of the image being bound must be equal to or smaller than the original image for which the allocation was created; and the arrayLayers parameter of the image being bound must be equal to or smaller than the original image for which the allocation was created.
It seems that no matter what, memoryOffset
must be zero, which conflicts with the earlier requirement of offset = 32768
.
If I ignore this requirement and just run the vkBindImageMemory
command despite it, I get garbled output and dmesg spam full of GPU VM faults.