A GPU-decoded video frame isn’t a linear buffer — it’s a
vendor-tiled, possibly compressed image whose layout is described by a
DRM format modifier (DRM_FORMAT_MOD_*). When FFmpeg/VAAPI hands
you a DMA-Buf, the modifier tells you how the pixels are laid out.
VK_EXT_image_drm_format_modifier lets Vulkan express that same
layout — create a VkImage that is the imported frame, no copy.
Why modifiers exist
linear: simplest layout, no compression, poor cache behavior
tiled: vendor-specific swizzle — good cache locality
modifier: a 64-bit tag that says WHICH tiling — e.g.
DRM_FORMAT_MOD_LINEAR (plain)
I915_FORMAT_MOD_X_TILED
DRM_FORMAT_MOD_AMD_GFX9_64K_S
+ per-vendor compression variants (DCC, FBC…)The exporter (VAAPI, V4L2, GBM, another Vulkan driver) tags the DMA-Buf with a modifier; the importer must declare it understands exactly that modifier — a mismatched modifier means the pixels decode wrong, silently.
The import path
// 1. query what modifiers the driver supports for the format:
vk::DrmFormatModifierPropertiesListEXT list{};
auto fmtProps = phys.getFormatProperties2(eR8, &list);
for (auto& p : list.drmFormatModifierProperties)
// p.drmFormatModifier, p.drmFormatModifierPlaneCount,
// p.drmFormatModifierTilingFeatures
// 2. image creation — declare the modifier + plane layouts:
vk::ImageDrmFormatModifierListCreateInfoEXT modList{};
modList.drmFormatModifierCount = 1;
modList.pDrmFormatModifiers = &mod;
vk::ImageCreateInfo ici{};
ici.tiling = vk::ImageTiling::eDrmFormatModifierEXT;
ici.pNext = &modList; // or explicit: VkImageDrmFormatModifierExplicitCreateInfoEXT
// 3. bind the DMA-Buf's fd via vkGetMemoryFdPropertiesKHR +
// ImportMemoryFdInfoKHR (external_memory_dma_buf, other post)
eDrmFormatModifierEXT replaces eOptimal/eLinear — the driver
interprets the modifier’s layout directly.
A real import pipeline
VAAPI decodes MJPEG/H.264 → AVDRMFrameDescriptor
├─ layer 0: NV12 plane 0 (Y) + modifier
└─ layer 1: NV12 plane 1 (UV) + same modifier
→ each plane → one VkImage (R8 for Y, R8G8 for UV)
tiling=eDrmFormatModifierEXT, modifier from the descriptor
→ sampled by the grid shader — zero copiesThe imported images are foreign-typed: usage is limited to
SAMPLED/TRANSFER_SRC typically, and the foreign queue family
(VK_EXT_queue_family_foreign, separate post) hands ownership from
the importer to your queue.
Practical rules
- Always query
VkDrmFormatModifierPropertiesListEXT— the modifier the decoder emits must be in the supported set for that format. drmFormatModifierPlaneCounttells you how many VkImages a multi-plane format needs — NV12 → 2 planes, I420 → 3, each a separateVkImagesharing the modifier.- Modifier-tiled images only allow
eGeneral/ePresentSrcKHRlayouts —eOptimaltransitions are invalid; transition toeGeneralonce. VK_EXT_image_drm_format_modifieris necessary but not sufficient — you also needVK_EXT_external_memory_dma_buffor the fd import andVK_EXT_queue_family_foreignfor the ownership transfer.- A failed modifier match is a fallback signal, not a bug — fall back
to
eLinearstaging +vkCmdCopyBufferToImagewhen the modifier isn’t in the device’s list.