/* * Copyright (c) 2024, Aliaksandr Kalenik * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include namespace Gfx { static ErrorOr create_instance(uint32_t api_version) { VkInstance instance; VkApplicationInfo app_info {}; app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; app_info.pApplicationName = "Ladybird"; app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0); app_info.pEngineName = nullptr; app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0); app_info.apiVersion = api_version; VkInstanceCreateInfo create_info {}; create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; create_info.pApplicationInfo = &app_info; auto result = vkCreateInstance(&create_info, nullptr, &instance); if (result != VK_SUCCESS) { dbgln("vkCreateInstance returned {}", to_underlying(result)); return Error::from_string_literal("Application instance creation failed"); } return instance; } static ErrorOr pick_physical_device(VkInstance instance) { uint32_t device_count = 0; vkEnumeratePhysicalDevices(instance, &device_count, nullptr); if (device_count == 0) return Error::from_string_literal("Can't find any physical devices available"); Vector devices; devices.resize(device_count); vkEnumeratePhysicalDevices(instance, &device_count, devices.data()); VkPhysicalDevice picked_device = VK_NULL_HANDLE; // Pick discrete GPU or the first device in the list for (auto const& device : devices) { if (picked_device == VK_NULL_HANDLE) picked_device = device; VkPhysicalDeviceProperties device_properties; vkGetPhysicalDeviceProperties(device, &device_properties); if (device_properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) picked_device = device; } if (picked_device != VK_NULL_HANDLE) return picked_device; VERIFY_NOT_REACHED(); } static ErrorOr create_logical_device(VkPhysicalDevice physical_device, uint32_t* graphics_queue_family) { VkDevice device; uint32_t queue_family_count = 0; vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, nullptr); Vector queue_families; queue_families.resize(queue_family_count); vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, queue_families.data()); bool graphics_queue_family_found = false; uint32_t graphics_queue_family_index = 0; for (; graphics_queue_family_index < queue_families.size(); ++graphics_queue_family_index) { if (queue_families[graphics_queue_family_index].queueFlags & VK_QUEUE_GRAPHICS_BIT) { graphics_queue_family_found = true; break; } } if (!graphics_queue_family_found) { return Error::from_string_literal("Graphics queue family not found"); } *graphics_queue_family = graphics_queue_family_index; VkDeviceQueueCreateInfo queue_create_info {}; queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; queue_create_info.queueFamilyIndex = graphics_queue_family_index; queue_create_info.queueCount = 1; float const queue_priority = 1.0f; queue_create_info.pQueuePriorities = &queue_priority; VkPhysicalDeviceFeatures deviceFeatures {}; #ifdef USE_VULKAN_IMAGES char const* device_extensions[] = { VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME, VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME }; uint32_t device_extension_count = array_size(device_extensions); #else const char** device_extensions = nullptr; uint32_t device_extension_count = 0; #endif VkDeviceCreateInfo create_device_info {}; create_device_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; create_device_info.pQueueCreateInfos = &queue_create_info; create_device_info.queueCreateInfoCount = 1; create_device_info.pEnabledFeatures = &deviceFeatures; create_device_info.enabledExtensionCount = device_extension_count; create_device_info.ppEnabledExtensionNames = device_extensions; if (vkCreateDevice(physical_device, &create_device_info, nullptr, &device) != VK_SUCCESS) { return Error::from_string_literal("Logical device creation failed"); } return device; } #ifdef USE_VULKAN_IMAGES static ErrorOr create_command_pool(VkDevice logical_device, uint32_t queue_family_index) { VkCommandPoolCreateInfo command_pool_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, .pNext = nullptr, .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, .queueFamilyIndex = queue_family_index, }; VkCommandPool command_pool = VK_NULL_HANDLE; VkResult result = vkCreateCommandPool(logical_device, &command_pool_info, nullptr, &command_pool); if (result != VK_SUCCESS) { dbgln("vkCreateCommandPool returned {}", to_underlying(result)); return Error::from_string_literal("command pool creation failed"); } return command_pool; } static ErrorOr allocate_command_buffer(VkDevice logical_device, VkCommandPool commandPool) { VkCommandBufferAllocateInfo command_buffer_alloc_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, .pNext = nullptr, .commandPool = commandPool, .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, .commandBufferCount = 1, }; VkCommandBuffer command_buffer = VK_NULL_HANDLE; VkResult result = vkAllocateCommandBuffers(logical_device, &command_buffer_alloc_info, &command_buffer); if (result != VK_SUCCESS) { dbgln("vkAllocateCommandBuffers returned {}", to_underlying(result)); return Error::from_string_literal("command buffer allocation failed"); } return command_buffer; } #endif ErrorOr create_vulkan_context() { uint32_t const api_version = VK_API_VERSION_1_1; // v1.1 needed for vkGetPhysicalDeviceFormatProperties2 auto* instance = TRY(create_instance(api_version)); auto* physical_device = TRY(pick_physical_device(instance)); uint32_t graphics_queue_family = 0; auto* logical_device = TRY(create_logical_device(physical_device, &graphics_queue_family)); VkQueue graphics_queue; vkGetDeviceQueue(logical_device, graphics_queue_family, 0, &graphics_queue); #ifdef USE_VULKAN_IMAGES VkCommandPool command_pool = TRY(create_command_pool(logical_device, graphics_queue_family)); VkCommandBuffer command_buffer = TRY(allocate_command_buffer(logical_device, command_pool)); auto pfn_vk_get_memory_fd_khr = reinterpret_cast(vkGetDeviceProcAddr(logical_device, "vkGetMemoryFdKHR")); if (pfn_vk_get_memory_fd_khr == nullptr) { return Error::from_string_literal("vkGetMemoryFdKHR unavailable"); } auto pfn_vk_get_image_drm_format_modifier_properties_khr = reinterpret_cast(vkGetDeviceProcAddr(logical_device, "vkGetImageDrmFormatModifierPropertiesEXT")); if (pfn_vk_get_image_drm_format_modifier_properties_khr == nullptr) { return Error::from_string_literal("vkGetImageDrmFormatModifierPropertiesEXT unavailable"); } #endif return VulkanContext { .api_version = api_version, .instance = instance, .physical_device = physical_device, .logical_device = logical_device, .graphics_queue = graphics_queue, .graphics_queue_family = graphics_queue_family, #ifdef USE_VULKAN_IMAGES .command_pool = command_pool, .command_buffer = command_buffer, .ext_procs = { .get_memory_fd = pfn_vk_get_memory_fd_khr, .get_image_drm_format_modifier_properties = pfn_vk_get_image_drm_format_modifier_properties_khr, }, #endif }; } #ifdef USE_VULKAN_IMAGES VulkanImage::~VulkanImage() { if (image != VK_NULL_HANDLE) { vkDestroyImage(context.logical_device, image, nullptr); } if (memory != VK_NULL_HANDLE) { vkFreeMemory(context.logical_device, memory, nullptr); } } void VulkanImage::transition_layout(VkImageLayout old_layout, VkImageLayout new_layout) { vkResetCommandBuffer(context.command_buffer, 0); VkCommandBufferBeginInfo begin_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .pNext = nullptr, .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, .pInheritanceInfo = nullptr, }; vkBeginCommandBuffer(context.command_buffer, &begin_info); VkImageMemoryBarrier imageMemoryBarrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, .srcAccessMask = 0, .dstAccessMask = 0, .oldLayout = old_layout, .newLayout = new_layout, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = image, .subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }, }; vkCmdPipelineBarrier(context.command_buffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); vkEndCommandBuffer(context.command_buffer); VkSubmitInfo submit_info = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .pNext = nullptr, .waitSemaphoreCount = 0, .pWaitSemaphores = nullptr, .pWaitDstStageMask = nullptr, .commandBufferCount = 1, .pCommandBuffers = &context.command_buffer, .signalSemaphoreCount = 0, .pSignalSemaphores = nullptr, }; vkQueueSubmit(context.graphics_queue, 1, &submit_info, nullptr); vkQueueWaitIdle(context.graphics_queue); } int VulkanImage::get_dma_buf_fd() { VkMemoryGetFdInfoKHR get_fd_info = { .sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR, .pNext = nullptr, .memory = memory, .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, }; int fd = -1; VkResult result = context.ext_procs.get_memory_fd(context.logical_device, &get_fd_info, &fd); if (result != VK_SUCCESS) { dbgln("vkGetMemoryFdKHR returned {}", to_underlying(result)); return -1; } return fd; } ErrorOr> create_shared_vulkan_image(VulkanContext const& context, uint32_t width, uint32_t height, VkFormat format, uint32_t num_modifiers, uint64_t const* modifiers) { VkDrmFormatModifierPropertiesListEXT format_mod_props_list = {}; format_mod_props_list.sType = VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT; format_mod_props_list.pNext = nullptr; VkFormatProperties2 format_props = {}; format_props.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; format_props.pNext = &format_mod_props_list; vkGetPhysicalDeviceFormatProperties2(context.physical_device, format, &format_props); Vector format_mod_props; format_mod_props.resize(format_mod_props_list.drmFormatModifierCount); format_mod_props_list.pDrmFormatModifierProperties = format_mod_props.data(); vkGetPhysicalDeviceFormatProperties2(context.physical_device, format, &format_props); // populate a list of all format modifiers that are both renderable and accepted by the caller Vector format_mods; for (VkDrmFormatModifierPropertiesEXT const& props : format_mod_props) { if ((props.drmFormatModifierTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT) && (props.drmFormatModifierPlaneCount == 1)) { for (uint32_t i = 0; i < num_modifiers; ++i) { if (modifiers[i] == props.drmFormatModifier) { format_mods.append(props.drmFormatModifier); break; } } } } NonnullRefPtr image = make_ref_counted(context); VkImageDrmFormatModifierListCreateInfoEXT image_drm_format_modifier_list_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT, .pNext = nullptr, .drmFormatModifierCount = static_cast(format_mods.size()), .pDrmFormatModifiers = format_mods.data(), }; VkExternalMemoryImageCreateInfo external_mem_image_info = { .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, .pNext = &image_drm_format_modifier_list_info, .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, }; uint32_t queue_families[] = { context.graphics_queue_family, VK_QUEUE_FAMILY_EXTERNAL }; VkImageCreateInfo image_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .pNext = &external_mem_image_info, .flags = 0, .imageType = VK_IMAGE_TYPE_2D, .format = format, .extent = { .width = width, .height = height, .depth = 1, }, .mipLevels = 1, .arrayLayers = 1, .samples = VK_SAMPLE_COUNT_1_BIT, .tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT, .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, .sharingMode = VK_SHARING_MODE_CONCURRENT, .queueFamilyIndexCount = array_size(queue_families), .pQueueFamilyIndices = queue_families, .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, }; auto result = vkCreateImage(context.logical_device, &image_info, nullptr, &image->image); if (result != VK_SUCCESS) { dbgln("vkCreateImage returned {}", to_underlying(result)); return Error::from_string_literal("image creation failed"); } VkMemoryRequirements mem_reqs; vkGetImageMemoryRequirements(context.logical_device, image->image, &mem_reqs); VkPhysicalDeviceMemoryProperties mem_props; vkGetPhysicalDeviceMemoryProperties(context.physical_device, &mem_props); uint32_t mem_type_idx; for (mem_type_idx = 0; mem_type_idx < mem_props.memoryTypeCount; ++mem_type_idx) { if ((mem_reqs.memoryTypeBits & (1 << mem_type_idx)) && (mem_props.memoryTypes[mem_type_idx].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) { break; } } if (mem_type_idx == mem_props.memoryTypeCount) { return Error::from_string_literal("unable to find suitable image memory type"); } VkExportMemoryAllocateInfo export_mem_alloc_info = { .sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO, .pNext = nullptr, .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, }; VkMemoryAllocateInfo mem_alloc_info = { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .pNext = &export_mem_alloc_info, .allocationSize = mem_reqs.size, .memoryTypeIndex = mem_type_idx, }; result = vkAllocateMemory(context.logical_device, &mem_alloc_info, nullptr, &image->memory); if (result != VK_SUCCESS) { dbgln("vkAllocateMemory returned {}", to_underlying(result)); return Error::from_string_literal("image memory allocation failed"); } result = vkBindImageMemory(context.logical_device, image->image, image->memory, 0); if (result != VK_SUCCESS) { dbgln("vkBindImageMemory returned {}", to_underlying(result)); return Error::from_string_literal("bind image memory failed"); } VkImageSubresource subresource = { VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT, 0, 0 }; VkSubresourceLayout subresource_layout = {}; vkGetImageSubresourceLayout(context.logical_device, image->image, &subresource, &subresource_layout); VkImageDrmFormatModifierPropertiesEXT image_format_mod_props = {}; image_format_mod_props.sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_PROPERTIES_EXT; image_format_mod_props.pNext = nullptr; result = context.ext_procs.get_image_drm_format_modifier_properties(context.logical_device, image->image, &image_format_mod_props); if (result != VK_SUCCESS) { dbgln("vkGetImageDrmFormatModifierPropertiesEXT returned {}", to_underlying(result)); return Error::from_string_literal("image format modifier retrieval failed"); } // external APIs require general layout VkImageLayout layout = VK_IMAGE_LAYOUT_GENERAL; image->transition_layout(VK_IMAGE_LAYOUT_UNDEFINED, layout); image->info = { .format = image_info.format, .extent = image_info.extent, .tiling = image_info.tiling, .usage = image_info.usage, .sharing_mode = image_info.sharingMode, .layout = layout, .row_pitch = subresource_layout.rowPitch, .modifier = image_format_mod_props.drmFormatModifier, }; return image; } #endif }