/* * 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" #include "vk_util.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(CreateFence); WSI_GET_CB(CreateImage); WSI_GET_CB(DestroyBuffer); WSI_GET_CB(DestroyCommandPool); WSI_GET_CB(DestroyFence); 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(GetImageSubresourceLayout); WSI_GET_CB(GetMemoryFdKHR); WSI_GET_CB(ResetFences); WSI_GET_CB(QueueSubmit); WSI_GET_CB(WaitForFences); #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); } VkResult wsi_create_native_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 struct wsi_image_create_info image_wsi_info = { .sType = VK_STRUCTURE_TYPE_WSI_IMAGE_CREATE_INFO_MESA, .pNext = NULL, .scanout = true, }; const VkImageCreateInfo image_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .pNext = &image_wsi_info, .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, .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; VkMemoryRequirements reqs; wsi->GetImageMemoryRequirements(chain->device, image->image, &reqs); VkSubresourceLayout image_layout; const VkImageSubresource image_subresource = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .mipLevel = 0, .arrayLayer = 0, }; wsi->GetImageSubresourceLayout(chain->device, image->image, &image_subresource, &image_layout); 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 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 memory_dedicated_info = { .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR, .pNext = &memory_export_info, .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; const VkMemoryGetFdInfoKHR memory_get_fd_info = { .sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR, .pNext = NULL, .memory = image->memory, .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, }; int fd; result = wsi->GetMemoryFdKHR(chain->device, &memory_get_fd_info, &fd); if (result != VK_SUCCESS) goto fail; image->size = reqs.size; image->row_pitch = image_layout.rowPitch; image->offset = 0; image->fd = fd; return VK_SUCCESS; fail: wsi_destroy_image(chain, image); return result; } #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_image(chain, image); return result; } void wsi_destroy_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); } VkResult wsi_common_queue_present(const struct wsi_device *wsi, VkDevice device, VkQueue queue, int queue_family_index, const VkPresentInfoKHR *pPresentInfo) { VkResult final_result = VK_SUCCESS; const VkPresentRegionsKHR *regions = vk_find_struct_const(pPresentInfo->pNext, PRESENT_REGIONS_KHR); for (uint32_t i = 0; i < pPresentInfo->swapchainCount; i++) { WSI_FROM_HANDLE(wsi_swapchain, swapchain, pPresentInfo->pSwapchains[i]); VkResult result; if (swapchain->fences[0] == VK_NULL_HANDLE) { const VkFenceCreateInfo fence_info = { .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, .pNext = NULL, .flags = 0, }; result = wsi->CreateFence(device, &fence_info, &swapchain->alloc, &swapchain->fences[0]); if (result != VK_SUCCESS) goto fail_present; } else { wsi->ResetFences(device, 1, &swapchain->fences[0]); } VkSubmitInfo submit_info = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .pNext = NULL, }; VkPipelineStageFlags *stage_flags = NULL; if (i == 0) { /* We only need/want to wait on semaphores once. After that, we're * guaranteed ordering since it all happens on the same queue. */ submit_info.waitSemaphoreCount = pPresentInfo->waitSemaphoreCount, submit_info.pWaitSemaphores = pPresentInfo->pWaitSemaphores, /* Set up the pWaitDstStageMasks */ stage_flags = vk_alloc(&swapchain->alloc, sizeof(VkPipelineStageFlags) * pPresentInfo->waitSemaphoreCount, 8, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND); if (!stage_flags) { result = VK_ERROR_OUT_OF_HOST_MEMORY; goto fail_present; } for (uint32_t s = 0; s < pPresentInfo->waitSemaphoreCount; s++) stage_flags[s] = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT; submit_info.pWaitDstStageMask = stage_flags; } result = wsi->QueueSubmit(queue, 1, &submit_info, swapchain->fences[0]); vk_free(&swapchain->alloc, stage_flags); if (result != VK_SUCCESS) goto fail_present; const VkPresentRegionKHR *region = NULL; if (regions && regions->pRegions) region = ®ions->pRegions[i]; result = swapchain->queue_present(swapchain, queue, pPresentInfo->waitSemaphoreCount, pPresentInfo->pWaitSemaphores, pPresentInfo->pImageIndices[i], region); if (result != VK_SUCCESS) goto fail_present; VkFence last = swapchain->fences[2]; swapchain->fences[2] = swapchain->fences[1]; swapchain->fences[1] = swapchain->fences[0]; swapchain->fences[0] = last; if (last != VK_NULL_HANDLE) { wsi->WaitForFences(device, 1, &last, true, 1); } fail_present: if (pPresentInfo->pResults != NULL) pPresentInfo->pResults[i] = result; /* Let the final result be our first unsuccessful result */ if (final_result == VK_SUCCESS) final_result = result; } return final_result; }