Message ID | 20180210044516.16944-2-keithp@keithp.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Friday, 2018-02-09 20:45:10 -0800, Keith Packard wrote: > This adds support for the KHR_display extension to the anv and radv > Vulkan drivers. The drivers now attempt to open the master DRM node > when the KHR_display extension is requested so that the common winsys > code can perform the necessary operations. > > Signed-off-by: Keith Packard <keithp@keithp.com> > --- > configure.ac | 1 + > meson.build | 4 +- > src/amd/vulkan/Makefile.am | 8 + > src/amd/vulkan/Makefile.sources | 3 + > src/amd/vulkan/meson.build | 7 + > src/amd/vulkan/radv_device.c | 28 +- > src/amd/vulkan/radv_extensions.py | 7 +- > src/amd/vulkan/radv_private.h | 2 + > src/amd/vulkan/radv_wsi.c | 3 +- > src/amd/vulkan/radv_wsi_display.c | 143 ++++ > src/intel/Makefile.sources | 3 + > src/intel/Makefile.vulkan.am | 7 + > src/intel/vulkan/anv_device.c | 18 +- > src/intel/vulkan/anv_extensions.py | 1 + > src/intel/vulkan/anv_extensions_gen.py | 5 +- > src/intel/vulkan/anv_wsi.c | 3 +- > src/intel/vulkan/anv_wsi_display.c | 129 +++ > src/intel/vulkan/meson.build | 7 + > src/vulkan/Makefile.am | 7 + > src/vulkan/Makefile.sources | 4 + > src/vulkan/wsi/meson.build | 10 + > src/vulkan/wsi/wsi_common.c | 19 +- > src/vulkan/wsi/wsi_common.h | 5 +- > src/vulkan/wsi/wsi_common_display.c | 1368 ++++++++++++++++++++++++++++++++ > src/vulkan/wsi/wsi_common_display.h | 72 ++ > src/vulkan/wsi/wsi_common_private.h | 10 + > 26 files changed, 1858 insertions(+), 16 deletions(-) > create mode 100644 src/amd/vulkan/radv_wsi_display.c > create mode 100644 src/intel/vulkan/anv_wsi_display.c > create mode 100644 src/vulkan/wsi/wsi_common_display.c > create mode 100644 src/vulkan/wsi/wsi_common_display.h > > diff --git a/configure.ac b/configure.ac > index 8ed606c7694..46318365603 100644 > --- a/configure.ac > +++ b/configure.ac > @@ -1849,6 +1849,7 @@ fi > AM_CONDITIONAL(HAVE_PLATFORM_X11, echo "$platforms" | grep -q 'x11') > AM_CONDITIONAL(HAVE_PLATFORM_WAYLAND, echo "$platforms" | grep -q 'wayland') > AM_CONDITIONAL(HAVE_PLATFORM_DRM, echo "$platforms" | grep -q 'drm') > +AM_CONDITIONAL(HAVE_PLATFORM_DISPLAY, echo "$platforms" | grep -q 'drm') copy/paste error: s/drm/display/ > AM_CONDITIONAL(HAVE_PLATFORM_SURFACELESS, echo "$platforms" | grep -q 'surfaceless') > AM_CONDITIONAL(HAVE_PLATFORM_ANDROID, echo "$platforms" | grep -q 'android') > > diff --git a/meson.build b/meson.build > index b39e2f8ab96..aeb7f5e2917 100644 > --- a/meson.build > +++ b/meson.build > @@ -239,11 +239,12 @@ with_platform_wayland = false > with_platform_x11 = false > with_platform_drm = false > with_platform_surfaceless = false > +with_platform_display = false > egl_native_platform = '' > _platforms = get_option('platforms') > if _platforms == 'auto' > if system_has_kms_drm > - _platforms = 'x11,wayland,drm,surfaceless' > + _platforms = 'x11,wayland,drm,surfaceless,display' > elif ['darwin', 'windows', 'cygwin'].contains(host_machine.system()) > _platforms = 'x11,surfaceless' > else > @@ -257,6 +258,7 @@ if _platforms != '' > with_platform_wayland = _split.contains('wayland') > with_platform_drm = _split.contains('drm') > with_platform_surfaceless = _split.contains('surfaceless') > + with_platform_display = _split.contains('display') > egl_native_platform = _split[0] > endif > > diff --git a/src/amd/vulkan/Makefile.am b/src/amd/vulkan/Makefile.am > index 61025968942..061b8144b88 100644 > --- a/src/amd/vulkan/Makefile.am > +++ b/src/amd/vulkan/Makefile.am > @@ -76,6 +76,14 @@ VULKAN_LIB_DEPS = \ > $(DLOPEN_LIBS) \ > -lm > > +if HAVE_PLATFORM_DISPLAY > +AM_CPPFLAGS += \ > + -DVK_USE_PLATFORM_DISPLAY_KHR > + > +VULKAN_SOURCES += $(VULKAN_WSI_DISPLAY_FILES) > + > +endif > + > if HAVE_PLATFORM_X11 > AM_CPPFLAGS += \ > $(XCB_DRI3_CFLAGS) \ > diff --git a/src/amd/vulkan/Makefile.sources b/src/amd/vulkan/Makefile.sources > index a510d88d965..618a6cdaed0 100644 > --- a/src/amd/vulkan/Makefile.sources > +++ b/src/amd/vulkan/Makefile.sources > @@ -78,6 +78,9 @@ VULKAN_WSI_WAYLAND_FILES := \ > VULKAN_WSI_X11_FILES := \ > radv_wsi_x11.c > > +VULKAN_WSI_DISPLAY_FILES := \ > + radv_wsi_display.c > + > VULKAN_GENERATED_FILES := \ > radv_entrypoints.c \ > radv_entrypoints.h \ > diff --git a/src/amd/vulkan/meson.build b/src/amd/vulkan/meson.build > index 0a7b7c0bf3c..b7bb1075e7d 100644 > --- a/src/amd/vulkan/meson.build > +++ b/src/amd/vulkan/meson.build > @@ -112,6 +112,13 @@ if with_platform_wayland > libradv_files += files('radv_wsi_wayland.c') > endif > > +if with_platform_display > + radv_flags += [ > + '-DVK_USE_PLATFORM_DISPLAY_KHR', > + ] Nit: this can be a simple radv_flags += '-DVK_USE_PLATFORM_DISPLAY_KHR' > + libradv_files += files('radv_wsi_display.c') > +endif > + > libvulkan_radeon = shared_library( > 'vulkan_radeon', > [libradv_files, radv_entrypoints, radv_extensions_c, vk_format_table_c], > diff --git a/src/amd/vulkan/radv_device.c b/src/amd/vulkan/radv_device.c > index 09bb382eeb8..adf33eb35dc 100644 > --- a/src/amd/vulkan/radv_device.c > +++ b/src/amd/vulkan/radv_device.c > @@ -191,9 +191,26 @@ radv_physical_device_init(struct radv_physical_device *device, > const char *path = drm_device->nodes[DRM_NODE_RENDER]; > VkResult result; > drmVersionPtr version; > - int fd; > - > - fd = open(path, O_RDWR | O_CLOEXEC); > + int fd = -1; > + > + if (instance->khr_display_requested) { > + fd = open(drm_device->nodes[DRM_NODE_PRIMARY], O_RDWR | O_CLOEXEC); > + if (fd >= 0) { > + uint32_t accel_working = 0; > + struct drm_amdgpu_info request = { > + .return_pointer = (uintptr_t)&accel_working, > + .return_size = sizeof(accel_working), > + .query = AMDGPU_INFO_ACCEL_WORKING > + }; > + > + if (drmCommandWrite(fd, DRM_AMDGPU_INFO, &request, sizeof (struct drm_amdgpu_info)) < 0 || !accel_working) { > + close(fd); > + fd = -1; > + } > + } > + } > + if (fd < 0) > + fd = open(path, O_RDWR | O_CLOEXEC); Nit: src/amd/vulkan/ uses tabs for indent, you used spaces. > if (fd < 0) > return vk_error(VK_ERROR_INCOMPATIBLE_DRIVER); > > @@ -209,6 +226,7 @@ radv_physical_device_init(struct radv_physical_device *device, > close(fd); > return VK_ERROR_INCOMPATIBLE_DRIVER; > } > + > drmFreeVersion(version); > > device->_loader_data.loaderMagic = ICD_LOADER_MAGIC; > @@ -387,6 +405,7 @@ VkResult radv_CreateInstance( > { > struct radv_instance *instance; > VkResult result; > + bool khr_display_requested = false; > > assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO); > > @@ -411,6 +430,8 @@ VkResult radv_CreateInstance( > const char *ext_name = pCreateInfo->ppEnabledExtensionNames[i]; > if (!radv_instance_extension_supported(ext_name)) > return vk_error(VK_ERROR_EXTENSION_NOT_PRESENT); > + if (strcmp(ext_name, VK_KHR_DISPLAY_EXTENSION_NAME) == 0) > + khr_display_requested = true; > } > > instance = vk_zalloc2(&default_alloc, pAllocator, sizeof(*instance), 8, > @@ -427,6 +448,7 @@ VkResult radv_CreateInstance( > > instance->apiVersion = client_version; > instance->physicalDeviceCount = -1; > + instance->khr_display_requested = khr_display_requested; > > result = vk_debug_report_instance_init(&instance->debug_report_callbacks); > if (result != VK_SUCCESS) { > diff --git a/src/amd/vulkan/radv_extensions.py b/src/amd/vulkan/radv_extensions.py > index d761895d3a0..24cab8cbb39 100644 > --- a/src/amd/vulkan/radv_extensions.py > +++ b/src/amd/vulkan/radv_extensions.py > @@ -81,6 +81,7 @@ EXTENSIONS = [ > Extension('VK_KHR_wayland_surface', 6, 'VK_USE_PLATFORM_WAYLAND_KHR'), > Extension('VK_KHR_xcb_surface', 6, 'VK_USE_PLATFORM_XCB_KHR'), > Extension('VK_KHR_xlib_surface', 6, 'VK_USE_PLATFORM_XLIB_KHR'), > + Extension('VK_KHR_display', 1, 'VK_USE_PLATFORM_DISPLAY_KHR'), > Extension('VK_KHX_multiview', 1, '!ANDROID'), > Extension('VK_EXT_debug_report', 9, True), > Extension('VK_EXT_discard_rectangles', 1, True), > @@ -168,7 +169,7 @@ _TEMPLATE = Template(COPYRIGHT + """ > #include "vk_util.h" > > /* Convert the VK_USE_PLATFORM_* defines to booleans */ > -%for platform in ['ANDROID', 'WAYLAND', 'XCB', 'XLIB']: > +%for platform in ['ANDROID', 'WAYLAND', 'XCB', 'XLIB', 'DISPLAY']: > #ifdef VK_USE_PLATFORM_${platform}_KHR > # undef VK_USE_PLATFORM_${platform}_KHR > # define VK_USE_PLATFORM_${platform}_KHR true > @@ -187,7 +188,9 @@ _TEMPLATE = Template(COPYRIGHT + """ > > #define RADV_HAS_SURFACE (VK_USE_PLATFORM_WAYLAND_KHR || \\ > VK_USE_PLATFORM_XCB_KHR || \\ > - VK_USE_PLATFORM_XLIB_KHR) > + VK_USE_PLATFORM_XLIB_KHR || \\ > + VK_USE_PLATFORM_DISPLAY_KHR) > + > > bool > radv_instance_extension_supported(const char *name) > diff --git a/src/amd/vulkan/radv_private.h b/src/amd/vulkan/radv_private.h > index be9e8f43964..1e3719bcc4f 100644 > --- a/src/amd/vulkan/radv_private.h > +++ b/src/amd/vulkan/radv_private.h > @@ -75,6 +75,7 @@ typedef uint32_t xcb_window_t; > #include "radv_entrypoints.h" > > #include "wsi_common.h" > +#include "wsi_common_display.h" > > #define ATI_VENDOR_ID 0x1002 > > @@ -300,6 +301,7 @@ struct radv_instance { > uint64_t perftest_flags; > > struct vk_debug_report_instance debug_report_callbacks; > + bool khr_display_requested; > }; > > VkResult radv_init_wsi(struct radv_physical_device *physical_device); > diff --git a/src/amd/vulkan/radv_wsi.c b/src/amd/vulkan/radv_wsi.c > index e016e837102..5ec872a63d0 100644 > --- a/src/amd/vulkan/radv_wsi.c > +++ b/src/amd/vulkan/radv_wsi.c > @@ -41,7 +41,8 @@ radv_init_wsi(struct radv_physical_device *physical_device) > return wsi_device_init(&physical_device->wsi_device, > radv_physical_device_to_handle(physical_device), > radv_wsi_proc_addr, > - &physical_device->instance->alloc); > + &physical_device->instance->alloc, > + physical_device->local_fd); > } > > void > diff --git a/src/amd/vulkan/radv_wsi_display.c b/src/amd/vulkan/radv_wsi_display.c > new file mode 100644 > index 00000000000..b0a4db0344b > --- /dev/null > +++ b/src/amd/vulkan/radv_wsi_display.c > @@ -0,0 +1,143 @@ > +/* > + * 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 <stdbool.h> > +#include <string.h> > +#include <unistd.h> > +#include <fcntl.h> > +#include "radv_private.h" > +#include "radv_cs.h" > +#include "util/disk_cache.h" > +#include "util/strtod.h" > +#include "vk_util.h" > +#include <xf86drm.h> > +#include <xf86drmMode.h> > +#include <amdgpu.h> > +#include <amdgpu_drm.h> > +#include "winsys/amdgpu/radv_amdgpu_winsys_public.h" > +#include "ac_llvm_util.h" > +#include "vk_format.h" > +#include "sid.h" > +#include "util/debug.h" > +#include "wsi_common_display.h" > + > +#define MM_PER_PIXEL (1.0/96.0 * 25.4) unused > + > +VkResult > +radv_GetPhysicalDeviceDisplayPropertiesKHR(VkPhysicalDevice physical_device, > + uint32_t *property_count, > + VkDisplayPropertiesKHR *properties) > +{ > + RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device); > + > + return wsi_display_get_physical_device_display_properties(physical_device, > + &pdevice->wsi_device, > + property_count, > + properties); > +} > + > +VkResult > +radv_GetPhysicalDeviceDisplayPlanePropertiesKHR(VkPhysicalDevice physical_device, > + uint32_t *property_count, > + VkDisplayPlanePropertiesKHR *properties) > +{ > + RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device); > + > + return wsi_display_get_physical_device_display_plane_properties(physical_device, > + &pdevice->wsi_device, > + property_count, > + properties); > +} > + > +VkResult > +radv_GetDisplayPlaneSupportedDisplaysKHR(VkPhysicalDevice physical_device, > + uint32_t plane_index, > + uint32_t *display_count, > + VkDisplayKHR *displays) > +{ > + RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device); > + > + return wsi_display_get_display_plane_supported_displays(physical_device, > + &pdevice->wsi_device, > + plane_index, > + display_count, > + displays); > +} > + > + > +VkResult > +radv_GetDisplayModePropertiesKHR(VkPhysicalDevice physical_device, > + VkDisplayKHR display, > + uint32_t *property_count, > + VkDisplayModePropertiesKHR *properties) > +{ > + RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device); > + > + return wsi_display_get_display_mode_properties(physical_device, > + &pdevice->wsi_device, > + display, > + property_count, > + properties); > +} > + > +VkResult > +radv_CreateDisplayModeKHR(VkPhysicalDevice physical_device, > + VkDisplayKHR display, > + const VkDisplayModeCreateInfoKHR *create_info, > + const VkAllocationCallbacks *allocator, > + VkDisplayModeKHR *mode) > +{ > + return VK_ERROR_INITIALIZATION_FAILED; > +} > + > + > +VkResult > +radv_GetDisplayPlaneCapabilitiesKHR(VkPhysicalDevice physical_device, > + VkDisplayModeKHR mode_khr, > + uint32_t plane_index, > + VkDisplayPlaneCapabilitiesKHR *capabilities) > +{ > + RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device); > + > + return wsi_get_display_plane_capabilities(physical_device, > + &pdevice->wsi_device, > + mode_khr, > + plane_index, > + capabilities); > +} > + > +VkResult > +radv_CreateDisplayPlaneSurfaceKHR(VkInstance _instance, > + const VkDisplaySurfaceCreateInfoKHR *create_info, > + const VkAllocationCallbacks *allocator, > + VkSurfaceKHR *surface) > +{ > + RADV_FROM_HANDLE(radv_instance, instance, _instance); > + const VkAllocationCallbacks *alloc; > + > + if (allocator) > + alloc = allocator; > + else > + alloc = &instance->alloc; > + > + return wsi_create_display_surface(_instance, alloc, create_info, surface); > +} > diff --git a/src/intel/Makefile.sources b/src/intel/Makefile.sources > index 9595bf42582..6c142729d94 100644 > --- a/src/intel/Makefile.sources > +++ b/src/intel/Makefile.sources > @@ -240,6 +240,9 @@ VULKAN_WSI_WAYLAND_FILES := \ > VULKAN_WSI_X11_FILES := \ > vulkan/anv_wsi_x11.c > > +VULKAN_WSI_DISPLAY_FILES := \ > + vulkan/anv_wsi_display.c > + > VULKAN_GEM_FILES := \ > vulkan/anv_gem.c > > diff --git a/src/intel/Makefile.vulkan.am b/src/intel/Makefile.vulkan.am > index 23fa877e77d..7c428a799d7 100644 > --- a/src/intel/Makefile.vulkan.am > +++ b/src/intel/Makefile.vulkan.am > @@ -187,6 +187,13 @@ VULKAN_SOURCES += $(VULKAN_WSI_WAYLAND_FILES) > VULKAN_LIB_DEPS += $(WAYLAND_CLIENT_LIBS) > endif > > +if HAVE_PLATFORM_DISPLAY > +VULKAN_CPPFLAGS += \ > + -DVK_USE_PLATFORM_DISPLAY_KHR > + > +VULKAN_SOURCES += $(VULKAN_WSI_DISPLAY_FILES) > +endif > + > noinst_LTLIBRARIES += vulkan/libvulkan_common.la > vulkan_libvulkan_common_la_SOURCES = $(VULKAN_SOURCES) > vulkan_libvulkan_common_la_CFLAGS = $(VULKAN_CFLAGS) > diff --git a/src/intel/vulkan/anv_device.c b/src/intel/vulkan/anv_device.c > index 86c1bdc1d51..9614907fda3 100644 > --- a/src/intel/vulkan/anv_device.c > +++ b/src/intel/vulkan/anv_device.c > @@ -277,14 +277,25 @@ anv_physical_device_init_uuids(struct anv_physical_device *device) > static VkResult > anv_physical_device_init(struct anv_physical_device *device, > struct anv_instance *instance, > - const char *path) > + const char *primary_path, > + const char *render_path) > { > VkResult result; > - int fd; > + int fd = -1; > + const char *path; > > brw_process_intel_debug_variable(); > > - fd = open(path, O_RDWR | O_CLOEXEC); > + if (instance->enabled_extensions.KHR_display) { > + path = primary_path; > + fd = open(path, O_RDWR | O_CLOEXEC); > + } > + > + if (fd < 0) { > + path = render_path; > + fd = open(path, O_RDWR | O_CLOEXEC); > + } > + > if (fd < 0) > return vk_error(VK_ERROR_INCOMPATIBLE_DRIVER); > > @@ -652,6 +663,7 @@ anv_enumerate_devices(struct anv_instance *instance) > > result = anv_physical_device_init(&instance->physicalDevice, > instance, > + devices[i]->nodes[DRM_NODE_PRIMARY], > devices[i]->nodes[DRM_NODE_RENDER]); > if (result != VK_ERROR_INCOMPATIBLE_DRIVER) > break; > diff --git a/src/intel/vulkan/anv_extensions.py b/src/intel/vulkan/anv_extensions.py > index 581921e62a1..978a219e2b2 100644 > --- a/src/intel/vulkan/anv_extensions.py > +++ b/src/intel/vulkan/anv_extensions.py > @@ -83,6 +83,7 @@ EXTENSIONS = [ > Extension('VK_KHR_wayland_surface', 6, 'VK_USE_PLATFORM_WAYLAND_KHR'), > Extension('VK_KHR_xcb_surface', 6, 'VK_USE_PLATFORM_XCB_KHR'), > Extension('VK_KHR_xlib_surface', 6, 'VK_USE_PLATFORM_XLIB_KHR'), > + Extension('VK_KHR_display', 1, 'VK_USE_PLATFORM_DISPLAY_KHR'), > Extension('VK_KHX_multiview', 1, True), > Extension('VK_EXT_debug_report', 8, True), > Extension('VK_EXT_external_memory_dma_buf', 1, True), > diff --git a/src/intel/vulkan/anv_extensions_gen.py b/src/intel/vulkan/anv_extensions_gen.py > index 33827ecd015..84d07f9767a 100644 > --- a/src/intel/vulkan/anv_extensions_gen.py > +++ b/src/intel/vulkan/anv_extensions_gen.py > @@ -113,7 +113,7 @@ _TEMPLATE_C = Template(COPYRIGHT + """ > #include "vk_util.h" > > /* Convert the VK_USE_PLATFORM_* defines to booleans */ > -%for platform in ['ANDROID', 'WAYLAND', 'XCB', 'XLIB']: > +%for platform in ['ANDROID', 'WAYLAND', 'XCB', 'XLIB', 'DISPLAY']: > #ifdef VK_USE_PLATFORM_${platform}_KHR > # undef VK_USE_PLATFORM_${platform}_KHR > # define VK_USE_PLATFORM_${platform}_KHR true > @@ -132,7 +132,8 @@ _TEMPLATE_C = Template(COPYRIGHT + """ > > #define ANV_HAS_SURFACE (VK_USE_PLATFORM_WAYLAND_KHR || \\ > VK_USE_PLATFORM_XCB_KHR || \\ > - VK_USE_PLATFORM_XLIB_KHR) > + VK_USE_PLATFORM_XLIB_KHR || \\ > + VK_USE_PLATFORM_DISPLAY_KHR) > > const VkExtensionProperties anv_instance_extensions[ANV_INSTANCE_EXTENSION_COUNT] = { > %for ext in instance_extensions: > diff --git a/src/intel/vulkan/anv_wsi.c b/src/intel/vulkan/anv_wsi.c > index 6082c3dd093..f86d83589ea 100644 > --- a/src/intel/vulkan/anv_wsi.c > +++ b/src/intel/vulkan/anv_wsi.c > @@ -39,7 +39,8 @@ anv_init_wsi(struct anv_physical_device *physical_device) > return wsi_device_init(&physical_device->wsi_device, > anv_physical_device_to_handle(physical_device), > anv_wsi_proc_addr, > - &physical_device->instance->alloc); > + &physical_device->instance->alloc, > + physical_device->local_fd); > } > > void > diff --git a/src/intel/vulkan/anv_wsi_display.c b/src/intel/vulkan/anv_wsi_display.c > new file mode 100644 > index 00000000000..9b00d7f02e4 > --- /dev/null > +++ b/src/intel/vulkan/anv_wsi_display.c > @@ -0,0 +1,129 @@ > +/* > + * 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 "anv_private.h" > +#include "wsi_common.h" > +#include "vk_format_info.h" > +#include "vk_util.h" > +#include "wsi_common_display.h" > + > +#define MM_PER_PIXEL (1.0/96.0 * 25.4) unused > + > +VkResult > +anv_GetPhysicalDeviceDisplayPropertiesKHR(VkPhysicalDevice physical_device, > + uint32_t *property_count, > + VkDisplayPropertiesKHR *properties) > +{ > + ANV_FROM_HANDLE(anv_physical_device, pdevice, physical_device); > + > + return wsi_display_get_physical_device_display_properties(physical_device, > + &pdevice->wsi_device, > + property_count, > + properties); > +} > + > +VkResult > +anv_GetPhysicalDeviceDisplayPlanePropertiesKHR(VkPhysicalDevice physical_device, > + uint32_t *property_count, > + VkDisplayPlanePropertiesKHR *properties) > +{ > + ANV_FROM_HANDLE(anv_physical_device, pdevice, physical_device); > + > + return wsi_display_get_physical_device_display_plane_properties(physical_device, > + &pdevice->wsi_device, > + property_count, > + properties); > +} > + > +VkResult > +anv_GetDisplayPlaneSupportedDisplaysKHR(VkPhysicalDevice physical_device, > + uint32_t plane_index, > + uint32_t *display_count, > + VkDisplayKHR *displays) > +{ > + ANV_FROM_HANDLE(anv_physical_device, pdevice, physical_device); > + > + return wsi_display_get_display_plane_supported_displays(physical_device, > + &pdevice->wsi_device, > + plane_index, > + display_count, > + displays); > +} > + > + > +VkResult > +anv_GetDisplayModePropertiesKHR(VkPhysicalDevice physical_device, > + VkDisplayKHR display, > + uint32_t *property_count, > + VkDisplayModePropertiesKHR *properties) > +{ > + ANV_FROM_HANDLE(anv_physical_device, pdevice, physical_device); > + > + return wsi_display_get_display_mode_properties(physical_device, > + &pdevice->wsi_device, > + display, > + property_count, > + properties); > +} > + > +VkResult > +anv_CreateDisplayModeKHR(VkPhysicalDevice physical_device, > + VkDisplayKHR display, > + const VkDisplayModeCreateInfoKHR *create_info, > + const VkAllocationCallbacks *allocator, > + VkDisplayModeKHR *mode) > +{ > + return VK_ERROR_INITIALIZATION_FAILED; > +} > + > + > +VkResult > +anv_GetDisplayPlaneCapabilitiesKHR(VkPhysicalDevice physical_device, > + VkDisplayModeKHR mode_khr, > + uint32_t plane_index, > + VkDisplayPlaneCapabilitiesKHR *capabilities) > +{ > + ANV_FROM_HANDLE(anv_physical_device, pdevice, physical_device); > + > + return wsi_get_display_plane_capabilities(physical_device, > + &pdevice->wsi_device, > + mode_khr, > + plane_index, > + capabilities); > +} > + > +VkResult > +anv_CreateDisplayPlaneSurfaceKHR(VkInstance _instance, > + const VkDisplaySurfaceCreateInfoKHR *create_info, > + const VkAllocationCallbacks *allocator, > + VkSurfaceKHR *surface) > +{ > + ANV_FROM_HANDLE(anv_instance, instance, _instance); > + const VkAllocationCallbacks *alloc; > + > + if (allocator) > + alloc = allocator; > + else > + alloc = &instance->alloc; > + > + return wsi_create_display_surface(_instance, alloc, create_info, surface); > +} > diff --git a/src/intel/vulkan/meson.build b/src/intel/vulkan/meson.build > index 69ec26e19b6..2e2ab8f7ecd 100644 > --- a/src/intel/vulkan/meson.build > +++ b/src/intel/vulkan/meson.build > @@ -171,6 +171,13 @@ if with_platform_wayland > libanv_files += files('anv_wsi_wayland.c') > endif > > +if with_platform_display > + anv_flags += [ > + '-DVK_USE_PLATFORM_DISPLAY_KHR', > + ] > + libanv_files += files('anv_wsi_display.c') > +endif > + > libanv_common = static_library( > 'anv_common', > [libanv_files, anv_entrypoints, anv_extensions_c, anv_extensions_h], > diff --git a/src/vulkan/Makefile.am b/src/vulkan/Makefile.am > index 037436c1cd7..c33ac5758f7 100644 > --- a/src/vulkan/Makefile.am > +++ b/src/vulkan/Makefile.am > @@ -57,6 +57,13 @@ AM_CPPFLAGS += \ > VULKAN_WSI_SOURCES += $(VULKAN_WSI_X11_FILES) > endif > > +if HAVE_PLATFORM_DISPLAY > +AM_CPPFLAGS += \ > + -DVK_USE_PLATFORM_DISPLAY_KHR > + > +VULKAN_WSI_SOURCES += $(VULKAN_WSI_DISPLAY_FILES) > +endif > + > BUILT_SOURCES += $(VULKAN_WSI_WAYLAND_GENERATED_FILES) > CLEANFILES = $(BUILT_SOURCES) > > diff --git a/src/vulkan/Makefile.sources b/src/vulkan/Makefile.sources > index a0a24ce7de8..3642c7662c4 100644 > --- a/src/vulkan/Makefile.sources > +++ b/src/vulkan/Makefile.sources > @@ -17,6 +17,10 @@ VULKAN_WSI_X11_FILES := \ > wsi/wsi_common_x11.c \ > wsi/wsi_common_x11.h > > +VULKAN_WSI_DISPLAY_FILES := \ > + wsi/wsi_common_display.c \ > + wsi/wsi_common_display.h > + > VULKAN_UTIL_FILES := \ > util/vk_alloc.h \ > util/vk_debug_report.c \ > diff --git a/src/vulkan/wsi/meson.build b/src/vulkan/wsi/meson.build > index bd0fd3cc53e..743631a6113 100644 > --- a/src/vulkan/wsi/meson.build > +++ b/src/vulkan/wsi/meson.build > @@ -57,6 +57,16 @@ if with_platform_wayland > ] > endif > > +if with_platform_display > + vulkan_wsi_args += [ > + '-DVK_USE_PLATFORM_DISPLAY_KHR', > + ] Ditto: vulkan_wsi_args += '-DVK_USE_PLATFORM_DISPLAY_KHR' > + files_vulkan_wsi += files( > + 'wsi_common_display.c', > + 'wsi_common_display.h', > + ) > +endif > + > libvulkan_wsi = static_library( > 'vulkan_wsi', > files_vulkan_wsi, > diff --git a/src/vulkan/wsi/wsi_common.c b/src/vulkan/wsi/wsi_common.c > index 90ed07b7857..c0a285e5814 100644 > --- a/src/vulkan/wsi/wsi_common.c > +++ b/src/vulkan/wsi/wsi_common.c > @@ -29,7 +29,8 @@ VkResult > wsi_device_init(struct wsi_device *wsi, > VkPhysicalDevice pdevice, > WSI_FN_GetPhysicalDeviceProcAddr proc_addr, > - const VkAllocationCallbacks *alloc) > + const VkAllocationCallbacks *alloc, > + int device_fd) > { > VkResult result; > > @@ -89,6 +90,19 @@ wsi_device_init(struct wsi_device *wsi, > } > #endif > > +#ifdef VK_USE_PLATFORM_DISPLAY_KHR > + result = wsi_display_init_wsi(wsi, alloc, pdevice, device_fd); > + if (result != VK_SUCCESS) { > +#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 > + return result; > + } > +#endif > + > return VK_SUCCESS; > } > > @@ -96,6 +110,9 @@ 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 > diff --git a/src/vulkan/wsi/wsi_common.h b/src/vulkan/wsi/wsi_common.h > index 3e0d3be1c24..1cb6aaebca0 100644 > --- a/src/vulkan/wsi/wsi_common.h > +++ b/src/vulkan/wsi/wsi_common.h > @@ -50,7 +50,7 @@ struct wsi_memory_allocate_info { > > struct wsi_interface; > > -#define VK_ICD_WSI_PLATFORM_MAX 5 > +#define VK_ICD_WSI_PLATFORM_MAX 6 > > struct wsi_device { > VkPhysicalDeviceMemoryProperties memory_props; > @@ -93,7 +93,8 @@ VkResult > wsi_device_init(struct wsi_device *wsi, > VkPhysicalDevice pdevice, > WSI_FN_GetPhysicalDeviceProcAddr proc_addr, > - const VkAllocationCallbacks *alloc); > + const VkAllocationCallbacks *alloc, > + int device_fd); > > void > wsi_device_finish(struct wsi_device *wsi, > diff --git a/src/vulkan/wsi/wsi_common_display.c b/src/vulkan/wsi/wsi_common_display.c > new file mode 100644 > index 00000000000..2732b1dd721 > --- /dev/null > +++ b/src/vulkan/wsi/wsi_common_display.c > @@ -0,0 +1,1368 @@ > +/* > + * 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 "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 `#if DEBUG` maybe? > +#define wsi_display_debug(...) fprintf(stderr, __VA_ARGS__) > +#define wsi_display_debug_code(...) __VA_ARGS__ that 2nd one is unused > +#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; > + wsi_display_mode *current_mode; > + drmModeModeInfo current_drm_mode; > +} wsi_display_connector; > + > +struct wsi_display { > + struct wsi_interface base; > + > + const VkAllocationCallbacks *alloc; > + VkPhysicalDevice physical_device; > + > + int master_fd; > + int render_fd; > + > + pthread_mutex_t wait_mutex; > + pthread_cond_t wait_cond; > + pthread_t wait_thread; > + > + struct list_head connectors; > + > + struct list_head display_modes; > +}; > + > +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; > + uint64_t flip_sequence; > +}; > + > +struct wsi_display_swapchain { > + struct wsi_swapchain base; > + struct wsi_display *wsi; > + VkIcdSurfaceDisplay *surface; > + uint64_t flip_sequence; > + struct wsi_display_image images[0]; > +}; > + > +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 && > + wsi->vscan == drm->vscan && > + 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) (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 struct wsi_display_mode * > +wsi_display_find_drm_mode(struct wsi_device *wsi_device, > + struct wsi_display_connector *connector, > + drmModeModeInfoPtr mode) > +{ > + struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; > + struct wsi_display_mode *display_mode; > + > + LIST_FOR_EACH_ENTRY(display_mode, &wsi->display_modes, list) { > + if (display_mode->connector == connector && > + 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) > +{ > + struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; > + struct wsi_display_mode *display_mode; > + > + LIST_FOR_EACH_ENTRY(display_mode, &wsi->display_modes, list) > + if (display_mode->connector == 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; > + > + 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_alloc(wsi->alloc, sizeof (struct wsi_display_mode), 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); > + 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, &wsi->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]; > + struct wsi_display_connector *connector; > + > + connector = NULL; > + LIST_FOR_EACH_ENTRY(connector, &wsi->connectors, list) { > + 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; > + > + connector = vk_alloc(wsi->alloc, sizeof (struct wsi_display_connector), 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); > + memset(connector, '\0', sizeof (*connector)); > + connector->id = connector_id; > + connector->wsi = wsi; > + connector->active = false; > + /* XXX use EDID name */ > + connector->name = "monitor"; > + 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]; > + struct wsi_display_connector *connector; > + drmModeConnectorPtr drm_connector; > + VkResult result; > + int m; > + > + if (wsi->master_fd < 0) > + return NULL; > + > + drm_connector = drmModeGetConnector(wsi->master_fd, connector_id); > + if (!drm_connector) > + return NULL; > + > + 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; > + > + /* 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 (m = 0; m < drm_connector->count_modes; m++) { > + 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 void > +wsi_display_fill_in_display_properties(struct wsi_device *wsi_device, > + struct wsi_display_connector *connector, > + VkDisplayPropertiesKHR *properties) > +{ > + struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; > + struct wsi_display_mode *display_mode, *preferred_mode = NULL;; > + > + properties->display = wsi_display_connector_to_handle(connector); > + properties->displayName = connector->name; > + > + /* Find the preferred mode and assume that's the physical resolution */ > + > + LIST_FOR_EACH_ENTRY(display_mode, &wsi->display_modes, list) { > + if (display_mode->valid && display_mode->connector == connector && display_mode->preferred) { > + preferred_mode = display_mode; > + break; > + } > + } > + > + if (preferred_mode) { > + properties->physicalResolution.width = preferred_mode->hdisplay; > + properties->physicalResolution.height = preferred_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->persistentContent = 0; > +} > + > +/* > + * 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]; > + struct wsi_display_connector *connector; > + int c; > + uint32_t connected; > + uint32_t property_count_requested = *property_count; > + drmModeResPtr mode_res; > + > + if (wsi->master_fd < 0) > + return VK_ERROR_INITIALIZATION_FAILED; > + > + mode_res = drmModeGetResources(wsi->master_fd); > + > + if (!mode_res) > + return VK_ERROR_INITIALIZATION_FAILED; > + > + connected = 0; > + > + /* Get current information */ > + for (c = 0; c < mode_res->count_connectors; c++) { > + 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) > + connected++; > + } > + > + /* Fill in property information if requested */ > + if (properties != NULL) { > + connected = 0; > + > + for (c = 0; c < mode_res->count_connectors; c++) { > + connector = wsi_display_find_connector(wsi_device, mode_res->connectors[c]); > + > + if (connector && connector->connected) { > + if (connected < property_count_requested) { > + wsi_display_fill_in_display_properties(wsi_device, > + connector, > + &properties[connected]); > + } > + connected++; > + } > + } > + } > + > + drmModeFreeResources(mode_res); > + > + *property_count = connected; > + > + if (connected > property_count_requested && properties != NULL) > + return VK_INCOMPLETE; > + > + return VK_SUCCESS; > +} > + > +/* > + * Implement vkGetPhysicalDeviceDisplayPlanePropertiesKHR (VK_KHR_display > + */ > +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]; > + struct wsi_display_connector *connector; > + uint32_t property_count_requested = *property_count; > + int c; > + > + if (!properties) > + property_count_requested = 0; > + > + c = 0; > + LIST_FOR_EACH_ENTRY(connector, &wsi->connectors, list) { > + if (c < property_count_requested) { > + if (connector && connector->active) { > + properties[c].currentDisplay = wsi_display_connector_to_handle(connector); > + properties[c].currentStackIndex = c; > + } else { > + properties[c].currentDisplay = NULL; > + properties[c].currentStackIndex = 0; > + } > + } > + c++; > + } > + > + *property_count = c; > + > + if (c > property_count_requested && properties != NULL) > + return VK_INCOMPLETE; > + > + return VK_SUCCESS; > +} > + > +/* > + * 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]; > + struct wsi_display_connector *connector; > + int c; > + > + > + if (displays == NULL) { > + *display_count = 1; > + return VK_SUCCESS; > + } > + > + if (*display_count < 1) > + return VK_INCOMPLETE; > + > + c = 0; > + LIST_FOR_EACH_ENTRY(connector, &wsi->connectors, list) { > + if (c == plane_index) { > + *displays = wsi_display_connector_to_handle(connector); > + *display_count = 1; > + return VK_SUCCESS; > + } > + c++; > + } > + > + *displays = 0; > + *display_count = 0; > + > + return VK_SUCCESS; > +} > + > +/* > + * Implement vkGetDisplayModePropertiesKHR (VK_KHR_display) > + */ > + > +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 *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; > + struct wsi_display_connector *connector = wsi_display_connector_from_handle(display); > + int i; > + struct wsi_display_mode *display_mode; > + uint32_t property_count_requested = *property_count; > + > + i = 0; > + > + if (properties == NULL) > + property_count_requested = 0; > + > + LIST_FOR_EACH_ENTRY(display_mode, &wsi->display_modes, list) { > + if (display_mode->valid && display_mode->connector == connector) { > + if (i < property_count_requested) { > + properties[i].displayMode = wsi_display_mode_to_handle(display_mode); > + properties[i].parameters.visibleRegion.width = display_mode->hdisplay; > + properties[i].parameters.visibleRegion.height = display_mode->vdisplay; > + properties[i].parameters.refreshRate = (uint32_t) (wsi_display_mode_refresh(display_mode) * 1000 + 0.5); > + } > + i++; > + } > + } > + > + *property_count = i; > + > + if (i > property_count_requested && properties != NULL) > + return VK_INCOMPLETE; > + > + return VK_SUCCESS; > + > +} > + > +/* > + * 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_create_display_surface(VkInstance instance, > + const VkAllocationCallbacks *allocator, > + const VkDisplaySurfaceCreateInfoKHR *create_info, > + VkSurfaceKHR *surface_khr) > +{ > + VkIcdSurfaceDisplay *surface; > + > + surface = vk_alloc(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, > + const VkAllocationCallbacks *allocator, > + uint32_t queueFamilyIndex, > + int local_fd, > + 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_INHERIT_BIT_KHR | > + 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_capabilities2(VkIcdSurfaceBase *icd_surface, > + const void *info_next, > + VkSurfaceCapabilities2KHR *caps) > +{ > + assert(caps->sType == VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR); > + > + return wsi_display_surface_get_capabilities(icd_surface, &caps->surfaceCapabilities); > +} > + > +static const VkFormat available_surface_formats[] = { > + VK_FORMAT_B8G8R8A8_SRGB, > + VK_FORMAT_B8G8R8A8_UNORM, > +}; > + > +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]; > + 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]; > + f->surfaceFormat.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; > + } > + } > + > + return vk_outarray_status(&out); > +} > + > +static const VkPresentModeKHR available_present_modes[] = { > + VK_PRESENT_MODE_FIFO_KHR, > +}; > + > +static VkResult > +wsi_display_surface_get_present_modes(VkIcdSurfaceBase *surface, > + uint32_t *present_mode_count, > + VkPresentModeKHR *present_modes) > +{ > + if (present_modes == NULL) { > + *present_mode_count = ARRAY_SIZE(available_present_modes); > + return VK_SUCCESS; > + } > + > + *present_mode_count = MIN2(*present_mode_count, ARRAY_SIZE(available_present_modes)); > + typed_memcpy(present_modes, available_present_modes, *present_mode_count); > + > + if (*present_mode_count < ARRAY_SIZE(available_present_modes)) > + return VK_INCOMPLETE; > + return VK_SUCCESS; > +} > + > +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; > + VkResult result; > + int ret; > + uint32_t image_handle; > + > + if (chain->base.use_prime_blit) > + result = wsi_create_prime_image(&chain->base, create_info, &image->base); > + else > + result = wsi_create_native_image(&chain->base, create_info, &image->base); > + if (result != VK_SUCCESS) > + return result; > + > + ret = drmPrimeFDToHandle(wsi->master_fd, image->base.fd, &image_handle); > + > + close(image->base.fd); > + image->base.fd = -1; > + > + if (ret < 0) > + goto fail_handle; > + > + image->chain = chain; > + image->state = wsi_image_idle; > + image->fb_id = 0; > + > + /* XXX extract depth and bpp from image somehow */ > + ret = drmModeAddFB(wsi->master_fd, create_info->imageExtent.width, create_info->imageExtent.height, > + 24, 32, image->base.row_pitch, image_handle, &image->fb_id); > + > + if (ret) > + goto fail_fb; > + > + return VK_SUCCESS; > + > +fail_fb: > + /* fall through */ > + > +fail_handle: > + 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; > + > + 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]); > + 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; > + > + 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); > + (void) _wsi_display_queue_next(&(image->chain->base)); > +} > + > +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 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 > +}; > + > +static void * > +wsi_display_wait_thread(void *data) > +{ > + struct wsi_display *wsi = data; > + struct pollfd pollfd = { > + .fd = wsi->master_fd, > + .events = POLLIN > + }; > + int ret; > + > + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); > + for (;;) { > + ret = poll(&pollfd, 1, -1); > + if (ret > 0) { > + pthread_mutex_lock(&wsi->wait_mutex); > + (void) drmHandleEvent(wsi->master_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; > +} > + > +/* 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 / ((uint64_t) 1000 * (uint64_t) 1000 * (uint64_t) 1000), > + .tv_nsec = timeout_ns % ((uint64_t) 1000 * (uint64_t) 1000 * (uint64_t) 1000) > + }; > + > + 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, > + uint64_t timeout, > + VkSemaphore semaphore, > + 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; > + > + if (timeout != 0 && timeout != UINT64_MAX) > + timeout += wsi_get_current_monotonic(); > + > + 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_OUT_OF_DATE_KHR; > + goto done; > + } > + } > +done: > + pthread_mutex_unlock(&wsi->wait_mutex); > + return result; > +} > + > +/* > + * 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) > +{ > + int c, e; > + > + /* See if any other connectors share the same encoder */ > + for (c = 0; c < mode_res->count_connectors; c++) { > + if (mode_res->connectors[c] == connector->connector_id) > + continue; > + > + drmModeConnectorPtr other_connector = drmModeGetConnector(wsi->master_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 (e = 0; e < mode_res->count_encoders; e++) { > + if (mode_res->encoders[e] == connector->encoder_id) > + continue; > + > + drmModeEncoderPtr other_encoder = drmModeGetEncoder(wsi->master_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(struct wsi_display_connector *connector, > + drmModeResPtr mode_res, > + drmModeConnectorPtr drm_connector) > +{ > + struct wsi_display *wsi = connector->wsi; > + int c; > + uint32_t crtc_id; > + > + /* See what CRTC is currently driving this connector */ > + if (drm_connector->encoder_id) { > + drmModeEncoderPtr encoder = drmModeGetEncoder(wsi->master_fd, drm_connector->encoder_id); > + if (encoder) { > + 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; > + } > + } > + } > + crtc_id = 0; > + for (c = 0; crtc_id == 0 && c < mode_res->count_crtcs; c++) { > + drmModeCrtcPtr crtc = drmModeGetCrtc(wsi->master_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; > + drmModeModeInfoPtr drm_mode; > + drmModeConnectorPtr drm_connector; > + drmModeResPtr mode_res; > + VkResult result; > + int m; > + > + if (connector->current_mode == display_mode && connector->crtc_id) > + return VK_SUCCESS; > + > + mode_res = drmModeGetResources(wsi->master_fd); > + if (!mode_res) { > + result = VK_ERROR_INITIALIZATION_FAILED; > + goto bail; > + } > + > + drm_connector = drmModeGetConnectorCurrent(wsi->master_fd, connector->id); > + if (!drm_connector) { > + result = VK_ERROR_INITIALIZATION_FAILED; > + 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_OUT_OF_DATE_KHR; > + goto bail_connector; > + } > + } > + > + if (connector->current_mode != display_mode) { > + > + /* Find the drm mode cooresponding to the requested VkDisplayMode */ > + drm_mode = NULL; > + for (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_OUT_OF_DATE_KHR; > + goto bail_connector; > + } > + > + connector->current_mode = display_mode; > + connector->current_drm_mode = *drm_mode; > + } > + > + result = VK_SUCCESS; > + > +bail_connector: > + drmModeFreeConnector(drm_connector); > +bail_mode_res: > + drmModeFreeResources(mode_res); > +bail: > + return result; > + > +} > + > +/* > + * 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; > + uint32_t i; > + struct wsi_display_image *image = NULL; > + VkIcdSurfaceDisplay *surface = chain->surface; > + wsi_display_mode *display_mode = wsi_display_mode_from_handle(surface->displayMode); > + wsi_display_connector *connector = display_mode->connector; > + int ret; > + VkResult result; > + > + if (wsi->master_fd < 0) > + return VK_ERROR_INITIALIZATION_FAILED; > + > + 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 */ > + > + for (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; > + > + if (connector->active) { > + ret = drmModePageFlip(wsi->master_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) { > + switch(-ret) { > + case EINVAL: > + > + 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->master_fd, connector->crtc_id, image->fb_id, 0, 0, > + &connector->id, 1, &connector->current_drm_mode); > + > + if (ret == 0) { > + image->state = wsi_image_displaying; > + wsi_display_idle_old_displaying(image); > + connector->active = true; > + return VK_SUCCESS; > + } > + break; > + case EACCES: > + usleep(1000 * 1000); > + connector->active = false; > + break; > + default: > + connector->active = false; > + image->state = wsi_image_idle; > + return VK_ERROR_OUT_OF_DATE_KHR; > + } > + } > + } > +} > + > +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; > + > + 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); > + > + pthread_mutex_unlock(&wsi->wait_mutex); > + > + return result; > +} > + > +static VkResult > +wsi_display_surface_create_swapchain(VkIcdSurfaceBase *icd_surface, > + VkDevice device, > + struct wsi_device *wsi_device, > + int local_fd, > + 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]; > + VkResult result; > + > + assert(create_info->sType == VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR); > + > + struct wsi_display_swapchain *chain; > + const unsigned num_images = create_info->minImageCount; > + size_t size = sizeof(*chain) + num_images * sizeof(chain->images[0]); > + > + chain = vk_alloc(allocator, size, 8, > + VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); > + > + if (chain == NULL) > + return VK_ERROR_OUT_OF_HOST_MEMORY; > + > + result = wsi_swapchain_init(wsi_device, &chain->base, device, > + create_info, allocator); > + > + 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->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) > + goto fail_init_images; > + } > + > + *swapchain_out = &chain->base; > + > + return VK_SUCCESS; > + > +fail_init_images: > + return result; > +} > + > +VkResult > +wsi_display_init_wsi(struct wsi_device *wsi_device, > + const VkAllocationCallbacks *alloc, > + VkPhysicalDevice physical_device, > + int device_fd) > +{ > + struct wsi_display *wsi; > + VkResult result; > + > + wsi = vk_alloc(alloc, sizeof(*wsi), 8, > + VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); > + if (!wsi) { > + result = VK_ERROR_OUT_OF_HOST_MEMORY; > + goto fail; > + } > + memset(wsi, '\0', sizeof (*wsi)); > + > + wsi->master_fd = -1; > + if (drmGetNodeTypeFromFd(device_fd) == DRM_NODE_PRIMARY) > + wsi->master_fd = device_fd; > + wsi->render_fd = device_fd; > + > + pthread_mutex_init(&wsi->wait_mutex, NULL); > + wsi->physical_device = physical_device; > + wsi->alloc = alloc; > + > + LIST_INITHEAD(&wsi->display_modes); > + LIST_INITHEAD(&wsi->connectors); > + > + pthread_condattr_t condattr; > + int ret; > + > + ret = pthread_mutex_init(&wsi->wait_mutex, NULL); > + if (ret) { > + result = VK_ERROR_OUT_OF_HOST_MEMORY; > + goto fail_mutex; > + } > + > + ret = pthread_condattr_init(&condattr); > + if (ret) { > + result = VK_ERROR_OUT_OF_HOST_MEMORY; > + goto fail_condattr; > + } > + > + ret = pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC); > + if (ret) { > + result = VK_ERROR_OUT_OF_HOST_MEMORY; > + goto fail_setclock; > + } > + > + ret = pthread_cond_init(&wsi->wait_cond, &condattr); > + if (ret) { > + result = VK_ERROR_OUT_OF_HOST_MEMORY; > + goto fail_cond; > + } > + > + pthread_condattr_destroy(&condattr); > + > + wsi->base.get_support = wsi_display_surface_get_support; > + wsi->base.get_capabilities = wsi_display_surface_get_capabilities; > + 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.create_swapchain = wsi_display_surface_create_swapchain; > + > + wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY] = &wsi->base; > + > + return VK_SUCCESS; > + > +fail_cond: > +fail_setclock: > + pthread_condattr_destroy(&condattr); > +fail_condattr: > + 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) { if (!wsi) return; and carry on without the extra indent I don't know enough for this to be an actual review though, but the rest of this file looks reasonable to me :) > + > + struct wsi_display_connector *connector, *connector_storage; > + LIST_FOR_EACH_ENTRY_SAFE(connector, connector_storage, &wsi->connectors, list) { > + vk_free(wsi->alloc, connector); > + } > + > + struct wsi_display_mode *mode, *mode_storage; > + LIST_FOR_EACH_ENTRY_SAFE(mode, mode_storage, &wsi->display_modes, list) { > + vk_free(wsi->alloc, mode); > + } > + > + 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); > + } > +} > diff --git a/src/vulkan/wsi/wsi_common_display.h b/src/vulkan/wsi/wsi_common_display.h > new file mode 100644 > index 00000000000..b414a226293 > --- /dev/null > +++ b/src/vulkan/wsi/wsi_common_display.h > @@ -0,0 +1,72 @@ > +/* > + * 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_plane_properties(VkPhysicalDevice physical_device, > + struct wsi_device *wsi_device, > + uint32_t *property_count, > + VkDisplayPlanePropertiesKHR *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_get_display_plane_capabilities(VkPhysicalDevice physical_device, > + struct wsi_device *wsi_device, > + VkDisplayModeKHR mode_khr, > + uint32_t plane_index, > + VkDisplayPlaneCapabilitiesKHR *capabilities); > + > +VkResult > +wsi_create_display_surface(VkInstance instance, > + const VkAllocationCallbacks *pAllocator, > + const VkDisplaySurfaceCreateInfoKHR *pCreateInfo, > + VkSurfaceKHR *pSurface); > + > +#endif > diff --git a/src/vulkan/wsi/wsi_common_private.h b/src/vulkan/wsi/wsi_common_private.h > index 503b2a015dc..d38d2efa116 100644 > --- a/src/vulkan/wsi/wsi_common_private.h > +++ b/src/vulkan/wsi/wsi_common_private.h > @@ -135,6 +135,16 @@ 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, > + VkPhysicalDevice physical_device, > + int device_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 * \ > -- > 2.15.1 > > _______________________________________________ > dri-devel mailing list > dri-devel@lists.freedesktop.org > https://lists.freedesktop.org/mailman/listinfo/dri-devel
Hi Keith, A few top level comments: - using card/primary node and (missing) authentication Current code opens the primary node when VK_KHR_display is available. Thus running a platform=drm,x11,wayland build will fail, as the client is not authenticated with the X/WL server. - reuse "drm" as a shorthand for the "display" We've been using drm for egl/gbm, va/drm, etc, one less platform plus no equivalent in either of egl/va/... - remove conditional compilation based on library version/features. Eg. checks like the following should be avoided. #if DRM_EVENT_CONTEXT_VERSION - use drmModeAddFB2 (or the withModifiers version even) over drmModeAddFB Might help with the XXX just above it ;-) - the spec says we're at VK_KHR_display rev 21, while the code advertises rev1 Extension('VK_KHR_display', 1, 'VK_USE_PLATFORM_DISPLAY_KHR'), - there are plenty of unnecessary of headers #include(d) - we could simplify the ifdef spaghetti by making wsi_*_{init,finish}_wsi empty stubs Definitely something that can be done, independently of your work. HTH Emil
Eric Engestrom <eric.engestrom@imgtec.com> writes: > copy/paste error: s/drm/display/ That's actually intentional -- any system which supports 'drm' can support the display backend as it's based on that. Maybe it should use a different test? (note that I haven't been using the autotools backend recently, so it may not be great at this point. At least my current tree builds?) >> +if with_platform_display >> + radv_flags += [ >> + '-DVK_USE_PLATFORM_DISPLAY_KHR', >> + ] > > Nit: this can be a simple > radv_flags += '-DVK_USE_PLATFORM_DISPLAY_KHR' > >> +if with_platform_display >> + vulkan_wsi_args += [ >> + '-DVK_USE_PLATFORM_DISPLAY_KHR', >> + ] > > Ditto: > vulkan_wsi_args += '-DVK_USE_PLATFORM_DISPLAY_KHR' Oh, good point -- I'd split out the RANDR and DISPLAY bits but didn't simplify the meson stuff. > Nit: src/amd/vulkan/ uses tabs for indent, you used spaces. Thanks; I'll reconfigure my environment so that it uses tabs in that directory, and re-indent the changes. >> +#define MM_PER_PIXEL (1.0/96.0 * 25.4) > > unused Good catch; those got left in both anv and radv directories after some refactoring. >> +#if 0 > > `#if DEBUG` maybe? Could do; I could also just strip out the printf debugging, but when you're doing timing-sensitive stuff at that level, printf debugging is pretty useful. >> +#define wsi_display_debug(...) fprintf(stderr, __VA_ARGS__) >> +#define wsi_display_debug_code(...) __VA_ARGS__ > > that 2nd one is unused It gets used in a later patch. >> + if (wsi) { > > if (!wsi) return; > and carry on without the extra indent Yeah, would probably look cleaner. > I don't know enough for this to be an actual review though, but the rest > of this file looks reasonable to me :) Thanks for reviewing the basic formatting and structure though; I totally missed the tabs/spaces issue.
Emil Velikov <emil.l.velikov@gmail.com> writes: > A few top level comments: Thanks much for looking over this code. I realize it's a large amount of change and hence more work to review. > - using card/primary node and (missing) authentication > Current code opens the primary node when VK_KHR_display is available. > Thus running a platform=drm,x11,wayland build will fail, as the client > is not authenticated with the X/WL server. this is really mostly there to make it easy to test the KHR_display backend without needing any further code. I added this only recently for this purpose. I'm not sure how we should be selecting for opening the primary device; perhaps another extension that adds a block in the pNext chain to look for? I don't know if you saw the first version of this series, but I had specified a new extension which provided the device FD from the application, letting you pass in whatever you could open (including a leased FD from X or a primary opened from the application). That seems like it could use some design ideas so we can make this do what we want. One option would be to remove the primary device code from the drivers once the leasing code is added so that the only way to use KHR_display is by leasing resources from the window system? The radv driver goes a bit further and checks with the kernel to see if rendering will work. For anv, the ioctls necessary for drawing are permitting on a non-authenticated node. Both drivers work fine for direct and X as-is, but I'm not very happy with having a random app with the primary device open as that seems like a mistake waiting to happen. > - reuse "drm" as a shorthand for the "display" > We've been using drm for egl/gbm, va/drm, etc, one less platform plus > no equivalent in either of egl/va/... Yeah, anything that supports drm can support display. I'll review the build process and try to simplify it further. > - remove conditional compilation based on library version/features. > Eg. checks like the following should be avoided. > #if DRM_EVENT_CONTEXT_VERSION Should I just have the build depend on a recent enough version of the library? > - use drmModeAddFB2 (or the withModifiers version even) over drmModeAddFB > Might help with the XXX just above it ;-) It won't help with that -- I just need more information from the driver. However, that information could come back as a format instead of depth/bpp, which would mean using drmModeAddFB2 instead of drmModeAddFB. Thanks for reminding me of this XXX. wsi_create_*_image gets a VkSwapchainCreateInfoKHR which has a VkFormat inside. Is that sufficient to compute a fourcc format for AddFB2? I think I could probably guess depth/bpp from it and be right most of the time? > - the spec says we're at VK_KHR_display rev 21, while the code advertises rev1 > Extension('VK_KHR_display', 1, > 'VK_USE_PLATFORM_DISPLAY_KHR'), Thanks. The version of the spec I've got (1.0.49) says it's already at rev 23! > - there are plenty of unnecessary of headers #include(d) As is often the case. I can go trim things down. > - we could simplify the ifdef spaghetti by making > wsi_*_{init,finish}_wsi empty stubs > Definitely something that can be done, independently of your work. That would be lovely.
On Fri, Feb 9, 2018 at 8:45 PM, Keith Packard <keithp@keithp.com> wrote: > This adds support for the KHR_display extension to the anv and radv > Vulkan drivers. The drivers now attempt to open the master DRM node > when the KHR_display extension is requested so that the common winsys > code can perform the necessary operations. > > Signed-off-by: Keith Packard <keithp@keithp.com> > --- > configure.ac | 1 + > meson.build | 4 +- > src/amd/vulkan/Makefile.am | 8 + > src/amd/vulkan/Makefile.sources | 3 + > src/amd/vulkan/meson.build | 7 + > src/amd/vulkan/radv_device.c | 28 +- > src/amd/vulkan/radv_extensions.py | 7 +- > src/amd/vulkan/radv_private.h | 2 + > src/amd/vulkan/radv_wsi.c | 3 +- > src/amd/vulkan/radv_wsi_display.c | 143 ++++ > src/intel/Makefile.sources | 3 + > src/intel/Makefile.vulkan.am | 7 + > src/intel/vulkan/anv_device.c | 18 +- > src/intel/vulkan/anv_extensions.py | 1 + > src/intel/vulkan/anv_extensions_gen.py | 5 +- > src/intel/vulkan/anv_wsi.c | 3 +- > src/intel/vulkan/anv_wsi_display.c | 129 +++ > src/intel/vulkan/meson.build | 7 + > src/vulkan/Makefile.am | 7 + > src/vulkan/Makefile.sources | 4 + > src/vulkan/wsi/meson.build | 10 + > src/vulkan/wsi/wsi_common.c | 19 +- > src/vulkan/wsi/wsi_common.h | 5 +- > src/vulkan/wsi/wsi_common_display.c | 1368 > ++++++++++++++++++++++++++++++++ > src/vulkan/wsi/wsi_common_display.h | 72 ++ > src/vulkan/wsi/wsi_common_private.h | 10 + > 26 files changed, 1858 insertions(+), 16 deletions(-) > create mode 100644 src/amd/vulkan/radv_wsi_display.c > create mode 100644 src/intel/vulkan/anv_wsi_display.c > create mode 100644 src/vulkan/wsi/wsi_common_display.c > create mode 100644 src/vulkan/wsi/wsi_common_display.h > > diff --git a/configure.ac b/configure.ac > index 8ed606c7694..46318365603 100644 > --- a/configure.ac > +++ b/configure.ac > @@ -1849,6 +1849,7 @@ fi > AM_CONDITIONAL(HAVE_PLATFORM_X11, echo "$platforms" | grep -q 'x11') > AM_CONDITIONAL(HAVE_PLATFORM_WAYLAND, echo "$platforms" | grep -q > 'wayland') > AM_CONDITIONAL(HAVE_PLATFORM_DRM, echo "$platforms" | grep -q 'drm') > +AM_CONDITIONAL(HAVE_PLATFORM_DISPLAY, echo "$platforms" | grep -q 'drm') > AM_CONDITIONAL(HAVE_PLATFORM_SURFACELESS, echo "$platforms" | grep -q > 'surfaceless') > AM_CONDITIONAL(HAVE_PLATFORM_ANDROID, echo "$platforms" | grep -q > 'android') > > diff --git a/meson.build b/meson.build > index b39e2f8ab96..aeb7f5e2917 100644 > --- a/meson.build > +++ b/meson.build > @@ -239,11 +239,12 @@ with_platform_wayland = false > with_platform_x11 = false > with_platform_drm = false > with_platform_surfaceless = false > +with_platform_display = false > egl_native_platform = '' > _platforms = get_option('platforms') > if _platforms == 'auto' > if system_has_kms_drm > - _platforms = 'x11,wayland,drm,surfaceless' > + _platforms = 'x11,wayland,drm,surfaceless,display' > elif ['darwin', 'windows', 'cygwin'].contains(host_machine.system()) > _platforms = 'x11,surfaceless' > else > @@ -257,6 +258,7 @@ if _platforms != '' > with_platform_wayland = _split.contains('wayland') > with_platform_drm = _split.contains('drm') > with_platform_surfaceless = _split.contains('surfaceless') > + with_platform_display = _split.contains('display') > egl_native_platform = _split[0] > endif > > diff --git a/src/amd/vulkan/Makefile.am b/src/amd/vulkan/Makefile.am > index 61025968942..061b8144b88 100644 > --- a/src/amd/vulkan/Makefile.am > +++ b/src/amd/vulkan/Makefile.am > @@ -76,6 +76,14 @@ VULKAN_LIB_DEPS = \ > $(DLOPEN_LIBS) \ > -lm > > +if HAVE_PLATFORM_DISPLAY > +AM_CPPFLAGS += \ > + -DVK_USE_PLATFORM_DISPLAY_KHR > + > +VULKAN_SOURCES += $(VULKAN_WSI_DISPLAY_FILES) > + > +endif > + > if HAVE_PLATFORM_X11 > AM_CPPFLAGS += \ > $(XCB_DRI3_CFLAGS) \ > diff --git a/src/amd/vulkan/Makefile.sources > b/src/amd/vulkan/Makefile.sources > index a510d88d965..618a6cdaed0 100644 > --- a/src/amd/vulkan/Makefile.sources > +++ b/src/amd/vulkan/Makefile.sources > @@ -78,6 +78,9 @@ VULKAN_WSI_WAYLAND_FILES := \ > VULKAN_WSI_X11_FILES := \ > radv_wsi_x11.c > > +VULKAN_WSI_DISPLAY_FILES := \ > + radv_wsi_display.c > + > VULKAN_GENERATED_FILES := \ > radv_entrypoints.c \ > radv_entrypoints.h \ > diff --git a/src/amd/vulkan/meson.build b/src/amd/vulkan/meson.build > index 0a7b7c0bf3c..b7bb1075e7d 100644 > --- a/src/amd/vulkan/meson.build > +++ b/src/amd/vulkan/meson.build > @@ -112,6 +112,13 @@ if with_platform_wayland > libradv_files += files('radv_wsi_wayland.c') > endif > > +if with_platform_display > + radv_flags += [ > + '-DVK_USE_PLATFORM_DISPLAY_KHR', > + ] > + libradv_files += files('radv_wsi_display.c') > +endif > + > libvulkan_radeon = shared_library( > 'vulkan_radeon', > [libradv_files, radv_entrypoints, radv_extensions_c, vk_format_table_c], > diff --git a/src/amd/vulkan/radv_device.c b/src/amd/vulkan/radv_device.c > index 09bb382eeb8..adf33eb35dc 100644 > --- a/src/amd/vulkan/radv_device.c > +++ b/src/amd/vulkan/radv_device.c > @@ -191,9 +191,26 @@ radv_physical_device_init(struct > radv_physical_device *device, > const char *path = drm_device->nodes[DRM_NODE_RENDER]; > VkResult result; > drmVersionPtr version; > - int fd; > - > - fd = open(path, O_RDWR | O_CLOEXEC); > + int fd = -1; > + > + if (instance->khr_display_requested) { > + fd = open(drm_device->nodes[DRM_NODE_PRIMARY], O_RDWR | > O_CLOEXEC); > + if (fd >= 0) { > + uint32_t accel_working = 0; > + struct drm_amdgpu_info request = { > + .return_pointer = > (uintptr_t)&accel_working, > + .return_size = sizeof(accel_working), > + .query = AMDGPU_INFO_ACCEL_WORKING > + }; > + > + if (drmCommandWrite(fd, DRM_AMDGPU_INFO, > &request, sizeof (struct drm_amdgpu_info)) < 0 || !accel_working) { > + close(fd); > + fd = -1; > + } > + } > + } > + if (fd < 0) > + fd = open(path, O_RDWR | O_CLOEXEC); > if (fd < 0) > return vk_error(VK_ERROR_INCOMPATIBLE_DRIVER); > > @@ -209,6 +226,7 @@ radv_physical_device_init(struct radv_physical_device > *device, > close(fd); > return VK_ERROR_INCOMPATIBLE_DRIVER; > } > + > drmFreeVersion(version); > > device->_loader_data.loaderMagic = ICD_LOADER_MAGIC; > @@ -387,6 +405,7 @@ VkResult radv_CreateInstance( > { > struct radv_instance *instance; > VkResult result; > + bool khr_display_requested = false; > > assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_INSTANCE_CRE > ATE_INFO); > > @@ -411,6 +430,8 @@ VkResult radv_CreateInstance( > const char *ext_name = pCreateInfo->ppEnabledExtensio > nNames[i]; > if (!radv_instance_extension_supported(ext_name)) > return vk_error(VK_ERROR_EXTENSION_NOT_PRESENT); > + if (strcmp(ext_name, VK_KHR_DISPLAY_EXTENSION_NAME) == 0) > + khr_display_requested = true; > } > > instance = vk_zalloc2(&default_alloc, pAllocator, > sizeof(*instance), 8, > @@ -427,6 +448,7 @@ VkResult radv_CreateInstance( > > instance->apiVersion = client_version; > instance->physicalDeviceCount = -1; > + instance->khr_display_requested = khr_display_requested; > > result = vk_debug_report_instance_init( > &instance->debug_report_callbacks); > if (result != VK_SUCCESS) { > diff --git a/src/amd/vulkan/radv_extensions.py > b/src/amd/vulkan/radv_extensions.py > index d761895d3a0..24cab8cbb39 100644 > --- a/src/amd/vulkan/radv_extensions.py > +++ b/src/amd/vulkan/radv_extensions.py > @@ -81,6 +81,7 @@ EXTENSIONS = [ > Extension('VK_KHR_wayland_surface', 6, > 'VK_USE_PLATFORM_WAYLAND_KHR'), > Extension('VK_KHR_xcb_surface', 6, > 'VK_USE_PLATFORM_XCB_KHR'), > Extension('VK_KHR_xlib_surface', 6, > 'VK_USE_PLATFORM_XLIB_KHR'), > + Extension('VK_KHR_display', 1, > 'VK_USE_PLATFORM_DISPLAY_KHR'), > Extension('VK_KHX_multiview', 1, '!ANDROID'), > Extension('VK_EXT_debug_report', 9, True), > Extension('VK_EXT_discard_rectangles', 1, True), > @@ -168,7 +169,7 @@ _TEMPLATE = Template(COPYRIGHT + """ > #include "vk_util.h" > > /* Convert the VK_USE_PLATFORM_* defines to booleans */ > -%for platform in ['ANDROID', 'WAYLAND', 'XCB', 'XLIB']: > +%for platform in ['ANDROID', 'WAYLAND', 'XCB', 'XLIB', 'DISPLAY']: > #ifdef VK_USE_PLATFORM_${platform}_KHR > # undef VK_USE_PLATFORM_${platform}_KHR > # define VK_USE_PLATFORM_${platform}_KHR true > @@ -187,7 +188,9 @@ _TEMPLATE = Template(COPYRIGHT + """ > > #define RADV_HAS_SURFACE (VK_USE_PLATFORM_WAYLAND_KHR || \\ > VK_USE_PLATFORM_XCB_KHR || \\ > - VK_USE_PLATFORM_XLIB_KHR) > + VK_USE_PLATFORM_XLIB_KHR || \\ > + VK_USE_PLATFORM_DISPLAY_KHR) > + > > bool > radv_instance_extension_supported(const char *name) > diff --git a/src/amd/vulkan/radv_private.h b/src/amd/vulkan/radv_private.h > index be9e8f43964..1e3719bcc4f 100644 > --- a/src/amd/vulkan/radv_private.h > +++ b/src/amd/vulkan/radv_private.h > @@ -75,6 +75,7 @@ typedef uint32_t xcb_window_t; > #include "radv_entrypoints.h" > > #include "wsi_common.h" > +#include "wsi_common_display.h" > > #define ATI_VENDOR_ID 0x1002 > > @@ -300,6 +301,7 @@ struct radv_instance { > uint64_t perftest_flags; > > struct vk_debug_report_instance debug_report_callbacks; > + bool khr_display_requested; > }; > > VkResult radv_init_wsi(struct radv_physical_device *physical_device); > diff --git a/src/amd/vulkan/radv_wsi.c b/src/amd/vulkan/radv_wsi.c > index e016e837102..5ec872a63d0 100644 > --- a/src/amd/vulkan/radv_wsi.c > +++ b/src/amd/vulkan/radv_wsi.c > @@ -41,7 +41,8 @@ radv_init_wsi(struct radv_physical_device > *physical_device) > return wsi_device_init(&physical_device->wsi_device, > radv_physical_device_to_handl > e(physical_device), > radv_wsi_proc_addr, > - &physical_device->instance->alloc); > + &physical_device->instance->alloc, > + physical_device->local_fd); > } > > void > diff --git a/src/amd/vulkan/radv_wsi_display.c > b/src/amd/vulkan/radv_wsi_display.c > new file mode 100644 > index 00000000000..b0a4db0344b > --- /dev/null > +++ b/src/amd/vulkan/radv_wsi_display.c > @@ -0,0 +1,143 @@ > +/* > + * 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 <stdbool.h> > +#include <string.h> > +#include <unistd.h> > +#include <fcntl.h> > +#include "radv_private.h" > +#include "radv_cs.h" > +#include "util/disk_cache.h" > +#include "util/strtod.h" > +#include "vk_util.h" > +#include <xf86drm.h> > +#include <xf86drmMode.h> > +#include <amdgpu.h> > +#include <amdgpu_drm.h> > +#include "winsys/amdgpu/radv_amdgpu_winsys_public.h" > +#include "ac_llvm_util.h" > +#include "vk_format.h" > +#include "sid.h" > +#include "util/debug.h" > +#include "wsi_common_display.h" > + > +#define MM_PER_PIXEL (1.0/96.0 * 25.4) > + > +VkResult > +radv_GetPhysicalDeviceDisplayPropertiesKHR(VkPhysicalDevice > physical_device, > + uint32_t > *property_count, > + VkDisplayPropertiesKHR > *properties) > +{ > + RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device); > + > + return wsi_display_get_physical_device_display_properties(physical_ > device, > + > &pdevice->wsi_device, > + > property_count, > + properties); > +} > + > +VkResult > +radv_GetPhysicalDeviceDisplayPlanePropertiesKHR(VkPhysicalDevice > physical_device, > + uint32_t > *property_count, > + > VkDisplayPlanePropertiesKHR *properties) > +{ > + RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device); > + > + return wsi_display_get_physical_device_display_plane_properties( > physical_device, > + > &pdevice->wsi_device, > + > property_count, > + > properties); > +} > + > +VkResult > +radv_GetDisplayPlaneSupportedDisplaysKHR(VkPhysicalDevice > physical_device, > + uint32_t > plane_index, > + uint32_t > *display_count, > + VkDisplayKHR > *displays) > +{ > + RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device); > + > + return wsi_display_get_display_plane_supported_displays(physical_de > vice, > + > &pdevice->wsi_device, > + plane_index, > + display_count, > + displays); > +} > + > + > +VkResult > +radv_GetDisplayModePropertiesKHR(VkPhysicalDevice > physical_device, > + VkDisplayKHR display, > + uint32_t > *property_count, > + VkDisplayModePropertiesKHR > *properties) > +{ > + RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device); > + > + return wsi_display_get_display_mode_properties(physical_device, > + &pdevice->wsi_device, > + display, > + property_count, > + properties); > +} > + > +VkResult > +radv_CreateDisplayModeKHR(VkPhysicalDevice > physical_device, > + VkDisplayKHR display, > + const VkDisplayModeCreateInfoKHR > *create_info, > + const VkAllocationCallbacks > *allocator, > + VkDisplayModeKHR *mode) > +{ > + return VK_ERROR_INITIALIZATION_FAILED; > +} > + > + > +VkResult > +radv_GetDisplayPlaneCapabilitiesKHR(VkPhysicalDevice > physical_device, > + VkDisplayModeKHR > mode_khr, > + uint32_t > plane_index, > + VkDisplayPlaneCapabilitiesKHR > *capabilities) > +{ > + RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device); > + > + return wsi_get_display_plane_capabilities(physical_device, > + &pdevice->wsi_device, > + mode_khr, > + plane_index, > + capabilities); > +} > + > +VkResult > +radv_CreateDisplayPlaneSurfaceKHR(VkInstance > _instance, > + const VkDisplaySurfaceCreateInfoKHR > *create_info, > + const VkAllocationCallbacks > *allocator, > + VkSurfaceKHR > *surface) > +{ > + RADV_FROM_HANDLE(radv_instance, instance, _instance); > + const VkAllocationCallbacks *alloc; > + > + if (allocator) > + alloc = allocator; > + else > + alloc = &instance->alloc; > + > + return wsi_create_display_surface(_instance, alloc, create_info, > surface); > +} > diff --git a/src/intel/Makefile.sources b/src/intel/Makefile.sources > index 9595bf42582..6c142729d94 100644 > --- a/src/intel/Makefile.sources > +++ b/src/intel/Makefile.sources > @@ -240,6 +240,9 @@ VULKAN_WSI_WAYLAND_FILES := \ > VULKAN_WSI_X11_FILES := \ > vulkan/anv_wsi_x11.c > > +VULKAN_WSI_DISPLAY_FILES := \ > + vulkan/anv_wsi_display.c > + > VULKAN_GEM_FILES := \ > vulkan/anv_gem.c > > diff --git a/src/intel/Makefile.vulkan.am b/src/intel/Makefile.vulkan.am > index 23fa877e77d..7c428a799d7 100644 > --- a/src/intel/Makefile.vulkan.am > +++ b/src/intel/Makefile.vulkan.am > @@ -187,6 +187,13 @@ VULKAN_SOURCES += $(VULKAN_WSI_WAYLAND_FILES) > VULKAN_LIB_DEPS += $(WAYLAND_CLIENT_LIBS) > endif > > +if HAVE_PLATFORM_DISPLAY > +VULKAN_CPPFLAGS += \ > + -DVK_USE_PLATFORM_DISPLAY_KHR > + > +VULKAN_SOURCES += $(VULKAN_WSI_DISPLAY_FILES) > +endif > + > noinst_LTLIBRARIES += vulkan/libvulkan_common.la > vulkan_libvulkan_common_la_SOURCES = $(VULKAN_SOURCES) > vulkan_libvulkan_common_la_CFLAGS = $(VULKAN_CFLAGS) > diff --git a/src/intel/vulkan/anv_device.c b/src/intel/vulkan/anv_device.c > index 86c1bdc1d51..9614907fda3 100644 > --- a/src/intel/vulkan/anv_device.c > +++ b/src/intel/vulkan/anv_device.c > @@ -277,14 +277,25 @@ anv_physical_device_init_uuids(struct > anv_physical_device *device) > static VkResult > anv_physical_device_init(struct anv_physical_device *device, > struct anv_instance *instance, > - const char *path) > + const char *primary_path, > + const char *render_path) > { > VkResult result; > - int fd; > + int fd = -1; > + const char *path; > > brw_process_intel_debug_variable(); > > - fd = open(path, O_RDWR | O_CLOEXEC); > + if (instance->enabled_extensions.KHR_display) { > + path = primary_path; > + fd = open(path, O_RDWR | O_CLOEXEC); > + } > + > + if (fd < 0) { > + path = render_path; > + fd = open(path, O_RDWR | O_CLOEXEC); > + } > It seems a little odd to me to default to opening the master node and then fall back to the render node if it doesn't work. I suppose that's probably ok so long as we ensure that vkGetPhysicalDeviceDisplayPropertiesKHR returns no displays if we're on the render node. We could always go back to the DRM fd extension idea but I'm not coming up with something nice and clean in the 60 seconds I've thought about it. + > if (fd < 0) > return vk_error(VK_ERROR_INCOMPATIBLE_DRIVER); > > @@ -652,6 +663,7 @@ anv_enumerate_devices(struct anv_instance *instance) > > result = anv_physical_device_init(&instance->physicalDevice, > instance, > + devices[i]->nodes[DRM_NODE_PRIMARY], > devices[i]->nodes[DRM_NODE_RENDER]); > if (result != VK_ERROR_INCOMPATIBLE_DRIVER) > break; > diff --git a/src/intel/vulkan/anv_extensions.py > b/src/intel/vulkan/anv_extensions.py > index 581921e62a1..978a219e2b2 100644 > --- a/src/intel/vulkan/anv_extensions.py > +++ b/src/intel/vulkan/anv_extensions.py > @@ -83,6 +83,7 @@ EXTENSIONS = [ > Extension('VK_KHR_wayland_surface', 6, > 'VK_USE_PLATFORM_WAYLAND_KHR'), > Extension('VK_KHR_xcb_surface', 6, > 'VK_USE_PLATFORM_XCB_KHR'), > Extension('VK_KHR_xlib_surface', 6, > 'VK_USE_PLATFORM_XLIB_KHR'), > + Extension('VK_KHR_display', 1, > 'VK_USE_PLATFORM_DISPLAY_KHR'), > Extension('VK_KHX_multiview', 1, True), > Extension('VK_EXT_debug_report', 8, True), > Extension('VK_EXT_external_memory_dma_buf', 1, True), > diff --git a/src/intel/vulkan/anv_extensions_gen.py > b/src/intel/vulkan/anv_extensions_gen.py > index 33827ecd015..84d07f9767a 100644 > --- a/src/intel/vulkan/anv_extensions_gen.py > +++ b/src/intel/vulkan/anv_extensions_gen.py > @@ -113,7 +113,7 @@ _TEMPLATE_C = Template(COPYRIGHT + """ > #include "vk_util.h" > > /* Convert the VK_USE_PLATFORM_* defines to booleans */ > -%for platform in ['ANDROID', 'WAYLAND', 'XCB', 'XLIB']: > +%for platform in ['ANDROID', 'WAYLAND', 'XCB', 'XLIB', 'DISPLAY']: > #ifdef VK_USE_PLATFORM_${platform}_KHR > # undef VK_USE_PLATFORM_${platform}_KHR > # define VK_USE_PLATFORM_${platform}_KHR true > @@ -132,7 +132,8 @@ _TEMPLATE_C = Template(COPYRIGHT + """ > > #define ANV_HAS_SURFACE (VK_USE_PLATFORM_WAYLAND_KHR || \\ > VK_USE_PLATFORM_XCB_KHR || \\ > - VK_USE_PLATFORM_XLIB_KHR) > + VK_USE_PLATFORM_XLIB_KHR || \\ > + VK_USE_PLATFORM_DISPLAY_KHR) > > const VkExtensionProperties anv_instance_extensions[ANV_INSTANCE_EXTENSION_COUNT] > = { > %for ext in instance_extensions: > diff --git a/src/intel/vulkan/anv_wsi.c b/src/intel/vulkan/anv_wsi.c > index 6082c3dd093..f86d83589ea 100644 > --- a/src/intel/vulkan/anv_wsi.c > +++ b/src/intel/vulkan/anv_wsi.c > @@ -39,7 +39,8 @@ anv_init_wsi(struct anv_physical_device *physical_device) > return wsi_device_init(&physical_device->wsi_device, > anv_physical_device_to_handle(physical_device), > anv_wsi_proc_addr, > - &physical_device->instance->alloc); > + &physical_device->instance->alloc, > + physical_device->local_fd); > } > > void > diff --git a/src/intel/vulkan/anv_wsi_display.c > b/src/intel/vulkan/anv_wsi_display.c > new file mode 100644 > index 00000000000..9b00d7f02e4 > --- /dev/null > +++ b/src/intel/vulkan/anv_wsi_display.c > @@ -0,0 +1,129 @@ > +/* > + * 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 "anv_private.h" > +#include "wsi_common.h" > +#include "vk_format_info.h" > +#include "vk_util.h" > +#include "wsi_common_display.h" > + > +#define MM_PER_PIXEL (1.0/96.0 * 25.4) > + > +VkResult > +anv_GetPhysicalDeviceDisplayPropertiesKHR(VkPhysicalDevice > physical_device, > + uint32_t > *property_count, > + VkDisplayPropertiesKHR > *properties) > +{ > + ANV_FROM_HANDLE(anv_physical_device, pdevice, physical_device); > + > + return wsi_display_get_physical_device_display_properties(physical_ > device, > + > &pdevice->wsi_device, > + > property_count, > + properties); > +} > + > +VkResult > +anv_GetPhysicalDeviceDisplayPlanePropertiesKHR(VkPhysicalDevice > physical_device, > + uint32_t > *property_count, > + > VkDisplayPlanePropertiesKHR *properties) > +{ > + ANV_FROM_HANDLE(anv_physical_device, pdevice, physical_device); > + > + return wsi_display_get_physical_device_display_plane_properties( > physical_device, > + > &pdevice->wsi_device, > + > property_count, > + > properties); > +} > + > +VkResult > +anv_GetDisplayPlaneSupportedDisplaysKHR(VkPhysicalDevice > physical_device, > + uint32_t > plane_index, > + uint32_t > *display_count, > + VkDisplayKHR > *displays) > +{ > + ANV_FROM_HANDLE(anv_physical_device, pdevice, physical_device); > + > + return wsi_display_get_display_plane_supported_displays(physical_de > vice, > + > &pdevice->wsi_device, > + plane_index, > + display_count, > + displays); > +} > + > + > +VkResult > +anv_GetDisplayModePropertiesKHR(VkPhysicalDevice > physical_device, > + VkDisplayKHR display, > + uint32_t > *property_count, > + VkDisplayModePropertiesKHR > *properties) > +{ > + ANV_FROM_HANDLE(anv_physical_device, pdevice, physical_device); > + > + return wsi_display_get_display_mode_properties(physical_device, > + &pdevice->wsi_device, > + display, > + property_count, > + properties); > +} > + > +VkResult > +anv_CreateDisplayModeKHR(VkPhysicalDevice > physical_device, > + VkDisplayKHR display, > + const VkDisplayModeCreateInfoKHR > *create_info, > + const VkAllocationCallbacks > *allocator, > + VkDisplayModeKHR *mode) > +{ > + return VK_ERROR_INITIALIZATION_FAILED; > +} > + > + > +VkResult > +anv_GetDisplayPlaneCapabilitiesKHR(VkPhysicalDevice > physical_device, > + VkDisplayModeKHR > mode_khr, > + uint32_t > plane_index, > + VkDisplayPlaneCapabilitiesKHR > *capabilities) > +{ > + ANV_FROM_HANDLE(anv_physical_device, pdevice, physical_device); > + > + return wsi_get_display_plane_capabilities(physical_device, > + &pdevice->wsi_device, > + mode_khr, > + plane_index, > + capabilities); > +} > + > +VkResult > +anv_CreateDisplayPlaneSurfaceKHR(VkInstance > _instance, > + const VkDisplaySurfaceCreateInfoKHR > *create_info, > + const VkAllocationCallbacks > *allocator, > + VkSurfaceKHR > *surface) > +{ > + ANV_FROM_HANDLE(anv_instance, instance, _instance); > + const VkAllocationCallbacks *alloc; > + > + if (allocator) > + alloc = allocator; > + else > + alloc = &instance->alloc; > + > + return wsi_create_display_surface(_instance, alloc, create_info, > surface); > +} > diff --git a/src/intel/vulkan/meson.build b/src/intel/vulkan/meson.build > index 69ec26e19b6..2e2ab8f7ecd 100644 > --- a/src/intel/vulkan/meson.build > +++ b/src/intel/vulkan/meson.build > @@ -171,6 +171,13 @@ if with_platform_wayland > libanv_files += files('anv_wsi_wayland.c') > endif > > +if with_platform_display > + anv_flags += [ > + '-DVK_USE_PLATFORM_DISPLAY_KHR', > + ] > + libanv_files += files('anv_wsi_display.c') > +endif > + > libanv_common = static_library( > 'anv_common', > [libanv_files, anv_entrypoints, anv_extensions_c, anv_extensions_h], > diff --git a/src/vulkan/Makefile.am b/src/vulkan/Makefile.am > index 037436c1cd7..c33ac5758f7 100644 > --- a/src/vulkan/Makefile.am > +++ b/src/vulkan/Makefile.am > @@ -57,6 +57,13 @@ AM_CPPFLAGS += \ > VULKAN_WSI_SOURCES += $(VULKAN_WSI_X11_FILES) > endif > > +if HAVE_PLATFORM_DISPLAY > +AM_CPPFLAGS += \ > + -DVK_USE_PLATFORM_DISPLAY_KHR > + > +VULKAN_WSI_SOURCES += $(VULKAN_WSI_DISPLAY_FILES) > +endif > + > BUILT_SOURCES += $(VULKAN_WSI_WAYLAND_GENERATED_FILES) > CLEANFILES = $(BUILT_SOURCES) > > diff --git a/src/vulkan/Makefile.sources b/src/vulkan/Makefile.sources > index a0a24ce7de8..3642c7662c4 100644 > --- a/src/vulkan/Makefile.sources > +++ b/src/vulkan/Makefile.sources > @@ -17,6 +17,10 @@ VULKAN_WSI_X11_FILES := \ > wsi/wsi_common_x11.c \ > wsi/wsi_common_x11.h > > +VULKAN_WSI_DISPLAY_FILES := \ > + wsi/wsi_common_display.c \ > + wsi/wsi_common_display.h > + > VULKAN_UTIL_FILES := \ > util/vk_alloc.h \ > util/vk_debug_report.c \ > diff --git a/src/vulkan/wsi/meson.build b/src/vulkan/wsi/meson.build > index bd0fd3cc53e..743631a6113 100644 > --- a/src/vulkan/wsi/meson.build > +++ b/src/vulkan/wsi/meson.build > @@ -57,6 +57,16 @@ if with_platform_wayland > ] > endif > > +if with_platform_display > + vulkan_wsi_args += [ > + '-DVK_USE_PLATFORM_DISPLAY_KHR', > + ] > + files_vulkan_wsi += files( > + 'wsi_common_display.c', > + 'wsi_common_display.h', > + ) > +endif > + > libvulkan_wsi = static_library( > 'vulkan_wsi', > files_vulkan_wsi, > diff --git a/src/vulkan/wsi/wsi_common.c b/src/vulkan/wsi/wsi_common.c > index 90ed07b7857..c0a285e5814 100644 > --- a/src/vulkan/wsi/wsi_common.c > +++ b/src/vulkan/wsi/wsi_common.c > @@ -29,7 +29,8 @@ VkResult > wsi_device_init(struct wsi_device *wsi, > VkPhysicalDevice pdevice, > WSI_FN_GetPhysicalDeviceProcAddr proc_addr, > - const VkAllocationCallbacks *alloc) > + const VkAllocationCallbacks *alloc, > + int device_fd) > { > VkResult result; > > @@ -89,6 +90,19 @@ wsi_device_init(struct wsi_device *wsi, > } > #endif > > +#ifdef VK_USE_PLATFORM_DISPLAY_KHR > + result = wsi_display_init_wsi(wsi, alloc, pdevice, device_fd); > + if (result != VK_SUCCESS) { > +#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 > + return result; > + } > +#endif > Not your problem but we really need a better clean-up path.... > + > return VK_SUCCESS; > } > > @@ -96,6 +110,9 @@ 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 > diff --git a/src/vulkan/wsi/wsi_common.h b/src/vulkan/wsi/wsi_common.h > index 3e0d3be1c24..1cb6aaebca0 100644 > --- a/src/vulkan/wsi/wsi_common.h > +++ b/src/vulkan/wsi/wsi_common.h > @@ -50,7 +50,7 @@ struct wsi_memory_allocate_info { > > struct wsi_interface; > > -#define VK_ICD_WSI_PLATFORM_MAX 5 > +#define VK_ICD_WSI_PLATFORM_MAX 6 > > struct wsi_device { > VkPhysicalDeviceMemoryProperties memory_props; > @@ -93,7 +93,8 @@ VkResult > wsi_device_init(struct wsi_device *wsi, > VkPhysicalDevice pdevice, > WSI_FN_GetPhysicalDeviceProcAddr proc_addr, > - const VkAllocationCallbacks *alloc); > + const VkAllocationCallbacks *alloc, > + int device_fd); > > void > wsi_device_finish(struct wsi_device *wsi, > diff --git a/src/vulkan/wsi/wsi_common_display.c > b/src/vulkan/wsi/wsi_common_display.c > new file mode 100644 > index 00000000000..2732b1dd721 > --- /dev/null > +++ b/src/vulkan/wsi/wsi_common_display.c > @@ -0,0 +1,1368 @@ > +/* > + * 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 "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; > Would it make anything easier if we just storred the DRM struct here? "No" is a perfectly valid answer. > +} 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; > + wsi_display_mode *current_mode; > + drmModeModeInfo current_drm_mode; > +} wsi_display_connector; > + > +struct wsi_display { > + struct wsi_interface base; > + > + const VkAllocationCallbacks *alloc; > + VkPhysicalDevice physical_device; > + > + int master_fd; > + int render_fd; > + > + pthread_mutex_t wait_mutex; > + pthread_cond_t wait_cond; > + pthread_t wait_thread; > + > + struct list_head connectors; > + > + struct list_head display_modes; > +}; > + > +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; > + uint64_t flip_sequence; > +}; > + > +struct wsi_display_swapchain { > + struct wsi_swapchain base; > + struct wsi_display *wsi; > + VkIcdSurfaceDisplay *surface; > + uint64_t flip_sequence; > + struct wsi_display_image images[0]; > +}; > + > +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 && > + wsi->vscan == drm->vscan && > + 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) (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; > +} > This should probably gone in a helper one day. Meh. > + > +static struct wsi_display_mode * > +wsi_display_find_drm_mode(struct wsi_device *wsi_device, > + struct wsi_display_connector *connector, > + drmModeModeInfoPtr mode) > +{ > + struct wsi_display *wsi = (struct wsi_display *) > wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; > + struct wsi_display_mode *display_mode; > + > + LIST_FOR_EACH_ENTRY(display_mode, &wsi->display_modes, list) { > + if (display_mode->connector == connector && > + wsi_display_mode_matches_drm(display_mode, mode)) > Any particular reason why the list of modes is global and not in the connector? It seems like it would be a tiny bit more efficient and convenient to put the list in the connector. > + return display_mode; > + } > + return NULL; > +} > + > +static void > +wsi_display_invalidate_connector_modes(struct wsi_device > *wsi_device, > + struct wsi_display_connector > *connector) > +{ > + struct wsi_display *wsi = (struct wsi_display *) > wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; > + struct wsi_display_mode *display_mode; > + > + LIST_FOR_EACH_ENTRY(display_mode, &wsi->display_modes, list) > + if (display_mode->connector == connector) > + display_mode->valid = false; > Please use braces for loops containing more than one line. > +} > + > +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; > + > + 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_alloc(wsi->alloc, sizeof (struct wsi_display_mode), > 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); > Since we're allocating these in a physical device query, we need to use the INSTANCE scope. the OBJECT scope is intended for vkCreate functions to allocated data that will live no longer than the associated vkDestroy function. > + 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, &wsi->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]; > + struct wsi_display_connector *connector; > + > + connector = NULL; > + LIST_FOR_EACH_ENTRY(connector, &wsi->connectors, list) { > + 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; > + > + connector = vk_alloc(wsi->alloc, sizeof (struct > wsi_display_connector), 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); > VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE > + memset(connector, '\0', sizeof (*connector)); > Just use vk_zalloc(). > + connector->id = connector_id; > + connector->wsi = wsi; > + connector->active = false; > + /* XXX use EDID name */ > + connector->name = "monitor"; > + 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]; > + struct wsi_display_connector *connector; > + drmModeConnectorPtr drm_connector; > + VkResult result; > + int m; > + > + if (wsi->master_fd < 0) > + return NULL; > + > + drm_connector = drmModeGetConnector(wsi->master_fd, connector_id); > + if (!drm_connector) > + return NULL; > + > + 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; > + > + /* 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 (m = 0; m < drm_connector->count_modes; m++) { > + 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) > Hooray for obviously false fixed constants! I know the answer to this will be "EDIDs lie, never trust them!" but can we get the real value somehow? As someone who has a 13" laptop with a 3200x1800 display, I know that number isn't always right. :-) > + > +static void > +wsi_display_fill_in_display_properties(struct wsi_device > *wsi_device, > + struct wsi_display_connector > *connector, > + VkDisplayPropertiesKHR > *properties) > +{ > + struct wsi_display *wsi = (struct wsi_display *) > wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; > + struct wsi_display_mode *display_mode, *preferred_mode = NULL;; > double-;; > + > + properties->display = wsi_display_connector_to_handle(connector); > + properties->displayName = connector->name; > + > + /* Find the preferred mode and assume that's the physical resolution */ > + > + LIST_FOR_EACH_ENTRY(display_mode, &wsi->display_modes, list) { > + if (display_mode->valid && display_mode->connector == connector && > display_mode->preferred) { > + preferred_mode = display_mode; > + break; > + } > + } > + > + if (preferred_mode) { > + properties->physicalResolution.width = preferred_mode->hdisplay; > + properties->physicalResolution.height = preferred_mode->vdisplay; > + } else { > + properties->physicalResolution.width = 1024; > + properties->physicalResolution.height = 768; > From the Vulkan spec: Note: For devices which have no natural value to return here, implementations *should* return the maximum resolution supported. We should walk the list and pick the biggest one. > + } > + > + /* 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); > See question about MM_PER_PIXEL above > + > + properties->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_ > BIT_KHR; > I know i915 can do better at least in some cases. Is there a practical way to expose this? If not, I'm fine with just exposing IDENTITY. > + properties->persistentContent = 0; > +} > + > +/* > + * 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]; > + struct wsi_display_connector *connector; > + int c; > + uint32_t connected; > + uint32_t property_count_requested = > *property_count; > + drmModeResPtr mode_res; > + > + if (wsi->master_fd < 0) > + return VK_ERROR_INITIALIZATION_FAILED; > + > + mode_res = drmModeGetResources(wsi->master_fd); > + > + if (!mode_res) > + return VK_ERROR_INITIALIZATION_FAILED; > This error is not allowed for this function. We should just write 0 to property_count and return VK_SUCCESS. Maybe add some asserts for debug builds if you really think this shouldn't ever happen. > + > + connected = 0; > + > + /* Get current information */ > + for (c = 0; c < mode_res->count_connectors; c++) { > + 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) > + connected++; > + } > + > + /* Fill in property information if requested */ > + if (properties != NULL) { > + connected = 0; > + > + for (c = 0; c < mode_res->count_connectors; c++) { > + connector = wsi_display_find_connector(wsi_device, > mode_res->connectors[c]); > + > + if (connector && connector->connected) { > + if (connected < property_count_requested) { > + wsi_display_fill_in_display_properties(wsi_device, > + connector, > + > &properties[connected]); > + } > + connected++; > + } > + } > + } > This could be made a lot easier with vk_outarray: VK_OUTARRAY_MAKE(conn, properties, property_count); for (int c = 0; c < mode_res->count_connectors; c++) { 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); It's a bit magic but a lot less typing and you don't have to think about all the details of whether or not you've gotten the return value right. > + > + drmModeFreeResources(mode_res); > + > + *property_count = connected; > + > + if (connected > property_count_requested && properties != NULL) > + return VK_INCOMPLETE; > + > + return VK_SUCCESS; > +} > + > +/* > + * Implement vkGetPhysicalDeviceDisplayPlanePropertiesKHR (VK_KHR_display > + */ > +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]; > + struct wsi_display_connector *connector; > + uint32_t property_count_requested = > *property_count; > + int c; > + > + if (!properties) > + property_count_requested = 0; > + > + c = 0; > + LIST_FOR_EACH_ENTRY(connector, &wsi->connectors, list) { > + if (c < property_count_requested) { > + if (connector && connector->active) { > + properties[c].currentDisplay = wsi_display_connector_to_handl > e(connector); > + properties[c].currentStackIndex = c; > + } else { > + properties[c].currentDisplay = NULL; > + properties[c].currentStackIndex = 0; > It's not entirely clear what the stack index for a disabled plane should be. I guess 0 is as good as anything. > + } > + } > + c++; > + } > + > + *property_count = c; > + > + if (c > property_count_requested && properties != NULL) > + return VK_INCOMPLETE; > Again, vk_outarray is your friend. > + > + return VK_SUCCESS; > +} > + > +/* > + * 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]; > + struct wsi_display_connector *connector; > + int c; > + > + > + if (displays == NULL) { > + *display_count = 1; > This isn't correct. The loop below could turn up no displays. Again, vk_outarray is your friend. > + return VK_SUCCESS; > + } > + > + if (*display_count < 1) > + return VK_INCOMPLETE; > + > + c = 0; > + LIST_FOR_EACH_ENTRY(connector, &wsi->connectors, list) { > + if (c == plane_index) { > + *displays = wsi_display_connector_to_handle(connector); > + *display_count = 1; > + return VK_SUCCESS; > + } > + c++; > + } > + > + *displays = 0; > + *display_count = 0; > One of these is wrong. Either you have zero displays or you have one display and it's VK_NULL_HANDLE. > + > + return VK_SUCCESS; > +} > + > +/* > + * Implement vkGetDisplayModePropertiesKHR (VK_KHR_display) > + */ > + > +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 *wsi = (struct wsi_display *) > wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; > + struct wsi_display_connector *connector = > wsi_display_connector_from_handle(display); > + int i; > + struct wsi_display_mode *display_mode; > + uint32_t property_count_requested = > *property_count; > + > + i = 0; > + > + if (properties == NULL) > + property_count_requested = 0; > + > + LIST_FOR_EACH_ENTRY(display_mode, &wsi->display_modes, list) { > + if (display_mode->valid && display_mode->connector == connector) { > + if (i < property_count_requested) { > + properties[i].displayMode = wsi_display_mode_to_handle(dis > play_mode); > + properties[i].parameters.visibleRegion.width = > display_mode->hdisplay; > + properties[i].parameters.visibleRegion.height = > display_mode->vdisplay; > + properties[i].parameters.refreshRate = (uint32_t) > (wsi_display_mode_refresh(display_mode) * 1000 + 0.5); > + } > + i++; > + } > + } > + > + *property_count = i; > + > + if (i > property_count_requested && properties != NULL) > + return VK_INCOMPLETE; > Yeah, I sound like a broken record now. vk_outarray. I think this one is actually correct, so you could leave it alone if you wanted. > + > + return VK_SUCCESS; > + > +} > + > +/* > + * 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(m > ode_khr); > + > + /* XXX use actual values */ > That would be good. I don't know enough about KMS to know where you'd get those. > + 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_create_display_surface(VkInstance instance, > + const VkAllocationCallbacks *allocator, > + const VkDisplaySurfaceCreateInfoKHR > *create_info, > + VkSurfaceKHR *surface_khr) > +{ > + VkIcdSurfaceDisplay *surface; > + > + surface = vk_alloc(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, > + const VkAllocationCallbacks *allocator, > + uint32_t queueFamilyIndex, > + int local_fd, > + VkBool32* pSupported) > +{ > + *pSupported = VK_TRUE; > As I commented above, I think we want this to be conditional on whether or not you actually got a master FD. > + 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(s > urface->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_INHERIT_BIT_KHR | > + VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR); > I don't think INHERIT is really appropreate here. I don't think it's practical to set the transparency using KMS without doing it as part of the modeset. Since it really has to be insde the WSI code, we just want OPAQUE_BIT for now. > + > + 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_capabilities2(VkIcdSurfaceBase *icd_surface, > + const void *info_next, > + VkSurfaceCapabilities2KHR *caps) > +{ > + assert(caps->sType == VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR); > + > + return wsi_display_surface_get_capabilities(icd_surface, > &caps->surfaceCapabilities); > +} > + > +static const VkFormat available_surface_formats[] = { > + VK_FORMAT_B8G8R8A8_SRGB, > + VK_FORMAT_B8G8R8A8_UNORM, > +}; > + > +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]; > + 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]; > + f->surfaceFormat.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; > + } > + } > + > + return vk_outarray_status(&out); > +} > ======================================================================= I'm done reviewing for the day. I'll try to resume tomorrow. If you'd like me to continue on the new patches, I can do that. > + > +static const VkPresentModeKHR available_present_modes[] = { > + VK_PRESENT_MODE_FIFO_KHR, > +}; > + > +static VkResult > +wsi_display_surface_get_present_modes(VkIcdSurfaceBase *surface, > + uint32_t > *present_mode_count, > + VkPresentModeKHR *present_modes) > +{ > + if (present_modes == NULL) { > + *present_mode_count = ARRAY_SIZE(available_present_modes); > + return VK_SUCCESS; > + } > + > + *present_mode_count = MIN2(*present_mode_count, > ARRAY_SIZE(available_present_modes)); > + typed_memcpy(present_modes, available_present_modes, > *present_mode_count); > + > + if (*present_mode_count < ARRAY_SIZE(available_present_modes)) > + return VK_INCOMPLETE; > + return VK_SUCCESS; > +} > + > +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; > + VkResult result; > + int ret; > + uint32_t image_handle; > + > + if (chain->base.use_prime_blit) > + result = wsi_create_prime_image(&chain->base, create_info, > &image->base); > + else > + result = wsi_create_native_image(&chain->base, create_info, > &image->base); > + if (result != VK_SUCCESS) > + return result; > + > + ret = drmPrimeFDToHandle(wsi->master_fd, image->base.fd, > &image_handle); > + > + close(image->base.fd); > + image->base.fd = -1; > + > + if (ret < 0) > + goto fail_handle; > + > + image->chain = chain; > + image->state = wsi_image_idle; > + image->fb_id = 0; > + > + /* XXX extract depth and bpp from image somehow */ > + ret = drmModeAddFB(wsi->master_fd, create_info->imageExtent.width, > create_info->imageExtent.height, > + 24, 32, image->base.row_pitch, image_handle, > &image->fb_id); > + > + if (ret) > + goto fail_fb; > + > + return VK_SUCCESS; > + > +fail_fb: > + /* fall through */ > + > +fail_handle: > + 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; > + > + 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]); > + 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; > + > + 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); > + (void) _wsi_display_queue_next(&(image->chain->base)); > +} > + > +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 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 > +}; > + > +static void * > +wsi_display_wait_thread(void *data) > +{ > + struct wsi_display *wsi = data; > + struct pollfd pollfd = { > + .fd = wsi->master_fd, > + .events = POLLIN > + }; > + int ret; > + > + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); > + for (;;) { > + ret = poll(&pollfd, 1, -1); > + if (ret > 0) { > + pthread_mutex_lock(&wsi->wait_mutex); > + (void) drmHandleEvent(wsi->master_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; > +} > + > +/* 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 / ((uint64_t) 1000 * (uint64_t) 1000 * > (uint64_t) 1000), > + .tv_nsec = timeout_ns % ((uint64_t) 1000 * (uint64_t) 1000 * > (uint64_t) 1000) > + }; > + > + 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, > + uint64_t timeout, > + VkSemaphore semaphore, > + 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; > + > + if (timeout != 0 && timeout != UINT64_MAX) > + timeout += wsi_get_current_monotonic(); > + > + 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_OUT_OF_DATE_KHR; > + goto done; > + } > + } > +done: > + pthread_mutex_unlock(&wsi->wait_mutex); > + return result; > +} > + > +/* > + * 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) > +{ > + int c, e; > + > + /* See if any other connectors share the same encoder */ > + for (c = 0; c < mode_res->count_connectors; c++) { > + if (mode_res->connectors[c] == connector->connector_id) > + continue; > + > + drmModeConnectorPtr other_connector = > drmModeGetConnector(wsi->master_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 (e = 0; e < mode_res->count_encoders; e++) { > + if (mode_res->encoders[e] == connector->encoder_id) > + continue; > + > + drmModeEncoderPtr other_encoder = > drmModeGetEncoder(wsi->master_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(struct wsi_display_connector *connector, > + drmModeResPtr mode_res, > + drmModeConnectorPtr drm_connector) > +{ > + struct wsi_display *wsi = connector->wsi; > + int c; > + uint32_t crtc_id; > + > + /* See what CRTC is currently driving this connector */ > + if (drm_connector->encoder_id) { > + drmModeEncoderPtr encoder = drmModeGetEncoder(wsi->master_fd, > drm_connector->encoder_id); > + if (encoder) { > + 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; > + } > + } > + } > + crtc_id = 0; > + for (c = 0; crtc_id == 0 && c < mode_res->count_crtcs; c++) { > + drmModeCrtcPtr crtc = drmModeGetCrtc(wsi->master_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; > + drmModeModeInfoPtr drm_mode; > + drmModeConnectorPtr drm_connector; > + drmModeResPtr mode_res; > + VkResult result; > + int m; > + > + if (connector->current_mode == display_mode && connector->crtc_id) > + return VK_SUCCESS; > + > + mode_res = drmModeGetResources(wsi->master_fd); > + if (!mode_res) { > + result = VK_ERROR_INITIALIZATION_FAILED; > + goto bail; > + } > + > + drm_connector = drmModeGetConnectorCurrent(wsi->master_fd, > connector->id); > + if (!drm_connector) { > + result = VK_ERROR_INITIALIZATION_FAILED; > + 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_OUT_OF_DATE_KHR; > + goto bail_connector; > + } > + } > + > + if (connector->current_mode != display_mode) { > + > + /* Find the drm mode cooresponding to the requested VkDisplayMode */ > + drm_mode = NULL; > + for (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_OUT_OF_DATE_KHR; > + goto bail_connector; > + } > + > + connector->current_mode = display_mode; > + connector->current_drm_mode = *drm_mode; > + } > + > + result = VK_SUCCESS; > + > +bail_connector: > + drmModeFreeConnector(drm_connector); > +bail_mode_res: > + drmModeFreeResources(mode_res); > +bail: > + return result; > + > +} > + > +/* > + * 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; > + uint32_t i; > + struct wsi_display_image *image = NULL; > + VkIcdSurfaceDisplay *surface = chain->surface; > + wsi_display_mode *display_mode = > wsi_display_mode_from_handle(surface->displayMode); > + wsi_display_connector *connector = display_mode->connector; > + int ret; > + VkResult result; > + > + if (wsi->master_fd < 0) > + return VK_ERROR_INITIALIZATION_FAILED; > + > + 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 */ > + > + for (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; > + > + if (connector->active) { > + ret = drmModePageFlip(wsi->master_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) { > + switch(-ret) { > + case EINVAL: > + > + 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->master_fd, connector->crtc_id, > image->fb_id, 0, 0, > + &connector->id, 1, > &connector->current_drm_mode); > + > + if (ret == 0) { > + image->state = wsi_image_displaying; > + wsi_display_idle_old_displaying(image); > + connector->active = true; > + return VK_SUCCESS; > + } > + break; > + case EACCES: > + usleep(1000 * 1000); > + connector->active = false; > + break; > + default: > + connector->active = false; > + image->state = wsi_image_idle; > + return VK_ERROR_OUT_OF_DATE_KHR; > + } > + } > + } > +} > + > +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; > + > + 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); > + > + pthread_mutex_unlock(&wsi->wait_mutex); > + > + return result; > +} > + > +static VkResult > +wsi_display_surface_create_swapchain(VkIcdSurfaceBase > *icd_surface, > + VkDevice > device, > + struct wsi_device > *wsi_device, > + int > local_fd, > + 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]; > + VkResult result; > + > + assert(create_info->sType == VK_STRUCTURE_TYPE_SWAPCHAIN_CR > EATE_INFO_KHR); > + > + struct wsi_display_swapchain *chain; > + const unsigned num_images = create_info->minImageCount; > + size_t size = sizeof(*chain) + num_images * sizeof(chain->images[0]); > + > + chain = vk_alloc(allocator, size, 8, > + VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); > + > + if (chain == NULL) > + return VK_ERROR_OUT_OF_HOST_MEMORY; > + > + result = wsi_swapchain_init(wsi_device, &chain->base, device, > + create_info, allocator); > + > + 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->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) > + goto fail_init_images; > + } > + > + *swapchain_out = &chain->base; > + > + return VK_SUCCESS; > + > +fail_init_images: > + return result; > +} > + > +VkResult > +wsi_display_init_wsi(struct wsi_device *wsi_device, > + const VkAllocationCallbacks *alloc, > + VkPhysicalDevice physical_device, > + int device_fd) > +{ > + struct wsi_display *wsi; > + VkResult result; > + > + wsi = vk_alloc(alloc, sizeof(*wsi), 8, > + VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); > + if (!wsi) { > + result = VK_ERROR_OUT_OF_HOST_MEMORY; > + goto fail; > + } > + memset(wsi, '\0', sizeof (*wsi)); > + > + wsi->master_fd = -1; > + if (drmGetNodeTypeFromFd(device_fd) == DRM_NODE_PRIMARY) > + wsi->master_fd = device_fd; > + wsi->render_fd = device_fd; > + > + pthread_mutex_init(&wsi->wait_mutex, NULL); > + wsi->physical_device = physical_device; > + wsi->alloc = alloc; > + > + LIST_INITHEAD(&wsi->display_modes); > + LIST_INITHEAD(&wsi->connectors); > + > + pthread_condattr_t condattr; > + int ret; > + > + ret = pthread_mutex_init(&wsi->wait_mutex, NULL); > + if (ret) { > + result = VK_ERROR_OUT_OF_HOST_MEMORY; > + goto fail_mutex; > + } > + > + ret = pthread_condattr_init(&condattr); > + if (ret) { > + result = VK_ERROR_OUT_OF_HOST_MEMORY; > + goto fail_condattr; > + } > + > + ret = pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC); > + if (ret) { > + result = VK_ERROR_OUT_OF_HOST_MEMORY; > + goto fail_setclock; > + } > + > + ret = pthread_cond_init(&wsi->wait_cond, &condattr); > + if (ret) { > + result = VK_ERROR_OUT_OF_HOST_MEMORY; > + goto fail_cond; > + } > + > + pthread_condattr_destroy(&condattr); > + > + wsi->base.get_support = wsi_display_surface_get_support; > + wsi->base.get_capabilities = wsi_display_surface_get_capabilities; > + 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.create_swapchain = wsi_display_surface_create_swapchain; > + > + wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY] = &wsi->base; > + > + return VK_SUCCESS; > + > +fail_cond: > +fail_setclock: > + pthread_condattr_destroy(&condattr); > +fail_condattr: > + 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) { > + > + struct wsi_display_connector *connector, *connector_storage; > + LIST_FOR_EACH_ENTRY_SAFE(connector, connector_storage, > &wsi->connectors, list) { > + vk_free(wsi->alloc, connector); > + } > + > + struct wsi_display_mode *mode, *mode_storage; > + LIST_FOR_EACH_ENTRY_SAFE(mode, mode_storage, &wsi->display_modes, > list) { > + vk_free(wsi->alloc, mode); > + } > + > + 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); > + } > +} > diff --git a/src/vulkan/wsi/wsi_common_display.h > b/src/vulkan/wsi/wsi_common_display.h > new file mode 100644 > index 00000000000..b414a226293 > --- /dev/null > +++ b/src/vulkan/wsi/wsi_common_display.h > @@ -0,0 +1,72 @@ > +/* > + * 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_plane_properties(VkPhysicalDevice > physical_device, > + struct > wsi_device *wsi_device, > + uint32_t > *property_count, > + > VkDisplayPlanePropertiesKHR *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_get_display_plane_capabilities(VkPhysicalDevice > physical_device, > + struct wsi_device > *wsi_device, > + VkDisplayModeKHR > mode_khr, > + uint32_t > plane_index, > + VkDisplayPlaneCapabilitiesKHR > *capabilities); > + > +VkResult > +wsi_create_display_surface(VkInstance instance, > + const VkAllocationCallbacks *pAllocator, > + const VkDisplaySurfaceCreateInfoKHR > *pCreateInfo, > + VkSurfaceKHR *pSurface); > + > +#endif > diff --git a/src/vulkan/wsi/wsi_common_private.h > b/src/vulkan/wsi/wsi_common_private.h > index 503b2a015dc..d38d2efa116 100644 > --- a/src/vulkan/wsi/wsi_common_private.h > +++ b/src/vulkan/wsi/wsi_common_private.h > @@ -135,6 +135,16 @@ 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, > + VkPhysicalDevice physical_device, > + int device_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 * > \ > -- > 2.15.1 > > _______________________________________________ > dri-devel mailing list > dri-devel@lists.freedesktop.org > https://lists.freedesktop.org/mailman/listinfo/dri-devel >
Jason Ekstrand <jason@jlekstrand.net> writes: > It seems a little odd to me to default to opening the master node and then > fall back to the render node if it doesn't work. I suppose that's probably > ok so long as we ensure that vkGetPhysicalDeviceDisplayPropertiesKHR > returns no displays if we're on the render node. > > We could always go back to the DRM fd extension idea but I'm not coming up > with something nice and clean in the 60 seconds I've thought about it. As I said in the last comments about this section, Dave Airlie and I added this code only recently so that we could test this extension without also needing the kernel and X leasing changes. I think we should decide how to enable this functionality "for real", and I have two easy options: 1) Use my KEITHP_kms_display extension (presumably renamed MESA), which exposes a way to pass the DRM fd from the application into the driver. This makes it possible for the application to get the FD through any mechanism at all (including RandR or the new Wayland extension) and leave that out of the Vulkan code entirely. 2) Add a new extension which passes a new data structure that directs the driver to open either the Render or Primary nodes. When this is done, we can switch from the current code which tries to open the Primary node whenever the KHR_display extension is requested. > Would it make anything easier if we just storred the DRM struct here? "No" > is a perfectly valid answer. Nope -- once we add the acquire_xlib extension, we get modes through either X or DRM, depending on whether we're pre-lease or post-lease. > Any particular reason why the list of modes is global and not in the > connector? It seems like it would be a tiny bit more efficient and > convenient to put the list in the connector. I think you're right. I have some vague memory of a lifetime issue with connectors, but can't think of what it might be, even after reviewing the relevant parts of the Vulkan spec. I've gone ahead and changed it; seems to work fine. >> + LIST_FOR_EACH_ENTRY(display_mode, &wsi->display_modes, list) >> + if (display_mode->connector == connector) >> + display_mode->valid = false; >> > > Please use braces for loops containing more than one line. Well, that was easy to fix -- the condition is now gone :-) > Since we're allocating these in a physical device query, we need to use the > INSTANCE scope. the OBJECT scope is intended for vkCreate functions to > allocated data that will live no longer than the associated vkDestroy > function. Thanks! The whole Vulkan memory model remains a mystery to me. I've changed allocation of wsi_display_mode and wsi_display_connector to use SCOPE_INSTANCE. VkIceSurfaceDisplay, wsi_display_fence and wsi_display_swapchain remain using SCOPE_OBJECT. I've also changed *all* instances of vk_alloc to use vk_zalloc. These are all small data structures allocated only during application startup, so I think the benefit of known memory contents is worth the cost of memset. > Hooray for obviously false fixed constants! > > I know the answer to this will be "EDIDs lie, never trust them!" but can we > get the real value somehow? As someone who has a 13" laptop with a > 3200x1800 display, I know that number isn't always right. :-) Yes, we could dig the real value out of the EDID, but we'd have to parse the entire EDID to manage that. I don't want to stick an EDID parser directly in Mesa, so I'm kinda waiting for someone to create a separate EDID parsing library that the X server, Mesa and others can share. Until then, I'd prefer to just lie here. > double-;; Thx. I remember seeing this while reviewing patches and forgot all about it... > From the Vulkan spec: > > Note: > For devices which have no natural value to return here, implementations > *should* return the maximum resolution supported. > > We should walk the list and pick the biggest one. I did this intentionally -- most monitors have a preferred resolution, which is their native pixel size. And, we want to tell applications to use that size, even if the monitor offers a larger (presumabl scaled) resolution in their mode list. > See question about MM_PER_PIXEL above Yeah, see response about not boiling the EDID ocean above ;-) > I know i915 can do better at least in some cases. Is there a practical way > to expose this? If not, I'm fine with just exposing IDENTITY. I'm not seeing this exposed through the common DRM mode interfaces yet. We should probably consider adding this to the kernel and then adding it here. > This error is not allowed for this function. We should just write 0 to > property_count and return VK_SUCCESS. Maybe add some asserts for debug > builds if you really think this shouldn't ever happen. I bet it will happen if you VT switch away and then try this function. I've added this at the end of the function: bail: *property_count = 0; return VK_SUCCESS; > This could be made a lot easier with vk_outarray: ... > Again, vk_outarray is your friend. ... > This isn't correct. The loop below could turn up no displays. Again, > vk_outarray is your friend. ... > Yeah, I sound like a broken record now. vk_outarray. I think this one is > actually correct, so you could leave it alone if you wanted. I've replaced all of the array returns with vk_outarray; I'm afraid I found a bunch of open-coded versions of this and didn't stumble on vk_outarray until too late. Thanks! >> + /* XXX use actual values */ >> > > That would be good. I don't know enough about KMS to know where you'd get > those. I think the real values will only be useful once we add multi-plane support to this code. At this point, with only a single plane, I'm not sure it's interesting to be able to pan it around? So, these values are 'safe', and sufficient to display a single plane covering the full screen. When we want more, we can go actually hook up multi-plane support, which will require more work in the window system extensions to allow applications to request the desired number of planes. >> +static VkResult >> +wsi_display_surface_get_support(VkIcdSurfaceBase *surface, >> + struct wsi_device *wsi_device, >> + const VkAllocationCallbacks *allocator, >> + uint32_t queueFamilyIndex, >> + int local_fd, >> + VkBool32* pSupported) >> +{ >> + *pSupported = VK_TRUE; >> > > As I commented above, I think we want this to be conditional on whether or > not you actually got a master FD. With just KHR_display, you can't get here without already having needed a master FD -- you need a VkSurfaceKHR, and you create one of those using vkCreateDisplayPlaneSurfaceKHR, which requires a plane and a mode; a mode comes from vkGetDisplayModePropertiesKHR or vkCreateDisplayModeKHR, both of which requires a VkDisplayKHR. A VkDisplayKHR comes from vkGetPhysicalDeviceDisplayPropertiesKHR, which fails if you don't have a master FD. Once we have AcquireXlibDisplay, then you will be able to create display surfaces before you have a master FD as you can create modes using X resources. And, you can't actually tell if you will be able to get a master FD until you try... >> + caps->supportedCompositeAlpha = (VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR | >> + VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR); >> > > I don't think INHERIT is really appropreate here. I don't think it's > practical to set the transparency using KMS without doing it as part of the > modeset. Since it really has to be insde the WSI code, we just want > OPAQUE_BIT for now. Yeah, another area which really needs to wait until we figure out what we want for multi-plane support. If you only have one plane, you don't exactly have any alpha compositing. > I'm done reviewing for the day. I'll try to resume tomorrow. If you'd > like me to continue on the new patches, I can do that. Thanks so much for your review so far; getting rid of the open-coded property queries is really valuable, along with using vk_zalloc everywhere. The "new" patches are exactly the same code changes, just split into core/anv/radv bits to make Dave Airlie happy. Feel free to either continue with the old patches or to switch over. I've pushed out a series of tiny patches which address each of the review comments above separately if you want to check those over. I'll merge them into the main patch series at some point.
On Thu, Feb 15, 2018 at 9:46 AM, Keith Packard <keithp@keithp.com> wrote: > Jason Ekstrand <jason@jlekstrand.net> writes: > > > It seems a little odd to me to default to opening the master node and > then > > fall back to the render node if it doesn't work. I suppose that's > probably > > ok so long as we ensure that vkGetPhysicalDeviceDisplayPropertiesKHR > > returns no displays if we're on the render node. > > > > We could always go back to the DRM fd extension idea but I'm not coming > up > > with something nice and clean in the 60 seconds I've thought about it. > > As I said in the last comments about this section, Dave Airlie and I > added this code only recently so that we could test this extension > without also needing the kernel and X leasing changes. > > I think we should decide how to enable this functionality "for real", > and I have two easy options: > > 1) Use my KEITHP_kms_display extension (presumably renamed MESA), which > exposes a way to pass the DRM fd from the application into the > driver. This makes it possible for the application to get the FD > through any mechanism at all (including RandR or the new Wayland > extension) and leave that out of the Vulkan code entirely. > > 2) Add a new extension which passes a new data structure that directs > the driver to open either the Render or Primary nodes. > > When this is done, we can switch from the current code which tries to > open the Primary node whenever the KHR_display extension is requested. > I think I like option 1. If the client knows the difference between render and primary for 2, then they are most likely already opening the master node themselves or at least have access to the FD. For an application that just wants to draw some stuff on the screen and is ok with letting Vulkan fully handle KMS for it, the current code may be a fine solution. Sorry, I'm just not feeling all that opinionated about this at the moment. :-) > > Would it make anything easier if we just storred the DRM struct here? > "No" > > is a perfectly valid answer. > > Nope -- once we add the acquire_xlib extension, we get modes through > either X or DRM, depending on whether we're pre-lease or post-lease. > Sounds good. > > Any particular reason why the list of modes is global and not in the > > connector? It seems like it would be a tiny bit more efficient and > > convenient to put the list in the connector. > > I think you're right. I have some vague memory of a lifetime issue with > connectors, but can't think of what it might be, even after reviewing > the relevant parts of the Vulkan spec. I've gone ahead and changed it; > seems to work fine. > > >> + LIST_FOR_EACH_ENTRY(display_mode, &wsi->display_modes, list) > >> + if (display_mode->connector == connector) > >> + display_mode->valid = false; > >> > > > > Please use braces for loops containing more than one line. > > Well, that was easy to fix -- the condition is now gone :-) > Even better! > > Since we're allocating these in a physical device query, we need to use > the > > INSTANCE scope. the OBJECT scope is intended for vkCreate functions to > > allocated data that will live no longer than the associated vkDestroy > > function. > > Thanks! The whole Vulkan memory model remains a mystery to me. > > I've changed allocation of wsi_display_mode and wsi_display_connector to > use SCOPE_INSTANCE. VkIceSurfaceDisplay, wsi_display_fence and > wsi_display_swapchain remain using SCOPE_OBJECT. > That sounds correct to me. > I've also changed *all* instances of vk_alloc to use > vk_zalloc. These are all small data structures allocated only during > application startup, so I think the benefit of known memory contents is > worth the cost of memset. > > > Hooray for obviously false fixed constants! > > > > I know the answer to this will be "EDIDs lie, never trust them!" but can > we > > get the real value somehow? As someone who has a 13" laptop with a > > 3200x1800 display, I know that number isn't always right. :-) > > Yes, we could dig the real value out of the EDID, but we'd have to parse > the entire EDID to manage that. I don't want to stick an EDID parser > directly in Mesa, so I'm kinda waiting for someone to create a separate > EDID parsing library that the X server, Mesa and others can share. Until > then, I'd prefer to just lie here. > Clearly, we need systemd-edidd. :-) > > double-;; > > Thx. I remember seeing this while reviewing patches and forgot all about > it... > > > From the Vulkan spec: > > > > Note: > > For devices which have no natural value to return here, > implementations > > *should* return the maximum resolution supported. > > > > We should walk the list and pick the biggest one. > > I did this intentionally -- most monitors have a preferred resolution, > which is their native pixel size. And, we want to tell applications to > use that size, even if the monitor offers a larger (presumabl scaled) > resolution in their mode list. > Yes, *if* it has a preferred resolution, we should return that one. If it doesn't, we should walk the list and return the largest instead of just defaulting to 1024x768. At least that's what the spec seems to say to me. > > See question about MM_PER_PIXEL above > > Yeah, see response about not boiling the EDID ocean above ;-) > > > I know i915 can do better at least in some cases. Is there a practical > way > > to expose this? If not, I'm fine with just exposing IDENTITY. > > I'm not seeing this exposed through the common DRM mode interfaces > yet. We should probably consider adding this to the kernel and then > adding it here. > That's fine. > > This error is not allowed for this function. We should just write 0 to > > property_count and return VK_SUCCESS. Maybe add some asserts for debug > > builds if you really think this shouldn't ever happen. > > I bet it will happen if you VT switch away and then try this function. > > I've added this at the end of the function: > > bail: > *property_count = 0; > return VK_SUCCESS; > Sounds good. > > This could be made a lot easier with vk_outarray: > ... > > Again, vk_outarray is your friend. > ... > > This isn't correct. The loop below could turn up no displays. Again, > > vk_outarray is your friend. > ... > > Yeah, I sound like a broken record now. vk_outarray. I think this one > is > > actually correct, so you could leave it alone if you wanted. > > I've replaced all of the array returns with vk_outarray; I'm afraid I > found a bunch of open-coded versions of this and didn't stumble on > vk_outarray until too late. Thanks! > You're welcome. I'm so glad Chad made that little helper. 50-75% of the open-coded versions were wrong. > >> + /* XXX use actual values */ > >> > > > > That would be good. I don't know enough about KMS to know where you'd > get > > those. > > I think the real values will only be useful once we add multi-plane > support to this code. At this point, with only a single plane, I'm not > sure it's interesting to be able to pan it around? So, these values are > 'safe', and sufficient to display a single plane covering the full > screen. When we want more, we can go actually hook up multi-plane > support, which will require more work in the window system extensions to > allow applications to request the desired number of planes. > Sounds good to me. > >> +static VkResult > >> +wsi_display_surface_get_support(VkIcdSurfaceBase *surface, > >> + struct wsi_device *wsi_device, > >> + const VkAllocationCallbacks *allocator, > >> + uint32_t queueFamilyIndex, > >> + int local_fd, > >> + VkBool32* pSupported) > >> +{ > >> + *pSupported = VK_TRUE; > >> > > > > As I commented above, I think we want this to be conditional on whether > or > > not you actually got a master FD. > > With just KHR_display, you can't get here without already having needed > a master FD -- you need a VkSurfaceKHR, and you create one of those > using vkCreateDisplayPlaneSurfaceKHR, which requires a plane and a mode; > a mode comes from vkGetDisplayModePropertiesKHR or > vkCreateDisplayModeKHR, both of which requires a VkDisplayKHR. A > VkDisplayKHR comes from vkGetPhysicalDeviceDisplayPropertiesKHR, which > fails if you don't have a master FD. > Right. That makes sense. > Once we have AcquireXlibDisplay, then you will be able to create display > surfaces before you have a master FD as you can create modes using X > resources. And, you can't actually tell if you will be able to get a > master FD until you try... > Yeah > >> + caps->supportedCompositeAlpha = (VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR > | > >> + VK_COMPOSITE_ALPHA_OPAQUE_BIT_ > KHR); > >> > > > > I don't think INHERIT is really appropreate here. I don't think it's > > practical to set the transparency using KMS without doing it as part of > the > > modeset. Since it really has to be insde the WSI code, we just want > > OPAQUE_BIT for now. > > Yeah, another area which really needs to wait until we figure out what > we want for multi-plane support. If you only have one plane, you don't > exactly have any alpha compositing. > Yup. Let's just drop INHERIT and only advertise OPAQUE. > > I'm done reviewing for the day. I'll try to resume tomorrow. If you'd > > like me to continue on the new patches, I can do that. > > Thanks so much for your review so far; getting rid of the open-coded > property queries is really valuable, along with using vk_zalloc > everywhere. > > The "new" patches are exactly the same code changes, just split into > core/anv/radv bits to make Dave Airlie happy. Feel free to either > continue with the old patches or to switch over. I've pushed out a > series of tiny patches which address each of the review comments above > separately if you want to check those over. I'll merge them into the > main patch series at some point. > > -- > -keith >
Jason Ekstrand <jason@jlekstrand.net> writes: > I think I like option 1 (KEITHP_kms_display). If the client knows the > difference between render and primary for 2, then they are most likely > already opening the master node themselves or at least have access to > the FD. Ok, I'll work on cleaning up that extension and renaming it. I have no idea how to get new words into the Vulkan spec, but it would be good to have that done too. I guess the question is whether I should bother to leave the current try-to-open-primary kludge in place. In good news, under X, both radv and anv "fail" to use the primary device as another master is already running, so at least we aren't running with a primary FD open? > Sorry, I'm just not feeling all that opinionated about this at the moment. > :-) No more than I; either way is fine with me, if you're happy to use something like the existing code I've got, that's even nicer. > Clearly, we need systemd-edidd. :-) A library would be nice; we have the edid data everywhere, we just don't have it in a useful form except inside the X server and kernel. > Yes, *if* it has a preferred resolution, we should return that one. If it > doesn't, we should walk the list and return the largest instead of just > defaulting to 1024x768. At least that's what the spec seems to say to > me. Oh. I totally missed that part. Yeah, that's just wrong. I've just pushed a patch that finds the largest mode when there isn't a preferred one. Oddly, I have no devices here which don't specify a preferred mode, so it will be somewhat difficult to test... > Yup. Let's just drop INHERIT and only advertise OPAQUE. Done. I've updated my drm-lease branch with all of these changes merged into the existing patches (and so noted), plus the new patch described above which looks for the largest mode when no preferred mode is specified. Thanks again for all of your careful review; while we haven't changed any significant semantics, we have found a bunch of outright bugs in the code.
On Fri, Feb 23, 2018 at 3:43 PM, Keith Packard <keithp@keithp.com> wrote: > Jason Ekstrand <jason@jlekstrand.net> writes: > > > I think I like option 1 (KEITHP_kms_display). If the client knows the > > difference between render and primary for 2, then they are most likely > > already opening the master node themselves or at least have access to > > the FD. > > Ok, I'll work on cleaning up that extension and renaming it. I have no > idea how to get new words into the Vulkan spec, but it would be good to > have that done too. > Once we're sure that's what we want, create an MR against the spec that just adds enough to the XML to reserve your extension number. That will get merged almost immediately. Then make a second one with the actual extension text and we'll iterate on that either in Khronos gitlab or, if you prefer, you can send it as a patch to mesa-dev and then make a Khrons MR once it's baked. > I guess the question is whether I should bother to leave the current > try-to-open-primary kludge in place. In good news, under X, both radv > and anv "fail" to use the primary device as another master is already > running, so at least we aren't running with a primary FD open? > See also my comments about GEM handle ownership. > > Sorry, I'm just not feeling all that opinionated about this at the > moment. > > :-) > > No more than I; either way is fine with me, if you're happy to use > something like the existing code I've got, that's even nicer. > > > Clearly, we need systemd-edidd. :-) > > A library would be nice; we have the edid data everywhere, we just don't > have it in a useful form except inside the X server and kernel. > Yeah, in the scary new world of Wayland compositors, having an edid library would be a very good thing. No sense in having everyone fail to handle it properly themselves. > > Yes, *if* it has a preferred resolution, we should return that one. If > it > > doesn't, we should walk the list and return the largest instead of just > > defaulting to 1024x768. At least that's what the spec seems to say to > > me. > > Oh. I totally missed that part. Yeah, that's just wrong. I've just > pushed a patch that finds the largest mode when there isn't a preferred > one. Oddly, I have no devices here which don't specify a preferred mode, > so it will be somewhat difficult to test... > > > Yup. Let's just drop INHERIT and only advertise OPAQUE. > > Done. > > I've updated my drm-lease branch with all of these changes merged into > the existing patches (and so noted), plus the new patch described above > which looks for the largest mode when no preferred mode is specified. > > Thanks again for all of your careful review; while we haven't changed > any significant semantics, we have found a bunch of outright bugs in the > code. > Glad to help. :-) I figure I should learn something about KMS one day and reviewing this is as good an opportunity as any.
Jason Ekstrand <jason@jlekstrand.net> writes: > On Fri, Feb 23, 2018 at 3:43 PM, Keith Packard <keithp@keithp.com> wrote: > > Once we're sure that's what we want, create an MR against the spec that > just adds enough to the XML to reserve your extension number. That will > get merged almost immediately. Then make a second one with the actual > extension text and we'll iterate on that either in Khronos gitlab or, if > you prefer, you can send it as a patch to mesa-dev and then make a Khrons > MR once it's baked. I just wrote up the full extension description for both extensions I need (the one for passing a KMS fd to the driver, and the second to get the GPU timestamp for doing GOOGLE_display_timing): https://github.com/keith-packard/Vulkan-Docs > See also my comments about GEM handle ownership. Yeah, I think I've got that all cleaned up now -- the code no longer shares the same file for rendering and display.
diff --git a/configure.ac b/configure.ac index 8ed606c7694..46318365603 100644 --- a/configure.ac +++ b/configure.ac @@ -1849,6 +1849,7 @@ fi AM_CONDITIONAL(HAVE_PLATFORM_X11, echo "$platforms" | grep -q 'x11') AM_CONDITIONAL(HAVE_PLATFORM_WAYLAND, echo "$platforms" | grep -q 'wayland') AM_CONDITIONAL(HAVE_PLATFORM_DRM, echo "$platforms" | grep -q 'drm') +AM_CONDITIONAL(HAVE_PLATFORM_DISPLAY, echo "$platforms" | grep -q 'drm') AM_CONDITIONAL(HAVE_PLATFORM_SURFACELESS, echo "$platforms" | grep -q 'surfaceless') AM_CONDITIONAL(HAVE_PLATFORM_ANDROID, echo "$platforms" | grep -q 'android') diff --git a/meson.build b/meson.build index b39e2f8ab96..aeb7f5e2917 100644 --- a/meson.build +++ b/meson.build @@ -239,11 +239,12 @@ with_platform_wayland = false with_platform_x11 = false with_platform_drm = false with_platform_surfaceless = false +with_platform_display = false egl_native_platform = '' _platforms = get_option('platforms') if _platforms == 'auto' if system_has_kms_drm - _platforms = 'x11,wayland,drm,surfaceless' + _platforms = 'x11,wayland,drm,surfaceless,display' elif ['darwin', 'windows', 'cygwin'].contains(host_machine.system()) _platforms = 'x11,surfaceless' else @@ -257,6 +258,7 @@ if _platforms != '' with_platform_wayland = _split.contains('wayland') with_platform_drm = _split.contains('drm') with_platform_surfaceless = _split.contains('surfaceless') + with_platform_display = _split.contains('display') egl_native_platform = _split[0] endif diff --git a/src/amd/vulkan/Makefile.am b/src/amd/vulkan/Makefile.am index 61025968942..061b8144b88 100644 --- a/src/amd/vulkan/Makefile.am +++ b/src/amd/vulkan/Makefile.am @@ -76,6 +76,14 @@ VULKAN_LIB_DEPS = \ $(DLOPEN_LIBS) \ -lm +if HAVE_PLATFORM_DISPLAY +AM_CPPFLAGS += \ + -DVK_USE_PLATFORM_DISPLAY_KHR + +VULKAN_SOURCES += $(VULKAN_WSI_DISPLAY_FILES) + +endif + if HAVE_PLATFORM_X11 AM_CPPFLAGS += \ $(XCB_DRI3_CFLAGS) \ diff --git a/src/amd/vulkan/Makefile.sources b/src/amd/vulkan/Makefile.sources index a510d88d965..618a6cdaed0 100644 --- a/src/amd/vulkan/Makefile.sources +++ b/src/amd/vulkan/Makefile.sources @@ -78,6 +78,9 @@ VULKAN_WSI_WAYLAND_FILES := \ VULKAN_WSI_X11_FILES := \ radv_wsi_x11.c +VULKAN_WSI_DISPLAY_FILES := \ + radv_wsi_display.c + VULKAN_GENERATED_FILES := \ radv_entrypoints.c \ radv_entrypoints.h \ diff --git a/src/amd/vulkan/meson.build b/src/amd/vulkan/meson.build index 0a7b7c0bf3c..b7bb1075e7d 100644 --- a/src/amd/vulkan/meson.build +++ b/src/amd/vulkan/meson.build @@ -112,6 +112,13 @@ if with_platform_wayland libradv_files += files('radv_wsi_wayland.c') endif +if with_platform_display + radv_flags += [ + '-DVK_USE_PLATFORM_DISPLAY_KHR', + ] + libradv_files += files('radv_wsi_display.c') +endif + libvulkan_radeon = shared_library( 'vulkan_radeon', [libradv_files, radv_entrypoints, radv_extensions_c, vk_format_table_c], diff --git a/src/amd/vulkan/radv_device.c b/src/amd/vulkan/radv_device.c index 09bb382eeb8..adf33eb35dc 100644 --- a/src/amd/vulkan/radv_device.c +++ b/src/amd/vulkan/radv_device.c @@ -191,9 +191,26 @@ radv_physical_device_init(struct radv_physical_device *device, const char *path = drm_device->nodes[DRM_NODE_RENDER]; VkResult result; drmVersionPtr version; - int fd; - - fd = open(path, O_RDWR | O_CLOEXEC); + int fd = -1; + + if (instance->khr_display_requested) { + fd = open(drm_device->nodes[DRM_NODE_PRIMARY], O_RDWR | O_CLOEXEC); + if (fd >= 0) { + uint32_t accel_working = 0; + struct drm_amdgpu_info request = { + .return_pointer = (uintptr_t)&accel_working, + .return_size = sizeof(accel_working), + .query = AMDGPU_INFO_ACCEL_WORKING + }; + + if (drmCommandWrite(fd, DRM_AMDGPU_INFO, &request, sizeof (struct drm_amdgpu_info)) < 0 || !accel_working) { + close(fd); + fd = -1; + } + } + } + if (fd < 0) + fd = open(path, O_RDWR | O_CLOEXEC); if (fd < 0) return vk_error(VK_ERROR_INCOMPATIBLE_DRIVER); @@ -209,6 +226,7 @@ radv_physical_device_init(struct radv_physical_device *device, close(fd); return VK_ERROR_INCOMPATIBLE_DRIVER; } + drmFreeVersion(version); device->_loader_data.loaderMagic = ICD_LOADER_MAGIC; @@ -387,6 +405,7 @@ VkResult radv_CreateInstance( { struct radv_instance *instance; VkResult result; + bool khr_display_requested = false; assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO); @@ -411,6 +430,8 @@ VkResult radv_CreateInstance( const char *ext_name = pCreateInfo->ppEnabledExtensionNames[i]; if (!radv_instance_extension_supported(ext_name)) return vk_error(VK_ERROR_EXTENSION_NOT_PRESENT); + if (strcmp(ext_name, VK_KHR_DISPLAY_EXTENSION_NAME) == 0) + khr_display_requested = true; } instance = vk_zalloc2(&default_alloc, pAllocator, sizeof(*instance), 8, @@ -427,6 +448,7 @@ VkResult radv_CreateInstance( instance->apiVersion = client_version; instance->physicalDeviceCount = -1; + instance->khr_display_requested = khr_display_requested; result = vk_debug_report_instance_init(&instance->debug_report_callbacks); if (result != VK_SUCCESS) { diff --git a/src/amd/vulkan/radv_extensions.py b/src/amd/vulkan/radv_extensions.py index d761895d3a0..24cab8cbb39 100644 --- a/src/amd/vulkan/radv_extensions.py +++ b/src/amd/vulkan/radv_extensions.py @@ -81,6 +81,7 @@ EXTENSIONS = [ Extension('VK_KHR_wayland_surface', 6, 'VK_USE_PLATFORM_WAYLAND_KHR'), Extension('VK_KHR_xcb_surface', 6, 'VK_USE_PLATFORM_XCB_KHR'), Extension('VK_KHR_xlib_surface', 6, 'VK_USE_PLATFORM_XLIB_KHR'), + Extension('VK_KHR_display', 1, 'VK_USE_PLATFORM_DISPLAY_KHR'), Extension('VK_KHX_multiview', 1, '!ANDROID'), Extension('VK_EXT_debug_report', 9, True), Extension('VK_EXT_discard_rectangles', 1, True), @@ -168,7 +169,7 @@ _TEMPLATE = Template(COPYRIGHT + """ #include "vk_util.h" /* Convert the VK_USE_PLATFORM_* defines to booleans */ -%for platform in ['ANDROID', 'WAYLAND', 'XCB', 'XLIB']: +%for platform in ['ANDROID', 'WAYLAND', 'XCB', 'XLIB', 'DISPLAY']: #ifdef VK_USE_PLATFORM_${platform}_KHR # undef VK_USE_PLATFORM_${platform}_KHR # define VK_USE_PLATFORM_${platform}_KHR true @@ -187,7 +188,9 @@ _TEMPLATE = Template(COPYRIGHT + """ #define RADV_HAS_SURFACE (VK_USE_PLATFORM_WAYLAND_KHR || \\ VK_USE_PLATFORM_XCB_KHR || \\ - VK_USE_PLATFORM_XLIB_KHR) + VK_USE_PLATFORM_XLIB_KHR || \\ + VK_USE_PLATFORM_DISPLAY_KHR) + bool radv_instance_extension_supported(const char *name) diff --git a/src/amd/vulkan/radv_private.h b/src/amd/vulkan/radv_private.h index be9e8f43964..1e3719bcc4f 100644 --- a/src/amd/vulkan/radv_private.h +++ b/src/amd/vulkan/radv_private.h @@ -75,6 +75,7 @@ typedef uint32_t xcb_window_t; #include "radv_entrypoints.h" #include "wsi_common.h" +#include "wsi_common_display.h" #define ATI_VENDOR_ID 0x1002 @@ -300,6 +301,7 @@ struct radv_instance { uint64_t perftest_flags; struct vk_debug_report_instance debug_report_callbacks; + bool khr_display_requested; }; VkResult radv_init_wsi(struct radv_physical_device *physical_device); diff --git a/src/amd/vulkan/radv_wsi.c b/src/amd/vulkan/radv_wsi.c index e016e837102..5ec872a63d0 100644 --- a/src/amd/vulkan/radv_wsi.c +++ b/src/amd/vulkan/radv_wsi.c @@ -41,7 +41,8 @@ radv_init_wsi(struct radv_physical_device *physical_device) return wsi_device_init(&physical_device->wsi_device, radv_physical_device_to_handle(physical_device), radv_wsi_proc_addr, - &physical_device->instance->alloc); + &physical_device->instance->alloc, + physical_device->local_fd); } void diff --git a/src/amd/vulkan/radv_wsi_display.c b/src/amd/vulkan/radv_wsi_display.c new file mode 100644 index 00000000000..b0a4db0344b --- /dev/null +++ b/src/amd/vulkan/radv_wsi_display.c @@ -0,0 +1,143 @@ +/* + * 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 <stdbool.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include "radv_private.h" +#include "radv_cs.h" +#include "util/disk_cache.h" +#include "util/strtod.h" +#include "vk_util.h" +#include <xf86drm.h> +#include <xf86drmMode.h> +#include <amdgpu.h> +#include <amdgpu_drm.h> +#include "winsys/amdgpu/radv_amdgpu_winsys_public.h" +#include "ac_llvm_util.h" +#include "vk_format.h" +#include "sid.h" +#include "util/debug.h" +#include "wsi_common_display.h" + +#define MM_PER_PIXEL (1.0/96.0 * 25.4) + +VkResult +radv_GetPhysicalDeviceDisplayPropertiesKHR(VkPhysicalDevice physical_device, + uint32_t *property_count, + VkDisplayPropertiesKHR *properties) +{ + RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device); + + return wsi_display_get_physical_device_display_properties(physical_device, + &pdevice->wsi_device, + property_count, + properties); +} + +VkResult +radv_GetPhysicalDeviceDisplayPlanePropertiesKHR(VkPhysicalDevice physical_device, + uint32_t *property_count, + VkDisplayPlanePropertiesKHR *properties) +{ + RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device); + + return wsi_display_get_physical_device_display_plane_properties(physical_device, + &pdevice->wsi_device, + property_count, + properties); +} + +VkResult +radv_GetDisplayPlaneSupportedDisplaysKHR(VkPhysicalDevice physical_device, + uint32_t plane_index, + uint32_t *display_count, + VkDisplayKHR *displays) +{ + RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device); + + return wsi_display_get_display_plane_supported_displays(physical_device, + &pdevice->wsi_device, + plane_index, + display_count, + displays); +} + + +VkResult +radv_GetDisplayModePropertiesKHR(VkPhysicalDevice physical_device, + VkDisplayKHR display, + uint32_t *property_count, + VkDisplayModePropertiesKHR *properties) +{ + RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device); + + return wsi_display_get_display_mode_properties(physical_device, + &pdevice->wsi_device, + display, + property_count, + properties); +} + +VkResult +radv_CreateDisplayModeKHR(VkPhysicalDevice physical_device, + VkDisplayKHR display, + const VkDisplayModeCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, + VkDisplayModeKHR *mode) +{ + return VK_ERROR_INITIALIZATION_FAILED; +} + + +VkResult +radv_GetDisplayPlaneCapabilitiesKHR(VkPhysicalDevice physical_device, + VkDisplayModeKHR mode_khr, + uint32_t plane_index, + VkDisplayPlaneCapabilitiesKHR *capabilities) +{ + RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device); + + return wsi_get_display_plane_capabilities(physical_device, + &pdevice->wsi_device, + mode_khr, + plane_index, + capabilities); +} + +VkResult +radv_CreateDisplayPlaneSurfaceKHR(VkInstance _instance, + const VkDisplaySurfaceCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, + VkSurfaceKHR *surface) +{ + RADV_FROM_HANDLE(radv_instance, instance, _instance); + const VkAllocationCallbacks *alloc; + + if (allocator) + alloc = allocator; + else + alloc = &instance->alloc; + + return wsi_create_display_surface(_instance, alloc, create_info, surface); +} diff --git a/src/intel/Makefile.sources b/src/intel/Makefile.sources index 9595bf42582..6c142729d94 100644 --- a/src/intel/Makefile.sources +++ b/src/intel/Makefile.sources @@ -240,6 +240,9 @@ VULKAN_WSI_WAYLAND_FILES := \ VULKAN_WSI_X11_FILES := \ vulkan/anv_wsi_x11.c +VULKAN_WSI_DISPLAY_FILES := \ + vulkan/anv_wsi_display.c + VULKAN_GEM_FILES := \ vulkan/anv_gem.c diff --git a/src/intel/Makefile.vulkan.am b/src/intel/Makefile.vulkan.am index 23fa877e77d..7c428a799d7 100644 --- a/src/intel/Makefile.vulkan.am +++ b/src/intel/Makefile.vulkan.am @@ -187,6 +187,13 @@ VULKAN_SOURCES += $(VULKAN_WSI_WAYLAND_FILES) VULKAN_LIB_DEPS += $(WAYLAND_CLIENT_LIBS) endif +if HAVE_PLATFORM_DISPLAY +VULKAN_CPPFLAGS += \ + -DVK_USE_PLATFORM_DISPLAY_KHR + +VULKAN_SOURCES += $(VULKAN_WSI_DISPLAY_FILES) +endif + noinst_LTLIBRARIES += vulkan/libvulkan_common.la vulkan_libvulkan_common_la_SOURCES = $(VULKAN_SOURCES) vulkan_libvulkan_common_la_CFLAGS = $(VULKAN_CFLAGS) diff --git a/src/intel/vulkan/anv_device.c b/src/intel/vulkan/anv_device.c index 86c1bdc1d51..9614907fda3 100644 --- a/src/intel/vulkan/anv_device.c +++ b/src/intel/vulkan/anv_device.c @@ -277,14 +277,25 @@ anv_physical_device_init_uuids(struct anv_physical_device *device) static VkResult anv_physical_device_init(struct anv_physical_device *device, struct anv_instance *instance, - const char *path) + const char *primary_path, + const char *render_path) { VkResult result; - int fd; + int fd = -1; + const char *path; brw_process_intel_debug_variable(); - fd = open(path, O_RDWR | O_CLOEXEC); + if (instance->enabled_extensions.KHR_display) { + path = primary_path; + fd = open(path, O_RDWR | O_CLOEXEC); + } + + if (fd < 0) { + path = render_path; + fd = open(path, O_RDWR | O_CLOEXEC); + } + if (fd < 0) return vk_error(VK_ERROR_INCOMPATIBLE_DRIVER); @@ -652,6 +663,7 @@ anv_enumerate_devices(struct anv_instance *instance) result = anv_physical_device_init(&instance->physicalDevice, instance, + devices[i]->nodes[DRM_NODE_PRIMARY], devices[i]->nodes[DRM_NODE_RENDER]); if (result != VK_ERROR_INCOMPATIBLE_DRIVER) break; diff --git a/src/intel/vulkan/anv_extensions.py b/src/intel/vulkan/anv_extensions.py index 581921e62a1..978a219e2b2 100644 --- a/src/intel/vulkan/anv_extensions.py +++ b/src/intel/vulkan/anv_extensions.py @@ -83,6 +83,7 @@ EXTENSIONS = [ Extension('VK_KHR_wayland_surface', 6, 'VK_USE_PLATFORM_WAYLAND_KHR'), Extension('VK_KHR_xcb_surface', 6, 'VK_USE_PLATFORM_XCB_KHR'), Extension('VK_KHR_xlib_surface', 6, 'VK_USE_PLATFORM_XLIB_KHR'), + Extension('VK_KHR_display', 1, 'VK_USE_PLATFORM_DISPLAY_KHR'), Extension('VK_KHX_multiview', 1, True), Extension('VK_EXT_debug_report', 8, True), Extension('VK_EXT_external_memory_dma_buf', 1, True), diff --git a/src/intel/vulkan/anv_extensions_gen.py b/src/intel/vulkan/anv_extensions_gen.py index 33827ecd015..84d07f9767a 100644 --- a/src/intel/vulkan/anv_extensions_gen.py +++ b/src/intel/vulkan/anv_extensions_gen.py @@ -113,7 +113,7 @@ _TEMPLATE_C = Template(COPYRIGHT + """ #include "vk_util.h" /* Convert the VK_USE_PLATFORM_* defines to booleans */ -%for platform in ['ANDROID', 'WAYLAND', 'XCB', 'XLIB']: +%for platform in ['ANDROID', 'WAYLAND', 'XCB', 'XLIB', 'DISPLAY']: #ifdef VK_USE_PLATFORM_${platform}_KHR # undef VK_USE_PLATFORM_${platform}_KHR # define VK_USE_PLATFORM_${platform}_KHR true @@ -132,7 +132,8 @@ _TEMPLATE_C = Template(COPYRIGHT + """ #define ANV_HAS_SURFACE (VK_USE_PLATFORM_WAYLAND_KHR || \\ VK_USE_PLATFORM_XCB_KHR || \\ - VK_USE_PLATFORM_XLIB_KHR) + VK_USE_PLATFORM_XLIB_KHR || \\ + VK_USE_PLATFORM_DISPLAY_KHR) const VkExtensionProperties anv_instance_extensions[ANV_INSTANCE_EXTENSION_COUNT] = { %for ext in instance_extensions: diff --git a/src/intel/vulkan/anv_wsi.c b/src/intel/vulkan/anv_wsi.c index 6082c3dd093..f86d83589ea 100644 --- a/src/intel/vulkan/anv_wsi.c +++ b/src/intel/vulkan/anv_wsi.c @@ -39,7 +39,8 @@ anv_init_wsi(struct anv_physical_device *physical_device) return wsi_device_init(&physical_device->wsi_device, anv_physical_device_to_handle(physical_device), anv_wsi_proc_addr, - &physical_device->instance->alloc); + &physical_device->instance->alloc, + physical_device->local_fd); } void diff --git a/src/intel/vulkan/anv_wsi_display.c b/src/intel/vulkan/anv_wsi_display.c new file mode 100644 index 00000000000..9b00d7f02e4 --- /dev/null +++ b/src/intel/vulkan/anv_wsi_display.c @@ -0,0 +1,129 @@ +/* + * 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 "anv_private.h" +#include "wsi_common.h" +#include "vk_format_info.h" +#include "vk_util.h" +#include "wsi_common_display.h" + +#define MM_PER_PIXEL (1.0/96.0 * 25.4) + +VkResult +anv_GetPhysicalDeviceDisplayPropertiesKHR(VkPhysicalDevice physical_device, + uint32_t *property_count, + VkDisplayPropertiesKHR *properties) +{ + ANV_FROM_HANDLE(anv_physical_device, pdevice, physical_device); + + return wsi_display_get_physical_device_display_properties(physical_device, + &pdevice->wsi_device, + property_count, + properties); +} + +VkResult +anv_GetPhysicalDeviceDisplayPlanePropertiesKHR(VkPhysicalDevice physical_device, + uint32_t *property_count, + VkDisplayPlanePropertiesKHR *properties) +{ + ANV_FROM_HANDLE(anv_physical_device, pdevice, physical_device); + + return wsi_display_get_physical_device_display_plane_properties(physical_device, + &pdevice->wsi_device, + property_count, + properties); +} + +VkResult +anv_GetDisplayPlaneSupportedDisplaysKHR(VkPhysicalDevice physical_device, + uint32_t plane_index, + uint32_t *display_count, + VkDisplayKHR *displays) +{ + ANV_FROM_HANDLE(anv_physical_device, pdevice, physical_device); + + return wsi_display_get_display_plane_supported_displays(physical_device, + &pdevice->wsi_device, + plane_index, + display_count, + displays); +} + + +VkResult +anv_GetDisplayModePropertiesKHR(VkPhysicalDevice physical_device, + VkDisplayKHR display, + uint32_t *property_count, + VkDisplayModePropertiesKHR *properties) +{ + ANV_FROM_HANDLE(anv_physical_device, pdevice, physical_device); + + return wsi_display_get_display_mode_properties(physical_device, + &pdevice->wsi_device, + display, + property_count, + properties); +} + +VkResult +anv_CreateDisplayModeKHR(VkPhysicalDevice physical_device, + VkDisplayKHR display, + const VkDisplayModeCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, + VkDisplayModeKHR *mode) +{ + return VK_ERROR_INITIALIZATION_FAILED; +} + + +VkResult +anv_GetDisplayPlaneCapabilitiesKHR(VkPhysicalDevice physical_device, + VkDisplayModeKHR mode_khr, + uint32_t plane_index, + VkDisplayPlaneCapabilitiesKHR *capabilities) +{ + ANV_FROM_HANDLE(anv_physical_device, pdevice, physical_device); + + return wsi_get_display_plane_capabilities(physical_device, + &pdevice->wsi_device, + mode_khr, + plane_index, + capabilities); +} + +VkResult +anv_CreateDisplayPlaneSurfaceKHR(VkInstance _instance, + const VkDisplaySurfaceCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, + VkSurfaceKHR *surface) +{ + ANV_FROM_HANDLE(anv_instance, instance, _instance); + const VkAllocationCallbacks *alloc; + + if (allocator) + alloc = allocator; + else + alloc = &instance->alloc; + + return wsi_create_display_surface(_instance, alloc, create_info, surface); +} diff --git a/src/intel/vulkan/meson.build b/src/intel/vulkan/meson.build index 69ec26e19b6..2e2ab8f7ecd 100644 --- a/src/intel/vulkan/meson.build +++ b/src/intel/vulkan/meson.build @@ -171,6 +171,13 @@ if with_platform_wayland libanv_files += files('anv_wsi_wayland.c') endif +if with_platform_display + anv_flags += [ + '-DVK_USE_PLATFORM_DISPLAY_KHR', + ] + libanv_files += files('anv_wsi_display.c') +endif + libanv_common = static_library( 'anv_common', [libanv_files, anv_entrypoints, anv_extensions_c, anv_extensions_h], diff --git a/src/vulkan/Makefile.am b/src/vulkan/Makefile.am index 037436c1cd7..c33ac5758f7 100644 --- a/src/vulkan/Makefile.am +++ b/src/vulkan/Makefile.am @@ -57,6 +57,13 @@ AM_CPPFLAGS += \ VULKAN_WSI_SOURCES += $(VULKAN_WSI_X11_FILES) endif +if HAVE_PLATFORM_DISPLAY +AM_CPPFLAGS += \ + -DVK_USE_PLATFORM_DISPLAY_KHR + +VULKAN_WSI_SOURCES += $(VULKAN_WSI_DISPLAY_FILES) +endif + BUILT_SOURCES += $(VULKAN_WSI_WAYLAND_GENERATED_FILES) CLEANFILES = $(BUILT_SOURCES) diff --git a/src/vulkan/Makefile.sources b/src/vulkan/Makefile.sources index a0a24ce7de8..3642c7662c4 100644 --- a/src/vulkan/Makefile.sources +++ b/src/vulkan/Makefile.sources @@ -17,6 +17,10 @@ VULKAN_WSI_X11_FILES := \ wsi/wsi_common_x11.c \ wsi/wsi_common_x11.h +VULKAN_WSI_DISPLAY_FILES := \ + wsi/wsi_common_display.c \ + wsi/wsi_common_display.h + VULKAN_UTIL_FILES := \ util/vk_alloc.h \ util/vk_debug_report.c \ diff --git a/src/vulkan/wsi/meson.build b/src/vulkan/wsi/meson.build index bd0fd3cc53e..743631a6113 100644 --- a/src/vulkan/wsi/meson.build +++ b/src/vulkan/wsi/meson.build @@ -57,6 +57,16 @@ if with_platform_wayland ] endif +if with_platform_display + vulkan_wsi_args += [ + '-DVK_USE_PLATFORM_DISPLAY_KHR', + ] + files_vulkan_wsi += files( + 'wsi_common_display.c', + 'wsi_common_display.h', + ) +endif + libvulkan_wsi = static_library( 'vulkan_wsi', files_vulkan_wsi, diff --git a/src/vulkan/wsi/wsi_common.c b/src/vulkan/wsi/wsi_common.c index 90ed07b7857..c0a285e5814 100644 --- a/src/vulkan/wsi/wsi_common.c +++ b/src/vulkan/wsi/wsi_common.c @@ -29,7 +29,8 @@ VkResult wsi_device_init(struct wsi_device *wsi, VkPhysicalDevice pdevice, WSI_FN_GetPhysicalDeviceProcAddr proc_addr, - const VkAllocationCallbacks *alloc) + const VkAllocationCallbacks *alloc, + int device_fd) { VkResult result; @@ -89,6 +90,19 @@ wsi_device_init(struct wsi_device *wsi, } #endif +#ifdef VK_USE_PLATFORM_DISPLAY_KHR + result = wsi_display_init_wsi(wsi, alloc, pdevice, device_fd); + if (result != VK_SUCCESS) { +#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 + return result; + } +#endif + return VK_SUCCESS; } @@ -96,6 +110,9 @@ 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 diff --git a/src/vulkan/wsi/wsi_common.h b/src/vulkan/wsi/wsi_common.h index 3e0d3be1c24..1cb6aaebca0 100644 --- a/src/vulkan/wsi/wsi_common.h +++ b/src/vulkan/wsi/wsi_common.h @@ -50,7 +50,7 @@ struct wsi_memory_allocate_info { struct wsi_interface; -#define VK_ICD_WSI_PLATFORM_MAX 5 +#define VK_ICD_WSI_PLATFORM_MAX 6 struct wsi_device { VkPhysicalDeviceMemoryProperties memory_props; @@ -93,7 +93,8 @@ VkResult wsi_device_init(struct wsi_device *wsi, VkPhysicalDevice pdevice, WSI_FN_GetPhysicalDeviceProcAddr proc_addr, - const VkAllocationCallbacks *alloc); + const VkAllocationCallbacks *alloc, + int device_fd); void wsi_device_finish(struct wsi_device *wsi, diff --git a/src/vulkan/wsi/wsi_common_display.c b/src/vulkan/wsi/wsi_common_display.c new file mode 100644 index 00000000000..2732b1dd721 --- /dev/null +++ b/src/vulkan/wsi/wsi_common_display.c @@ -0,0 +1,1368 @@ +/* + * 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 "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; + wsi_display_mode *current_mode; + drmModeModeInfo current_drm_mode; +} wsi_display_connector; + +struct wsi_display { + struct wsi_interface base; + + const VkAllocationCallbacks *alloc; + VkPhysicalDevice physical_device; + + int master_fd; + int render_fd; + + pthread_mutex_t wait_mutex; + pthread_cond_t wait_cond; + pthread_t wait_thread; + + struct list_head connectors; + + struct list_head display_modes; +}; + +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; + uint64_t flip_sequence; +}; + +struct wsi_display_swapchain { + struct wsi_swapchain base; + struct wsi_display *wsi; + VkIcdSurfaceDisplay *surface; + uint64_t flip_sequence; + struct wsi_display_image images[0]; +}; + +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 && + wsi->vscan == drm->vscan && + 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) (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 struct wsi_display_mode * +wsi_display_find_drm_mode(struct wsi_device *wsi_device, + struct wsi_display_connector *connector, + drmModeModeInfoPtr mode) +{ + struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + struct wsi_display_mode *display_mode; + + LIST_FOR_EACH_ENTRY(display_mode, &wsi->display_modes, list) { + if (display_mode->connector == connector && + 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) +{ + struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + struct wsi_display_mode *display_mode; + + LIST_FOR_EACH_ENTRY(display_mode, &wsi->display_modes, list) + if (display_mode->connector == 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; + + 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_alloc(wsi->alloc, sizeof (struct wsi_display_mode), 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); + 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, &wsi->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]; + struct wsi_display_connector *connector; + + connector = NULL; + LIST_FOR_EACH_ENTRY(connector, &wsi->connectors, list) { + 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; + + connector = vk_alloc(wsi->alloc, sizeof (struct wsi_display_connector), 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); + memset(connector, '\0', sizeof (*connector)); + connector->id = connector_id; + connector->wsi = wsi; + connector->active = false; + /* XXX use EDID name */ + connector->name = "monitor"; + 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]; + struct wsi_display_connector *connector; + drmModeConnectorPtr drm_connector; + VkResult result; + int m; + + if (wsi->master_fd < 0) + return NULL; + + drm_connector = drmModeGetConnector(wsi->master_fd, connector_id); + if (!drm_connector) + return NULL; + + 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; + + /* 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 (m = 0; m < drm_connector->count_modes; m++) { + 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 void +wsi_display_fill_in_display_properties(struct wsi_device *wsi_device, + struct wsi_display_connector *connector, + VkDisplayPropertiesKHR *properties) +{ + struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + struct wsi_display_mode *display_mode, *preferred_mode = NULL;; + + properties->display = wsi_display_connector_to_handle(connector); + properties->displayName = connector->name; + + /* Find the preferred mode and assume that's the physical resolution */ + + LIST_FOR_EACH_ENTRY(display_mode, &wsi->display_modes, list) { + if (display_mode->valid && display_mode->connector == connector && display_mode->preferred) { + preferred_mode = display_mode; + break; + } + } + + if (preferred_mode) { + properties->physicalResolution.width = preferred_mode->hdisplay; + properties->physicalResolution.height = preferred_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->persistentContent = 0; +} + +/* + * 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]; + struct wsi_display_connector *connector; + int c; + uint32_t connected; + uint32_t property_count_requested = *property_count; + drmModeResPtr mode_res; + + if (wsi->master_fd < 0) + return VK_ERROR_INITIALIZATION_FAILED; + + mode_res = drmModeGetResources(wsi->master_fd); + + if (!mode_res) + return VK_ERROR_INITIALIZATION_FAILED; + + connected = 0; + + /* Get current information */ + for (c = 0; c < mode_res->count_connectors; c++) { + 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) + connected++; + } + + /* Fill in property information if requested */ + if (properties != NULL) { + connected = 0; + + for (c = 0; c < mode_res->count_connectors; c++) { + connector = wsi_display_find_connector(wsi_device, mode_res->connectors[c]); + + if (connector && connector->connected) { + if (connected < property_count_requested) { + wsi_display_fill_in_display_properties(wsi_device, + connector, + &properties[connected]); + } + connected++; + } + } + } + + drmModeFreeResources(mode_res); + + *property_count = connected; + + if (connected > property_count_requested && properties != NULL) + return VK_INCOMPLETE; + + return VK_SUCCESS; +} + +/* + * Implement vkGetPhysicalDeviceDisplayPlanePropertiesKHR (VK_KHR_display + */ +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]; + struct wsi_display_connector *connector; + uint32_t property_count_requested = *property_count; + int c; + + if (!properties) + property_count_requested = 0; + + c = 0; + LIST_FOR_EACH_ENTRY(connector, &wsi->connectors, list) { + if (c < property_count_requested) { + if (connector && connector->active) { + properties[c].currentDisplay = wsi_display_connector_to_handle(connector); + properties[c].currentStackIndex = c; + } else { + properties[c].currentDisplay = NULL; + properties[c].currentStackIndex = 0; + } + } + c++; + } + + *property_count = c; + + if (c > property_count_requested && properties != NULL) + return VK_INCOMPLETE; + + return VK_SUCCESS; +} + +/* + * 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]; + struct wsi_display_connector *connector; + int c; + + + if (displays == NULL) { + *display_count = 1; + return VK_SUCCESS; + } + + if (*display_count < 1) + return VK_INCOMPLETE; + + c = 0; + LIST_FOR_EACH_ENTRY(connector, &wsi->connectors, list) { + if (c == plane_index) { + *displays = wsi_display_connector_to_handle(connector); + *display_count = 1; + return VK_SUCCESS; + } + c++; + } + + *displays = 0; + *display_count = 0; + + return VK_SUCCESS; +} + +/* + * Implement vkGetDisplayModePropertiesKHR (VK_KHR_display) + */ + +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 *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + struct wsi_display_connector *connector = wsi_display_connector_from_handle(display); + int i; + struct wsi_display_mode *display_mode; + uint32_t property_count_requested = *property_count; + + i = 0; + + if (properties == NULL) + property_count_requested = 0; + + LIST_FOR_EACH_ENTRY(display_mode, &wsi->display_modes, list) { + if (display_mode->valid && display_mode->connector == connector) { + if (i < property_count_requested) { + properties[i].displayMode = wsi_display_mode_to_handle(display_mode); + properties[i].parameters.visibleRegion.width = display_mode->hdisplay; + properties[i].parameters.visibleRegion.height = display_mode->vdisplay; + properties[i].parameters.refreshRate = (uint32_t) (wsi_display_mode_refresh(display_mode) * 1000 + 0.5); + } + i++; + } + } + + *property_count = i; + + if (i > property_count_requested && properties != NULL) + return VK_INCOMPLETE; + + return VK_SUCCESS; + +} + +/* + * 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_create_display_surface(VkInstance instance, + const VkAllocationCallbacks *allocator, + const VkDisplaySurfaceCreateInfoKHR *create_info, + VkSurfaceKHR *surface_khr) +{ + VkIcdSurfaceDisplay *surface; + + surface = vk_alloc(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, + const VkAllocationCallbacks *allocator, + uint32_t queueFamilyIndex, + int local_fd, + 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_INHERIT_BIT_KHR | + 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_capabilities2(VkIcdSurfaceBase *icd_surface, + const void *info_next, + VkSurfaceCapabilities2KHR *caps) +{ + assert(caps->sType == VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR); + + return wsi_display_surface_get_capabilities(icd_surface, &caps->surfaceCapabilities); +} + +static const VkFormat available_surface_formats[] = { + VK_FORMAT_B8G8R8A8_SRGB, + VK_FORMAT_B8G8R8A8_UNORM, +}; + +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]; + 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]; + f->surfaceFormat.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; + } + } + + return vk_outarray_status(&out); +} + +static const VkPresentModeKHR available_present_modes[] = { + VK_PRESENT_MODE_FIFO_KHR, +}; + +static VkResult +wsi_display_surface_get_present_modes(VkIcdSurfaceBase *surface, + uint32_t *present_mode_count, + VkPresentModeKHR *present_modes) +{ + if (present_modes == NULL) { + *present_mode_count = ARRAY_SIZE(available_present_modes); + return VK_SUCCESS; + } + + *present_mode_count = MIN2(*present_mode_count, ARRAY_SIZE(available_present_modes)); + typed_memcpy(present_modes, available_present_modes, *present_mode_count); + + if (*present_mode_count < ARRAY_SIZE(available_present_modes)) + return VK_INCOMPLETE; + return VK_SUCCESS; +} + +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; + VkResult result; + int ret; + uint32_t image_handle; + + if (chain->base.use_prime_blit) + result = wsi_create_prime_image(&chain->base, create_info, &image->base); + else + result = wsi_create_native_image(&chain->base, create_info, &image->base); + if (result != VK_SUCCESS) + return result; + + ret = drmPrimeFDToHandle(wsi->master_fd, image->base.fd, &image_handle); + + close(image->base.fd); + image->base.fd = -1; + + if (ret < 0) + goto fail_handle; + + image->chain = chain; + image->state = wsi_image_idle; + image->fb_id = 0; + + /* XXX extract depth and bpp from image somehow */ + ret = drmModeAddFB(wsi->master_fd, create_info->imageExtent.width, create_info->imageExtent.height, + 24, 32, image->base.row_pitch, image_handle, &image->fb_id); + + if (ret) + goto fail_fb; + + return VK_SUCCESS; + +fail_fb: + /* fall through */ + +fail_handle: + 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; + + 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]); + 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; + + 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); + (void) _wsi_display_queue_next(&(image->chain->base)); +} + +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 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 +}; + +static void * +wsi_display_wait_thread(void *data) +{ + struct wsi_display *wsi = data; + struct pollfd pollfd = { + .fd = wsi->master_fd, + .events = POLLIN + }; + int ret; + + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + for (;;) { + ret = poll(&pollfd, 1, -1); + if (ret > 0) { + pthread_mutex_lock(&wsi->wait_mutex); + (void) drmHandleEvent(wsi->master_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; +} + +/* 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 / ((uint64_t) 1000 * (uint64_t) 1000 * (uint64_t) 1000), + .tv_nsec = timeout_ns % ((uint64_t) 1000 * (uint64_t) 1000 * (uint64_t) 1000) + }; + + 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, + uint64_t timeout, + VkSemaphore semaphore, + 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; + + if (timeout != 0 && timeout != UINT64_MAX) + timeout += wsi_get_current_monotonic(); + + 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_OUT_OF_DATE_KHR; + goto done; + } + } +done: + pthread_mutex_unlock(&wsi->wait_mutex); + return result; +} + +/* + * 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) +{ + int c, e; + + /* See if any other connectors share the same encoder */ + for (c = 0; c < mode_res->count_connectors; c++) { + if (mode_res->connectors[c] == connector->connector_id) + continue; + + drmModeConnectorPtr other_connector = drmModeGetConnector(wsi->master_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 (e = 0; e < mode_res->count_encoders; e++) { + if (mode_res->encoders[e] == connector->encoder_id) + continue; + + drmModeEncoderPtr other_encoder = drmModeGetEncoder(wsi->master_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(struct wsi_display_connector *connector, + drmModeResPtr mode_res, + drmModeConnectorPtr drm_connector) +{ + struct wsi_display *wsi = connector->wsi; + int c; + uint32_t crtc_id; + + /* See what CRTC is currently driving this connector */ + if (drm_connector->encoder_id) { + drmModeEncoderPtr encoder = drmModeGetEncoder(wsi->master_fd, drm_connector->encoder_id); + if (encoder) { + 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; + } + } + } + crtc_id = 0; + for (c = 0; crtc_id == 0 && c < mode_res->count_crtcs; c++) { + drmModeCrtcPtr crtc = drmModeGetCrtc(wsi->master_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; + drmModeModeInfoPtr drm_mode; + drmModeConnectorPtr drm_connector; + drmModeResPtr mode_res; + VkResult result; + int m; + + if (connector->current_mode == display_mode && connector->crtc_id) + return VK_SUCCESS; + + mode_res = drmModeGetResources(wsi->master_fd); + if (!mode_res) { + result = VK_ERROR_INITIALIZATION_FAILED; + goto bail; + } + + drm_connector = drmModeGetConnectorCurrent(wsi->master_fd, connector->id); + if (!drm_connector) { + result = VK_ERROR_INITIALIZATION_FAILED; + 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_OUT_OF_DATE_KHR; + goto bail_connector; + } + } + + if (connector->current_mode != display_mode) { + + /* Find the drm mode cooresponding to the requested VkDisplayMode */ + drm_mode = NULL; + for (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_OUT_OF_DATE_KHR; + goto bail_connector; + } + + connector->current_mode = display_mode; + connector->current_drm_mode = *drm_mode; + } + + result = VK_SUCCESS; + +bail_connector: + drmModeFreeConnector(drm_connector); +bail_mode_res: + drmModeFreeResources(mode_res); +bail: + return result; + +} + +/* + * 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; + uint32_t i; + struct wsi_display_image *image = NULL; + VkIcdSurfaceDisplay *surface = chain->surface; + wsi_display_mode *display_mode = wsi_display_mode_from_handle(surface->displayMode); + wsi_display_connector *connector = display_mode->connector; + int ret; + VkResult result; + + if (wsi->master_fd < 0) + return VK_ERROR_INITIALIZATION_FAILED; + + 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 */ + + for (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; + + if (connector->active) { + ret = drmModePageFlip(wsi->master_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) { + switch(-ret) { + case EINVAL: + + 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->master_fd, connector->crtc_id, image->fb_id, 0, 0, + &connector->id, 1, &connector->current_drm_mode); + + if (ret == 0) { + image->state = wsi_image_displaying; + wsi_display_idle_old_displaying(image); + connector->active = true; + return VK_SUCCESS; + } + break; + case EACCES: + usleep(1000 * 1000); + connector->active = false; + break; + default: + connector->active = false; + image->state = wsi_image_idle; + return VK_ERROR_OUT_OF_DATE_KHR; + } + } + } +} + +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; + + 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); + + pthread_mutex_unlock(&wsi->wait_mutex); + + return result; +} + +static VkResult +wsi_display_surface_create_swapchain(VkIcdSurfaceBase *icd_surface, + VkDevice device, + struct wsi_device *wsi_device, + int local_fd, + 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]; + VkResult result; + + assert(create_info->sType == VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR); + + struct wsi_display_swapchain *chain; + const unsigned num_images = create_info->minImageCount; + size_t size = sizeof(*chain) + num_images * sizeof(chain->images[0]); + + chain = vk_alloc(allocator, size, 8, + VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); + + if (chain == NULL) + return VK_ERROR_OUT_OF_HOST_MEMORY; + + result = wsi_swapchain_init(wsi_device, &chain->base, device, + create_info, allocator); + + 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->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) + goto fail_init_images; + } + + *swapchain_out = &chain->base; + + return VK_SUCCESS; + +fail_init_images: + return result; +} + +VkResult +wsi_display_init_wsi(struct wsi_device *wsi_device, + const VkAllocationCallbacks *alloc, + VkPhysicalDevice physical_device, + int device_fd) +{ + struct wsi_display *wsi; + VkResult result; + + wsi = vk_alloc(alloc, sizeof(*wsi), 8, + VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); + if (!wsi) { + result = VK_ERROR_OUT_OF_HOST_MEMORY; + goto fail; + } + memset(wsi, '\0', sizeof (*wsi)); + + wsi->master_fd = -1; + if (drmGetNodeTypeFromFd(device_fd) == DRM_NODE_PRIMARY) + wsi->master_fd = device_fd; + wsi->render_fd = device_fd; + + pthread_mutex_init(&wsi->wait_mutex, NULL); + wsi->physical_device = physical_device; + wsi->alloc = alloc; + + LIST_INITHEAD(&wsi->display_modes); + LIST_INITHEAD(&wsi->connectors); + + pthread_condattr_t condattr; + int ret; + + ret = pthread_mutex_init(&wsi->wait_mutex, NULL); + if (ret) { + result = VK_ERROR_OUT_OF_HOST_MEMORY; + goto fail_mutex; + } + + ret = pthread_condattr_init(&condattr); + if (ret) { + result = VK_ERROR_OUT_OF_HOST_MEMORY; + goto fail_condattr; + } + + ret = pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC); + if (ret) { + result = VK_ERROR_OUT_OF_HOST_MEMORY; + goto fail_setclock; + } + + ret = pthread_cond_init(&wsi->wait_cond, &condattr); + if (ret) { + result = VK_ERROR_OUT_OF_HOST_MEMORY; + goto fail_cond; + } + + pthread_condattr_destroy(&condattr); + + wsi->base.get_support = wsi_display_surface_get_support; + wsi->base.get_capabilities = wsi_display_surface_get_capabilities; + 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.create_swapchain = wsi_display_surface_create_swapchain; + + wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY] = &wsi->base; + + return VK_SUCCESS; + +fail_cond: +fail_setclock: + pthread_condattr_destroy(&condattr); +fail_condattr: + 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) { + + struct wsi_display_connector *connector, *connector_storage; + LIST_FOR_EACH_ENTRY_SAFE(connector, connector_storage, &wsi->connectors, list) { + vk_free(wsi->alloc, connector); + } + + struct wsi_display_mode *mode, *mode_storage; + LIST_FOR_EACH_ENTRY_SAFE(mode, mode_storage, &wsi->display_modes, list) { + vk_free(wsi->alloc, mode); + } + + 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); + } +} diff --git a/src/vulkan/wsi/wsi_common_display.h b/src/vulkan/wsi/wsi_common_display.h new file mode 100644 index 00000000000..b414a226293 --- /dev/null +++ b/src/vulkan/wsi/wsi_common_display.h @@ -0,0 +1,72 @@ +/* + * 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_plane_properties(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + uint32_t *property_count, + VkDisplayPlanePropertiesKHR *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_get_display_plane_capabilities(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + VkDisplayModeKHR mode_khr, + uint32_t plane_index, + VkDisplayPlaneCapabilitiesKHR *capabilities); + +VkResult +wsi_create_display_surface(VkInstance instance, + const VkAllocationCallbacks *pAllocator, + const VkDisplaySurfaceCreateInfoKHR *pCreateInfo, + VkSurfaceKHR *pSurface); + +#endif diff --git a/src/vulkan/wsi/wsi_common_private.h b/src/vulkan/wsi/wsi_common_private.h index 503b2a015dc..d38d2efa116 100644 --- a/src/vulkan/wsi/wsi_common_private.h +++ b/src/vulkan/wsi/wsi_common_private.h @@ -135,6 +135,16 @@ 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, + VkPhysicalDevice physical_device, + int device_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 * \
This adds support for the KHR_display extension to the anv and radv Vulkan drivers. The drivers now attempt to open the master DRM node when the KHR_display extension is requested so that the common winsys code can perform the necessary operations. Signed-off-by: Keith Packard <keithp@keithp.com> --- configure.ac | 1 + meson.build | 4 +- src/amd/vulkan/Makefile.am | 8 + src/amd/vulkan/Makefile.sources | 3 + src/amd/vulkan/meson.build | 7 + src/amd/vulkan/radv_device.c | 28 +- src/amd/vulkan/radv_extensions.py | 7 +- src/amd/vulkan/radv_private.h | 2 + src/amd/vulkan/radv_wsi.c | 3 +- src/amd/vulkan/radv_wsi_display.c | 143 ++++ src/intel/Makefile.sources | 3 + src/intel/Makefile.vulkan.am | 7 + src/intel/vulkan/anv_device.c | 18 +- src/intel/vulkan/anv_extensions.py | 1 + src/intel/vulkan/anv_extensions_gen.py | 5 +- src/intel/vulkan/anv_wsi.c | 3 +- src/intel/vulkan/anv_wsi_display.c | 129 +++ src/intel/vulkan/meson.build | 7 + src/vulkan/Makefile.am | 7 + src/vulkan/Makefile.sources | 4 + src/vulkan/wsi/meson.build | 10 + src/vulkan/wsi/wsi_common.c | 19 +- src/vulkan/wsi/wsi_common.h | 5 +- src/vulkan/wsi/wsi_common_display.c | 1368 ++++++++++++++++++++++++++++++++ src/vulkan/wsi/wsi_common_display.h | 72 ++ src/vulkan/wsi/wsi_common_private.h | 10 + 26 files changed, 1858 insertions(+), 16 deletions(-) create mode 100644 src/amd/vulkan/radv_wsi_display.c create mode 100644 src/intel/vulkan/anv_wsi_display.c create mode 100644 src/vulkan/wsi/wsi_common_display.c create mode 100644 src/vulkan/wsi/wsi_common_display.h