summaryrefslogtreecommitdiff
path: root/lib/mesa/src/vulkan
diff options
context:
space:
mode:
authorJonathan Gray <jsg@cvs.openbsd.org>2019-01-29 11:08:07 +0000
committerJonathan Gray <jsg@cvs.openbsd.org>2019-01-29 11:08:07 +0000
commit6b139c2063623e9310025247cd966490b9aa57ea (patch)
tree375acfd898ca3d721250aa17291bbb90a8d7250a /lib/mesa/src/vulkan
parentcce99579dcfb1d54c54cff65573be3430e77f2c5 (diff)
Import Mesa 18.3.2
Diffstat (limited to 'lib/mesa/src/vulkan')
-rw-r--r--lib/mesa/src/vulkan/meson.build27
-rw-r--r--lib/mesa/src/vulkan/util/meson.build45
-rw-r--r--lib/mesa/src/vulkan/util/vk_debug_report.c122
-rw-r--r--lib/mesa/src/vulkan/util/vk_debug_report.h72
-rw-r--r--lib/mesa/src/vulkan/wsi/meson.build69
-rw-r--r--lib/mesa/src/vulkan/wsi/wsi_common.c1021
-rw-r--r--lib/mesa/src/vulkan/wsi/wsi_common_display.c2490
-rw-r--r--lib/mesa/src/vulkan/wsi/wsi_common_display.h163
-rw-r--r--lib/mesa/src/vulkan/wsi/wsi_common_private.h174
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 = &regions->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 */