From f00052ffe24b8540a6b27b93ab9197278c80d157 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Fri, 17 Jul 2026 19:05:34 -0700
Subject: [PATCH] testffmpeg: fixed memory leak using DRM frames with the
Vulkan renderer
---
test/testffmpeg_vulkan.c | 47 +++++++++++++++++++++++++++++++++-------
1 file changed, 39 insertions(+), 8 deletions(-)
diff --git a/test/testffmpeg_vulkan.c b/test/testffmpeg_vulkan.c
index c43e7380e7eff..ad7dfcf1a08fe 100644
--- a/test/testffmpeg_vulkan.c
+++ b/test/testffmpeg_vulkan.c
@@ -59,10 +59,12 @@
VULKAN_DEVICE_FUNCTION(vkCreateSemaphore) \
VULKAN_DEVICE_FUNCTION(vkDestroyCommandPool) \
VULKAN_DEVICE_FUNCTION(vkDestroyDevice) \
+ VULKAN_DEVICE_FUNCTION(vkDestroyImage) \
VULKAN_DEVICE_FUNCTION(vkDestroySemaphore) \
VULKAN_DEVICE_FUNCTION(vkDeviceWaitIdle) \
VULKAN_DEVICE_FUNCTION(vkEndCommandBuffer) \
VULKAN_DEVICE_FUNCTION(vkFreeCommandBuffers) \
+ VULKAN_DEVICE_FUNCTION(vkFreeMemory) \
VULKAN_DEVICE_FUNCTION(vkGetDeviceQueue) \
VULKAN_DEVICE_FUNCTION(vkGetImageMemoryRequirements) \
VULKAN_DEVICE_FUNCTION(vkQueueSubmit) \
@@ -920,6 +922,7 @@ static SDL_Texture *CreateVulkanVideoTexturePixFmtVulkan(VulkanVideoContext *con
}
#ifdef FFMPEG_DRMPRIME_SUPPORT
+
static bool FindMemoryIndex(VulkanVideoContext *context, uint32_t memoryTypeBits, uint32_t *memoryIndex)
{
VkPhysicalDeviceMemoryProperties mem_properties;
@@ -933,6 +936,23 @@ static bool FindMemoryIndex(VulkanVideoContext *context, uint32_t memoryTypeBits
}
return SDL_SetError("Couldn't find memory index for type %u", memoryTypeBits);
}
+
+static void SDLCALL CleanupExternalVulkanImage(void *userdata, void *value)
+{
+ VulkanVideoContext *context = (VulkanVideoContext *)userdata;
+ VkImage image = (VkImage)value;
+
+ context->vkDestroyImage(context->device, image, NULL);
+}
+
+static void SDLCALL CleanupExternalVulkanImageMemory(void *userdata, void *value)
+{
+ VulkanVideoContext *context = (VulkanVideoContext *)userdata;
+ VkDeviceMemory imageMemory = (VkDeviceMemory)value;
+
+ context->vkFreeMemory(context->device, imageMemory, NULL);
+}
+
#endif /* FFMPEG_DRMPRIME_SUPPORT */
static SDL_Texture *CreateVulkanVideoTexturePixFmtDRMPrime(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer, SDL_PropertiesID props)
@@ -941,6 +961,8 @@ static SDL_Texture *CreateVulkanVideoTexturePixFmtDRMPrime(VulkanVideoContext *c
const AVDRMFrameDescriptor *drm_desc = (const AVDRMFrameDescriptor *)frame->data[0];
VkFormat format = VK_FORMAT_UNDEFINED;
VkResult result;
+ VkImage image = 0;
+ VkDeviceMemory imageMemory = 0;
SDL_Texture *texture;
if (drm_desc->nb_objects != 1) {
@@ -1007,7 +1029,6 @@ static SDL_Texture *CreateVulkanVideoTexturePixFmtDRMPrime(VulkanVideoContext *c
int dma_buf_fds[AV_DRM_MAX_PLANES];
for (int i = 0; i < drm_desc->nb_objects; i++) {
- // Duplicate the descriptor if your Vulkan driver or framework closes it automatically
dma_buf_fds[i] = dup(drm_desc->objects[i].fd);
if (dma_buf_fds[i] < 0) {
while (--i >= 0) {
@@ -1018,7 +1039,6 @@ static SDL_Texture *CreateVulkanVideoTexturePixFmtDRMPrime(VulkanVideoContext *c
}
}
- // Map your descriptor planes to Vulkan plane layouts
uint32_t planes = 0;
VkSubresourceLayout plane_layouts[AV_DRM_MAX_PLANES];
SDL_zeroa(plane_layouts);
@@ -1061,7 +1081,6 @@ static SDL_Texture *CreateVulkanVideoTexturePixFmtDRMPrime(VulkanVideoContext *c
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
image_info.pNext = &external_info;
- VkImage image;
result = context->vkCreateImage(context->device, &image_info, NULL, &image);
if (result != VK_SUCCESS) {
SDL_SetError("vkCreateImage(): %s", getVulkanResultString(result));
@@ -1090,13 +1109,12 @@ static SDL_Texture *CreateVulkanVideoTexturePixFmtDRMPrime(VulkanVideoContext *c
alloc_info.memoryTypeIndex = memoryTypeIndex;
alloc_info.pNext = &import_fd_info;
- VkDeviceMemory image_memory;
- result = context->vkAllocateMemory(context->device, &alloc_info, NULL, &image_memory);
+ result = context->vkAllocateMemory(context->device, &alloc_info, NULL, &imageMemory);
if (result != VK_SUCCESS) {
SDL_SetError("vkAllocateMemory(): %s", getVulkanResultString(result));
goto error;
}
- result = context->vkBindImageMemory(context->device, image, image_memory, 0);
+ result = context->vkBindImageMemory(context->device, image, imageMemory, 0);
if (result != VK_SUCCESS) {
SDL_SetError("vkBindImageMemory(): %s", getVulkanResultString(result));
goto error;
@@ -1106,11 +1124,24 @@ static SDL_Texture *CreateVulkanVideoTexturePixFmtDRMPrime(VulkanVideoContext *c
if (!texture) {
goto error;
}
+
+ // Make sure this image is freed when the texture is destroyed
+ props = SDL_GetTextureProperties(texture);
+ SDL_SetPointerPropertyWithCleanup(props, "CleanupVulkanImage", (void *)image, CleanupExternalVulkanImage, context);
+ SDL_SetPointerPropertyWithCleanup(props, "CleanupVulkanImageMemory", (void *)imageMemory, CleanupExternalVulkanImageMemory, context);
+
return texture;
error:
- for (int i = 0; i < drm_desc->nb_objects; i++) {
- close(dma_buf_fds[i]);
+ if (image) {
+ context->vkDestroyImage(context->device, image, NULL);
+ }
+ if (imageMemory) {
+ context->vkFreeMemory(context->device, imageMemory, NULL);
+ } else {
+ for (int i = 0; i < drm_desc->nb_objects; i++) {
+ close(dma_buf_fds[i]);
+ }
}
return NULL;
#else