diff options
author | Jonathan Gray <jsg@cvs.openbsd.org> | 2019-01-29 11:08:07 +0000 |
---|---|---|
committer | Jonathan Gray <jsg@cvs.openbsd.org> | 2019-01-29 11:08:07 +0000 |
commit | 6b139c2063623e9310025247cd966490b9aa57ea (patch) | |
tree | 375acfd898ca3d721250aa17291bbb90a8d7250a /lib/mesa/src/vulkan | |
parent | cce99579dcfb1d54c54cff65573be3430e77f2c5 (diff) |
Import Mesa 18.3.2
Diffstat (limited to 'lib/mesa/src/vulkan')
-rw-r--r-- | lib/mesa/src/vulkan/meson.build | 27 | ||||
-rw-r--r-- | lib/mesa/src/vulkan/util/meson.build | 45 | ||||
-rw-r--r-- | lib/mesa/src/vulkan/util/vk_debug_report.c | 122 | ||||
-rw-r--r-- | lib/mesa/src/vulkan/util/vk_debug_report.h | 72 | ||||
-rw-r--r-- | lib/mesa/src/vulkan/wsi/meson.build | 69 | ||||
-rw-r--r-- | lib/mesa/src/vulkan/wsi/wsi_common.c | 1021 | ||||
-rw-r--r-- | lib/mesa/src/vulkan/wsi/wsi_common_display.c | 2490 | ||||
-rw-r--r-- | lib/mesa/src/vulkan/wsi/wsi_common_display.h | 163 | ||||
-rw-r--r-- | lib/mesa/src/vulkan/wsi/wsi_common_private.h | 174 |
9 files changed, 4183 insertions, 0 deletions
diff --git a/lib/mesa/src/vulkan/meson.build b/lib/mesa/src/vulkan/meson.build new file mode 100644 index 000000000..59e1fd1fa --- /dev/null +++ b/lib/mesa/src/vulkan/meson.build @@ -0,0 +1,27 @@ +# 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 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. + +vk_api_xml = files('registry/vk.xml') + +inc_vulkan_util = include_directories('util') +inc_vulkan_wsi = include_directories('wsi') + +subdir('util') +subdir('wsi') diff --git a/lib/mesa/src/vulkan/util/meson.build b/lib/mesa/src/vulkan/util/meson.build new file mode 100644 index 000000000..15e4ff491 --- /dev/null +++ b/lib/mesa/src/vulkan/util/meson.build @@ -0,0 +1,45 @@ +# 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 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. + +files_vulkan_util = files( + 'vk_alloc.h', + 'vk_debug_report.c', + 'vk_debug_report.h', + 'vk_util.c', + 'vk_util.h', +) + +vk_enum_to_str = custom_target( + 'vk_enum_to_str', + input : ['gen_enum_to_str.py', vk_api_xml[0]], + output : ['vk_enum_to_str.c', 'vk_enum_to_str.h'], + command : [ + prog_python, '@INPUT0@', '--xml', '@INPUT1@', '--outdir', + meson.current_build_dir() + ], +) + +libvulkan_util = static_library( + 'vulkan_util', + [files_vulkan_util, vk_enum_to_str], + include_directories : [inc_common, inc_vulkan], + c_args : [c_vis_args], + build_by_default : false, +) diff --git a/lib/mesa/src/vulkan/util/vk_debug_report.c b/lib/mesa/src/vulkan/util/vk_debug_report.c new file mode 100644 index 000000000..22ae4a7d9 --- /dev/null +++ b/lib/mesa/src/vulkan/util/vk_debug_report.c @@ -0,0 +1,122 @@ +/* + * 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 "vk_debug_report.h" + +#include "vk_alloc.h" +#include "vk_util.h" + +VkResult vk_debug_report_instance_init(struct vk_debug_report_instance *instance) +{ + if (pthread_mutex_init(&instance->callbacks_mutex, NULL) != 0) { + return VK_ERROR_INITIALIZATION_FAILED; + } + + list_inithead(&instance->callbacks); + + return VK_SUCCESS; +} + +void vk_debug_report_instance_destroy(struct vk_debug_report_instance *instance) +{ + pthread_mutex_destroy(&instance->callbacks_mutex); +} + +VkResult +vk_create_debug_report_callback(struct vk_debug_report_instance *instance, + const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + const VkAllocationCallbacks* instance_allocator, + VkDebugReportCallbackEXT* pCallback) +{ + + struct vk_debug_report_callback *cb = + vk_alloc2(instance_allocator, pAllocator, + sizeof(struct vk_debug_report_callback), 8, + VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); + + if (!cb) + return VK_ERROR_OUT_OF_HOST_MEMORY; + + cb->flags = pCreateInfo->flags; + cb->callback = pCreateInfo->pfnCallback; + cb->data = pCreateInfo->pUserData; + + pthread_mutex_lock(&instance->callbacks_mutex); + list_addtail(&cb->link, &instance->callbacks); + pthread_mutex_unlock(&instance->callbacks_mutex); + + *pCallback = (VkDebugReportCallbackEXT)(uintptr_t)cb; + + return VK_SUCCESS; +} + +void +vk_destroy_debug_report_callback(struct vk_debug_report_instance *instance, + VkDebugReportCallbackEXT _callback, + const VkAllocationCallbacks* pAllocator, + const VkAllocationCallbacks* instance_allocator) +{ + struct vk_debug_report_callback *callback = + (struct vk_debug_report_callback *)(uintptr_t)_callback; + + /* Remove from list and destroy given callback. */ + pthread_mutex_lock(&instance->callbacks_mutex); + list_del(&callback->link); + vk_free2(instance_allocator, pAllocator, callback); + pthread_mutex_unlock(&instance->callbacks_mutex); +} + + +void +vk_debug_report(struct vk_debug_report_instance *instance, + VkDebugReportFlagsEXT flags, + VkDebugReportObjectTypeEXT object_type, + uint64_t handle, + size_t location, + int32_t messageCode, + const char* pLayerPrefix, + const char *pMessage) +{ + /* Allow NULL for convinience, return if no callbacks registered. */ + if (!instance || list_empty(&instance->callbacks)) + return; + + pthread_mutex_lock(&instance->callbacks_mutex); + + /* Section 33.2 of the Vulkan 1.0.59 spec says: + * + * "callback is an externally synchronized object and must not be + * used on more than one thread at a time. This means that + * vkDestroyDebugReportCallbackEXT must not be called when a callback + * is active." + */ + list_for_each_entry(struct vk_debug_report_callback, cb, + &instance->callbacks, link) { + if (cb->flags & flags) + cb->callback(flags, object_type, handle, location, messageCode, + pLayerPrefix, pMessage, cb->data); + } + + pthread_mutex_unlock(&instance->callbacks_mutex); +} diff --git a/lib/mesa/src/vulkan/util/vk_debug_report.h b/lib/mesa/src/vulkan/util/vk_debug_report.h new file mode 100644 index 000000000..625ecbb69 --- /dev/null +++ b/lib/mesa/src/vulkan/util/vk_debug_report.h @@ -0,0 +1,72 @@ +/* + * Copyright © 2018, Google Inc. + * + * based on the anv driver which is: + * 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. + */ +#ifndef VK_DEBUG_REPORT_H +#define VK_DEBUG_REPORT_H + +#include <pthread.h> + +#include "util/list.h" +#include <vulkan/vulkan.h> + +struct vk_debug_report_callback { + /* Link in the 'callbacks' list in anv_instance struct. */ + struct list_head link; + VkDebugReportFlagsEXT flags; + PFN_vkDebugReportCallbackEXT callback; + void * data; +}; + +struct vk_debug_report_instance { + /* VK_EXT_debug_report debug callbacks */ + pthread_mutex_t callbacks_mutex; + struct list_head callbacks; +}; + +VkResult vk_debug_report_instance_init(struct vk_debug_report_instance *instance); +void vk_debug_report_instance_destroy(struct vk_debug_report_instance *instance); + +VkResult +vk_create_debug_report_callback(struct vk_debug_report_instance *instance, + const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + const VkAllocationCallbacks* instance_allocator, + VkDebugReportCallbackEXT* pCallback); +void +vk_destroy_debug_report_callback(struct vk_debug_report_instance *instance, + VkDebugReportCallbackEXT _callback, + const VkAllocationCallbacks* pAllocator, + const VkAllocationCallbacks* instance_allocator); + +void +vk_debug_report(struct vk_debug_report_instance *instance, + VkDebugReportFlagsEXT flags, + VkDebugReportObjectTypeEXT object_type, + uint64_t handle, + size_t location, + int32_t messageCode, + const char* pLayerPrefix, + const char *pMessage); +#endif diff --git a/lib/mesa/src/vulkan/wsi/meson.build b/lib/mesa/src/vulkan/wsi/meson.build new file mode 100644 index 000000000..e9812b663 --- /dev/null +++ b/lib/mesa/src/vulkan/wsi/meson.build @@ -0,0 +1,69 @@ +# 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 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. + +vulkan_wsi_args = [] +vulkan_wsi_deps = [] + +files_vulkan_wsi = files('wsi_common.c') + +if with_platform_x11 + vulkan_wsi_args += ['-DVK_USE_PLATFORM_XCB_KHR', '-DVK_USE_PLATFORM_XLIB_KHR'] + vulkan_wsi_deps += [ + dep_xcb, + dep_x11_xcb, + dep_xcb_dri2, + dep_xcb_dri3, + dep_xcb_present, + dep_xcb_sync, + dep_xshmfence, + ] + files_vulkan_wsi += files('wsi_common_x11.c') +endif + +if with_platform_wayland + vulkan_wsi_deps += dep_wayland_client + vulkan_wsi_args += ['-DVK_USE_PLATFORM_WAYLAND_KHR'] + files_vulkan_wsi += files('wsi_common_wayland.c') + files_vulkan_wsi += [ + wayland_drm_client_protocol_h, + wayland_drm_protocol_c, + linux_dmabuf_unstable_v1_client_protocol_h, + linux_dmabuf_unstable_v1_protocol_c, + ] +endif + +if with_platform_drm + vulkan_wsi_args += '-DVK_USE_PLATFORM_DISPLAY_KHR' + files_vulkan_wsi += files('wsi_common_display.c') +endif + +if with_xlib_lease + vulkan_wsi_deps += [dep_xcb_xrandr, dep_xlib_xrandr] + vulkan_wsi_args += '-DVK_USE_PLATFORM_XLIB_XRANDR_EXT' +endif + +libvulkan_wsi = static_library( + 'vulkan_wsi', + files_vulkan_wsi, + include_directories : [inc_common, inc_vulkan_util, inc_drm_uapi], + dependencies : [vulkan_wsi_deps, dep_libdrm], + c_args : [c_vis_args, vulkan_wsi_args], + build_by_default : false, +) diff --git a/lib/mesa/src/vulkan/wsi/wsi_common.c b/lib/mesa/src/vulkan/wsi/wsi_common.c new file mode 100644 index 000000000..58e252141 --- /dev/null +++ b/lib/mesa/src/vulkan/wsi/wsi_common.c @@ -0,0 +1,1021 @@ +/* + * 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 "drm_fourcc.h" +#include "util/macros.h" +#include "vk_util.h" + +#include <unistd.h> +#include <xf86drm.h> + +VkResult +wsi_device_init(struct wsi_device *wsi, + VkPhysicalDevice pdevice, + WSI_FN_GetPhysicalDeviceProcAddr proc_addr, + const VkAllocationCallbacks *alloc, + int display_fd) +{ + VkResult result; + + memset(wsi, 0, sizeof(*wsi)); + + wsi->instance_alloc = *alloc; + wsi->pdevice = pdevice; + +#define WSI_GET_CB(func) \ + PFN_vk##func func = (PFN_vk##func)proc_addr(pdevice, "vk" #func) + WSI_GET_CB(GetPhysicalDeviceProperties2); + WSI_GET_CB(GetPhysicalDeviceMemoryProperties); + WSI_GET_CB(GetPhysicalDeviceQueueFamilyProperties); +#undef WSI_GET_CB + + wsi->pci_bus_info.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT; + VkPhysicalDeviceProperties2 pdp2 = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, + .pNext = &wsi->pci_bus_info, + }; + GetPhysicalDeviceProperties2(pdevice, &pdp2); + + 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(GetPhysicalDeviceFormatProperties); + WSI_GET_CB(GetPhysicalDeviceFormatProperties2KHR); + WSI_GET_CB(ResetFences); + WSI_GET_CB(QueueSubmit); + WSI_GET_CB(WaitForFences); +#undef WSI_GET_CB + +#ifdef VK_USE_PLATFORM_XCB_KHR + result = wsi_x11_init_wsi(wsi, alloc); + if (result != VK_SUCCESS) + goto fail; +#endif + +#ifdef VK_USE_PLATFORM_WAYLAND_KHR + result = wsi_wl_init_wsi(wsi, alloc, pdevice); + if (result != VK_SUCCESS) + goto fail; +#endif + +#ifdef VK_USE_PLATFORM_DISPLAY_KHR + result = wsi_display_init_wsi(wsi, alloc, display_fd); + if (result != VK_SUCCESS) + goto fail; +#endif + + return VK_SUCCESS; + +fail: + wsi_device_finish(wsi, alloc); + return result; +} + +void +wsi_device_finish(struct wsi_device *wsi, + const VkAllocationCallbacks *alloc) +{ +#ifdef VK_USE_PLATFORM_DISPLAY_KHR + wsi_display_finish_wsi(wsi, alloc); +#endif +#ifdef VK_USE_PLATFORM_WAYLAND_KHR + wsi_wl_finish_wsi(wsi, alloc); +#endif +#ifdef VK_USE_PLATFORM_XCB_KHR + wsi_x11_finish_wsi(wsi, alloc); +#endif +} + +bool +wsi_device_matches_drm_fd(const struct wsi_device *wsi, int drm_fd) +{ + drmDevicePtr fd_device; + int ret = drmGetDevice2(drm_fd, 0, &fd_device); + if (ret) + return false; + + bool match = false; + switch (fd_device->bustype) { + case DRM_BUS_PCI: + match = wsi->pci_bus_info.pciDomain == fd_device->businfo.pci->domain && + wsi->pci_bus_info.pciBus == fd_device->businfo.pci->bus && + wsi->pci_bus_info.pciDevice == fd_device->businfo.pci->dev && + wsi->pci_bus_info.pciFunction == fd_device->businfo.pci->func; + break; + + default: + break; + } + + drmFreeDevice(&fd_device); + + return match; +} + +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->use_prime_blit = false; + + 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 (unsigned i = 0; i < ARRAY_SIZE(chain->fences); i++) + chain->wsi->DestroyFence(chain->device, chain->fences[i], &chain->alloc); + + for (uint32_t i = 0; i < chain->wsi->queue_family_count; i++) { + chain->wsi->DestroyCommandPool(chain->device, chain->cmd_pools[i], + &chain->alloc); + } + vk_free(&chain->alloc, chain->cmd_pools); +} + +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, + uint32_t num_modifier_lists, + const uint32_t *num_modifiers, + const uint64_t *const *modifiers, + struct wsi_image *image) +{ + const struct wsi_device *wsi = chain->wsi; + VkResult result; + + memset(image, 0, sizeof(*image)); + for (int i = 0; i < ARRAY_SIZE(image->fds); i++) + image->fds[i] = -1; + + struct wsi_image_create_info image_wsi_info = { + .sType = VK_STRUCTURE_TYPE_WSI_IMAGE_CREATE_INFO_MESA, + .pNext = NULL, + }; + + uint32_t image_modifier_count = 0, modifier_prop_count = 0; + struct wsi_format_modifier_properties *modifier_props = NULL; + uint64_t *image_modifiers = NULL; + if (num_modifier_lists == 0) { + /* If we don't have modifiers, fall back to the legacy "scanout" flag */ + image_wsi_info.scanout = true; + } else { + /* The winsys can't request modifiers if we don't support them. */ + assert(wsi->supports_modifiers); + struct wsi_format_modifier_properties_list modifier_props_list = { + .sType = VK_STRUCTURE_TYPE_WSI_FORMAT_MODIFIER_PROPERTIES_LIST_MESA, + .pNext = NULL, + }; + VkFormatProperties2KHR format_props = { + .sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR, + .pNext = &modifier_props_list, + }; + wsi->GetPhysicalDeviceFormatProperties2KHR(wsi->pdevice, + pCreateInfo->imageFormat, + &format_props); + assert(modifier_props_list.modifier_count > 0); + modifier_props = vk_alloc(&chain->alloc, + sizeof(*modifier_props) * + modifier_props_list.modifier_count, + 8, + VK_SYSTEM_ALLOCATION_SCOPE_COMMAND); + if (!modifier_props) { + result = VK_ERROR_OUT_OF_HOST_MEMORY; + goto fail; + } + + modifier_props_list.modifier_properties = modifier_props; + wsi->GetPhysicalDeviceFormatProperties2KHR(wsi->pdevice, + pCreateInfo->imageFormat, + &format_props); + modifier_prop_count = modifier_props_list.modifier_count; + + uint32_t max_modifier_count = 0; + for (uint32_t l = 0; l < num_modifier_lists; l++) + max_modifier_count = MAX2(max_modifier_count, num_modifiers[l]); + + image_modifiers = vk_alloc(&chain->alloc, + sizeof(*image_modifiers) * + max_modifier_count, + 8, + VK_SYSTEM_ALLOCATION_SCOPE_COMMAND); + if (!image_modifiers) { + result = VK_ERROR_OUT_OF_HOST_MEMORY; + goto fail; + } + + image_modifier_count = 0; + for (uint32_t l = 0; l < num_modifier_lists; l++) { + /* Walk the modifier lists and construct a list of supported + * modifiers. + */ + for (uint32_t i = 0; i < num_modifiers[l]; i++) { + for (uint32_t j = 0; j < modifier_prop_count; j++) { + if (modifier_props[j].modifier == modifiers[l][i]) + image_modifiers[image_modifier_count++] = modifiers[l][i]; + } + } + + /* We only want to take the modifiers from the first list */ + if (image_modifier_count > 0) + break; + } + + if (image_modifier_count > 0) { + image_wsi_info.modifier_count = image_modifier_count; + image_wsi_info.modifiers = image_modifiers; + } else { + /* TODO: Add a proper error here */ + assert(!"Failed to find a supported modifier! This should never " + "happen because LINEAR should always be available"); + result = VK_ERROR_OUT_OF_HOST_MEMORY; + goto fail; + } + } + + 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); + + 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; + + if (num_modifier_lists > 0) { + image->drm_modifier = wsi->image_get_modifier(image->image); + assert(image->drm_modifier != DRM_FORMAT_MOD_INVALID); + + for (uint32_t j = 0; j < modifier_prop_count; j++) { + if (modifier_props[j].modifier == image->drm_modifier) { + image->num_planes = modifier_props[j].modifier_plane_count; + break; + } + } + + for (uint32_t p = 0; p < image->num_planes; p++) { + const VkImageSubresource image_subresource = { + .aspectMask = VK_IMAGE_ASPECT_PLANE_0_BIT_KHR << p, + .mipLevel = 0, + .arrayLayer = 0, + }; + VkSubresourceLayout image_layout; + wsi->GetImageSubresourceLayout(chain->device, image->image, + &image_subresource, &image_layout); + image->sizes[p] = image_layout.size; + image->row_pitches[p] = image_layout.rowPitch; + image->offsets[p] = image_layout.offset; + if (p == 0) { + image->fds[p] = fd; + } else { + image->fds[p] = dup(fd); + if (image->fds[p] == -1) { + for (uint32_t i = 0; i < p; i++) + close(image->fds[p]); + + goto fail; + } + } + } + } else { + const VkImageSubresource image_subresource = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0, + .arrayLayer = 0, + }; + VkSubresourceLayout image_layout; + wsi->GetImageSubresourceLayout(chain->device, image->image, + &image_subresource, &image_layout); + + image->drm_modifier = DRM_FORMAT_MOD_INVALID; + image->num_planes = 1; + image->sizes[0] = reqs.size; + image->row_pitches[0] = image_layout.rowPitch; + image->offsets[0] = 0; + image->fds[0] = fd; + } + + vk_free(&chain->alloc, modifier_props); + vk_free(&chain->alloc, image_modifiers); + + return VK_SUCCESS; + +fail: + vk_free(&chain->alloc, modifier_props); + vk_free(&chain->alloc, image_modifiers); + 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, + bool use_modifier, + 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) { + result = VK_ERROR_OUT_OF_HOST_MEMORY; + 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->drm_modifier = use_modifier ? DRM_FORMAT_MOD_LINEAR : DRM_FORMAT_MOD_INVALID; + image->num_planes = 1; + image->sizes[0] = linear_size; + image->row_pitches[0] = linear_stride; + image->offsets[0] = 0; + image->fds[0] = 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_common_get_surface_support(struct wsi_device *wsi_device, + uint32_t queueFamilyIndex, + VkSurfaceKHR _surface, + VkBool32* pSupported) +{ + ICD_FROM_HANDLE(VkIcdSurfaceBase, surface, _surface); + struct wsi_interface *iface = wsi_device->wsi[surface->platform]; + + return iface->get_support(surface, wsi_device, + queueFamilyIndex, pSupported); +} + +VkResult +wsi_common_get_surface_capabilities(struct wsi_device *wsi_device, + VkSurfaceKHR _surface, + VkSurfaceCapabilitiesKHR *pSurfaceCapabilities) +{ + ICD_FROM_HANDLE(VkIcdSurfaceBase, surface, _surface); + struct wsi_interface *iface = wsi_device->wsi[surface->platform]; + + VkSurfaceCapabilities2KHR caps2 = { + .sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR, + }; + + VkResult result = iface->get_capabilities2(surface, NULL, &caps2); + + if (result == VK_SUCCESS) + *pSurfaceCapabilities = caps2.surfaceCapabilities; + + return result; +} + +VkResult +wsi_common_get_surface_capabilities2(struct wsi_device *wsi_device, + const VkPhysicalDeviceSurfaceInfo2KHR *pSurfaceInfo, + VkSurfaceCapabilities2KHR *pSurfaceCapabilities) +{ + ICD_FROM_HANDLE(VkIcdSurfaceBase, surface, pSurfaceInfo->surface); + struct wsi_interface *iface = wsi_device->wsi[surface->platform]; + + return iface->get_capabilities2(surface, pSurfaceInfo->pNext, + pSurfaceCapabilities); +} + +VkResult +wsi_common_get_surface_capabilities2ext( + struct wsi_device *wsi_device, + VkSurfaceKHR _surface, + VkSurfaceCapabilities2EXT *pSurfaceCapabilities) +{ + ICD_FROM_HANDLE(VkIcdSurfaceBase, surface, _surface); + struct wsi_interface *iface = wsi_device->wsi[surface->platform]; + + assert(pSurfaceCapabilities->sType == + VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_EXT); + + struct wsi_surface_supported_counters counters = { + .sType = VK_STRUCTURE_TYPE_WSI_SURFACE_SUPPORTED_COUNTERS_MESA, + .pNext = pSurfaceCapabilities->pNext, + .supported_surface_counters = 0, + }; + + VkSurfaceCapabilities2KHR caps2 = { + .sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR, + .pNext = &counters, + }; + + VkResult result = iface->get_capabilities2(surface, NULL, &caps2); + + if (result == VK_SUCCESS) { + VkSurfaceCapabilities2EXT *ext_caps = pSurfaceCapabilities; + VkSurfaceCapabilitiesKHR khr_caps = caps2.surfaceCapabilities; + + ext_caps->minImageCount = khr_caps.minImageCount; + ext_caps->maxImageCount = khr_caps.maxImageCount; + ext_caps->currentExtent = khr_caps.currentExtent; + ext_caps->minImageExtent = khr_caps.minImageExtent; + ext_caps->maxImageExtent = khr_caps.maxImageExtent; + ext_caps->maxImageArrayLayers = khr_caps.maxImageArrayLayers; + ext_caps->supportedTransforms = khr_caps.supportedTransforms; + ext_caps->currentTransform = khr_caps.currentTransform; + ext_caps->supportedCompositeAlpha = khr_caps.supportedCompositeAlpha; + ext_caps->supportedUsageFlags = khr_caps.supportedUsageFlags; + ext_caps->supportedSurfaceCounters = counters.supported_surface_counters; + } + + return result; +} + +VkResult +wsi_common_get_surface_formats(struct wsi_device *wsi_device, + VkSurfaceKHR _surface, + uint32_t *pSurfaceFormatCount, + VkSurfaceFormatKHR *pSurfaceFormats) +{ + ICD_FROM_HANDLE(VkIcdSurfaceBase, surface, _surface); + struct wsi_interface *iface = wsi_device->wsi[surface->platform]; + + return iface->get_formats(surface, wsi_device, + pSurfaceFormatCount, pSurfaceFormats); +} + +VkResult +wsi_common_get_surface_formats2(struct wsi_device *wsi_device, + const VkPhysicalDeviceSurfaceInfo2KHR *pSurfaceInfo, + uint32_t *pSurfaceFormatCount, + VkSurfaceFormat2KHR *pSurfaceFormats) +{ + ICD_FROM_HANDLE(VkIcdSurfaceBase, surface, pSurfaceInfo->surface); + struct wsi_interface *iface = wsi_device->wsi[surface->platform]; + + return iface->get_formats2(surface, wsi_device, pSurfaceInfo->pNext, + pSurfaceFormatCount, pSurfaceFormats); +} + +VkResult +wsi_common_get_surface_present_modes(struct wsi_device *wsi_device, + VkSurfaceKHR _surface, + uint32_t *pPresentModeCount, + VkPresentModeKHR *pPresentModes) +{ + ICD_FROM_HANDLE(VkIcdSurfaceBase, surface, _surface); + struct wsi_interface *iface = wsi_device->wsi[surface->platform]; + + return iface->get_present_modes(surface, pPresentModeCount, + pPresentModes); +} + +VkResult +wsi_common_get_present_rectangles(struct wsi_device *wsi_device, + VkSurfaceKHR _surface, + uint32_t* pRectCount, + VkRect2D* pRects) +{ + ICD_FROM_HANDLE(VkIcdSurfaceBase, surface, _surface); + struct wsi_interface *iface = wsi_device->wsi[surface->platform]; + + return iface->get_present_rectangles(surface, wsi_device, + pRectCount, pRects); +} + +VkResult +wsi_common_create_swapchain(struct wsi_device *wsi, + VkDevice device, + const VkSwapchainCreateInfoKHR *pCreateInfo, + const VkAllocationCallbacks *pAllocator, + VkSwapchainKHR *pSwapchain) +{ + ICD_FROM_HANDLE(VkIcdSurfaceBase, surface, pCreateInfo->surface); + struct wsi_interface *iface = wsi->wsi[surface->platform]; + struct wsi_swapchain *swapchain; + + VkResult result = iface->create_swapchain(surface, device, wsi, + pCreateInfo, pAllocator, + &swapchain); + if (result != VK_SUCCESS) + return result; + + *pSwapchain = wsi_swapchain_to_handle(swapchain); + + return VK_SUCCESS; +} + +void +wsi_common_destroy_swapchain(VkDevice device, + VkSwapchainKHR _swapchain, + const VkAllocationCallbacks *pAllocator) +{ + WSI_FROM_HANDLE(wsi_swapchain, swapchain, _swapchain); + if (!swapchain) + return; + + swapchain->destroy(swapchain, pAllocator); +} + +VkResult +wsi_common_get_images(VkSwapchainKHR _swapchain, + uint32_t *pSwapchainImageCount, + VkImage *pSwapchainImages) +{ + WSI_FROM_HANDLE(wsi_swapchain, swapchain, _swapchain); + VK_OUTARRAY_MAKE(images, pSwapchainImages, pSwapchainImageCount); + + for (uint32_t i = 0; i < swapchain->image_count; i++) { + vk_outarray_append(&images, image) { + *image = swapchain->get_wsi_image(swapchain, i)->image; + } + } + + return vk_outarray_status(&images); +} + +VkResult +wsi_common_acquire_next_image2(const struct wsi_device *wsi, + VkDevice device, + const VkAcquireNextImageInfoKHR *pAcquireInfo, + uint32_t *pImageIndex) +{ + WSI_FROM_HANDLE(wsi_swapchain, swapchain, pAcquireInfo->swapchain); + + return swapchain->acquire_next_image(swapchain, pAcquireInfo, pImageIndex); +} + +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; + } + + if (swapchain->use_prime_blit) { + /* If we are using prime blits, we need to perform the blit now. The + * command buffer is attached to the image. + */ + struct wsi_image *image = + swapchain->get_wsi_image(swapchain, pPresentInfo->pImageIndices[i]); + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = + &image->prime.blit_cmd_buffers[queue_family_index]; + } + + 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, + 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; +} diff --git a/lib/mesa/src/vulkan/wsi/wsi_common_display.c b/lib/mesa/src/vulkan/wsi/wsi_common_display.c new file mode 100644 index 000000000..856040b4f --- /dev/null +++ b/lib/mesa/src/vulkan/wsi/wsi_common_display.c @@ -0,0 +1,2490 @@ +/* + * Copyright © 2017 Keith Packard + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include "util/macros.h" +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <fcntl.h> +#include <poll.h> +#include <stdbool.h> +#include <math.h> +#include <xf86drm.h> +#include <xf86drmMode.h> +#include <drm_fourcc.h> +#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT +#include <xcb/randr.h> +#include <X11/Xlib-xcb.h> +#endif +#include "util/hash_table.h" +#include "util/list.h" + +#include "vk_util.h" +#include "wsi_common_private.h" +#include "wsi_common_display.h" +#include "wsi_common_queue.h" + +#if 0 +#define wsi_display_debug(...) fprintf(stderr, __VA_ARGS__) +#define wsi_display_debug_code(...) __VA_ARGS__ +#else +#define wsi_display_debug(...) +#define wsi_display_debug_code(...) +#endif + +/* These have lifetime equal to the instance, so they effectively + * never go away. This means we must keep track of them separately + * from all other resources. + */ +typedef struct wsi_display_mode { + struct list_head list; + struct wsi_display_connector *connector; + bool valid; /* was found in most recent poll */ + bool preferred; + uint32_t clock; /* in kHz */ + uint16_t hdisplay, hsync_start, hsync_end, htotal, hskew; + uint16_t vdisplay, vsync_start, vsync_end, vtotal, vscan; + uint32_t flags; +} wsi_display_mode; + +typedef struct wsi_display_connector { + struct list_head list; + struct wsi_display *wsi; + uint32_t id; + uint32_t crtc_id; + char *name; + bool connected; + bool active; + struct list_head display_modes; + wsi_display_mode *current_mode; + drmModeModeInfo current_drm_mode; + uint32_t dpms_property; +#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT + xcb_randr_output_t output; +#endif +} wsi_display_connector; + +struct wsi_display { + struct wsi_interface base; + + const VkAllocationCallbacks *alloc; + + int fd; + + pthread_mutex_t wait_mutex; + pthread_cond_t wait_cond; + pthread_t wait_thread; + + struct list_head connectors; +}; + +#define wsi_for_each_display_mode(_mode, _conn) \ + list_for_each_entry_safe(struct wsi_display_mode, _mode, \ + &(_conn)->display_modes, list) + +#define wsi_for_each_connector(_conn, _dev) \ + list_for_each_entry_safe(struct wsi_display_connector, _conn, \ + &(_dev)->connectors, list) + +enum wsi_image_state { + WSI_IMAGE_IDLE, + WSI_IMAGE_DRAWING, + WSI_IMAGE_QUEUED, + WSI_IMAGE_FLIPPING, + WSI_IMAGE_DISPLAYING +}; + +struct wsi_display_image { + struct wsi_image base; + struct wsi_display_swapchain *chain; + enum wsi_image_state state; + uint32_t fb_id; + uint32_t buffer[4]; + uint64_t flip_sequence; +}; + +struct wsi_display_swapchain { + struct wsi_swapchain base; + struct wsi_display *wsi; + VkIcdSurfaceDisplay *surface; + uint64_t flip_sequence; + VkResult status; + struct wsi_display_image images[0]; +}; + +struct wsi_display_fence { + struct wsi_fence base; + bool event_received; + bool destroyed; + uint64_t sequence; +}; + +static uint64_t fence_sequence; + +ICD_DEFINE_NONDISP_HANDLE_CASTS(wsi_display_mode, VkDisplayModeKHR) +ICD_DEFINE_NONDISP_HANDLE_CASTS(wsi_display_connector, VkDisplayKHR) + +static bool +wsi_display_mode_matches_drm(wsi_display_mode *wsi, + drmModeModeInfoPtr drm) +{ + return wsi->clock == drm->clock && + wsi->hdisplay == drm->hdisplay && + wsi->hsync_start == drm->hsync_start && + wsi->hsync_end == drm->hsync_end && + wsi->htotal == drm->htotal && + wsi->hskew == drm->hskew && + wsi->vdisplay == drm->vdisplay && + wsi->vsync_start == drm->vsync_start && + wsi->vsync_end == drm->vsync_end && + wsi->vtotal == drm->vtotal && + MAX2(wsi->vscan, 1) == MAX2(drm->vscan, 1) && + wsi->flags == drm->flags; +} + +static double +wsi_display_mode_refresh(struct wsi_display_mode *wsi) +{ + return (double) wsi->clock * 1000.0 / ((double) wsi->htotal * + (double) wsi->vtotal * + (double) MAX2(wsi->vscan, 1)); +} + +static uint64_t wsi_get_current_monotonic(void) +{ + struct timespec tv; + + clock_gettime(CLOCK_MONOTONIC, &tv); + return tv.tv_nsec + tv.tv_sec*1000000000ull; +} + +static uint64_t wsi_rel_to_abs_time(uint64_t rel_time) +{ + uint64_t current_time = wsi_get_current_monotonic(); + + /* check for overflow */ + if (rel_time > UINT64_MAX - current_time) + return UINT64_MAX; + + return current_time + rel_time; +} + +static struct wsi_display_mode * +wsi_display_find_drm_mode(struct wsi_device *wsi_device, + struct wsi_display_connector *connector, + drmModeModeInfoPtr mode) +{ + wsi_for_each_display_mode(display_mode, connector) { + if (wsi_display_mode_matches_drm(display_mode, mode)) + return display_mode; + } + return NULL; +} + +static void +wsi_display_invalidate_connector_modes(struct wsi_device *wsi_device, + struct wsi_display_connector *connector) +{ + wsi_for_each_display_mode(display_mode, connector) { + display_mode->valid = false; + } +} + +static VkResult +wsi_display_register_drm_mode(struct wsi_device *wsi_device, + struct wsi_display_connector *connector, + drmModeModeInfoPtr drm_mode) +{ + struct wsi_display *wsi = + (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + struct wsi_display_mode *display_mode = + wsi_display_find_drm_mode(wsi_device, connector, drm_mode); + + if (display_mode) { + display_mode->valid = true; + return VK_SUCCESS; + } + + display_mode = vk_zalloc(wsi->alloc, sizeof (struct wsi_display_mode), + 8, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); + if (!display_mode) + return VK_ERROR_OUT_OF_HOST_MEMORY; + + display_mode->connector = connector; + display_mode->valid = true; + display_mode->preferred = (drm_mode->type & DRM_MODE_TYPE_PREFERRED) != 0; + display_mode->clock = drm_mode->clock; /* kHz */ + display_mode->hdisplay = drm_mode->hdisplay; + display_mode->hsync_start = drm_mode->hsync_start; + display_mode->hsync_end = drm_mode->hsync_end; + display_mode->htotal = drm_mode->htotal; + display_mode->hskew = drm_mode->hskew; + display_mode->vdisplay = drm_mode->vdisplay; + display_mode->vsync_start = drm_mode->vsync_start; + display_mode->vsync_end = drm_mode->vsync_end; + display_mode->vtotal = drm_mode->vtotal; + display_mode->vscan = drm_mode->vscan; + display_mode->flags = drm_mode->flags; + + list_addtail(&display_mode->list, &connector->display_modes); + return VK_SUCCESS; +} + +/* + * Update our information about a specific connector + */ + +static struct wsi_display_connector * +wsi_display_find_connector(struct wsi_device *wsi_device, + uint32_t connector_id) +{ + struct wsi_display *wsi = + (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + + wsi_for_each_connector(connector, wsi) { + if (connector->id == connector_id) + return connector; + } + + return NULL; +} + +static struct wsi_display_connector * +wsi_display_alloc_connector(struct wsi_display *wsi, + uint32_t connector_id) +{ + struct wsi_display_connector *connector = + vk_zalloc(wsi->alloc, sizeof (struct wsi_display_connector), + 8, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); + + connector->id = connector_id; + connector->wsi = wsi; + connector->active = false; + /* XXX use EDID name */ + connector->name = "monitor"; + list_inithead(&connector->display_modes); + return connector; +} + +static struct wsi_display_connector * +wsi_display_get_connector(struct wsi_device *wsi_device, + uint32_t connector_id) +{ + struct wsi_display *wsi = + (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + + if (wsi->fd < 0) + return NULL; + + drmModeConnectorPtr drm_connector = + drmModeGetConnector(wsi->fd, connector_id); + + if (!drm_connector) + return NULL; + + struct wsi_display_connector *connector = + wsi_display_find_connector(wsi_device, connector_id); + + if (!connector) { + connector = wsi_display_alloc_connector(wsi, connector_id); + if (!connector) { + drmModeFreeConnector(drm_connector); + return NULL; + } + list_addtail(&connector->list, &wsi->connectors); + } + + connector->connected = drm_connector->connection != DRM_MODE_DISCONNECTED; + + /* Look for a DPMS property if we haven't already found one */ + for (int p = 0; connector->dpms_property == 0 && + p < drm_connector->count_props; p++) + { + drmModePropertyPtr prop = drmModeGetProperty(wsi->fd, + drm_connector->props[p]); + if (!prop) + continue; + if (prop->flags & DRM_MODE_PROP_ENUM) { + if (!strcmp(prop->name, "DPMS")) + connector->dpms_property = drm_connector->props[p]; + } + drmModeFreeProperty(prop); + } + + /* Mark all connector modes as invalid */ + wsi_display_invalidate_connector_modes(wsi_device, connector); + + /* + * List current modes, adding new ones and marking existing ones as + * valid + */ + for (int m = 0; m < drm_connector->count_modes; m++) { + VkResult result = wsi_display_register_drm_mode(wsi_device, + connector, + &drm_connector->modes[m]); + if (result != VK_SUCCESS) { + drmModeFreeConnector(drm_connector); + return NULL; + } + } + + drmModeFreeConnector(drm_connector); + + return connector; +} + +#define MM_PER_PIXEL (1.0/96.0 * 25.4) + +static uint32_t +mode_size(struct wsi_display_mode *mode) +{ + /* fortunately, these are both uint16_t, so this is easy */ + return (uint32_t) mode->hdisplay * (uint32_t) mode->vdisplay; +} + +static void +wsi_display_fill_in_display_properties(struct wsi_device *wsi_device, + struct wsi_display_connector *connector, + VkDisplayProperties2KHR *properties2) +{ + assert(properties2->sType == VK_STRUCTURE_TYPE_DISPLAY_PROPERTIES_2_KHR); + VkDisplayPropertiesKHR *properties = &properties2->displayProperties; + + properties->display = wsi_display_connector_to_handle(connector); + properties->displayName = connector->name; + + /* Find the first preferred mode and assume that's the physical + * resolution. If there isn't a preferred mode, find the largest mode and + * use that. + */ + + struct wsi_display_mode *preferred_mode = NULL, *largest_mode = NULL; + wsi_for_each_display_mode(display_mode, connector) { + if (!display_mode->valid) + continue; + if (display_mode->preferred) { + preferred_mode = display_mode; + break; + } + if (largest_mode == NULL || + mode_size(display_mode) > mode_size(largest_mode)) + { + largest_mode = display_mode; + } + } + + if (preferred_mode) { + properties->physicalResolution.width = preferred_mode->hdisplay; + properties->physicalResolution.height = preferred_mode->vdisplay; + } else if (largest_mode) { + properties->physicalResolution.width = largest_mode->hdisplay; + properties->physicalResolution.height = largest_mode->vdisplay; + } else { + properties->physicalResolution.width = 1024; + properties->physicalResolution.height = 768; + } + + /* Make up physical size based on 96dpi */ + properties->physicalDimensions.width = + floor(properties->physicalResolution.width * MM_PER_PIXEL + 0.5); + properties->physicalDimensions.height = + floor(properties->physicalResolution.height * MM_PER_PIXEL + 0.5); + + properties->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + properties->planeReorderPossible = VK_FALSE; + properties->persistentContent = VK_FALSE; +} + +/* + * Implement vkGetPhysicalDeviceDisplayPropertiesKHR (VK_KHR_display) + */ +VkResult +wsi_display_get_physical_device_display_properties( + VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + uint32_t *property_count, + VkDisplayPropertiesKHR *properties) +{ + struct wsi_display *wsi = + (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + + if (properties == NULL) { + return wsi_display_get_physical_device_display_properties2( + physical_device, wsi_device, property_count, NULL); + } else { + /* If we're actually returning properties, allocate a temporary array of + * VkDisplayProperties2KHR structs, call properties2 to fill them out, + * and then copy them to the client. This seems a bit expensive but + * wsi_display_get_physical_device_display_properties2() calls + * drmModeGetResources() which does an ioctl and then a bunch of + * allocations so this should get lost in the noise. + */ + VkDisplayProperties2KHR *props2 = + vk_zalloc(wsi->alloc, sizeof(*props2) * *property_count, 8, + VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); + if (props2 == NULL) + return VK_ERROR_OUT_OF_HOST_MEMORY; + + for (uint32_t i = 0; i < *property_count; i++) + props2[i].sType = VK_STRUCTURE_TYPE_DISPLAY_PROPERTIES_2_KHR; + + VkResult result = wsi_display_get_physical_device_display_properties2( + physical_device, wsi_device, property_count, props2); + + if (result == VK_SUCCESS || result == VK_INCOMPLETE) { + for (uint32_t i = 0; i < *property_count; i++) + properties[i] = props2[i].displayProperties; + } + + vk_free(wsi->alloc, props2); + + return result; + } +} + +VkResult +wsi_display_get_physical_device_display_properties2( + VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + uint32_t *property_count, + VkDisplayProperties2KHR *properties) +{ + struct wsi_display *wsi = + (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + + if (wsi->fd < 0) + goto bail; + + drmModeResPtr mode_res = drmModeGetResources(wsi->fd); + + if (!mode_res) + goto bail; + + VK_OUTARRAY_MAKE(conn, properties, property_count); + + /* Get current information */ + + for (int c = 0; c < mode_res->count_connectors; c++) { + struct wsi_display_connector *connector = + wsi_display_get_connector(wsi_device, mode_res->connectors[c]); + + if (!connector) { + drmModeFreeResources(mode_res); + return VK_ERROR_OUT_OF_HOST_MEMORY; + } + + if (connector->connected) { + vk_outarray_append(&conn, prop) { + wsi_display_fill_in_display_properties(wsi_device, + connector, + prop); + } + } + } + + drmModeFreeResources(mode_res); + + return vk_outarray_status(&conn); + +bail: + *property_count = 0; + return VK_SUCCESS; +} + +/* + * Implement vkGetPhysicalDeviceDisplayPlanePropertiesKHR (VK_KHR_display + */ +static void +wsi_display_fill_in_display_plane_properties( + struct wsi_device *wsi_device, + struct wsi_display_connector *connector, + VkDisplayPlaneProperties2KHR *properties) +{ + assert(properties->sType == VK_STRUCTURE_TYPE_DISPLAY_PLANE_PROPERTIES_2_KHR); + VkDisplayPlanePropertiesKHR *prop = &properties->displayPlaneProperties; + + if (connector && connector->active) { + prop->currentDisplay = wsi_display_connector_to_handle(connector); + prop->currentStackIndex = 0; + } else { + prop->currentDisplay = VK_NULL_HANDLE; + prop->currentStackIndex = 0; + } +} + +VkResult +wsi_display_get_physical_device_display_plane_properties( + VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + uint32_t *property_count, + VkDisplayPlanePropertiesKHR *properties) +{ + struct wsi_display *wsi = + (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + + VK_OUTARRAY_MAKE(conn, properties, property_count); + + wsi_for_each_connector(connector, wsi) { + vk_outarray_append(&conn, prop) { + VkDisplayPlaneProperties2KHR prop2 = { + .sType = VK_STRUCTURE_TYPE_DISPLAY_PLANE_PROPERTIES_2_KHR, + }; + wsi_display_fill_in_display_plane_properties(wsi_device, connector, + &prop2); + *prop = prop2.displayPlaneProperties; + } + } + return vk_outarray_status(&conn); +} + +VkResult +wsi_display_get_physical_device_display_plane_properties2( + VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + uint32_t *property_count, + VkDisplayPlaneProperties2KHR *properties) +{ + struct wsi_display *wsi = + (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + + VK_OUTARRAY_MAKE(conn, properties, property_count); + + wsi_for_each_connector(connector, wsi) { + vk_outarray_append(&conn, prop) { + wsi_display_fill_in_display_plane_properties(wsi_device, connector, + prop); + } + } + return vk_outarray_status(&conn); +} + +/* + * Implement vkGetDisplayPlaneSupportedDisplaysKHR (VK_KHR_display) + */ + +VkResult +wsi_display_get_display_plane_supported_displays( + VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + uint32_t plane_index, + uint32_t *display_count, + VkDisplayKHR *displays) +{ + struct wsi_display *wsi = + (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + + VK_OUTARRAY_MAKE(conn, displays, display_count); + + int c = 0; + + wsi_for_each_connector(connector, wsi) { + if (c == plane_index && connector->connected) { + vk_outarray_append(&conn, display) { + *display = wsi_display_connector_to_handle(connector); + } + } + c++; + } + return vk_outarray_status(&conn); +} + +/* + * Implement vkGetDisplayModePropertiesKHR (VK_KHR_display) + */ + +static void +wsi_display_fill_in_display_mode_properties( + struct wsi_device *wsi_device, + struct wsi_display_mode *display_mode, + VkDisplayModeProperties2KHR *properties) +{ + assert(properties->sType == VK_STRUCTURE_TYPE_DISPLAY_MODE_PROPERTIES_2_KHR); + VkDisplayModePropertiesKHR *prop = &properties->displayModeProperties; + + prop->displayMode = wsi_display_mode_to_handle(display_mode); + prop->parameters.visibleRegion.width = display_mode->hdisplay; + prop->parameters.visibleRegion.height = display_mode->vdisplay; + prop->parameters.refreshRate = + (uint32_t) (wsi_display_mode_refresh(display_mode) * 1000 + 0.5); +} + +VkResult +wsi_display_get_display_mode_properties(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + VkDisplayKHR display, + uint32_t *property_count, + VkDisplayModePropertiesKHR *properties) +{ + struct wsi_display_connector *connector = + wsi_display_connector_from_handle(display); + + VK_OUTARRAY_MAKE(conn, properties, property_count); + + wsi_for_each_display_mode(display_mode, connector) { + if (!display_mode->valid) + continue; + + vk_outarray_append(&conn, prop) { + VkDisplayModeProperties2KHR prop2 = { + .sType = VK_STRUCTURE_TYPE_DISPLAY_MODE_PROPERTIES_2_KHR, + }; + wsi_display_fill_in_display_mode_properties(wsi_device, + display_mode, &prop2); + *prop = prop2.displayModeProperties; + } + } + return vk_outarray_status(&conn); +} + +VkResult +wsi_display_get_display_mode_properties2(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + VkDisplayKHR display, + uint32_t *property_count, + VkDisplayModeProperties2KHR *properties) +{ + struct wsi_display_connector *connector = + wsi_display_connector_from_handle(display); + + VK_OUTARRAY_MAKE(conn, properties, property_count); + + wsi_for_each_display_mode(display_mode, connector) { + if (!display_mode->valid) + continue; + + vk_outarray_append(&conn, prop) { + wsi_display_fill_in_display_mode_properties(wsi_device, + display_mode, prop); + } + } + return vk_outarray_status(&conn); +} + +static bool +wsi_display_mode_matches_vk(wsi_display_mode *wsi, + const VkDisplayModeParametersKHR *vk) +{ + return (vk->visibleRegion.width == wsi->hdisplay && + vk->visibleRegion.height == wsi->vdisplay && + fabs(wsi_display_mode_refresh(wsi) * 1000.0 - vk->refreshRate) < 10); +} + +/* + * Implement vkCreateDisplayModeKHR (VK_KHR_display) + */ +VkResult +wsi_display_create_display_mode(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + VkDisplayKHR display, + const VkDisplayModeCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, + VkDisplayModeKHR *mode) +{ + struct wsi_display_connector *connector = + wsi_display_connector_from_handle(display); + + if (create_info->flags != 0) + return VK_ERROR_INITIALIZATION_FAILED; + + /* Check and see if the requested mode happens to match an existing one and + * return that. This makes the conformance suite happy. Doing more than + * this would involve embedding the CVT function into the driver, which seems + * excessive. + */ + wsi_for_each_display_mode(display_mode, connector) { + if (display_mode->valid) { + if (wsi_display_mode_matches_vk(display_mode, &create_info->parameters)) { + *mode = wsi_display_mode_to_handle(display_mode); + return VK_SUCCESS; + } + } + } + return VK_ERROR_INITIALIZATION_FAILED; +} + +/* + * Implement vkGetDisplayPlaneCapabilities + */ +VkResult +wsi_get_display_plane_capabilities(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + VkDisplayModeKHR mode_khr, + uint32_t plane_index, + VkDisplayPlaneCapabilitiesKHR *capabilities) +{ + struct wsi_display_mode *mode = wsi_display_mode_from_handle(mode_khr); + + /* XXX use actual values */ + capabilities->supportedAlpha = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR; + capabilities->minSrcPosition.x = 0; + capabilities->minSrcPosition.y = 0; + capabilities->maxSrcPosition.x = 0; + capabilities->maxSrcPosition.y = 0; + capabilities->minSrcExtent.width = mode->hdisplay; + capabilities->minSrcExtent.height = mode->vdisplay; + capabilities->maxSrcExtent.width = mode->hdisplay; + capabilities->maxSrcExtent.height = mode->vdisplay; + capabilities->minDstPosition.x = 0; + capabilities->minDstPosition.y = 0; + capabilities->maxDstPosition.x = 0; + capabilities->maxDstPosition.y = 0; + capabilities->minDstExtent.width = mode->hdisplay; + capabilities->minDstExtent.height = mode->vdisplay; + capabilities->maxDstExtent.width = mode->hdisplay; + capabilities->maxDstExtent.height = mode->vdisplay; + return VK_SUCCESS; +} + +VkResult +wsi_get_display_plane_capabilities2( + VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + const VkDisplayPlaneInfo2KHR *pDisplayPlaneInfo, + VkDisplayPlaneCapabilities2KHR *capabilities) +{ + assert(capabilities->sType == + VK_STRUCTURE_TYPE_DISPLAY_PLANE_CAPABILITIES_2_KHR); + + return wsi_get_display_plane_capabilities(physical_device, wsi_device, + pDisplayPlaneInfo->mode, + pDisplayPlaneInfo->planeIndex, + &capabilities->capabilities); +} + +VkResult +wsi_create_display_surface(VkInstance instance, + const VkAllocationCallbacks *allocator, + const VkDisplaySurfaceCreateInfoKHR *create_info, + VkSurfaceKHR *surface_khr) +{ + VkIcdSurfaceDisplay *surface = vk_zalloc(allocator, sizeof *surface, 8, + VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); + + if (surface == NULL) + return VK_ERROR_OUT_OF_HOST_MEMORY; + + surface->base.platform = VK_ICD_WSI_PLATFORM_DISPLAY; + + surface->displayMode = create_info->displayMode; + surface->planeIndex = create_info->planeIndex; + surface->planeStackIndex = create_info->planeStackIndex; + surface->transform = create_info->transform; + surface->globalAlpha = create_info->globalAlpha; + surface->alphaMode = create_info->alphaMode; + surface->imageExtent = create_info->imageExtent; + + *surface_khr = VkIcdSurfaceBase_to_handle(&surface->base); + return VK_SUCCESS; +} + + +static VkResult +wsi_display_surface_get_support(VkIcdSurfaceBase *surface, + struct wsi_device *wsi_device, + uint32_t queueFamilyIndex, + VkBool32* pSupported) +{ + *pSupported = VK_TRUE; + return VK_SUCCESS; +} + +static VkResult +wsi_display_surface_get_capabilities(VkIcdSurfaceBase *surface_base, + VkSurfaceCapabilitiesKHR* caps) +{ + VkIcdSurfaceDisplay *surface = (VkIcdSurfaceDisplay *) surface_base; + wsi_display_mode *mode = wsi_display_mode_from_handle(surface->displayMode); + + caps->currentExtent.width = mode->hdisplay; + caps->currentExtent.height = mode->vdisplay; + + /* XXX Figure out extents based on driver capabilities */ + caps->maxImageExtent = caps->minImageExtent = caps->currentExtent; + + caps->supportedCompositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + + caps->minImageCount = 2; + caps->maxImageCount = 0; + + caps->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + caps->currentTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + caps->maxImageArrayLayers = 1; + caps->supportedUsageFlags = + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | + VK_IMAGE_USAGE_SAMPLED_BIT | + VK_IMAGE_USAGE_TRANSFER_DST_BIT | + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + + return VK_SUCCESS; +} + +static VkResult +wsi_display_surface_get_surface_counters( + VkIcdSurfaceBase *surface_base, + VkSurfaceCounterFlagsEXT *counters) +{ + *counters = VK_SURFACE_COUNTER_VBLANK_EXT; + return VK_SUCCESS; +} + +static VkResult +wsi_display_surface_get_capabilities2(VkIcdSurfaceBase *icd_surface, + const void *info_next, + VkSurfaceCapabilities2KHR *caps) +{ + assert(caps->sType == VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR); + VkResult result; + + result = wsi_display_surface_get_capabilities(icd_surface, + &caps->surfaceCapabilities); + if (result != VK_SUCCESS) + return result; + + struct wsi_surface_supported_counters *counters = + vk_find_struct( caps->pNext, WSI_SURFACE_SUPPORTED_COUNTERS_MESA); + + if (counters) { + result = wsi_display_surface_get_surface_counters( + icd_surface, + &counters->supported_surface_counters); + } + + return result; +} + +static const struct { + VkFormat format; + uint32_t drm_format; +} available_surface_formats[] = { + { .format = VK_FORMAT_B8G8R8A8_SRGB, .drm_format = DRM_FORMAT_XRGB8888 }, + { .format = VK_FORMAT_B8G8R8A8_UNORM, .drm_format = DRM_FORMAT_XRGB8888 }, +}; + +static VkResult +wsi_display_surface_get_formats(VkIcdSurfaceBase *icd_surface, + struct wsi_device *wsi_device, + uint32_t *surface_format_count, + VkSurfaceFormatKHR *surface_formats) +{ + VK_OUTARRAY_MAKE(out, surface_formats, surface_format_count); + + for (unsigned i = 0; i < ARRAY_SIZE(available_surface_formats); i++) { + vk_outarray_append(&out, f) { + f->format = available_surface_formats[i].format; + f->colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; + } + } + + return vk_outarray_status(&out); +} + +static VkResult +wsi_display_surface_get_formats2(VkIcdSurfaceBase *surface, + struct wsi_device *wsi_device, + const void *info_next, + uint32_t *surface_format_count, + VkSurfaceFormat2KHR *surface_formats) +{ + VK_OUTARRAY_MAKE(out, surface_formats, surface_format_count); + + for (unsigned i = 0; i < ARRAY_SIZE(available_surface_formats); i++) { + vk_outarray_append(&out, f) { + assert(f->sType == VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR); + f->surfaceFormat.format = available_surface_formats[i].format; + f->surfaceFormat.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; + } + } + + return vk_outarray_status(&out); +} + +static VkResult +wsi_display_surface_get_present_modes(VkIcdSurfaceBase *surface, + uint32_t *present_mode_count, + VkPresentModeKHR *present_modes) +{ + VK_OUTARRAY_MAKE(conn, present_modes, present_mode_count); + + vk_outarray_append(&conn, present) { + *present = VK_PRESENT_MODE_FIFO_KHR; + } + + return vk_outarray_status(&conn); +} + +static VkResult +wsi_display_surface_get_present_rectangles(VkIcdSurfaceBase *surface_base, + struct wsi_device *wsi_device, + uint32_t* pRectCount, + VkRect2D* pRects) +{ + VkIcdSurfaceDisplay *surface = (VkIcdSurfaceDisplay *) surface_base; + wsi_display_mode *mode = wsi_display_mode_from_handle(surface->displayMode); + VK_OUTARRAY_MAKE(out, pRects, pRectCount); + + if (wsi_device_matches_drm_fd(wsi_device, mode->connector->wsi->fd)) { + vk_outarray_append(&out, rect) { + *rect = (VkRect2D) { + .offset = { 0, 0 }, + .extent = { mode->hdisplay, mode->vdisplay }, + }; + } + } + + return vk_outarray_status(&out); +} + +static void +wsi_display_destroy_buffer(struct wsi_display *wsi, + uint32_t buffer) +{ + (void) drmIoctl(wsi->fd, DRM_IOCTL_MODE_DESTROY_DUMB, + &((struct drm_mode_destroy_dumb) { .handle = buffer })); +} + +static VkResult +wsi_display_image_init(VkDevice device_h, + struct wsi_swapchain *drv_chain, + const VkSwapchainCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, + struct wsi_display_image *image) +{ + struct wsi_display_swapchain *chain = + (struct wsi_display_swapchain *) drv_chain; + struct wsi_display *wsi = chain->wsi; + uint32_t drm_format = 0; + + for (unsigned i = 0; i < ARRAY_SIZE(available_surface_formats); i++) { + if (create_info->imageFormat == available_surface_formats[i].format) { + drm_format = available_surface_formats[i].drm_format; + break; + } + } + + /* the application provided an invalid format, bail */ + if (drm_format == 0) + return VK_ERROR_DEVICE_LOST; + + VkResult result = wsi_create_native_image(&chain->base, create_info, + 0, NULL, NULL, + &image->base); + if (result != VK_SUCCESS) + return result; + + memset(image->buffer, 0, sizeof (image->buffer)); + + for (unsigned int i = 0; i < image->base.num_planes; i++) { + int ret = drmPrimeFDToHandle(wsi->fd, image->base.fds[i], + &image->buffer[i]); + + close(image->base.fds[i]); + image->base.fds[i] = -1; + if (ret < 0) + goto fail_handle; + } + + image->chain = chain; + image->state = WSI_IMAGE_IDLE; + image->fb_id = 0; + + int ret = drmModeAddFB2(wsi->fd, + create_info->imageExtent.width, + create_info->imageExtent.height, + drm_format, + image->buffer, + image->base.row_pitches, + image->base.offsets, + &image->fb_id, 0); + + if (ret) + goto fail_fb; + + return VK_SUCCESS; + +fail_fb: +fail_handle: + for (unsigned int i = 0; i < image->base.num_planes; i++) { + if (image->buffer[i]) + wsi_display_destroy_buffer(wsi, image->buffer[i]); + if (image->base.fds[i] != -1) { + close(image->base.fds[i]); + image->base.fds[i] = -1; + } + } + + wsi_destroy_image(&chain->base, &image->base); + + return VK_ERROR_OUT_OF_HOST_MEMORY; +} + +static void +wsi_display_image_finish(struct wsi_swapchain *drv_chain, + const VkAllocationCallbacks *allocator, + struct wsi_display_image *image) +{ + struct wsi_display_swapchain *chain = + (struct wsi_display_swapchain *) drv_chain; + struct wsi_display *wsi = chain->wsi; + + drmModeRmFB(wsi->fd, image->fb_id); + for (unsigned int i = 0; i < image->base.num_planes; i++) + wsi_display_destroy_buffer(wsi, image->buffer[i]); + wsi_destroy_image(&chain->base, &image->base); +} + +static VkResult +wsi_display_swapchain_destroy(struct wsi_swapchain *drv_chain, + const VkAllocationCallbacks *allocator) +{ + struct wsi_display_swapchain *chain = + (struct wsi_display_swapchain *) drv_chain; + + for (uint32_t i = 0; i < chain->base.image_count; i++) + wsi_display_image_finish(drv_chain, allocator, &chain->images[i]); + + wsi_swapchain_finish(&chain->base); + vk_free(allocator, chain); + return VK_SUCCESS; +} + +static struct wsi_image * +wsi_display_get_wsi_image(struct wsi_swapchain *drv_chain, + uint32_t image_index) +{ + struct wsi_display_swapchain *chain = + (struct wsi_display_swapchain *) drv_chain; + + return &chain->images[image_index].base; +} + +static void +wsi_display_idle_old_displaying(struct wsi_display_image *active_image) +{ + struct wsi_display_swapchain *chain = active_image->chain; + + wsi_display_debug("idle everyone but %ld\n", + active_image - &(chain->images[0])); + for (uint32_t i = 0; i < chain->base.image_count; i++) + if (chain->images[i].state == WSI_IMAGE_DISPLAYING && + &chain->images[i] != active_image) + { + wsi_display_debug("idle %d\n", i); + chain->images[i].state = WSI_IMAGE_IDLE; + } +} + +static VkResult +_wsi_display_queue_next(struct wsi_swapchain *drv_chain); + +static void +wsi_display_page_flip_handler2(int fd, + unsigned int frame, + unsigned int sec, + unsigned int usec, + uint32_t crtc_id, + void *data) +{ + struct wsi_display_image *image = data; + struct wsi_display_swapchain *chain = image->chain; + + wsi_display_debug("image %ld displayed at %d\n", + image - &(image->chain->images[0]), frame); + image->state = WSI_IMAGE_DISPLAYING; + wsi_display_idle_old_displaying(image); + VkResult result = _wsi_display_queue_next(&(chain->base)); + if (result != VK_SUCCESS) + chain->status = result; +} + +static void wsi_display_fence_event_handler(struct wsi_display_fence *fence); + +static void wsi_display_page_flip_handler(int fd, + unsigned int frame, + unsigned int sec, + unsigned int usec, + void *data) +{ + wsi_display_page_flip_handler2(fd, frame, sec, usec, 0, data); +} + +static void wsi_display_vblank_handler(int fd, unsigned int frame, + unsigned int sec, unsigned int usec, + void *data) +{ + struct wsi_display_fence *fence = data; + + wsi_display_fence_event_handler(fence); +} + +static void wsi_display_sequence_handler(int fd, uint64_t frame, + uint64_t nsec, uint64_t user_data) +{ + struct wsi_display_fence *fence = + (struct wsi_display_fence *) (uintptr_t) user_data; + + wsi_display_fence_event_handler(fence); +} + +static drmEventContext event_context = { + .version = DRM_EVENT_CONTEXT_VERSION, + .page_flip_handler = wsi_display_page_flip_handler, +#if DRM_EVENT_CONTEXT_VERSION >= 3 + .page_flip_handler2 = wsi_display_page_flip_handler2, +#endif + .vblank_handler = wsi_display_vblank_handler, + .sequence_handler = wsi_display_sequence_handler, +}; + +static void * +wsi_display_wait_thread(void *data) +{ + struct wsi_display *wsi = data; + struct pollfd pollfd = { + .fd = wsi->fd, + .events = POLLIN + }; + + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + for (;;) { + int ret = poll(&pollfd, 1, -1); + if (ret > 0) { + pthread_mutex_lock(&wsi->wait_mutex); + (void) drmHandleEvent(wsi->fd, &event_context); + pthread_mutex_unlock(&wsi->wait_mutex); + pthread_cond_broadcast(&wsi->wait_cond); + } + } + return NULL; +} + +static int +wsi_display_start_wait_thread(struct wsi_display *wsi) +{ + if (!wsi->wait_thread) { + int ret = pthread_create(&wsi->wait_thread, NULL, + wsi_display_wait_thread, wsi); + if (ret) + return ret; + } + return 0; +} + +/* + * Wait for at least one event from the kernel to be processed. + * Call with wait_mutex held + */ +static int +wsi_display_wait_for_event(struct wsi_display *wsi, + uint64_t timeout_ns) +{ + int ret; + + ret = wsi_display_start_wait_thread(wsi); + + if (ret) + return ret; + + struct timespec abs_timeout = { + .tv_sec = timeout_ns / 1000000000ULL, + .tv_nsec = timeout_ns % 1000000000ULL, + }; + + ret = pthread_cond_timedwait(&wsi->wait_cond, &wsi->wait_mutex, + &abs_timeout); + + wsi_display_debug("%9ld done waiting for event %d\n", pthread_self(), ret); + return ret; +} + +static VkResult +wsi_display_acquire_next_image(struct wsi_swapchain *drv_chain, + const VkAcquireNextImageInfoKHR *info, + uint32_t *image_index) +{ + struct wsi_display_swapchain *chain = + (struct wsi_display_swapchain *)drv_chain; + struct wsi_display *wsi = chain->wsi; + int ret = 0; + VkResult result = VK_SUCCESS; + + /* Bail early if the swapchain is broken */ + if (chain->status != VK_SUCCESS) + return chain->status; + + uint64_t timeout = info->timeout; + if (timeout != 0 && timeout != UINT64_MAX) + timeout = wsi_rel_to_abs_time(timeout); + + pthread_mutex_lock(&wsi->wait_mutex); + for (;;) { + for (uint32_t i = 0; i < chain->base.image_count; i++) { + if (chain->images[i].state == WSI_IMAGE_IDLE) { + *image_index = i; + wsi_display_debug("image %d available\n", i); + chain->images[i].state = WSI_IMAGE_DRAWING; + result = VK_SUCCESS; + goto done; + } + wsi_display_debug("image %d state %d\n", i, chain->images[i].state); + } + + if (ret == ETIMEDOUT) { + result = VK_TIMEOUT; + goto done; + } + + ret = wsi_display_wait_for_event(wsi, timeout); + + if (ret && ret != ETIMEDOUT) { + result = VK_ERROR_SURFACE_LOST_KHR; + goto done; + } + } +done: + pthread_mutex_unlock(&wsi->wait_mutex); + + if (result != VK_SUCCESS) + return result; + + return chain->status; +} + +/* + * Check whether there are any other connectors driven by this crtc + */ +static bool +wsi_display_crtc_solo(struct wsi_display *wsi, + drmModeResPtr mode_res, + drmModeConnectorPtr connector, + uint32_t crtc_id) +{ + /* See if any other connectors share the same encoder */ + for (int c = 0; c < mode_res->count_connectors; c++) { + if (mode_res->connectors[c] == connector->connector_id) + continue; + + drmModeConnectorPtr other_connector = + drmModeGetConnector(wsi->fd, mode_res->connectors[c]); + + if (other_connector) { + bool match = (other_connector->encoder_id == connector->encoder_id); + drmModeFreeConnector(other_connector); + if (match) + return false; + } + } + + /* See if any other encoders share the same crtc */ + for (int e = 0; e < mode_res->count_encoders; e++) { + if (mode_res->encoders[e] == connector->encoder_id) + continue; + + drmModeEncoderPtr other_encoder = + drmModeGetEncoder(wsi->fd, mode_res->encoders[e]); + + if (other_encoder) { + bool match = (other_encoder->crtc_id == crtc_id); + drmModeFreeEncoder(other_encoder); + if (match) + return false; + } + } + return true; +} + +/* + * Pick a suitable CRTC to drive this connector. Prefer a CRTC which is + * currently driving this connector and not any others. Settle for a CRTC + * which is currently idle. + */ +static uint32_t +wsi_display_select_crtc(const struct wsi_display_connector *connector, + drmModeResPtr mode_res, + drmModeConnectorPtr drm_connector) +{ + struct wsi_display *wsi = connector->wsi; + + /* See what CRTC is currently driving this connector */ + if (drm_connector->encoder_id) { + drmModeEncoderPtr encoder = + drmModeGetEncoder(wsi->fd, drm_connector->encoder_id); + + if (encoder) { + uint32_t crtc_id = encoder->crtc_id; + drmModeFreeEncoder(encoder); + if (crtc_id) { + if (wsi_display_crtc_solo(wsi, mode_res, drm_connector, crtc_id)) + return crtc_id; + } + } + } + uint32_t crtc_id = 0; + for (int c = 0; crtc_id == 0 && c < mode_res->count_crtcs; c++) { + drmModeCrtcPtr crtc = drmModeGetCrtc(wsi->fd, mode_res->crtcs[c]); + if (crtc && crtc->buffer_id == 0) + crtc_id = crtc->crtc_id; + drmModeFreeCrtc(crtc); + } + return crtc_id; +} + +static VkResult +wsi_display_setup_connector(wsi_display_connector *connector, + wsi_display_mode *display_mode) +{ + struct wsi_display *wsi = connector->wsi; + + if (connector->current_mode == display_mode && connector->crtc_id) + return VK_SUCCESS; + + VkResult result = VK_SUCCESS; + + drmModeResPtr mode_res = drmModeGetResources(wsi->fd); + if (!mode_res) { + if (errno == ENOMEM) + result = VK_ERROR_OUT_OF_HOST_MEMORY; + else + result = VK_ERROR_SURFACE_LOST_KHR; + goto bail; + } + + drmModeConnectorPtr drm_connector = + drmModeGetConnectorCurrent(wsi->fd, connector->id); + + if (!drm_connector) { + if (errno == ENOMEM) + result = VK_ERROR_OUT_OF_HOST_MEMORY; + else + result = VK_ERROR_SURFACE_LOST_KHR; + goto bail_mode_res; + } + + /* Pick a CRTC if we don't have one */ + if (!connector->crtc_id) { + connector->crtc_id = wsi_display_select_crtc(connector, + mode_res, drm_connector); + if (!connector->crtc_id) { + result = VK_ERROR_SURFACE_LOST_KHR; + goto bail_connector; + } + } + + if (connector->current_mode != display_mode) { + + /* Find the drm mode corresponding to the requested VkDisplayMode */ + drmModeModeInfoPtr drm_mode = NULL; + + for (int m = 0; m < drm_connector->count_modes; m++) { + drm_mode = &drm_connector->modes[m]; + if (wsi_display_mode_matches_drm(display_mode, drm_mode)) + break; + drm_mode = NULL; + } + + if (!drm_mode) { + result = VK_ERROR_SURFACE_LOST_KHR; + goto bail_connector; + } + + connector->current_mode = display_mode; + connector->current_drm_mode = *drm_mode; + } + +bail_connector: + drmModeFreeConnector(drm_connector); +bail_mode_res: + drmModeFreeResources(mode_res); +bail: + return result; + +} + +static VkResult +wsi_display_fence_wait(struct wsi_fence *fence_wsi, uint64_t timeout) +{ + const struct wsi_device *wsi_device = fence_wsi->wsi_device; + struct wsi_display *wsi = + (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + struct wsi_display_fence *fence = (struct wsi_display_fence *) fence_wsi; + + wsi_display_debug("%9lu wait fence %lu %ld\n", + pthread_self(), fence->sequence, + (int64_t) (timeout - wsi_get_current_monotonic())); + wsi_display_debug_code(uint64_t start_ns = wsi_get_current_monotonic()); + pthread_mutex_lock(&wsi->wait_mutex); + + VkResult result; + int ret = 0; + for (;;) { + if (fence->event_received) { + wsi_display_debug("%9lu fence %lu passed\n", + pthread_self(), fence->sequence); + result = VK_SUCCESS; + break; + } + + if (ret == ETIMEDOUT) { + wsi_display_debug("%9lu fence %lu timeout\n", + pthread_self(), fence->sequence); + result = VK_TIMEOUT; + break; + } + + ret = wsi_display_wait_for_event(wsi, timeout); + + if (ret && ret != ETIMEDOUT) { + wsi_display_debug("%9lu fence %lu error\n", + pthread_self(), fence->sequence); + result = VK_ERROR_DEVICE_LOST; + break; + } + } + pthread_mutex_unlock(&wsi->wait_mutex); + wsi_display_debug("%9lu fence wait %f ms\n", + pthread_self(), + ((int64_t) (wsi_get_current_monotonic() - start_ns)) / + 1.0e6); + return result; +} + +static void +wsi_display_fence_check_free(struct wsi_display_fence *fence) +{ + if (fence->event_received && fence->destroyed) + vk_free(fence->base.alloc, fence); +} + +static void wsi_display_fence_event_handler(struct wsi_display_fence *fence) +{ + fence->event_received = true; + wsi_display_fence_check_free(fence); +} + +static void +wsi_display_fence_destroy(struct wsi_fence *fence_wsi) +{ + struct wsi_display_fence *fence = (struct wsi_display_fence *) fence_wsi; + + assert(!fence->destroyed); + fence->destroyed = true; + wsi_display_fence_check_free(fence); +} + +static struct wsi_display_fence * +wsi_display_fence_alloc(VkDevice device, + const struct wsi_device *wsi_device, + VkDisplayKHR display, + const VkAllocationCallbacks *allocator) +{ + struct wsi_display *wsi = + (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + struct wsi_display_fence *fence = + vk_zalloc2(wsi->alloc, allocator, sizeof (*fence), + 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); + + if (!fence) + return NULL; + + fence->base.device = device; + fence->base.display = display; + fence->base.wsi_device = wsi_device; + fence->base.alloc = allocator ? allocator : wsi->alloc; + fence->base.wait = wsi_display_fence_wait; + fence->base.destroy = wsi_display_fence_destroy; + fence->event_received = false; + fence->destroyed = false; + fence->sequence = ++fence_sequence; + return fence; +} + +static VkResult +wsi_register_vblank_event(struct wsi_display_fence *fence, + const struct wsi_device *wsi_device, + VkDisplayKHR display, + uint32_t flags, + uint64_t frame_requested, + uint64_t *frame_queued) +{ + struct wsi_display *wsi = + (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + struct wsi_display_connector *connector = + wsi_display_connector_from_handle(display); + + if (wsi->fd < 0) + return VK_ERROR_INITIALIZATION_FAILED; + + for (;;) { + int ret = drmCrtcQueueSequence(wsi->fd, connector->crtc_id, + flags, + frame_requested, + frame_queued, + (uintptr_t) fence); + + if (!ret) + return VK_SUCCESS; + + if (errno != ENOMEM) { + + /* Something unexpected happened. Pause for a moment so the + * application doesn't just spin and then return a failure indication + */ + + wsi_display_debug("queue vblank event %lu failed\n", fence->sequence); + struct timespec delay = { + .tv_sec = 0, + .tv_nsec = 100000000ull, + }; + nanosleep(&delay, NULL); + return VK_ERROR_OUT_OF_HOST_MEMORY; + } + + /* The kernel event queue is full. Wait for some events to be + * processed and try again + */ + + pthread_mutex_lock(&wsi->wait_mutex); + ret = wsi_display_wait_for_event(wsi, wsi_rel_to_abs_time(100000000ull)); + pthread_mutex_unlock(&wsi->wait_mutex); + + if (ret) { + wsi_display_debug("vblank queue full, event wait failed\n"); + return VK_ERROR_OUT_OF_HOST_MEMORY; + } + } +} + +/* + * Check to see if the kernel has no flip queued and if there's an image + * waiting to be displayed. + */ +static VkResult +_wsi_display_queue_next(struct wsi_swapchain *drv_chain) +{ + struct wsi_display_swapchain *chain = + (struct wsi_display_swapchain *) drv_chain; + struct wsi_display *wsi = chain->wsi; + VkIcdSurfaceDisplay *surface = chain->surface; + wsi_display_mode *display_mode = + wsi_display_mode_from_handle(surface->displayMode); + wsi_display_connector *connector = display_mode->connector; + + if (wsi->fd < 0) + return VK_ERROR_SURFACE_LOST_KHR; + + if (display_mode != connector->current_mode) + connector->active = false; + + for (;;) { + + /* Check to see if there is an image to display, or if some image is + * already queued */ + + struct wsi_display_image *image = NULL; + + for (uint32_t i = 0; i < chain->base.image_count; i++) { + struct wsi_display_image *tmp_image = &chain->images[i]; + + switch (tmp_image->state) { + case WSI_IMAGE_FLIPPING: + /* already flipping, don't send another to the kernel yet */ + return VK_SUCCESS; + case WSI_IMAGE_QUEUED: + /* find the oldest queued */ + if (!image || tmp_image->flip_sequence < image->flip_sequence) + image = tmp_image; + break; + default: + break; + } + } + + if (!image) + return VK_SUCCESS; + + int ret; + if (connector->active) { + ret = drmModePageFlip(wsi->fd, connector->crtc_id, image->fb_id, + DRM_MODE_PAGE_FLIP_EVENT, image); + if (ret == 0) { + image->state = WSI_IMAGE_FLIPPING; + return VK_SUCCESS; + } + wsi_display_debug("page flip err %d %s\n", ret, strerror(-ret)); + } else { + ret = -EINVAL; + } + + if (ret == -EINVAL) { + VkResult result = wsi_display_setup_connector(connector, display_mode); + + if (result != VK_SUCCESS) { + image->state = WSI_IMAGE_IDLE; + return result; + } + + /* XXX allow setting of position */ + ret = drmModeSetCrtc(wsi->fd, connector->crtc_id, + image->fb_id, 0, 0, + &connector->id, 1, + &connector->current_drm_mode); + if (ret == 0) { + /* Assume that the mode set is synchronous and that any + * previous image is now idle. + */ + image->state = WSI_IMAGE_DISPLAYING; + wsi_display_idle_old_displaying(image); + connector->active = true; + return VK_SUCCESS; + } + } + + if (ret != -EACCES) { + connector->active = false; + image->state = WSI_IMAGE_IDLE; + return VK_ERROR_SURFACE_LOST_KHR; + } + + /* Some other VT is currently active. Sit here waiting for + * our VT to become active again by polling once a second + */ + usleep(1000 * 1000); + connector->active = false; + } +} + +static VkResult +wsi_display_queue_present(struct wsi_swapchain *drv_chain, + uint32_t image_index, + const VkPresentRegionKHR *damage) +{ + struct wsi_display_swapchain *chain = + (struct wsi_display_swapchain *) drv_chain; + struct wsi_display *wsi = chain->wsi; + struct wsi_display_image *image = &chain->images[image_index]; + VkResult result; + + /* Bail early if the swapchain is broken */ + if (chain->status != VK_SUCCESS) + return chain->status; + + assert(image->state == WSI_IMAGE_DRAWING); + wsi_display_debug("present %d\n", image_index); + + pthread_mutex_lock(&wsi->wait_mutex); + + image->flip_sequence = ++chain->flip_sequence; + image->state = WSI_IMAGE_QUEUED; + + result = _wsi_display_queue_next(drv_chain); + if (result != VK_SUCCESS) + chain->status = result; + + pthread_mutex_unlock(&wsi->wait_mutex); + + if (result != VK_SUCCESS) + return result; + + return chain->status; +} + +static VkResult +wsi_display_surface_create_swapchain( + VkIcdSurfaceBase *icd_surface, + VkDevice device, + struct wsi_device *wsi_device, + const VkSwapchainCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, + struct wsi_swapchain **swapchain_out) +{ + struct wsi_display *wsi = + (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + + assert(create_info->sType == VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR); + + const unsigned num_images = create_info->minImageCount; + struct wsi_display_swapchain *chain = + vk_zalloc(allocator, + sizeof(*chain) + num_images * sizeof(chain->images[0]), + 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); + + if (chain == NULL) + return VK_ERROR_OUT_OF_HOST_MEMORY; + + VkResult result = wsi_swapchain_init(wsi_device, &chain->base, device, + create_info, allocator); + if (result != VK_SUCCESS) { + vk_free(allocator, chain); + return result; + } + + chain->base.destroy = wsi_display_swapchain_destroy; + chain->base.get_wsi_image = wsi_display_get_wsi_image; + chain->base.acquire_next_image = wsi_display_acquire_next_image; + chain->base.queue_present = wsi_display_queue_present; + chain->base.present_mode = create_info->presentMode; + chain->base.image_count = num_images; + + chain->wsi = wsi; + chain->status = VK_SUCCESS; + + chain->surface = (VkIcdSurfaceDisplay *) icd_surface; + + for (uint32_t image = 0; image < chain->base.image_count; image++) { + result = wsi_display_image_init(device, &chain->base, + create_info, allocator, + &chain->images[image]); + if (result != VK_SUCCESS) { + while (image > 0) { + --image; + wsi_display_image_finish(&chain->base, allocator, + &chain->images[image]); + } + vk_free(allocator, chain); + goto fail_init_images; + } + } + + *swapchain_out = &chain->base; + + return VK_SUCCESS; + +fail_init_images: + return result; +} + +static bool +wsi_init_pthread_cond_monotonic(pthread_cond_t *cond) +{ + pthread_condattr_t condattr; + bool ret = false; + + if (pthread_condattr_init(&condattr) != 0) + goto fail_attr_init; + + if (pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC) != 0) + goto fail_attr_set; + + if (pthread_cond_init(cond, &condattr) != 0) + goto fail_cond_init; + + ret = true; + +fail_cond_init: +fail_attr_set: + pthread_condattr_destroy(&condattr); +fail_attr_init: + return ret; +} + +VkResult +wsi_display_init_wsi(struct wsi_device *wsi_device, + const VkAllocationCallbacks *alloc, + int display_fd) +{ + struct wsi_display *wsi = vk_zalloc(alloc, sizeof(*wsi), 8, + VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); + VkResult result; + + if (!wsi) { + result = VK_ERROR_OUT_OF_HOST_MEMORY; + goto fail; + } + + wsi->fd = display_fd; + wsi->alloc = alloc; + + list_inithead(&wsi->connectors); + + int ret = pthread_mutex_init(&wsi->wait_mutex, NULL); + if (ret) { + result = VK_ERROR_OUT_OF_HOST_MEMORY; + goto fail_mutex; + } + + if (!wsi_init_pthread_cond_monotonic(&wsi->wait_cond)) { + result = VK_ERROR_OUT_OF_HOST_MEMORY; + goto fail_cond; + } + + wsi->base.get_support = wsi_display_surface_get_support; + wsi->base.get_capabilities2 = wsi_display_surface_get_capabilities2; + wsi->base.get_formats = wsi_display_surface_get_formats; + wsi->base.get_formats2 = wsi_display_surface_get_formats2; + wsi->base.get_present_modes = wsi_display_surface_get_present_modes; + wsi->base.get_present_rectangles = wsi_display_surface_get_present_rectangles; + wsi->base.create_swapchain = wsi_display_surface_create_swapchain; + + wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY] = &wsi->base; + + return VK_SUCCESS; + +fail_cond: + pthread_mutex_destroy(&wsi->wait_mutex); +fail_mutex: + vk_free(alloc, wsi); +fail: + return result; +} + +void +wsi_display_finish_wsi(struct wsi_device *wsi_device, + const VkAllocationCallbacks *alloc) +{ + struct wsi_display *wsi = + (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + + if (wsi) { + wsi_for_each_connector(connector, wsi) { + wsi_for_each_display_mode(mode, connector) { + vk_free(wsi->alloc, mode); + } + vk_free(wsi->alloc, connector); + } + + pthread_mutex_lock(&wsi->wait_mutex); + if (wsi->wait_thread) { + pthread_cancel(wsi->wait_thread); + pthread_join(wsi->wait_thread, NULL); + } + pthread_mutex_unlock(&wsi->wait_mutex); + pthread_mutex_destroy(&wsi->wait_mutex); + pthread_cond_destroy(&wsi->wait_cond); + + vk_free(alloc, wsi); + } +} + +/* + * Implement vkReleaseDisplay + */ +VkResult +wsi_release_display(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + VkDisplayKHR display) +{ + struct wsi_display *wsi = + (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + + if (wsi->fd >= 0) { + close(wsi->fd); + wsi->fd = -1; + } +#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT + wsi_display_connector_from_handle(display)->output = None; +#endif + + return VK_SUCCESS; +} + +#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT + +static struct wsi_display_connector * +wsi_display_find_output(struct wsi_device *wsi_device, + xcb_randr_output_t output) +{ + struct wsi_display *wsi = + (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + + wsi_for_each_connector(connector, wsi) { + if (connector->output == output) + return connector; + } + + return NULL; +} + +/* + * Given a RandR output, find the associated kernel connector_id by + * looking at the CONNECTOR_ID property provided by the X server + */ + +static uint32_t +wsi_display_output_to_connector_id(xcb_connection_t *connection, + xcb_atom_t *connector_id_atom_p, + xcb_randr_output_t output) +{ + uint32_t connector_id = 0; + xcb_atom_t connector_id_atom = *connector_id_atom_p; + + if (connector_id_atom == 0) { + /* Go dig out the CONNECTOR_ID property */ + xcb_intern_atom_cookie_t ia_c = xcb_intern_atom(connection, + true, + 12, + "CONNECTOR_ID"); + xcb_intern_atom_reply_t *ia_r = xcb_intern_atom_reply(connection, + ia_c, + NULL); + if (ia_r) { + *connector_id_atom_p = connector_id_atom = ia_r->atom; + free(ia_r); + } + } + + /* If there's an CONNECTOR_ID atom in the server, then there may be a + * CONNECTOR_ID property. Otherwise, there will not be and we don't even + * need to bother. + */ + if (connector_id_atom) { + + xcb_randr_query_version_cookie_t qv_c = + xcb_randr_query_version(connection, 1, 6); + xcb_randr_get_output_property_cookie_t gop_c = + xcb_randr_get_output_property(connection, + output, + connector_id_atom, + 0, + 0, + 0xffffffffUL, + 0, + 0); + xcb_randr_query_version_reply_t *qv_r = + xcb_randr_query_version_reply(connection, qv_c, NULL); + free(qv_r); + xcb_randr_get_output_property_reply_t *gop_r = + xcb_randr_get_output_property_reply(connection, gop_c, NULL); + if (gop_r) { + if (gop_r->num_items == 1 && gop_r->format == 32) + memcpy(&connector_id, xcb_randr_get_output_property_data(gop_r), 4); + free(gop_r); + } + } + return connector_id; +} + +static bool +wsi_display_check_randr_version(xcb_connection_t *connection) +{ + xcb_randr_query_version_cookie_t qv_c = + xcb_randr_query_version(connection, 1, 6); + xcb_randr_query_version_reply_t *qv_r = + xcb_randr_query_version_reply(connection, qv_c, NULL); + bool ret = false; + + if (!qv_r) + return false; + + /* Check for version 1.6 or newer */ + ret = (qv_r->major_version > 1 || + (qv_r->major_version == 1 && qv_r->minor_version >= 6)); + + free(qv_r); + return ret; +} + +/* + * Given a kernel connector id, find the associated RandR output using the + * CONNECTOR_ID property + */ + +static xcb_randr_output_t +wsi_display_connector_id_to_output(xcb_connection_t *connection, + uint32_t connector_id) +{ + if (!wsi_display_check_randr_version(connection)) + return 0; + + const xcb_setup_t *setup = xcb_get_setup(connection); + + xcb_atom_t connector_id_atom = 0; + xcb_randr_output_t output = 0; + + /* Search all of the screens for the provided output */ + xcb_screen_iterator_t iter; + for (iter = xcb_setup_roots_iterator(setup); + output == 0 && iter.rem; + xcb_screen_next(&iter)) + { + xcb_randr_get_screen_resources_cookie_t gsr_c = + xcb_randr_get_screen_resources(connection, iter.data->root); + xcb_randr_get_screen_resources_reply_t *gsr_r = + xcb_randr_get_screen_resources_reply(connection, gsr_c, NULL); + + if (!gsr_r) + return 0; + + xcb_randr_output_t *ro = xcb_randr_get_screen_resources_outputs(gsr_r); + int o; + + for (o = 0; o < gsr_r->num_outputs; o++) { + if (wsi_display_output_to_connector_id(connection, + &connector_id_atom, ro[o]) + == connector_id) + { + output = ro[o]; + break; + } + } + free(gsr_r); + } + return output; +} + +/* + * Given a RandR output, find out which screen it's associated with + */ +static xcb_window_t +wsi_display_output_to_root(xcb_connection_t *connection, + xcb_randr_output_t output) +{ + if (!wsi_display_check_randr_version(connection)) + return 0; + + const xcb_setup_t *setup = xcb_get_setup(connection); + xcb_window_t root = 0; + + /* Search all of the screens for the provided output */ + for (xcb_screen_iterator_t iter = xcb_setup_roots_iterator(setup); + root == 0 && iter.rem; + xcb_screen_next(&iter)) + { + xcb_randr_get_screen_resources_cookie_t gsr_c = + xcb_randr_get_screen_resources(connection, iter.data->root); + xcb_randr_get_screen_resources_reply_t *gsr_r = + xcb_randr_get_screen_resources_reply(connection, gsr_c, NULL); + + if (!gsr_r) + return 0; + + xcb_randr_output_t *ro = xcb_randr_get_screen_resources_outputs(gsr_r); + + for (int o = 0; o < gsr_r->num_outputs; o++) { + if (ro[o] == output) { + root = iter.data->root; + break; + } + } + free(gsr_r); + } + return root; +} + +static bool +wsi_display_mode_matches_x(struct wsi_display_mode *wsi, + xcb_randr_mode_info_t *xcb) +{ + return wsi->clock == (xcb->dot_clock + 500) / 1000 && + wsi->hdisplay == xcb->width && + wsi->hsync_start == xcb->hsync_start && + wsi->hsync_end == xcb->hsync_end && + wsi->htotal == xcb->htotal && + wsi->hskew == xcb->hskew && + wsi->vdisplay == xcb->height && + wsi->vsync_start == xcb->vsync_start && + wsi->vsync_end == xcb->vsync_end && + wsi->vtotal == xcb->vtotal && + wsi->vscan <= 1 && + wsi->flags == xcb->mode_flags; +} + +static struct wsi_display_mode * +wsi_display_find_x_mode(struct wsi_device *wsi_device, + struct wsi_display_connector *connector, + xcb_randr_mode_info_t *mode) +{ + wsi_for_each_display_mode(display_mode, connector) { + if (wsi_display_mode_matches_x(display_mode, mode)) + return display_mode; + } + return NULL; +} + +static VkResult +wsi_display_register_x_mode(struct wsi_device *wsi_device, + struct wsi_display_connector *connector, + xcb_randr_mode_info_t *x_mode, + bool preferred) +{ + struct wsi_display *wsi = + (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + struct wsi_display_mode *display_mode = + wsi_display_find_x_mode(wsi_device, connector, x_mode); + + if (display_mode) { + display_mode->valid = true; + return VK_SUCCESS; + } + + display_mode = vk_zalloc(wsi->alloc, sizeof (struct wsi_display_mode), + 8, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); + if (!display_mode) + return VK_ERROR_OUT_OF_HOST_MEMORY; + + display_mode->connector = connector; + display_mode->valid = true; + display_mode->preferred = preferred; + display_mode->clock = (x_mode->dot_clock + 500) / 1000; /* kHz */ + display_mode->hdisplay = x_mode->width; + display_mode->hsync_start = x_mode->hsync_start; + display_mode->hsync_end = x_mode->hsync_end; + display_mode->htotal = x_mode->htotal; + display_mode->hskew = x_mode->hskew; + display_mode->vdisplay = x_mode->height; + display_mode->vsync_start = x_mode->vsync_start; + display_mode->vsync_end = x_mode->vsync_end; + display_mode->vtotal = x_mode->vtotal; + display_mode->vscan = 0; + display_mode->flags = x_mode->mode_flags; + + list_addtail(&display_mode->list, &connector->display_modes); + return VK_SUCCESS; +} + +static struct wsi_display_connector * +wsi_display_get_output(struct wsi_device *wsi_device, + xcb_connection_t *connection, + xcb_randr_output_t output) +{ + struct wsi_display *wsi = + (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + struct wsi_display_connector *connector; + uint32_t connector_id; + + xcb_window_t root = wsi_display_output_to_root(connection, output); + if (!root) + return NULL; + + /* See if we already have a connector for this output */ + connector = wsi_display_find_output(wsi_device, output); + + if (!connector) { + xcb_atom_t connector_id_atom = 0; + + /* + * Go get the kernel connector ID for this X output + */ + connector_id = wsi_display_output_to_connector_id(connection, + &connector_id_atom, + output); + + /* Any X server with lease support will have this atom */ + if (!connector_id) { + return NULL; + } + + /* See if we already have a connector for this id */ + connector = wsi_display_find_connector(wsi_device, connector_id); + + if (connector == NULL) { + connector = wsi_display_alloc_connector(wsi, connector_id); + if (!connector) { + return NULL; + } + list_addtail(&connector->list, &wsi->connectors); + } + connector->output = output; + } + + xcb_randr_get_screen_resources_cookie_t src = + xcb_randr_get_screen_resources(connection, root); + xcb_randr_get_output_info_cookie_t oic = + xcb_randr_get_output_info(connection, output, XCB_CURRENT_TIME); + xcb_randr_get_screen_resources_reply_t *srr = + xcb_randr_get_screen_resources_reply(connection, src, NULL); + xcb_randr_get_output_info_reply_t *oir = + xcb_randr_get_output_info_reply(connection, oic, NULL); + + if (oir && srr) { + /* Get X modes and add them */ + + connector->connected = + oir->connection != XCB_RANDR_CONNECTION_DISCONNECTED; + + wsi_display_invalidate_connector_modes(wsi_device, connector); + + xcb_randr_mode_t *x_modes = xcb_randr_get_output_info_modes(oir); + for (int m = 0; m < oir->num_modes; m++) { + xcb_randr_mode_info_iterator_t i = + xcb_randr_get_screen_resources_modes_iterator(srr); + while (i.rem) { + xcb_randr_mode_info_t *mi = i.data; + if (mi->id == x_modes[m]) { + VkResult result = wsi_display_register_x_mode( + wsi_device, connector, mi, m < oir->num_preferred); + if (result != VK_SUCCESS) { + free(oir); + free(srr); + return NULL; + } + break; + } + xcb_randr_mode_info_next(&i); + } + } + } + + free(oir); + free(srr); + return connector; +} + +static xcb_randr_crtc_t +wsi_display_find_crtc_for_output(xcb_connection_t *connection, + xcb_window_t root, + xcb_randr_output_t output) +{ + xcb_randr_get_screen_resources_cookie_t gsr_c = + xcb_randr_get_screen_resources(connection, root); + xcb_randr_get_screen_resources_reply_t *gsr_r = + xcb_randr_get_screen_resources_reply(connection, gsr_c, NULL); + + if (!gsr_r) + return 0; + + xcb_randr_crtc_t *rc = xcb_randr_get_screen_resources_crtcs(gsr_r); + xcb_randr_crtc_t idle_crtc = 0; + xcb_randr_crtc_t active_crtc = 0; + + /* Find either a crtc already connected to the desired output or idle */ + for (int c = 0; active_crtc == 0 && c < gsr_r->num_crtcs; c++) { + xcb_randr_get_crtc_info_cookie_t gci_c = + xcb_randr_get_crtc_info(connection, rc[c], gsr_r->config_timestamp); + xcb_randr_get_crtc_info_reply_t *gci_r = + xcb_randr_get_crtc_info_reply(connection, gci_c, NULL); + + if (gci_r) { + if (gci_r->mode) { + int num_outputs = xcb_randr_get_crtc_info_outputs_length(gci_r); + xcb_randr_output_t *outputs = + xcb_randr_get_crtc_info_outputs(gci_r); + + if (num_outputs == 1 && outputs[0] == output) + active_crtc = rc[c]; + + } else if (idle_crtc == 0) { + int num_possible = xcb_randr_get_crtc_info_possible_length(gci_r); + xcb_randr_output_t *possible = + xcb_randr_get_crtc_info_possible(gci_r); + + for (int p = 0; p < num_possible; p++) + if (possible[p] == output) { + idle_crtc = rc[c]; + break; + } + } + free(gci_r); + } + } + free(gsr_r); + + if (active_crtc) + return active_crtc; + return idle_crtc; +} + +VkResult +wsi_acquire_xlib_display(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + Display *dpy, + VkDisplayKHR display) +{ + struct wsi_display *wsi = + (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + xcb_connection_t *connection = XGetXCBConnection(dpy); + struct wsi_display_connector *connector = + wsi_display_connector_from_handle(display); + xcb_window_t root; + + /* XXX no support for multiple leases yet */ + if (wsi->fd >= 0) + return VK_ERROR_INITIALIZATION_FAILED; + + if (!connector->output) { + connector->output = wsi_display_connector_id_to_output(connection, + connector->id); + + /* Check and see if we found the output */ + if (!connector->output) + return VK_ERROR_INITIALIZATION_FAILED; + } + + root = wsi_display_output_to_root(connection, connector->output); + if (!root) + return VK_ERROR_INITIALIZATION_FAILED; + + xcb_randr_crtc_t crtc = wsi_display_find_crtc_for_output(connection, + root, + connector->output); + + if (!crtc) + return VK_ERROR_INITIALIZATION_FAILED; + +#ifdef HAVE_DRI3_MODIFIERS + xcb_randr_lease_t lease = xcb_generate_id(connection); + xcb_randr_create_lease_cookie_t cl_c = + xcb_randr_create_lease(connection, root, lease, 1, 1, + &crtc, &connector->output); + xcb_randr_create_lease_reply_t *cl_r = + xcb_randr_create_lease_reply(connection, cl_c, NULL); + if (!cl_r) + return VK_ERROR_INITIALIZATION_FAILED; + + int fd = -1; + if (cl_r->nfd > 0) { + int *rcl_f = xcb_randr_create_lease_reply_fds(connection, cl_r); + + fd = rcl_f[0]; + } + free (cl_r); + if (fd < 0) + return VK_ERROR_INITIALIZATION_FAILED; + + wsi->fd = fd; +#endif + + return VK_SUCCESS; +} + +VkResult +wsi_get_randr_output_display(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + Display *dpy, + RROutput output, + VkDisplayKHR *display) +{ + xcb_connection_t *connection = XGetXCBConnection(dpy); + struct wsi_display_connector *connector = + wsi_display_get_output(wsi_device, connection, (xcb_randr_output_t) output); + + if (connector) + *display = wsi_display_connector_to_handle(connector); + else + *display = VK_NULL_HANDLE; + return VK_SUCCESS; +} + +#endif + +/* VK_EXT_display_control */ +VkResult +wsi_display_power_control(VkDevice device, + struct wsi_device *wsi_device, + VkDisplayKHR display, + const VkDisplayPowerInfoEXT *display_power_info) +{ + struct wsi_display *wsi = + (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + struct wsi_display_connector *connector = + wsi_display_connector_from_handle(display); + int mode; + + if (wsi->fd < 0) + return VK_ERROR_INITIALIZATION_FAILED; + + switch (display_power_info->powerState) { + case VK_DISPLAY_POWER_STATE_OFF_EXT: + mode = DRM_MODE_DPMS_OFF; + break; + case VK_DISPLAY_POWER_STATE_SUSPEND_EXT: + mode = DRM_MODE_DPMS_SUSPEND; + break; + default: + mode = DRM_MODE_DPMS_ON; + break; + } + drmModeConnectorSetProperty(wsi->fd, + connector->id, + connector->dpms_property, + mode); + return VK_SUCCESS; +} + +VkResult +wsi_register_device_event(VkDevice device, + struct wsi_device *wsi_device, + const VkDeviceEventInfoEXT *device_event_info, + const VkAllocationCallbacks *allocator, + struct wsi_fence **fence_p) +{ + return VK_ERROR_FEATURE_NOT_PRESENT; +} + +VkResult +wsi_register_display_event(VkDevice device, + struct wsi_device *wsi_device, + VkDisplayKHR display, + const VkDisplayEventInfoEXT *display_event_info, + const VkAllocationCallbacks *allocator, + struct wsi_fence **fence_p) +{ + struct wsi_display *wsi = + (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + struct wsi_display_fence *fence; + VkResult ret; + + switch (display_event_info->displayEvent) { + case VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT: + + fence = wsi_display_fence_alloc(device, wsi_device, display, allocator); + + if (!fence) + return VK_ERROR_OUT_OF_HOST_MEMORY; + + ret = wsi_register_vblank_event(fence, wsi_device, display, + DRM_CRTC_SEQUENCE_RELATIVE, 1, NULL); + + if (ret == VK_SUCCESS) + *fence_p = &fence->base; + else if (fence != NULL) + vk_free2(wsi->alloc, allocator, fence); + + break; + default: + ret = VK_ERROR_FEATURE_NOT_PRESENT; + break; + } + + return ret; +} + + +VkResult +wsi_get_swapchain_counter(VkDevice device, + struct wsi_device *wsi_device, + VkSwapchainKHR _swapchain, + VkSurfaceCounterFlagBitsEXT flag_bits, + uint64_t *value) +{ + struct wsi_display *wsi = + (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + struct wsi_display_swapchain *swapchain = + (struct wsi_display_swapchain *) wsi_swapchain_from_handle(_swapchain); + struct wsi_display_connector *connector = + wsi_display_mode_from_handle(swapchain->surface->displayMode)->connector; + + if (wsi->fd < 0) + return VK_ERROR_INITIALIZATION_FAILED; + + if (!connector->active) { + *value = 0; + return VK_SUCCESS; + } + + int ret = drmCrtcGetSequence(wsi->fd, connector->crtc_id, value, NULL); + if (ret) + *value = 0; + + return VK_SUCCESS; +} + diff --git a/lib/mesa/src/vulkan/wsi/wsi_common_display.h b/lib/mesa/src/vulkan/wsi/wsi_common_display.h new file mode 100644 index 000000000..50d7f836a --- /dev/null +++ b/lib/mesa/src/vulkan/wsi/wsi_common_display.h @@ -0,0 +1,163 @@ +/* + * Copyright © 2017 Keith Packard + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef WSI_COMMON_DISPLAY_H +#define WSI_COMMON_DISPLAY_H + +#include "wsi_common.h" +#include <xf86drm.h> +#include <xf86drmMode.h> + +#define typed_memcpy(dest, src, count) ({ \ + STATIC_ASSERT(sizeof(*src) == sizeof(*dest)); \ + memcpy((dest), (src), (count) * sizeof(*(src))); \ +}) + +VkResult +wsi_display_get_physical_device_display_properties( + VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + uint32_t *property_count, + VkDisplayPropertiesKHR *properties); + +VkResult +wsi_display_get_physical_device_display_properties2( + VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + uint32_t *pPropertyCount, + VkDisplayProperties2KHR *pProperties); + +VkResult +wsi_display_get_physical_device_display_plane_properties( + VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + uint32_t *property_count, + VkDisplayPlanePropertiesKHR *properties); + +VkResult +wsi_display_get_physical_device_display_plane_properties2( + VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + uint32_t *property_count, + VkDisplayPlaneProperties2KHR *properties); + +VkResult +wsi_display_get_display_plane_supported_displays( + VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + uint32_t plane_index, + uint32_t *display_count, + VkDisplayKHR *displays); + +VkResult +wsi_display_get_display_mode_properties(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + VkDisplayKHR display, + uint32_t *property_count, + VkDisplayModePropertiesKHR *properties); + +VkResult +wsi_display_get_display_mode_properties2(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + VkDisplayKHR display, + uint32_t *property_count, + VkDisplayModeProperties2KHR *properties); + +VkResult +wsi_display_create_display_mode(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + VkDisplayKHR display, + const VkDisplayModeCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, + VkDisplayModeKHR *mode); + +VkResult +wsi_get_display_plane_capabilities(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + VkDisplayModeKHR mode_khr, + uint32_t plane_index, + VkDisplayPlaneCapabilitiesKHR *capabilities); + +VkResult +wsi_get_display_plane_capabilities2(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + const VkDisplayPlaneInfo2KHR *pDisplayPlaneInfo, + VkDisplayPlaneCapabilities2KHR *capabilities); + +VkResult +wsi_create_display_surface(VkInstance instance, + const VkAllocationCallbacks *pAllocator, + const VkDisplaySurfaceCreateInfoKHR *pCreateInfo, + VkSurfaceKHR *pSurface); + +VkResult +wsi_release_display(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + VkDisplayKHR display); + + +#if VK_USE_PLATFORM_XLIB_XRANDR_EXT +VkResult +wsi_acquire_xlib_display(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + Display *dpy, + VkDisplayKHR display); + +VkResult +wsi_get_randr_output_display(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + Display *dpy, + RROutput output, + VkDisplayKHR *display); + +#endif /* VK_USE_PLATFORM_XLIB_XRANDR_EXT */ + +/* VK_EXT_display_control */ +VkResult +wsi_display_power_control(VkDevice device, + struct wsi_device *wsi_device, + VkDisplayKHR display, + const VkDisplayPowerInfoEXT *display_power_info); + +VkResult +wsi_register_device_event(VkDevice device, + struct wsi_device *wsi_device, + const VkDeviceEventInfoEXT *device_event_info, + const VkAllocationCallbacks *allocator, + struct wsi_fence **fence); + +VkResult +wsi_register_display_event(VkDevice device, + struct wsi_device *wsi_device, + VkDisplayKHR display, + const VkDisplayEventInfoEXT *display_event_info, + const VkAllocationCallbacks *allocator, + struct wsi_fence **fence); + +VkResult +wsi_get_swapchain_counter(VkDevice device, + struct wsi_device *wsi_device, + VkSwapchainKHR swapchain, + VkSurfaceCounterFlagBitsEXT flag_bits, + uint64_t *value); + +#endif diff --git a/lib/mesa/src/vulkan/wsi/wsi_common_private.h b/lib/mesa/src/vulkan/wsi/wsi_common_private.h new file mode 100644 index 000000000..50a78acac --- /dev/null +++ b/lib/mesa/src/vulkan/wsi/wsi_common_private.h @@ -0,0 +1,174 @@ +/* + * 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. + */ +#ifndef WSI_COMMON_PRIVATE_H +#define WSI_COMMON_PRIVATE_H + +#include "wsi_common.h" + +struct wsi_image { + VkImage image; + VkDeviceMemory memory; + + struct { + VkBuffer buffer; + VkDeviceMemory memory; + VkCommandBuffer *blit_cmd_buffers; + } prime; + + uint64_t drm_modifier; + int num_planes; + uint32_t sizes[4]; + uint32_t offsets[4]; + uint32_t row_pitches[4]; + int fds[4]; +}; + +struct wsi_swapchain { + const struct wsi_device *wsi; + + VkDevice device; + VkAllocationCallbacks alloc; + VkFence fences[3]; + VkPresentModeKHR present_mode; + uint32_t image_count; + + bool use_prime_blit; + + /* Command pools, one per queue family */ + VkCommandPool *cmd_pools; + + VkResult (*destroy)(struct wsi_swapchain *swapchain, + const VkAllocationCallbacks *pAllocator); + struct wsi_image *(*get_wsi_image)(struct wsi_swapchain *swapchain, + uint32_t image_index); + VkResult (*acquire_next_image)(struct wsi_swapchain *swap_chain, + const VkAcquireNextImageInfoKHR *info, + uint32_t *image_index); + VkResult (*queue_present)(struct wsi_swapchain *swap_chain, + uint32_t image_index, + const VkPresentRegionKHR *damage); +}; + +bool +wsi_device_matches_drm_fd(const struct wsi_device *wsi, int drm_fd); + +VkResult +wsi_swapchain_init(const struct wsi_device *wsi, + struct wsi_swapchain *chain, + VkDevice device, + const VkSwapchainCreateInfoKHR *pCreateInfo, + const VkAllocationCallbacks *pAllocator); + +void wsi_swapchain_finish(struct wsi_swapchain *chain); + +VkResult +wsi_create_native_image(const struct wsi_swapchain *chain, + const VkSwapchainCreateInfoKHR *pCreateInfo, + uint32_t num_modifier_lists, + const uint32_t *num_modifiers, + const uint64_t *const *modifiers, + struct wsi_image *image); + +VkResult +wsi_create_prime_image(const struct wsi_swapchain *chain, + const VkSwapchainCreateInfoKHR *pCreateInfo, + bool use_modifier, + struct wsi_image *image); + +void +wsi_destroy_image(const struct wsi_swapchain *chain, + struct wsi_image *image); + + +struct wsi_interface { + VkResult (*get_support)(VkIcdSurfaceBase *surface, + struct wsi_device *wsi_device, + uint32_t queueFamilyIndex, + VkBool32* pSupported); + VkResult (*get_capabilities2)(VkIcdSurfaceBase *surface, + const void *info_next, + VkSurfaceCapabilities2KHR* pSurfaceCapabilities); + VkResult (*get_formats)(VkIcdSurfaceBase *surface, + struct wsi_device *wsi_device, + uint32_t* pSurfaceFormatCount, + VkSurfaceFormatKHR* pSurfaceFormats); + VkResult (*get_formats2)(VkIcdSurfaceBase *surface, + struct wsi_device *wsi_device, + const void *info_next, + uint32_t* pSurfaceFormatCount, + VkSurfaceFormat2KHR* pSurfaceFormats); + VkResult (*get_present_modes)(VkIcdSurfaceBase *surface, + uint32_t* pPresentModeCount, + VkPresentModeKHR* pPresentModes); + VkResult (*get_present_rectangles)(VkIcdSurfaceBase *surface, + struct wsi_device *wsi_device, + uint32_t* pRectCount, + VkRect2D* pRects); + VkResult (*create_swapchain)(VkIcdSurfaceBase *surface, + VkDevice device, + struct wsi_device *wsi_device, + const VkSwapchainCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + struct wsi_swapchain **swapchain); +}; + +VkResult wsi_x11_init_wsi(struct wsi_device *wsi_device, + const VkAllocationCallbacks *alloc); +void wsi_x11_finish_wsi(struct wsi_device *wsi_device, + const VkAllocationCallbacks *alloc); +VkResult wsi_wl_init_wsi(struct wsi_device *wsi_device, + const VkAllocationCallbacks *alloc, + VkPhysicalDevice physical_device); +void wsi_wl_finish_wsi(struct wsi_device *wsi_device, + const VkAllocationCallbacks *alloc); + + +VkResult +wsi_display_init_wsi(struct wsi_device *wsi_device, + const VkAllocationCallbacks *alloc, + int display_fd); + +void +wsi_display_finish_wsi(struct wsi_device *wsi_device, + const VkAllocationCallbacks *alloc); + +#define WSI_DEFINE_NONDISP_HANDLE_CASTS(__wsi_type, __VkType) \ + \ + static inline struct __wsi_type * \ + __wsi_type ## _from_handle(__VkType _handle) \ + { \ + return (struct __wsi_type *)(uintptr_t) _handle; \ + } \ + \ + static inline __VkType \ + __wsi_type ## _to_handle(struct __wsi_type *_obj) \ + { \ + return (__VkType)(uintptr_t) _obj; \ + } + +#define WSI_FROM_HANDLE(__wsi_type, __name, __handle) \ + struct __wsi_type *__name = __wsi_type ## _from_handle(__handle) + +WSI_DEFINE_NONDISP_HANDLE_CASTS(wsi_swapchain, VkSwapchainKHR) + +#endif /* WSI_COMMON_PRIVATE_H */ |