<div style="font-family: Arial, sans-serif; font-size: 14px;">Hi everyone, I'm looking to use the libav h264_vulkan encoder to render some videos on my GPU. After some trial and error I figured out how to encode a simple video, but the application leaks VkImageView objects and throws other validation errors, and I don't know whether it's a bug in libav or I'm just using it wrong.</div><div style="font-family: Arial, sans-serif; font-size: 14px;"><br></div><div style="font-family: Arial, sans-serif; font-size: 14px;">Here's the source code of a minimal application that triggers the errors:</div><div style="font-family: Arial, sans-serif; font-size: 14px;">```</div><div style="font-family: Arial, sans-serif; font-size: 14px;"><span>#include "vulkan/vulkan_core.h"</span><div><span>#include <libavcodec/avcodec.h></span></div><div><span>#include <libavformat/avformat.h></span></div><div><span>#include <libavutil/avutil.h></span></div><div><span>#include <libavutil/error.h></span></div><div><span>#include <libavutil/frame.h></span></div><div><span>#include <libavutil/hwcontext.h></span></div><div><span>#include <libavutil/hwcontext_vulkan.h></span></div><div><span>#include <libavutil/imgutils.h></span></div><div><span>#include <libavutil/opt.h></span></div><div><span>#include <libavutil/pixdesc.h></span></div><div><span>#include <stdbool.h></span></div><div><span>#include <stdint.h></span></div><div><span>#include <stdio.h></span></div><div><span>#include <stdlib.h></span></div><div><span>#include <string.h></span></div><div><br></div><div><span>const VkExtent2D IMAGE_SIZE = {1920, 1080};</span></div><div><br></div><div><span>static const char *validationLayers[] = {</span></div><div><span> "VK_LAYER_KHRONOS_validation",</span></div><div><span>};</span></div><div><span>static const size_t validationLayersCount = 1;</span></div><div><br></div><div><span>static const char *instanceExtensions[] = {</span></div><div><span> VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME,</span></div><div><span>};</span></div><div><span>static const size_t instanceExtensionsCount = 1;</span></div><div><br></div><div><span>static const char *deviceExtensions[] = {</span></div><div><span> VK_KHR_VIDEO_MAINTENANCE_1_EXTENSION_NAME,</span></div><div><span> VK_KHR_VIDEO_QUEUE_EXTENSION_NAME,</span></div><div><span> VK_KHR_VIDEO_ENCODE_QUEUE_EXTENSION_NAME,</span></div><div><span> VK_KHR_VIDEO_ENCODE_H264_EXTENSION_NAME,</span></div><div><span>};</span></div><div><span>static const size_t deviceExtensionsCount = 4;</span></div><div><br></div><div><span>typedef struct {</span></div><div><span> uint32_t transferAndComputeFamily;</span></div><div><span> uint32_t encodeFamily;</span></div><div><span>} QueueFamilyIndices;</span></div><div><br></div><div><span>VkInstance instance;</span></div><div><span>VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;</span></div><div><span>QueueFamilyIndices queueFamilyIndices;</span></div><div><span>VkPhysicalDeviceFeatures2 deviceFeatures;</span></div><div><span>VkDevice device;</span></div><div><br></div><div><span>AVBufferRef *ff_hw_dev;</span></div><div><span>AVBufferRef *ff_hw_frames_ctx;</span></div><div><span>AVFrame *ff_frame;</span></div><div><span>AVVkFrame *ff_vk_frame;</span></div><div><br></div><div><span>VkImage imageOut;</span></div><div><br></div><div><span>void check(VkResult result, const char *msg) {</span></div><div><span> if (result != VK_SUCCESS) {</span></div><div><span> fprintf(stderr, "%s\n", msg);</span></div><div><span> exit(1);</span></div><div><span> }</span></div><div><span>}</span></div><div><br></div><div><span>QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) {</span></div><div><span> QueueFamilyIndices indices = {</span></div><div><span> .transferAndComputeFamily = UINT32_MAX,</span></div><div><span> .encodeFamily = UINT32_MAX,</span></div><div><span> };</span></div><div><br></div><div><span> uint32_t familyCount;</span></div><div><span> vkGetPhysicalDeviceQueueFamilyProperties(device, &familyCount, NULL);</span></div><div><span> VkQueueFamilyProperties *families =</span></div><div><span> malloc(familyCount * sizeof(VkQueueFamilyProperties));</span></div><div><span> if (!families) {</span></div><div><span> fprintf(stderr, "Failed to allocate memory for queue families\n");</span></div><div><span> exit(1);</span></div><div><span> }</span></div><div><span> vkGetPhysicalDeviceQueueFamilyProperties(device, &familyCount, families);</span></div><div><br></div><div><span> for (uint32_t i = 0; i < familyCount; i++) {</span></div><div><span> if (families[i].queueFlags & VK_QUEUE_COMPUTE_BIT &&</span></div><div><span> families[i].queueFlags & VK_QUEUE_TRANSFER_BIT) {</span></div><div><span> indices.transferAndComputeFamily = i;</span></div><div><span> }</span></div><div><br></div><div><span> if (families[i].queueFlags & VK_QUEUE_VIDEO_ENCODE_BIT_KHR) {</span></div><div><span> indices.encodeFamily = i;</span></div><div><span> }</span></div><div><br></div><div><span> if (indices.transferAndComputeFamily != UINT32_MAX &&</span></div><div><span> indices.encodeFamily != UINT32_MAX) {</span></div><div><span> break;</span></div><div><span> }</span></div><div><span> }</span></div><div><br></div><div><span> free(families);</span></div><div><span> return indices;</span></div><div><span>}</span></div><div><br></div><div><span>void encodeVideo() {</span></div><div><span> ff_hw_dev = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_VULKAN);</span></div><div><span> AVHWDeviceContext *hwctx = (AVHWDeviceContext *)ff_hw_dev->data;</span></div><div><span> AVVulkanDeviceContext *vk = (AVVulkanDeviceContext *)hwctx->hwctx;</span></div><div><br></div><div><span> vk->get_proc_addr = vkGetInstanceProcAddr;</span></div><div><span> vk->inst = instance;</span></div><div><span> vk->phys_dev = physicalDevice;</span></div><div><span> vk->act_dev = device;</span></div><div><span> vk->device_features = deviceFeatures;</span></div><div><span> vk->enabled_inst_extensions = instanceExtensions;</span></div><div><span> vk->nb_enabled_inst_extensions = instanceExtensionsCount;</span></div><div><span> vk->enabled_dev_extensions = deviceExtensions;</span></div><div><span> vk->nb_enabled_dev_extensions = deviceExtensionsCount;</span></div><div><br></div><div><span> vk->qf[0].idx = queueFamilyIndices.transferAndComputeFamily;</span></div><div><span> vk->qf[0].num = 1;</span></div><div><span> vk->qf[0].flags =</span></div><div><span> (VkQueueFlagBits)(VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT);</span></div><div><br></div><div><span> vk->qf[1].idx = queueFamilyIndices.encodeFamily;</span></div><div><span> vk->qf[1].num = 1;</span></div><div><span> vk->qf[1].flags = VK_QUEUE_VIDEO_ENCODE_BIT_KHR;</span></div><div><span> vk->qf[1].video_caps = VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR;</span></div><div><br></div><div><span> vk->nb_qf = 2;</span></div><div><br></div><div><span> int result = av_hwdevice_ctx_init(ff_hw_dev);</span></div><div><span> if (result < 0) {</span></div><div><span> fprintf(stderr, "failed to init ffmpeg vulkan hwdevice\n");</span></div><div><span> exit(1);</span></div><div><span> }</span></div><div><br></div><div><span> ff_hw_frames_ctx = av_hwframe_ctx_alloc(ff_hw_dev);</span></div><div><span> if (!ff_hw_frames_ctx) {</span></div><div><span> fprintf(stderr, "Failed to allocate hardware frames context\n");</span></div><div><span> exit(1);</span></div><div><span> }</span></div><div><br></div><div><span> AVHWFramesContext *frames_ctx = (AVHWFramesContext *)ff_hw_frames_ctx->data;</span></div><div><span> frames_ctx->format = AV_PIX_FMT_VULKAN;</span></div><div><span> frames_ctx->sw_format = AV_PIX_FMT_NV12;</span></div><div><span> frames_ctx->width = IMAGE_SIZE.width;</span></div><div><span> frames_ctx->height = IMAGE_SIZE.height;</span></div><div><br></div><div><span> AVVulkanFramesContext *vk_frames_ctx =</span></div><div><span> (AVVulkanFramesContext *)frames_ctx->hwctx;</span></div><div><span> vk_frames_ctx->tiling = VK_IMAGE_TILING_OPTIMAL;</span></div><div><span> vk_frames_ctx->img_flags = VK_IMAGE_CREATE_VIDEO_PROFILE_INDEPENDENT_BIT_KHR;</span></div><div><span> vk_frames_ctx->flags = AV_VK_FRAME_FLAG_NONE;</span></div><div><span> vk_frames_ctx->usage =</span></div><div><span> (VkImageUsageFlagBits)(VK_IMAGE_USAGE_SAMPLED_BIT |</span></div><div><span> VK_IMAGE_USAGE_TRANSFER_SRC_BIT |</span></div><div><span> VK_IMAGE_USAGE_TRANSFER_DST_BIT |</span></div><div><span> VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR);</span></div><div><br></div><div><span> result = av_hwframe_ctx_init(ff_hw_frames_ctx);</span></div><div><span> if (result < 0) {</span></div><div><span> fprintf(stderr, "Failed to initialize hardware frames context\n");</span></div><div><span> exit(1);</span></div><div><span> }</span></div><div><br></div><div><span> ff_frame = av_frame_alloc();</span></div><div><span> if (!ff_frame) {</span></div><div><span> fprintf(stderr, "Failed to allocate Vulkan frame\n");</span></div><div><span> exit(1);</span></div><div><span> }</span></div><div><br></div><div><span> result = av_hwframe_get_buffer(ff_hw_frames_ctx, ff_frame, 0);</span></div><div><span> if (result < 0) {</span></div><div><span> fprintf(stderr, "Failed to allocate Vulkan frame buffer\n");</span></div><div><span> exit(1);</span></div><div><span> }</span></div><div><br></div><div><span> ff_vk_frame = (AVVkFrame *)ff_frame->data[0];</span></div><div><span> if (!ff_vk_frame) {</span></div><div><span> fprintf(stderr, "Failed to get VkFrame from AVFrame\n");</span></div><div><span> exit(1);</span></div><div><span> }</span></div><div><br></div><div><span> imageOut = ff_vk_frame->img[0];</span></div><div><br></div><div><span> const AVCodec *codec = avcodec_find_encoder_by_name("h264_vulkan");</span></div><div><span> if (!codec) {</span></div><div><span> fprintf(stderr, "h264_vulkan encoder not found\n");</span></div><div><span> exit(1);</span></div><div><span> }</span></div><div><br></div><div><span> AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);</span></div><div><span> if (!codec_ctx) {</span></div><div><span> fprintf(stderr, "Failed to allocate codec context\n");</span></div><div><span> exit(1);</span></div><div><span> }</span></div><div><br></div><div><span> codec_ctx->width = IMAGE_SIZE.width;</span></div><div><span> codec_ctx->height = IMAGE_SIZE.height;</span></div><div><span> codec_ctx->time_base = (AVRational){1, 30}; // 30 FPS</span></div><div><span> codec_ctx->framerate = (AVRational){30, 1};</span></div><div><span> codec_ctx->pix_fmt = AV_PIX_FMT_VULKAN;</span></div><div><span> codec_ctx->bit_rate = 5000000; // 5 Mbps</span></div><div><span> codec_ctx->hw_frames_ctx = ff_hw_frames_ctx;</span></div><div><br></div><div><span> result = avcodec_open2(codec_ctx, codec, NULL);</span></div><div><span> if (result < 0) {</span></div><div><span> fprintf(stderr, "Failed to open codec\n");</span></div><div><span> exit(1);</span></div><div><span> }</span></div><div><br></div><div><span> AVFormatContext *fmt_ctx;</span></div><div><br></div><div><span> result = avformat_alloc_output_context2(&fmt_ctx, NULL, NULL, "output.mp4");</span></div><div><span> if (result < 0) {</span></div><div><span> fprintf(stderr, "Failed to create output context\n");</span></div><div><span> exit(1);</span></div><div><span> }</span></div><div><br></div><div><span> AVStream *stream = avformat_new_stream(fmt_ctx, NULL);</span></div><div><span> if (!stream) {</span></div><div><span> fprintf(stderr, "Failed to create stream\n");</span></div><div><span> exit(1);</span></div><div><span> }</span></div><div><br></div><div><span> result = avcodec_parameters_from_context(stream->codecpar, codec_ctx);</span></div><div><span> if (result < 0) {</span></div><div><span> fprintf(stderr, "Failed to copy codec parameters\n");</span></div><div><span> exit(1);</span></div><div><span> }</span></div><div><br></div><div><span> if (!(fmt_ctx->oformat->flags & AVFMT_NOFILE)) {</span></div><div><span> result = avio_open(&fmt_ctx->pb, "output.mp4", AVIO_FLAG_WRITE);</span></div><div><span> if (result < 0) {</span></div><div><span> fprintf(stderr, "Failed to open output file\n");</span></div><div><span> exit(1);</span></div><div><span> }</span></div><div><span> }</span></div><div><br></div><div><span> result = avformat_write_header(fmt_ctx, NULL);</span></div><div><span> if (result < 0) {</span></div><div><span> fprintf(stderr, "Failed to write header\n");</span></div><div><span> exit(1);</span></div><div><span> }</span></div><div><br></div><div><span> AVPacket *pkt = av_packet_alloc();</span></div><div><span> if (!pkt) {</span></div><div><span> fprintf(stderr, "Failet to allocate frame or packet\n");</span></div><div><span> exit(1);</span></div><div><span> }</span></div><div><br></div><div><span> uint32_t totalFrames = 30;</span></div><div><span> for (uint32_t i = 0; i < totalFrames; i++) {</span></div><div><span> ff_frame->pts = i;</span></div><div><br></div><div><span> result = avcodec_send_frame(codec_ctx, ff_frame);</span></div><div><span> if (result < 0) {</span></div><div><span> fprintf(stderr, "Failed to send frame to encoder\n");</span></div><div><span> exit(1);</span></div><div><span> }</span></div><div><br></div><div><span> while (result >= 0) {</span></div><div><span> result = avcodec_receive_packet(codec_ctx, pkt);</span></div><div><span> if (result == AVERROR(EAGAIN) || result == AVERROR_EOF) {</span></div><div><span> break;</span></div><div><span> } else if (result < 0) {</span></div><div><span> fprintf(stderr, "Failed to receive packet\n");</span></div><div><span> exit(1);</span></div><div><span> }</span></div><div><br></div><div><span> pkt->stream_index = stream->index;</span></div><div><span> av_packet_rescale_ts(pkt, codec_ctx->time_base, stream->time_base);</span></div><div><span> result = av_interleaved_write_frame(fmt_ctx, pkt);</span></div><div><span> av_packet_unref(pkt);</span></div><div><br></div><div><span> if (result < 0) {</span></div><div><span> fprintf(stderr, "Failed to write packet\n");</span></div><div><span> exit(1);</span></div><div><span> }</span></div><div><span> }</span></div><div><span> }</span></div><div><br></div><div><span> result = avcodec_send_frame(codec_ctx, NULL);</span></div><div><span> while (result >= 0) {</span></div><div><span> result = avcodec_receive_packet(codec_ctx, pkt);</span></div><div><span> if (result == AVERROR_EOF) {</span></div><div><span> break;</span></div><div><span> } else if (result < 0) {</span></div><div><span> break;</span></div><div><span> }</span></div><div><br></div><div><span> pkt->stream_index = stream->index;</span></div><div><span> av_packet_rescale_ts(pkt, codec_ctx->time_base, stream->time_base);</span></div><div><span> av_interleaved_write_frame(fmt_ctx, pkt);</span></div><div><span> av_packet_unref(pkt);</span></div><div><span> }</span></div><div><br></div><div><span> av_write_trailer(fmt_ctx);</span></div><div><span> printf("Encoding completed successfully!\n");</span></div><div><br></div><div><span> // Cleanup</span></div><div><span> vkDeviceWaitIdle(device); // unsure if needed</span></div><div><span> avcodec_free_context(&codec_ctx);</span></div><div><span> if (fmt_ctx) {</span></div><div><span> if (fmt_ctx->pb) {</span></div><div><span> avio_closep(&fmt_ctx->pb);</span></div><div><span> }</span></div><div><span> avformat_free_context(fmt_ctx);</span></div><div><span> }</span></div><div><br></div><div><span> av_packet_free(&pkt);</span></div><div><span> av_frame_free(&ff_frame);</span></div><div><span> av_buffer_unref(&ff_hw_dev);</span></div><div><span>}</span></div><div><br></div><div><span>void cleanupVulkan() {</span></div><div><span> vkDestroyDevice(device, NULL);</span></div><div><span> vkDestroyInstance(instance, NULL);</span></div><div><span>}</span></div><div><br></div><div><span>void createInstance() {</span></div><div><span> VkApplicationInfo appInfo = {0};</span></div><div><span> appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;</span></div><div><span> appInfo.pApplicationName = "FFmpeg";</span></div><div><span> appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);</span></div><div><span> appInfo.pEngineName = "No Engine";</span></div><div><span> appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);</span></div><div><span> appInfo.apiVersion = VK_API_VERSION_1_3;</span></div><div><br></div><div><span> VkInstanceCreateInfo createInfo = {0};</span></div><div><span> createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;</span></div><div><span> createInfo.pApplicationInfo = &appInfo;</span></div><div><span> createInfo.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;</span></div><div><br></div><div><span> createInfo.enabledLayerCount = validationLayersCount;</span></div><div><span> createInfo.ppEnabledLayerNames = validationLayers;</span></div><div><br></div><div><span> createInfo.enabledExtensionCount = instanceExtensionsCount;</span></div><div><span> createInfo.ppEnabledExtensionNames = instanceExtensions;</span></div><div><br></div><div><span> check(vkCreateInstance(&createInfo, NULL, &instance),</span></div><div><span> "failed to create instance");</span></div><div><span>}</span></div><div><br></div><div><span>bool checkDeviceExtensionSupport(VkPhysicalDevice device) {</span></div><div><span> uint32_t extensionCount;</span></div><div><span> vkEnumerateDeviceExtensionProperties(device, NULL, &extensionCount, NULL);</span></div><div><span> VkExtensionProperties *supportedExtensions =</span></div><div><span> malloc(extensionCount * sizeof(VkExtensionProperties));</span></div><div><span> if (!supportedExtensions) {</span></div><div><span> fprintf(stderr, "Failed to allocate memory for device extensions\n");</span></div><div><span> exit(1);</span></div><div><span> }</span></div><div><span> vkEnumerateDeviceExtensionProperties(device, NULL, &extensionCount,</span></div><div><span> supportedExtensions);</span></div><div><br></div><div><span> for (size_t i = 0; i < deviceExtensionsCount; i++) {</span></div><div><span> const char *requiredExtension = deviceExtensions[i];</span></div><div><span> bool isFound = false;</span></div><div><span> for (uint32_t j = 0; j < extensionCount; j++) {</span></div><div><span> if (strcmp(requiredExtension, supportedExtensions[j].extensionName) ==</span></div><div><span> 0) {</span></div><div><span> isFound = true;</span></div><div><span> break;</span></div><div><span> }</span></div><div><span> }</span></div><div><br></div><div><span> if (!isFound) {</span></div><div><span> free(supportedExtensions);</span></div><div><span> return false;</span></div><div><span> }</span></div><div><span> }</span></div><div><br></div><div><span> free(supportedExtensions);</span></div><div><span> return true;</span></div><div><span>}</span></div><div><br></div><div><span>void pickPhysicalDevice() {</span></div><div><span> uint32_t deviceCount;</span></div><div><span> vkEnumeratePhysicalDevices(instance, &deviceCount, NULL);</span></div><div><span> if (deviceCount == 0) {</span></div><div><span> fprintf(stderr, "no physical devices found\n");</span></div><div><span> exit(1);</span></div><div><span> }</span></div><div><br></div><div><span> VkPhysicalDevice *devices = malloc(deviceCount * sizeof(VkPhysicalDevice));</span></div><div><span> if (!devices) {</span></div><div><span> fprintf(stderr, "Failed to allocate memory for devices\n");</span></div><div><span> exit(1);</span></div><div><span> }</span></div><div><span> vkEnumeratePhysicalDevices(instance, &deviceCount, devices);</span></div><div><br></div><div><span> for (uint32_t i = 0; i < deviceCount; i++) {</span></div><div><span> VkPhysicalDevice device = devices[i];</span></div><div><span> VkPhysicalDeviceProperties props;</span></div><div><span> vkGetPhysicalDeviceProperties(device, &props);</span></div><div><br></div><div><span> if (!checkDeviceExtensionSupport(device)) {</span></div><div><span> continue;</span></div><div><span> }</span></div><div><br></div><div><span> QueueFamilyIndices indices = findQueueFamilies(device);</span></div><div><span> if (indices.transferAndComputeFamily == UINT32_MAX ||</span></div><div><span> indices.encodeFamily == UINT32_MAX) {</span></div><div><span> continue;</span></div><div><span> }</span></div><div><br></div><div><span> physicalDevice = device;</span></div><div><span> queueFamilyIndices = indices;</span></div><div><span> break;</span></div><div><span> }</span></div><div><br></div><div><span> free(devices);</span></div><div><br></div><div><span> if (physicalDevice == VK_NULL_HANDLE) {</span></div><div><span> fprintf(stderr, "no suitable physical device found\n");</span></div><div><span> exit(1);</span></div><div><span> }</span></div><div><span>}</span></div><div><br></div><div><span>void createLogicalDevice() {</span></div><div><span> VkDeviceQueueCreateInfo queueCreateInfos[2] = {0};</span></div><div><span> float queuePriority = 1.0f;</span></div><div><br></div><div><span> queueCreateInfos[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;</span></div><div><span> queueCreateInfos[0].queueFamilyIndex =</span></div><div><span> queueFamilyIndices.transferAndComputeFamily;</span></div><div><span> queueCreateInfos[0].queueCount = 1;</span></div><div><span> queueCreateInfos[0].pQueuePriorities = &queuePriority;</span></div><div><br></div><div><span> queueCreateInfos[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;</span></div><div><span> queueCreateInfos[1].queueFamilyIndex = queueFamilyIndices.encodeFamily;</span></div><div><span> queueCreateInfos[1].queueCount = 1;</span></div><div><span> queueCreateInfos[1].pQueuePriorities = &queuePriority;</span></div><div><br></div><div><span> VkPhysicalDeviceFeatures coreDeviceFeatures = {0};</span></div><div><span> coreDeviceFeatures.shaderImageGatherExtended = VK_TRUE;</span></div><div><span> coreDeviceFeatures.fragmentStoresAndAtomics = VK_TRUE;</span></div><div><span> coreDeviceFeatures.shaderInt64 = VK_TRUE;</span></div><div><span> coreDeviceFeatures.vertexPipelineStoresAndAtomics = VK_TRUE;</span></div><div><br></div><div><span> VkPhysicalDeviceVideoMaintenance1FeaturesKHR videoMaint1Features = {0};</span></div><div><span> videoMaint1Features.sType =</span></div><div><span> VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_MAINTENANCE_1_FEATURES_KHR;</span></div><div><span> videoMaint1Features.videoMaintenance1 = VK_TRUE;</span></div><div><br></div><div><span> VkPhysicalDeviceSamplerYcbcrConversionFeatures ycbcrFeatures = {0};</span></div><div><span> ycbcrFeatures.sType =</span></div><div><span> VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES;</span></div><div><span> ycbcrFeatures.samplerYcbcrConversion = VK_TRUE;</span></div><div><span> ycbcrFeatures.pNext = &videoMaint1Features;</span></div><div><br></div><div><span> VkPhysicalDeviceSynchronization2Features enabledSync2Features = {0};</span></div><div><span> enabledSync2Features.sType =</span></div><div><span> VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES;</span></div><div><span> enabledSync2Features.synchronization2 = VK_TRUE;</span></div><div><span> enabledSync2Features.pNext = &ycbcrFeatures;</span></div><div><br></div><div><span> VkPhysicalDeviceTimelineSemaphoreFeatures timelineSemaphoreFeatures = {0};</span></div><div><span> timelineSemaphoreFeatures.sType =</span></div><div><span> VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES;</span></div><div><span> timelineSemaphoreFeatures.timelineSemaphore = VK_TRUE;</span></div><div><span> timelineSemaphoreFeatures.pNext = &enabledSync2Features;</span></div><div><br></div><div><span> deviceFeatures = (VkPhysicalDeviceFeatures2){0};</span></div><div><span> deviceFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;</span></div><div><span> deviceFeatures.features = coreDeviceFeatures;</span></div><div><span> deviceFeatures.pNext = &timelineSemaphoreFeatures;</span></div><div><br></div><div><span> VkDeviceCreateInfo createInfo = {0};</span></div><div><span> createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;</span></div><div><span> createInfo.queueCreateInfoCount = 2;</span></div><div><span> createInfo.pQueueCreateInfos = queueCreateInfos;</span></div><div><span> createInfo.pNext = &deviceFeatures;</span></div><div><br></div><div><span> createInfo.enabledLayerCount = validationLayersCount;</span></div><div><span> createInfo.ppEnabledLayerNames = validationLayers;</span></div><div><br></div><div><span> createInfo.enabledExtensionCount = deviceExtensionsCount;</span></div><div><span> createInfo.ppEnabledExtensionNames = deviceExtensions;</span></div><div><br></div><div><span> check(vkCreateDevice(physicalDevice, &createInfo, NULL, &device),</span></div><div><span> "failed to create logical device");</span></div><div><span>}</span></div><div><br></div><div><span>int main() {</span></div><div><span> createInstance();</span></div><div><span> pickPhysicalDevice();</span></div><div><span> createLogicalDevice();</span></div><div><span> encodeVideo();</span></div><div><span> cleanupVulkan();</span></div><div><span> return 0;</span></div><div><span>}</span></div></div><div style="font-family: Arial, sans-serif; font-size: 14px;">```</div><div style="font-family: Arial, sans-serif; font-size: 14px;"><br></div><div style="font-family: Arial, sans-serif; font-size: 14px;">Interesting code is in the encodeVideo function. It creates an empty frame and sends it to the h264_vulkan encoder to create a video.</div><div style="font-family: Arial, sans-serif; font-size: 14px;"><br></div><div style="font-family: Arial, sans-serif; font-size: 14px;">When running it, there are two different validation layer errors, which I don't know how to fix:</div><div style="font-family: Arial, sans-serif; font-size: 14px;">```</div><div style="font-family: Arial, sans-serif; font-size: 14px;"><span>Validation Error: [ VUID-VkImageCreateInfo-pNext-06811 ] | MessageID = 0x30f4ac70</span><div><span>vkCreateImage(): pCreateInfo specifies flags (VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT), format (VK_FORMAT_G8_B8R8_2PLANE_420_UNORM), imageType (VK_IMAGE_TYPE_2D), and tiling (VK_IMAGE_TILING_OPTIMAL) which are not supported by any of the supported video format properties for the video profiles specified in the VkVideoProfileListInfoKHR structure included in the pCreateInfo->pNext chain, as reported by vkGetPhysicalDeviceVideoFormatPropertiesKHR for the same video profiles and the image usage flags specified in pCreateInfo->usage (VK_IMAGE_USAGE_SAMPLED_BIT|VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR).</span></div><div><span>The Vulkan spec states: If the pNext chain includes a VkVideoProfileListInfoKHR structure with profileCount greater than 0, then supportedVideoFormat must be VK_TRUE (<a target="_blank" rel="noreferrer nofollow noopener" href="https://docs.vulkan.org/spec/latest/chapters/resources.html#VUID-VkImageCreateInfo-pNext-06811">https://docs.vulkan.org/spec/latest/chapters/resources.html#VUID-VkImageCreateInfo-pNext-06811</a>)</span></div><div><br></div><div><span>Validation Error: [ VUID-VkImageCreateInfo-pNext-06811 ] | MessageID = 0x30f4ac70</span></div><div><span>vkCreateImage(): pCreateInfo specifies flags (VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT), format (VK_FORMAT_G8_B8R8_2PLANE_420_UNORM), imageType (VK_IMAGE_TYPE_2D), and tiling (VK_IMAGE_TILING_OPTIMAL) which are not supported by any of the supported video format properties for the video profiles specified in the VkVideoProfileListInfoKHR structure included in the pCreateInfo->pNext chain, as reported by vkGetPhysicalDeviceVideoFormatPropertiesKHR for the same video profiles and the image usage flags specified in pCreateInfo->usage (VK_IMAGE_USAGE_SAMPLED_BIT|VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR).</span></div><div><span>The Vulkan spec states: If the pNext chain includes a VkVideoProfileListInfoKHR structure with profileCount greater than 0, then supportedVideoFormat must be VK_TRUE (<a target="_blank" rel="noreferrer nofollow noopener" href="https://docs.vulkan.org/spec/latest/chapters/resources.html#VUID-VkImageCreateInfo-pNext-06811">https://docs.vulkan.org/spec/latest/chapters/resources.html#VUID-VkImageCreateInfo-pNext-06811</a>)</span></div><div><br></div><div><span>Encoding completed successfully!</span></div><div><span>Validation Error: [ VUID-vkDestroyDevice-device-05137 ] | MessageID = 0x4872eaa0</span></div><div><span>vkDestroyDevice(): Object Tracking - For VkDevice 0x237f16b0, VkImageView 0x450000000045 has not been destroyed.</span></div><div><span>The Vulkan spec states: All child objects created on device that can be destroyed or freed must have been destroyed or freed prior to destroying device (<a target="_blank" rel="noreferrer nofollow noopener" href="https://docs.vulkan.org/spec/latest/chapters/devsandqueues.html#VUID-vkDestroyDevice-device-05137">https://docs.vulkan.org/spec/latest/chapters/devsandqueues.html#VUID-vkDestroyDevice-device-05137</a>)</span></div><div><span>Objects: 1</span></div><div><span> [0] VkImageView 0x450000000045</span></div><div><br></div><div><span>Validation Error: [ VUID-vkDestroyDevice-device-05137 ] | MessageID = 0x4872eaa0</span></div><div><span>vkDestroyDevice(): Object Tracking - For VkDevice 0x237f16b0, VkImageView 0x460000000046 has not been destroyed.</span></div><div><span>The Vulkan spec states: All child objects created on device that can be destroyed or freed must have been destroyed or freed prior to destroying device (<a target="_blank" rel="noreferrer nofollow noopener" href="https://docs.vulkan.org/spec/latest/chapters/devsandqueues.html#VUID-vkDestroyDevice-device-05137">https://docs.vulkan.org/spec/latest/chapters/devsandqueues.html#VUID-vkDestroyDevice-device-05137</a>)</span></div><div><span>Objects: 1</span></div><div><span> [0] VkImageView 0x460000000046</span></div><div><br></div><div><span>Validation Error: [ VUID-vkDestroyDevice-device-05137 ] | MessageID = 0x4872eaa0</span></div><div><span>vkDestroyDevice(): Object Tracking - For VkDevice 0x237f16b0, VkImageView 0x470000000047 has not been destroyed.</span></div><div><span>The Vulkan spec states: All child objects created on device that can be destroyed or freed must have been destroyed or freed prior to destroying device (<a target="_blank" rel="noreferrer nofollow noopener" href="https://docs.vulkan.org/spec/latest/chapters/devsandqueues.html#VUID-vkDestroyDevice-device-05137">https://docs.vulkan.org/spec/latest/chapters/devsandqueues.html#VUID-vkDestroyDevice-device-05137</a>)</span></div><div><span>Objects: 1</span></div><span> [0] VkImageView 0x470000000047</span><br></div><div style="font-family: Arial, sans-serif; font-size: 14px;">```</div><div style="font-family: Arial, sans-serif; font-size: 14px;"><br></div><div style="font-family: Arial, sans-serif; font-size: 14px;"><span style="scrollbar-width:thin;scrollbar-color:rgba(0, 0, 0, 0.35) rgba(0, 0, 0, 0)"><span style="scrollbar-width: thin; scrollbar-color: rgba(0, 0, 0, 0.35) rgba(0, 0, 0, 0); display: inline !important; background-color: rgb(255, 255, 255);">I'm using FFmpeg commit </span><span style="scrollbar-width: thin; scrollbar-color: rgba(0, 0, 0, 0.35) rgba(0, 0, 0, 0); background-color: rgb(255, 255, 255);">da18c2a373, compiled with `--enable-vulkan`, and the application compiled with `<span style="scrollbar-width: thin; scrollbar-color: rgba(0, 0, 0, 0.35) rgba(0, 0, 0, 0); display: inline !important; background-color: rgb(255, 255, 255);">gcc main.c -Iinclude -lm -Llib -lvulkan -lavformat -lavcodec -lavutil -lswresample -o main</span>`.</span></span><br></div><div style="font-family: Arial, sans-serif; font-size: 14px;"><br></div><div style="font-family: Arial, sans-serif; font-size: 14px;">Could anyone familiar with Vulkan encoding take a look at my code and see if there any obvious mistakes in it?</div><div style="font-family: Arial, sans-serif; font-size: 14px;"><br></div><div style="font-family: Arial, sans-serif; font-size: 14px;">Also something to note is that the first validation errors disappear when using the FFmpeg stable 7.1.1 release instead of master branch, but the VkImageView leaks are still there.</div><div style="font-family: Arial, sans-serif; font-size: 14px;"><br></div><div style="font-family: Arial, sans-serif; font-size: 14px;">Thanks!</div><div style="font-family: Arial, sans-serif; font-size: 14px;"><br></div><div style="font-family: Arial, sans-serif; font-size: 14px;"></div><div style="font-family: Arial, sans-serif; font-size: 14px;" class="protonmail_signature_block">
</div>