/* * Copyright © 2017 Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "wsi_common_private.h" #include "util/macros.h" void wsi_device_init(struct wsi_device *wsi, VkPhysicalDevice pdevice, WSI_FN_GetPhysicalDeviceProcAddr proc_addr) { memset(wsi, 0, sizeof(*wsi)); #define WSI_GET_CB(func) \ PFN_vk##func func = (PFN_vk##func)proc_addr(pdevice, "vk" #func) WSI_GET_CB(GetPhysicalDeviceMemoryProperties); WSI_GET_CB(GetPhysicalDeviceQueueFamilyProperties); #undef WSI_GET_CB GetPhysicalDeviceMemoryProperties(pdevice, &wsi->memory_props); GetPhysicalDeviceQueueFamilyProperties(pdevice, &wsi->queue_family_count, NULL); #define WSI_GET_CB(func) \ wsi->func = (PFN_vk##func)proc_addr(pdevice, "vk" #func) WSI_GET_CB(AllocateMemory); WSI_GET_CB(AllocateCommandBuffers); WSI_GET_CB(BindBufferMemory); WSI_GET_CB(BindImageMemory); WSI_GET_CB(BeginCommandBuffer); WSI_GET_CB(CmdCopyImageToBuffer); WSI_GET_CB(CreateBuffer); WSI_GET_CB(CreateCommandPool); WSI_GET_CB(CreateImage); WSI_GET_CB(DestroyBuffer); WSI_GET_CB(DestroyCommandPool); WSI_GET_CB(DestroyImage); WSI_GET_CB(EndCommandBuffer); WSI_GET_CB(FreeMemory); WSI_GET_CB(FreeCommandBuffers); WSI_GET_CB(GetBufferMemoryRequirements); WSI_GET_CB(GetImageMemoryRequirements); WSI_GET_CB(GetMemoryFdKHR); WSI_GET_CB(QueueSubmit); #undef WSI_GET_CB } VkResult wsi_swapchain_init(const struct wsi_device *wsi, struct wsi_swapchain *chain, VkDevice device, const VkSwapchainCreateInfoKHR *pCreateInfo, const VkAllocationCallbacks *pAllocator) { VkResult result; memset(chain, 0, sizeof(*chain)); chain->wsi = wsi; chain->device = device; chain->alloc = *pAllocator; chain->cmd_pools = vk_zalloc(pAllocator, sizeof(VkCommandPool) * wsi->queue_family_count, 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); if (!chain->cmd_pools) return VK_ERROR_OUT_OF_HOST_MEMORY; for (uint32_t i = 0; i < wsi->queue_family_count; i++) { const VkCommandPoolCreateInfo cmd_pool_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, .pNext = NULL, .flags = 0, .queueFamilyIndex = i, }; result = wsi->CreateCommandPool(device, &cmd_pool_info, &chain->alloc, &chain->cmd_pools[i]); if (result != VK_SUCCESS) goto fail; } return VK_SUCCESS; fail: wsi_swapchain_finish(chain); return result; } void wsi_swapchain_finish(struct wsi_swapchain *chain) { for (uint32_t i = 0; i < chain->wsi->queue_family_count; i++) { chain->wsi->DestroyCommandPool(chain->device, chain->cmd_pools[i], &chain->alloc); } } static uint32_t select_memory_type(const struct wsi_device *wsi, VkMemoryPropertyFlags props, uint32_t type_bits) { for (uint32_t i = 0; i < wsi->memory_props.memoryTypeCount; i++) { const VkMemoryType type = wsi->memory_props.memoryTypes[i]; if ((type_bits & (1 << i)) && (type.propertyFlags & props) == props) return i; } unreachable("No memory type found"); } static uint32_t vk_format_size(VkFormat format) { switch (format) { case VK_FORMAT_B8G8R8A8_UNORM: case VK_FORMAT_B8G8R8A8_SRGB: return 4; default: unreachable("Unknown WSI Format"); } } static inline uint32_t align_u32(uint32_t v, uint32_t a) { assert(a != 0 && a == (a & -a)); return (v + a - 1) & ~(a - 1); } #define WSI_PRIME_LINEAR_STRIDE_ALIGN 256 VkResult wsi_create_prime_image(const struct wsi_swapchain *chain, const VkSwapchainCreateInfoKHR *pCreateInfo, struct wsi_image *image) { const struct wsi_device *wsi = chain->wsi; VkResult result; memset(image, 0, sizeof(*image)); const uint32_t cpp = vk_format_size(pCreateInfo->imageFormat); const uint32_t linear_stride = align_u32(pCreateInfo->imageExtent.width * cpp, WSI_PRIME_LINEAR_STRIDE_ALIGN); uint32_t linear_size = linear_stride * pCreateInfo->imageExtent.height; linear_size = align_u32(linear_size, 4096); const VkExternalMemoryBufferCreateInfoKHR prime_buffer_external_info = { .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHR, .pNext = NULL, .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, }; const VkBufferCreateInfo prime_buffer_info = { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .pNext = &prime_buffer_external_info, .size = linear_size, .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, }; result = wsi->CreateBuffer(chain->device, &prime_buffer_info, &chain->alloc, &image->prime.buffer); if (result != VK_SUCCESS) goto fail; VkMemoryRequirements reqs; wsi->GetBufferMemoryRequirements(chain->device, image->prime.buffer, &reqs); assert(reqs.size <= linear_size); const struct wsi_memory_allocate_info memory_wsi_info = { .sType = VK_STRUCTURE_TYPE_WSI_MEMORY_ALLOCATE_INFO_MESA, .pNext = NULL, .implicit_sync = true, }; const VkExportMemoryAllocateInfoKHR prime_memory_export_info = { .sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR, .pNext = &memory_wsi_info, .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, }; const VkMemoryDedicatedAllocateInfoKHR prime_memory_dedicated_info = { .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR, .pNext = &prime_memory_export_info, .image = VK_NULL_HANDLE, .buffer = image->prime.buffer, }; const VkMemoryAllocateInfo prime_memory_info = { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .pNext = &prime_memory_dedicated_info, .allocationSize = linear_size, .memoryTypeIndex = select_memory_type(wsi, 0, reqs.memoryTypeBits), }; result = wsi->AllocateMemory(chain->device, &prime_memory_info, &chain->alloc, &image->prime.memory); if (result != VK_SUCCESS) goto fail; result = wsi->BindBufferMemory(chain->device, image->prime.buffer, image->prime.memory, 0); if (result != VK_SUCCESS) goto fail; const VkImageCreateInfo image_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .pNext = NULL, .flags = 0, .imageType = VK_IMAGE_TYPE_2D, .format = pCreateInfo->imageFormat, .extent = { .width = pCreateInfo->imageExtent.width, .height = pCreateInfo->imageExtent.height, .depth = 1, }, .mipLevels = 1, .arrayLayers = 1, .samples = VK_SAMPLE_COUNT_1_BIT, .tiling = VK_IMAGE_TILING_OPTIMAL, .usage = pCreateInfo->imageUsage | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, .sharingMode = pCreateInfo->imageSharingMode, .queueFamilyIndexCount = pCreateInfo->queueFamilyIndexCount, .pQueueFamilyIndices = pCreateInfo->pQueueFamilyIndices, .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, }; result = wsi->CreateImage(chain->device, &image_info, &chain->alloc, &image->image); if (result != VK_SUCCESS) goto fail; wsi->GetImageMemoryRequirements(chain->device, image->image, &reqs); const VkMemoryDedicatedAllocateInfoKHR memory_dedicated_info = { .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR, .pNext = NULL, .image = image->image, .buffer = VK_NULL_HANDLE, }; const VkMemoryAllocateInfo memory_info = { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .pNext = &memory_dedicated_info, .allocationSize = reqs.size, .memoryTypeIndex = select_memory_type(wsi, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, reqs.memoryTypeBits), }; result = wsi->AllocateMemory(chain->device, &memory_info, &chain->alloc, &image->memory); if (result != VK_SUCCESS) goto fail; result = wsi->BindImageMemory(chain->device, image->image, image->memory, 0); if (result != VK_SUCCESS) goto fail; image->prime.blit_cmd_buffers = vk_zalloc(&chain->alloc, sizeof(VkCommandBuffer) * wsi->queue_family_count, 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); if (!image->prime.blit_cmd_buffers) goto fail; for (uint32_t i = 0; i < wsi->queue_family_count; i++) { const VkCommandBufferAllocateInfo cmd_buffer_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, .pNext = NULL, .commandPool = chain->cmd_pools[i], .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, .commandBufferCount = 1, }; result = wsi->AllocateCommandBuffers(chain->device, &cmd_buffer_info, &image->prime.blit_cmd_buffers[i]); if (result != VK_SUCCESS) goto fail; const VkCommandBufferBeginInfo begin_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, }; wsi->BeginCommandBuffer(image->prime.blit_cmd_buffers[i], &begin_info); struct VkBufferImageCopy buffer_image_copy = { .bufferOffset = 0, .bufferRowLength = linear_stride / cpp, .bufferImageHeight = 0, .imageSubresource = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .mipLevel = 0, .baseArrayLayer = 0, .layerCount = 1, }, .imageOffset = { .x = 0, .y = 0, .z = 0 }, .imageExtent = { .width = pCreateInfo->imageExtent.width, .height = pCreateInfo->imageExtent.height, .depth = 1, }, }; wsi->CmdCopyImageToBuffer(image->prime.blit_cmd_buffers[i], image->image, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, image->prime.buffer, 1, &buffer_image_copy); result = wsi->EndCommandBuffer(image->prime.blit_cmd_buffers[i]); if (result != VK_SUCCESS) goto fail; } const VkMemoryGetFdInfoKHR linear_memory_get_fd_info = { .sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR, .pNext = NULL, .memory = image->prime.memory, .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, }; int fd; result = wsi->GetMemoryFdKHR(chain->device, &linear_memory_get_fd_info, &fd); if (result != VK_SUCCESS) goto fail; image->size = linear_size; image->row_pitch = linear_stride; image->offset = 0; image->fd = fd; return VK_SUCCESS; fail: wsi_destroy_prime_image(chain, image); return result; } void wsi_destroy_prime_image(const struct wsi_swapchain *chain, struct wsi_image *image) { const struct wsi_device *wsi = chain->wsi; if (image->prime.blit_cmd_buffers) { for (uint32_t i = 0; i < wsi->queue_family_count; i++) { wsi->FreeCommandBuffers(chain->device, chain->cmd_pools[i], 1, &image->prime.blit_cmd_buffers[i]); } vk_free(&chain->alloc, image->prime.blit_cmd_buffers); } wsi->FreeMemory(chain->device, image->memory, &chain->alloc); wsi->DestroyImage(chain->device, image->image, &chain->alloc); wsi->FreeMemory(chain->device, image->prime.memory, &chain->alloc); wsi->DestroyBuffer(chain->device, image->prime.buffer, &chain->alloc); } VkResult wsi_prime_image_blit_to_linear(const struct wsi_swapchain *chain, struct wsi_image *image, VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore *pWaitSemaphores) { uint32_t queue_family = chain->wsi->queue_get_family_index(queue); VkPipelineStageFlags stage_flags = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT; const VkSubmitInfo submit_info = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .pNext = NULL, .waitSemaphoreCount = waitSemaphoreCount, .pWaitSemaphores = pWaitSemaphores, .pWaitDstStageMask = &stage_flags, .commandBufferCount = 1, .pCommandBuffers = &image->prime.blit_cmd_buffers[queue_family], .signalSemaphoreCount = 0, .pSignalSemaphores = NULL, }; return chain->wsi->QueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE); }