Path: blob/21.2-virgl/src/vulkan/wsi/wsi_common_display.c
7263 views
/*1* Copyright © 2017 Keith Packard2*3* Permission to use, copy, modify, distribute, and sell this software and its4* documentation for any purpose is hereby granted without fee, provided that5* the above copyright notice appear in all copies and that both that copyright6* notice and this permission notice appear in supporting documentation, and7* that the name of the copyright holders not be used in advertising or8* publicity pertaining to distribution of the software without specific,9* written prior permission. The copyright holders make no representations10* about the suitability of this software for any purpose. It is provided "as11* is" without express or implied warranty.12*13* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,14* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO15* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR16* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,17* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER18* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE19* OF THIS SOFTWARE.20*/2122#include "util/macros.h"23#include <stdlib.h>24#include <stdio.h>25#include <unistd.h>26#include <errno.h>27#include <string.h>28#include <fcntl.h>29#include <poll.h>30#include <stdbool.h>31#include <math.h>32#include <xf86drm.h>33#include <xf86drmMode.h>34#include "drm-uapi/drm_fourcc.h"35#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT36#include <xcb/randr.h>37#include <X11/Xlib-xcb.h>38#endif39#include "util/hash_table.h"40#include "util/list.h"4142#include "vk_util.h"43#include "wsi_common_private.h"44#include "wsi_common_display.h"45#include "wsi_common_queue.h"4647#if 048#define wsi_display_debug(...) fprintf(stderr, __VA_ARGS__)49#define wsi_display_debug_code(...) __VA_ARGS__50#else51#define wsi_display_debug(...)52#define wsi_display_debug_code(...)53#endif5455/* These have lifetime equal to the instance, so they effectively56* never go away. This means we must keep track of them separately57* from all other resources.58*/59typedef struct wsi_display_mode {60struct list_head list;61struct wsi_display_connector *connector;62bool valid; /* was found in most recent poll */63bool preferred;64uint32_t clock; /* in kHz */65uint16_t hdisplay, hsync_start, hsync_end, htotal, hskew;66uint16_t vdisplay, vsync_start, vsync_end, vtotal, vscan;67uint32_t flags;68} wsi_display_mode;6970typedef struct wsi_display_connector {71struct list_head list;72struct wsi_display *wsi;73uint32_t id;74uint32_t crtc_id;75char *name;76bool connected;77bool active;78struct list_head display_modes;79wsi_display_mode *current_mode;80drmModeModeInfo current_drm_mode;81uint32_t dpms_property;82#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT83xcb_randr_output_t output;84#endif85} wsi_display_connector;8687struct wsi_display {88struct wsi_interface base;8990const VkAllocationCallbacks *alloc;9192int fd;9394pthread_mutex_t wait_mutex;95pthread_cond_t wait_cond;96pthread_t wait_thread;9798struct list_head connectors; /* list of all discovered connectors */99};100101#define wsi_for_each_display_mode(_mode, _conn) \102list_for_each_entry_safe(struct wsi_display_mode, _mode, \103&(_conn)->display_modes, list)104105#define wsi_for_each_connector(_conn, _dev) \106list_for_each_entry_safe(struct wsi_display_connector, _conn, \107&(_dev)->connectors, list)108109enum wsi_image_state {110WSI_IMAGE_IDLE,111WSI_IMAGE_DRAWING,112WSI_IMAGE_QUEUED,113WSI_IMAGE_FLIPPING,114WSI_IMAGE_DISPLAYING115};116117struct wsi_display_image {118struct wsi_image base;119struct wsi_display_swapchain *chain;120enum wsi_image_state state;121uint32_t fb_id;122uint32_t buffer[4];123uint64_t flip_sequence;124};125126struct wsi_display_swapchain {127struct wsi_swapchain base;128struct wsi_display *wsi;129VkIcdSurfaceDisplay *surface;130uint64_t flip_sequence;131VkResult status;132struct wsi_display_image images[0];133};134135struct wsi_display_fence {136struct wsi_fence base;137bool event_received;138bool destroyed;139uint32_t syncobj; /* syncobj to signal on event */140uint64_t sequence;141};142143static uint64_t fence_sequence;144145ICD_DEFINE_NONDISP_HANDLE_CASTS(wsi_display_mode, VkDisplayModeKHR)146ICD_DEFINE_NONDISP_HANDLE_CASTS(wsi_display_connector, VkDisplayKHR)147148static bool149wsi_display_mode_matches_drm(wsi_display_mode *wsi,150drmModeModeInfoPtr drm)151{152return wsi->clock == drm->clock &&153wsi->hdisplay == drm->hdisplay &&154wsi->hsync_start == drm->hsync_start &&155wsi->hsync_end == drm->hsync_end &&156wsi->htotal == drm->htotal &&157wsi->hskew == drm->hskew &&158wsi->vdisplay == drm->vdisplay &&159wsi->vsync_start == drm->vsync_start &&160wsi->vsync_end == drm->vsync_end &&161wsi->vtotal == drm->vtotal &&162MAX2(wsi->vscan, 1) == MAX2(drm->vscan, 1) &&163wsi->flags == drm->flags;164}165166static double167wsi_display_mode_refresh(struct wsi_display_mode *wsi)168{169return (double) wsi->clock * 1000.0 / ((double) wsi->htotal *170(double) wsi->vtotal *171(double) MAX2(wsi->vscan, 1));172}173174static uint64_t wsi_rel_to_abs_time(uint64_t rel_time)175{176uint64_t current_time = wsi_common_get_current_time();177178/* check for overflow */179if (rel_time > UINT64_MAX - current_time)180return UINT64_MAX;181182return current_time + rel_time;183}184185static struct wsi_display_mode *186wsi_display_find_drm_mode(struct wsi_device *wsi_device,187struct wsi_display_connector *connector,188drmModeModeInfoPtr mode)189{190wsi_for_each_display_mode(display_mode, connector) {191if (wsi_display_mode_matches_drm(display_mode, mode))192return display_mode;193}194return NULL;195}196197static void198wsi_display_invalidate_connector_modes(struct wsi_device *wsi_device,199struct wsi_display_connector *connector)200{201wsi_for_each_display_mode(display_mode, connector) {202display_mode->valid = false;203}204}205206static VkResult207wsi_display_register_drm_mode(struct wsi_device *wsi_device,208struct wsi_display_connector *connector,209drmModeModeInfoPtr drm_mode)210{211struct wsi_display *wsi =212(struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];213struct wsi_display_mode *display_mode =214wsi_display_find_drm_mode(wsi_device, connector, drm_mode);215216if (display_mode) {217display_mode->valid = true;218return VK_SUCCESS;219}220221display_mode = vk_zalloc(wsi->alloc, sizeof (struct wsi_display_mode),2228, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);223if (!display_mode)224return VK_ERROR_OUT_OF_HOST_MEMORY;225226display_mode->connector = connector;227display_mode->valid = true;228display_mode->preferred = (drm_mode->type & DRM_MODE_TYPE_PREFERRED) != 0;229display_mode->clock = drm_mode->clock; /* kHz */230display_mode->hdisplay = drm_mode->hdisplay;231display_mode->hsync_start = drm_mode->hsync_start;232display_mode->hsync_end = drm_mode->hsync_end;233display_mode->htotal = drm_mode->htotal;234display_mode->hskew = drm_mode->hskew;235display_mode->vdisplay = drm_mode->vdisplay;236display_mode->vsync_start = drm_mode->vsync_start;237display_mode->vsync_end = drm_mode->vsync_end;238display_mode->vtotal = drm_mode->vtotal;239display_mode->vscan = drm_mode->vscan;240display_mode->flags = drm_mode->flags;241242list_addtail(&display_mode->list, &connector->display_modes);243return VK_SUCCESS;244}245246/*247* Update our information about a specific connector248*/249250static struct wsi_display_connector *251wsi_display_find_connector(struct wsi_device *wsi_device,252uint32_t connector_id)253{254struct wsi_display *wsi =255(struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];256257wsi_for_each_connector(connector, wsi) {258if (connector->id == connector_id)259return connector;260}261262return NULL;263}264265static struct wsi_display_connector *266wsi_display_alloc_connector(struct wsi_display *wsi,267uint32_t connector_id)268{269struct wsi_display_connector *connector =270vk_zalloc(wsi->alloc, sizeof (struct wsi_display_connector),2718, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);272273connector->id = connector_id;274connector->wsi = wsi;275connector->active = false;276/* XXX use EDID name */277connector->name = "monitor";278list_inithead(&connector->display_modes);279return connector;280}281282static struct wsi_display_connector *283wsi_display_get_connector(struct wsi_device *wsi_device,284int drm_fd,285uint32_t connector_id)286{287struct wsi_display *wsi =288(struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];289290if (drm_fd < 0)291return NULL;292293drmModeConnectorPtr drm_connector =294drmModeGetConnector(drm_fd, connector_id);295296if (!drm_connector)297return NULL;298299struct wsi_display_connector *connector =300wsi_display_find_connector(wsi_device, connector_id);301302if (!connector) {303connector = wsi_display_alloc_connector(wsi, connector_id);304if (!connector) {305drmModeFreeConnector(drm_connector);306return NULL;307}308list_addtail(&connector->list, &wsi->connectors);309}310311connector->connected = drm_connector->connection != DRM_MODE_DISCONNECTED;312313/* Look for a DPMS property if we haven't already found one */314for (int p = 0; connector->dpms_property == 0 &&315p < drm_connector->count_props; p++)316{317drmModePropertyPtr prop = drmModeGetProperty(drm_fd,318drm_connector->props[p]);319if (!prop)320continue;321if (prop->flags & DRM_MODE_PROP_ENUM) {322if (!strcmp(prop->name, "DPMS"))323connector->dpms_property = drm_connector->props[p];324}325drmModeFreeProperty(prop);326}327328/* Mark all connector modes as invalid */329wsi_display_invalidate_connector_modes(wsi_device, connector);330331/*332* List current modes, adding new ones and marking existing ones as333* valid334*/335for (int m = 0; m < drm_connector->count_modes; m++) {336VkResult result = wsi_display_register_drm_mode(wsi_device,337connector,338&drm_connector->modes[m]);339if (result != VK_SUCCESS) {340drmModeFreeConnector(drm_connector);341return NULL;342}343}344345drmModeFreeConnector(drm_connector);346347return connector;348}349350#define MM_PER_PIXEL (1.0/96.0 * 25.4)351352static uint32_t353mode_size(struct wsi_display_mode *mode)354{355/* fortunately, these are both uint16_t, so this is easy */356return (uint32_t) mode->hdisplay * (uint32_t) mode->vdisplay;357}358359static void360wsi_display_fill_in_display_properties(struct wsi_device *wsi_device,361struct wsi_display_connector *connector,362VkDisplayProperties2KHR *properties2)363{364assert(properties2->sType == VK_STRUCTURE_TYPE_DISPLAY_PROPERTIES_2_KHR);365VkDisplayPropertiesKHR *properties = &properties2->displayProperties;366367properties->display = wsi_display_connector_to_handle(connector);368properties->displayName = connector->name;369370/* Find the first preferred mode and assume that's the physical371* resolution. If there isn't a preferred mode, find the largest mode and372* use that.373*/374375struct wsi_display_mode *preferred_mode = NULL, *largest_mode = NULL;376wsi_for_each_display_mode(display_mode, connector) {377if (!display_mode->valid)378continue;379if (display_mode->preferred) {380preferred_mode = display_mode;381break;382}383if (largest_mode == NULL ||384mode_size(display_mode) > mode_size(largest_mode))385{386largest_mode = display_mode;387}388}389390if (preferred_mode) {391properties->physicalResolution.width = preferred_mode->hdisplay;392properties->physicalResolution.height = preferred_mode->vdisplay;393} else if (largest_mode) {394properties->physicalResolution.width = largest_mode->hdisplay;395properties->physicalResolution.height = largest_mode->vdisplay;396} else {397properties->physicalResolution.width = 1024;398properties->physicalResolution.height = 768;399}400401/* Make up physical size based on 96dpi */402properties->physicalDimensions.width =403floor(properties->physicalResolution.width * MM_PER_PIXEL + 0.5);404properties->physicalDimensions.height =405floor(properties->physicalResolution.height * MM_PER_PIXEL + 0.5);406407properties->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;408properties->planeReorderPossible = VK_FALSE;409properties->persistentContent = VK_FALSE;410}411412/*413* Implement vkGetPhysicalDeviceDisplayPropertiesKHR (VK_KHR_display)414*/415VkResult416wsi_display_get_physical_device_display_properties(417VkPhysicalDevice physical_device,418struct wsi_device *wsi_device,419uint32_t *property_count,420VkDisplayPropertiesKHR *properties)421{422struct wsi_display *wsi =423(struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];424425if (properties == NULL) {426return wsi_display_get_physical_device_display_properties2(427physical_device, wsi_device, property_count, NULL);428} else {429/* If we're actually returning properties, allocate a temporary array of430* VkDisplayProperties2KHR structs, call properties2 to fill them out,431* and then copy them to the client. This seems a bit expensive but432* wsi_display_get_physical_device_display_properties2() calls433* drmModeGetResources() which does an ioctl and then a bunch of434* allocations so this should get lost in the noise.435*/436VkDisplayProperties2KHR *props2 =437vk_zalloc(wsi->alloc, sizeof(*props2) * *property_count, 8,438VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);439if (props2 == NULL)440return VK_ERROR_OUT_OF_HOST_MEMORY;441442for (uint32_t i = 0; i < *property_count; i++)443props2[i].sType = VK_STRUCTURE_TYPE_DISPLAY_PROPERTIES_2_KHR;444445VkResult result = wsi_display_get_physical_device_display_properties2(446physical_device, wsi_device, property_count, props2);447448if (result == VK_SUCCESS || result == VK_INCOMPLETE) {449for (uint32_t i = 0; i < *property_count; i++)450properties[i] = props2[i].displayProperties;451}452453vk_free(wsi->alloc, props2);454455return result;456}457}458459VkResult460wsi_display_get_physical_device_display_properties2(461VkPhysicalDevice physical_device,462struct wsi_device *wsi_device,463uint32_t *property_count,464VkDisplayProperties2KHR *properties)465{466struct wsi_display *wsi =467(struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];468469if (wsi->fd < 0)470goto bail;471472drmModeResPtr mode_res = drmModeGetResources(wsi->fd);473474if (!mode_res)475goto bail;476477VK_OUTARRAY_MAKE(conn, properties, property_count);478479/* Get current information */480481for (int c = 0; c < mode_res->count_connectors; c++) {482struct wsi_display_connector *connector =483wsi_display_get_connector(wsi_device, wsi->fd,484mode_res->connectors[c]);485486if (!connector) {487drmModeFreeResources(mode_res);488return VK_ERROR_OUT_OF_HOST_MEMORY;489}490491if (connector->connected) {492vk_outarray_append(&conn, prop) {493wsi_display_fill_in_display_properties(wsi_device,494connector,495prop);496}497}498}499500drmModeFreeResources(mode_res);501502return vk_outarray_status(&conn);503504bail:505*property_count = 0;506return VK_SUCCESS;507}508509/*510* Implement vkGetPhysicalDeviceDisplayPlanePropertiesKHR (VK_KHR_display511*/512static void513wsi_display_fill_in_display_plane_properties(514struct wsi_device *wsi_device,515struct wsi_display_connector *connector,516VkDisplayPlaneProperties2KHR *properties)517{518assert(properties->sType == VK_STRUCTURE_TYPE_DISPLAY_PLANE_PROPERTIES_2_KHR);519VkDisplayPlanePropertiesKHR *prop = &properties->displayPlaneProperties;520521if (connector && connector->active) {522prop->currentDisplay = wsi_display_connector_to_handle(connector);523prop->currentStackIndex = 0;524} else {525prop->currentDisplay = VK_NULL_HANDLE;526prop->currentStackIndex = 0;527}528}529530VkResult531wsi_display_get_physical_device_display_plane_properties(532VkPhysicalDevice physical_device,533struct wsi_device *wsi_device,534uint32_t *property_count,535VkDisplayPlanePropertiesKHR *properties)536{537struct wsi_display *wsi =538(struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];539540VK_OUTARRAY_MAKE(conn, properties, property_count);541542wsi_for_each_connector(connector, wsi) {543vk_outarray_append(&conn, prop) {544VkDisplayPlaneProperties2KHR prop2 = {545.sType = VK_STRUCTURE_TYPE_DISPLAY_PLANE_PROPERTIES_2_KHR,546};547wsi_display_fill_in_display_plane_properties(wsi_device, connector,548&prop2);549*prop = prop2.displayPlaneProperties;550}551}552return vk_outarray_status(&conn);553}554555VkResult556wsi_display_get_physical_device_display_plane_properties2(557VkPhysicalDevice physical_device,558struct wsi_device *wsi_device,559uint32_t *property_count,560VkDisplayPlaneProperties2KHR *properties)561{562struct wsi_display *wsi =563(struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];564565VK_OUTARRAY_MAKE(conn, properties, property_count);566567wsi_for_each_connector(connector, wsi) {568vk_outarray_append(&conn, prop) {569wsi_display_fill_in_display_plane_properties(wsi_device, connector,570prop);571}572}573return vk_outarray_status(&conn);574}575576/*577* Implement vkGetDisplayPlaneSupportedDisplaysKHR (VK_KHR_display)578*/579580VkResult581wsi_display_get_display_plane_supported_displays(582VkPhysicalDevice physical_device,583struct wsi_device *wsi_device,584uint32_t plane_index,585uint32_t *display_count,586VkDisplayKHR *displays)587{588struct wsi_display *wsi =589(struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];590591VK_OUTARRAY_MAKE(conn, displays, display_count);592593int c = 0;594595wsi_for_each_connector(connector, wsi) {596if (c == plane_index && connector->connected) {597vk_outarray_append(&conn, display) {598*display = wsi_display_connector_to_handle(connector);599}600}601c++;602}603return vk_outarray_status(&conn);604}605606/*607* Implement vkGetDisplayModePropertiesKHR (VK_KHR_display)608*/609610static void611wsi_display_fill_in_display_mode_properties(612struct wsi_device *wsi_device,613struct wsi_display_mode *display_mode,614VkDisplayModeProperties2KHR *properties)615{616assert(properties->sType == VK_STRUCTURE_TYPE_DISPLAY_MODE_PROPERTIES_2_KHR);617VkDisplayModePropertiesKHR *prop = &properties->displayModeProperties;618619prop->displayMode = wsi_display_mode_to_handle(display_mode);620prop->parameters.visibleRegion.width = display_mode->hdisplay;621prop->parameters.visibleRegion.height = display_mode->vdisplay;622prop->parameters.refreshRate =623(uint32_t) (wsi_display_mode_refresh(display_mode) * 1000 + 0.5);624}625626VkResult627wsi_display_get_display_mode_properties(VkPhysicalDevice physical_device,628struct wsi_device *wsi_device,629VkDisplayKHR display,630uint32_t *property_count,631VkDisplayModePropertiesKHR *properties)632{633struct wsi_display_connector *connector =634wsi_display_connector_from_handle(display);635636VK_OUTARRAY_MAKE(conn, properties, property_count);637638wsi_for_each_display_mode(display_mode, connector) {639if (!display_mode->valid)640continue;641642vk_outarray_append(&conn, prop) {643VkDisplayModeProperties2KHR prop2 = {644.sType = VK_STRUCTURE_TYPE_DISPLAY_MODE_PROPERTIES_2_KHR,645};646wsi_display_fill_in_display_mode_properties(wsi_device,647display_mode, &prop2);648*prop = prop2.displayModeProperties;649}650}651return vk_outarray_status(&conn);652}653654VkResult655wsi_display_get_display_mode_properties2(VkPhysicalDevice physical_device,656struct wsi_device *wsi_device,657VkDisplayKHR display,658uint32_t *property_count,659VkDisplayModeProperties2KHR *properties)660{661struct wsi_display_connector *connector =662wsi_display_connector_from_handle(display);663664VK_OUTARRAY_MAKE(conn, properties, property_count);665666wsi_for_each_display_mode(display_mode, connector) {667if (!display_mode->valid)668continue;669670vk_outarray_append(&conn, prop) {671wsi_display_fill_in_display_mode_properties(wsi_device,672display_mode, prop);673}674}675return vk_outarray_status(&conn);676}677678static bool679wsi_display_mode_matches_vk(wsi_display_mode *wsi,680const VkDisplayModeParametersKHR *vk)681{682return (vk->visibleRegion.width == wsi->hdisplay &&683vk->visibleRegion.height == wsi->vdisplay &&684fabs(wsi_display_mode_refresh(wsi) * 1000.0 - vk->refreshRate) < 10);685}686687/*688* Implement vkCreateDisplayModeKHR (VK_KHR_display)689*/690VkResult691wsi_display_create_display_mode(VkPhysicalDevice physical_device,692struct wsi_device *wsi_device,693VkDisplayKHR display,694const VkDisplayModeCreateInfoKHR *create_info,695const VkAllocationCallbacks *allocator,696VkDisplayModeKHR *mode)697{698struct wsi_display_connector *connector =699wsi_display_connector_from_handle(display);700701if (create_info->flags != 0)702return VK_ERROR_INITIALIZATION_FAILED;703704/* Check and see if the requested mode happens to match an existing one and705* return that. This makes the conformance suite happy. Doing more than706* this would involve embedding the CVT function into the driver, which seems707* excessive.708*/709wsi_for_each_display_mode(display_mode, connector) {710if (display_mode->valid) {711if (wsi_display_mode_matches_vk(display_mode, &create_info->parameters)) {712*mode = wsi_display_mode_to_handle(display_mode);713return VK_SUCCESS;714}715}716}717return VK_ERROR_INITIALIZATION_FAILED;718}719720/*721* Implement vkGetDisplayPlaneCapabilities722*/723VkResult724wsi_get_display_plane_capabilities(VkPhysicalDevice physical_device,725struct wsi_device *wsi_device,726VkDisplayModeKHR mode_khr,727uint32_t plane_index,728VkDisplayPlaneCapabilitiesKHR *capabilities)729{730struct wsi_display_mode *mode = wsi_display_mode_from_handle(mode_khr);731732/* XXX use actual values */733capabilities->supportedAlpha = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR;734capabilities->minSrcPosition.x = 0;735capabilities->minSrcPosition.y = 0;736capabilities->maxSrcPosition.x = 0;737capabilities->maxSrcPosition.y = 0;738capabilities->minSrcExtent.width = mode->hdisplay;739capabilities->minSrcExtent.height = mode->vdisplay;740capabilities->maxSrcExtent.width = mode->hdisplay;741capabilities->maxSrcExtent.height = mode->vdisplay;742capabilities->minDstPosition.x = 0;743capabilities->minDstPosition.y = 0;744capabilities->maxDstPosition.x = 0;745capabilities->maxDstPosition.y = 0;746capabilities->minDstExtent.width = mode->hdisplay;747capabilities->minDstExtent.height = mode->vdisplay;748capabilities->maxDstExtent.width = mode->hdisplay;749capabilities->maxDstExtent.height = mode->vdisplay;750return VK_SUCCESS;751}752753VkResult754wsi_get_display_plane_capabilities2(755VkPhysicalDevice physical_device,756struct wsi_device *wsi_device,757const VkDisplayPlaneInfo2KHR *pDisplayPlaneInfo,758VkDisplayPlaneCapabilities2KHR *capabilities)759{760assert(capabilities->sType ==761VK_STRUCTURE_TYPE_DISPLAY_PLANE_CAPABILITIES_2_KHR);762763VkResult result =764wsi_get_display_plane_capabilities(physical_device, wsi_device,765pDisplayPlaneInfo->mode,766pDisplayPlaneInfo->planeIndex,767&capabilities->capabilities);768769vk_foreach_struct(ext, capabilities->pNext) {770switch (ext->sType) {771case VK_STRUCTURE_TYPE_SURFACE_PROTECTED_CAPABILITIES_KHR: {772VkSurfaceProtectedCapabilitiesKHR *protected = (void *)ext;773protected->supportsProtected = VK_FALSE;774break;775}776777default:778/* Ignored */779break;780}781}782783return result;784}785786VkResult787wsi_create_display_surface(VkInstance instance,788const VkAllocationCallbacks *allocator,789const VkDisplaySurfaceCreateInfoKHR *create_info,790VkSurfaceKHR *surface_khr)791{792VkIcdSurfaceDisplay *surface = vk_zalloc(allocator, sizeof *surface, 8,793VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);794795if (surface == NULL)796return VK_ERROR_OUT_OF_HOST_MEMORY;797798surface->base.platform = VK_ICD_WSI_PLATFORM_DISPLAY;799800surface->displayMode = create_info->displayMode;801surface->planeIndex = create_info->planeIndex;802surface->planeStackIndex = create_info->planeStackIndex;803surface->transform = create_info->transform;804surface->globalAlpha = create_info->globalAlpha;805surface->alphaMode = create_info->alphaMode;806surface->imageExtent = create_info->imageExtent;807808*surface_khr = VkIcdSurfaceBase_to_handle(&surface->base);809return VK_SUCCESS;810}811812813static VkResult814wsi_display_surface_get_support(VkIcdSurfaceBase *surface,815struct wsi_device *wsi_device,816uint32_t queueFamilyIndex,817VkBool32* pSupported)818{819struct wsi_display *wsi =820(struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];821822*pSupported = wsi->fd != -1;823return VK_SUCCESS;824}825826static VkResult827wsi_display_surface_get_capabilities(VkIcdSurfaceBase *surface_base,828struct wsi_device *wsi_device,829VkSurfaceCapabilitiesKHR* caps)830{831VkIcdSurfaceDisplay *surface = (VkIcdSurfaceDisplay *) surface_base;832wsi_display_mode *mode = wsi_display_mode_from_handle(surface->displayMode);833834caps->currentExtent.width = mode->hdisplay;835caps->currentExtent.height = mode->vdisplay;836837caps->minImageExtent = (VkExtent2D) { 1, 1 };838caps->maxImageExtent = (VkExtent2D) {839wsi_device->maxImageDimension2D,840wsi_device->maxImageDimension2D,841};842843caps->supportedCompositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;844845caps->minImageCount = 2;846caps->maxImageCount = 0;847848caps->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;849caps->currentTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;850caps->maxImageArrayLayers = 1;851caps->supportedUsageFlags =852VK_IMAGE_USAGE_TRANSFER_SRC_BIT |853VK_IMAGE_USAGE_SAMPLED_BIT |854VK_IMAGE_USAGE_TRANSFER_DST_BIT |855VK_IMAGE_USAGE_STORAGE_BIT |856VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;857858return VK_SUCCESS;859}860861static VkResult862wsi_display_surface_get_surface_counters(863VkIcdSurfaceBase *surface_base,864VkSurfaceCounterFlagsEXT *counters)865{866*counters = VK_SURFACE_COUNTER_VBLANK_EXT;867return VK_SUCCESS;868}869870static VkResult871wsi_display_surface_get_capabilities2(VkIcdSurfaceBase *icd_surface,872struct wsi_device *wsi_device,873const void *info_next,874VkSurfaceCapabilities2KHR *caps)875{876assert(caps->sType == VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR);877VkResult result;878879result = wsi_display_surface_get_capabilities(icd_surface, wsi_device,880&caps->surfaceCapabilities);881if (result != VK_SUCCESS)882return result;883884struct wsi_surface_supported_counters *counters =885vk_find_struct( caps->pNext, WSI_SURFACE_SUPPORTED_COUNTERS_MESA);886887if (counters) {888result = wsi_display_surface_get_surface_counters(889icd_surface,890&counters->supported_surface_counters);891}892893return result;894}895896static const struct {897VkFormat format;898uint32_t drm_format;899} available_surface_formats[] = {900{ .format = VK_FORMAT_B8G8R8A8_SRGB, .drm_format = DRM_FORMAT_XRGB8888 },901{ .format = VK_FORMAT_B8G8R8A8_UNORM, .drm_format = DRM_FORMAT_XRGB8888 },902};903904static void905get_sorted_vk_formats(struct wsi_device *wsi_device, VkFormat *sorted_formats)906{907for (unsigned i = 0; i < ARRAY_SIZE(available_surface_formats); i++)908sorted_formats[i] = available_surface_formats[i].format;909910if (wsi_device->force_bgra8_unorm_first) {911for (unsigned i = 0; i < ARRAY_SIZE(available_surface_formats); i++) {912if (sorted_formats[i] == VK_FORMAT_B8G8R8A8_UNORM) {913sorted_formats[i] = sorted_formats[0];914sorted_formats[0] = VK_FORMAT_B8G8R8A8_UNORM;915break;916}917}918}919}920921static VkResult922wsi_display_surface_get_formats(VkIcdSurfaceBase *icd_surface,923struct wsi_device *wsi_device,924uint32_t *surface_format_count,925VkSurfaceFormatKHR *surface_formats)926{927VK_OUTARRAY_MAKE(out, surface_formats, surface_format_count);928929VkFormat sorted_formats[ARRAY_SIZE(available_surface_formats)];930get_sorted_vk_formats(wsi_device, sorted_formats);931932for (unsigned i = 0; i < ARRAY_SIZE(sorted_formats); i++) {933vk_outarray_append(&out, f) {934f->format = sorted_formats[i];935f->colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;936}937}938939return vk_outarray_status(&out);940}941942static VkResult943wsi_display_surface_get_formats2(VkIcdSurfaceBase *surface,944struct wsi_device *wsi_device,945const void *info_next,946uint32_t *surface_format_count,947VkSurfaceFormat2KHR *surface_formats)948{949VK_OUTARRAY_MAKE(out, surface_formats, surface_format_count);950951VkFormat sorted_formats[ARRAY_SIZE(available_surface_formats)];952get_sorted_vk_formats(wsi_device, sorted_formats);953954for (unsigned i = 0; i < ARRAY_SIZE(sorted_formats); i++) {955vk_outarray_append(&out, f) {956assert(f->sType == VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR);957f->surfaceFormat.format = sorted_formats[i];958f->surfaceFormat.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;959}960}961962return vk_outarray_status(&out);963}964965static VkResult966wsi_display_surface_get_present_modes(VkIcdSurfaceBase *surface,967uint32_t *present_mode_count,968VkPresentModeKHR *present_modes)969{970VK_OUTARRAY_MAKE(conn, present_modes, present_mode_count);971972vk_outarray_append(&conn, present) {973*present = VK_PRESENT_MODE_FIFO_KHR;974}975976return vk_outarray_status(&conn);977}978979static VkResult980wsi_display_surface_get_present_rectangles(VkIcdSurfaceBase *surface_base,981struct wsi_device *wsi_device,982uint32_t* pRectCount,983VkRect2D* pRects)984{985VkIcdSurfaceDisplay *surface = (VkIcdSurfaceDisplay *) surface_base;986wsi_display_mode *mode = wsi_display_mode_from_handle(surface->displayMode);987VK_OUTARRAY_MAKE(out, pRects, pRectCount);988989if (wsi_device_matches_drm_fd(wsi_device, mode->connector->wsi->fd)) {990vk_outarray_append(&out, rect) {991*rect = (VkRect2D) {992.offset = { 0, 0 },993.extent = { mode->hdisplay, mode->vdisplay },994};995}996}997998return vk_outarray_status(&out);999}10001001static void1002wsi_display_destroy_buffer(struct wsi_display *wsi,1003uint32_t buffer)1004{1005(void) drmIoctl(wsi->fd, DRM_IOCTL_GEM_CLOSE,1006&((struct drm_gem_close) { .handle = buffer }));1007}10081009static VkResult1010wsi_display_image_init(VkDevice device_h,1011struct wsi_swapchain *drv_chain,1012const VkSwapchainCreateInfoKHR *create_info,1013const VkAllocationCallbacks *allocator,1014struct wsi_display_image *image)1015{1016struct wsi_display_swapchain *chain =1017(struct wsi_display_swapchain *) drv_chain;1018struct wsi_display *wsi = chain->wsi;1019uint32_t drm_format = 0;10201021for (unsigned i = 0; i < ARRAY_SIZE(available_surface_formats); i++) {1022if (create_info->imageFormat == available_surface_formats[i].format) {1023drm_format = available_surface_formats[i].drm_format;1024break;1025}1026}10271028/* the application provided an invalid format, bail */1029if (drm_format == 0)1030return VK_ERROR_DEVICE_LOST;10311032VkResult result = wsi_create_native_image(&chain->base, create_info,10330, NULL, NULL,1034&image->base);1035if (result != VK_SUCCESS)1036return result;10371038memset(image->buffer, 0, sizeof (image->buffer));10391040for (unsigned int i = 0; i < image->base.num_planes; i++) {1041int ret = drmPrimeFDToHandle(wsi->fd, image->base.fds[i],1042&image->buffer[i]);10431044close(image->base.fds[i]);1045image->base.fds[i] = -1;1046if (ret < 0)1047goto fail_handle;1048}10491050image->chain = chain;1051image->state = WSI_IMAGE_IDLE;1052image->fb_id = 0;10531054int ret = drmModeAddFB2(wsi->fd,1055create_info->imageExtent.width,1056create_info->imageExtent.height,1057drm_format,1058image->buffer,1059image->base.row_pitches,1060image->base.offsets,1061&image->fb_id, 0);10621063if (ret)1064goto fail_fb;10651066return VK_SUCCESS;10671068fail_fb:1069fail_handle:1070for (unsigned int i = 0; i < image->base.num_planes; i++) {1071if (image->buffer[i])1072wsi_display_destroy_buffer(wsi, image->buffer[i]);1073if (image->base.fds[i] != -1) {1074close(image->base.fds[i]);1075image->base.fds[i] = -1;1076}1077}10781079wsi_destroy_image(&chain->base, &image->base);10801081return VK_ERROR_OUT_OF_HOST_MEMORY;1082}10831084static void1085wsi_display_image_finish(struct wsi_swapchain *drv_chain,1086const VkAllocationCallbacks *allocator,1087struct wsi_display_image *image)1088{1089struct wsi_display_swapchain *chain =1090(struct wsi_display_swapchain *) drv_chain;1091struct wsi_display *wsi = chain->wsi;10921093drmModeRmFB(wsi->fd, image->fb_id);1094for (unsigned int i = 0; i < image->base.num_planes; i++)1095wsi_display_destroy_buffer(wsi, image->buffer[i]);1096wsi_destroy_image(&chain->base, &image->base);1097}10981099static VkResult1100wsi_display_swapchain_destroy(struct wsi_swapchain *drv_chain,1101const VkAllocationCallbacks *allocator)1102{1103struct wsi_display_swapchain *chain =1104(struct wsi_display_swapchain *) drv_chain;11051106for (uint32_t i = 0; i < chain->base.image_count; i++)1107wsi_display_image_finish(drv_chain, allocator, &chain->images[i]);11081109wsi_swapchain_finish(&chain->base);1110vk_free(allocator, chain);1111return VK_SUCCESS;1112}11131114static struct wsi_image *1115wsi_display_get_wsi_image(struct wsi_swapchain *drv_chain,1116uint32_t image_index)1117{1118struct wsi_display_swapchain *chain =1119(struct wsi_display_swapchain *) drv_chain;11201121return &chain->images[image_index].base;1122}11231124static void1125wsi_display_idle_old_displaying(struct wsi_display_image *active_image)1126{1127struct wsi_display_swapchain *chain = active_image->chain;11281129wsi_display_debug("idle everyone but %ld\n",1130active_image - &(chain->images[0]));1131for (uint32_t i = 0; i < chain->base.image_count; i++)1132if (chain->images[i].state == WSI_IMAGE_DISPLAYING &&1133&chain->images[i] != active_image)1134{1135wsi_display_debug("idle %d\n", i);1136chain->images[i].state = WSI_IMAGE_IDLE;1137}1138}11391140static VkResult1141_wsi_display_queue_next(struct wsi_swapchain *drv_chain);11421143static void1144wsi_display_page_flip_handler2(int fd,1145unsigned int frame,1146unsigned int sec,1147unsigned int usec,1148uint32_t crtc_id,1149void *data)1150{1151struct wsi_display_image *image = data;1152struct wsi_display_swapchain *chain = image->chain;11531154wsi_display_debug("image %ld displayed at %d\n",1155image - &(image->chain->images[0]), frame);1156image->state = WSI_IMAGE_DISPLAYING;1157wsi_display_idle_old_displaying(image);1158VkResult result = _wsi_display_queue_next(&(chain->base));1159if (result != VK_SUCCESS)1160chain->status = result;1161}11621163static void wsi_display_fence_event_handler(struct wsi_display_fence *fence);11641165static void wsi_display_page_flip_handler(int fd,1166unsigned int frame,1167unsigned int sec,1168unsigned int usec,1169void *data)1170{1171wsi_display_page_flip_handler2(fd, frame, sec, usec, 0, data);1172}11731174static void wsi_display_vblank_handler(int fd, unsigned int frame,1175unsigned int sec, unsigned int usec,1176void *data)1177{1178struct wsi_display_fence *fence = data;11791180wsi_display_fence_event_handler(fence);1181}11821183static void wsi_display_sequence_handler(int fd, uint64_t frame,1184uint64_t nsec, uint64_t user_data)1185{1186struct wsi_display_fence *fence =1187(struct wsi_display_fence *) (uintptr_t) user_data;11881189wsi_display_fence_event_handler(fence);1190}11911192static drmEventContext event_context = {1193.version = DRM_EVENT_CONTEXT_VERSION,1194.page_flip_handler = wsi_display_page_flip_handler,1195#if DRM_EVENT_CONTEXT_VERSION >= 31196.page_flip_handler2 = wsi_display_page_flip_handler2,1197#endif1198.vblank_handler = wsi_display_vblank_handler,1199.sequence_handler = wsi_display_sequence_handler,1200};12011202static void *1203wsi_display_wait_thread(void *data)1204{1205struct wsi_display *wsi = data;1206struct pollfd pollfd = {1207.fd = wsi->fd,1208.events = POLLIN1209};12101211pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);1212for (;;) {1213int ret = poll(&pollfd, 1, -1);1214if (ret > 0) {1215pthread_mutex_lock(&wsi->wait_mutex);1216(void) drmHandleEvent(wsi->fd, &event_context);1217pthread_cond_broadcast(&wsi->wait_cond);1218pthread_mutex_unlock(&wsi->wait_mutex);1219}1220}1221return NULL;1222}12231224static int1225wsi_display_start_wait_thread(struct wsi_display *wsi)1226{1227if (!wsi->wait_thread) {1228int ret = pthread_create(&wsi->wait_thread, NULL,1229wsi_display_wait_thread, wsi);1230if (ret)1231return ret;1232}1233return 0;1234}12351236static void1237wsi_display_stop_wait_thread(struct wsi_display *wsi)1238{1239pthread_mutex_lock(&wsi->wait_mutex);1240if (wsi->wait_thread) {1241pthread_cancel(wsi->wait_thread);1242pthread_join(wsi->wait_thread, NULL);1243wsi->wait_thread = 0;1244}1245pthread_mutex_unlock(&wsi->wait_mutex);1246}12471248/*1249* Wait for at least one event from the kernel to be processed.1250* Call with wait_mutex held1251*/1252static int1253wsi_display_wait_for_event(struct wsi_display *wsi,1254uint64_t timeout_ns)1255{1256int ret;12571258ret = wsi_display_start_wait_thread(wsi);12591260if (ret)1261return ret;12621263struct timespec abs_timeout = {1264.tv_sec = timeout_ns / 1000000000ULL,1265.tv_nsec = timeout_ns % 1000000000ULL,1266};12671268ret = pthread_cond_timedwait(&wsi->wait_cond, &wsi->wait_mutex,1269&abs_timeout);12701271wsi_display_debug("%9ld done waiting for event %d\n", pthread_self(), ret);1272return ret;1273}12741275static VkResult1276wsi_display_acquire_next_image(struct wsi_swapchain *drv_chain,1277const VkAcquireNextImageInfoKHR *info,1278uint32_t *image_index)1279{1280struct wsi_display_swapchain *chain =1281(struct wsi_display_swapchain *)drv_chain;1282struct wsi_display *wsi = chain->wsi;1283int ret = 0;1284VkResult result = VK_SUCCESS;12851286/* Bail early if the swapchain is broken */1287if (chain->status != VK_SUCCESS)1288return chain->status;12891290uint64_t timeout = info->timeout;1291if (timeout != 0 && timeout != UINT64_MAX)1292timeout = wsi_rel_to_abs_time(timeout);12931294pthread_mutex_lock(&wsi->wait_mutex);1295for (;;) {1296for (uint32_t i = 0; i < chain->base.image_count; i++) {1297if (chain->images[i].state == WSI_IMAGE_IDLE) {1298*image_index = i;1299wsi_display_debug("image %d available\n", i);1300chain->images[i].state = WSI_IMAGE_DRAWING;1301result = VK_SUCCESS;1302goto done;1303}1304wsi_display_debug("image %d state %d\n", i, chain->images[i].state);1305}13061307if (ret == ETIMEDOUT) {1308result = VK_TIMEOUT;1309goto done;1310}13111312ret = wsi_display_wait_for_event(wsi, timeout);13131314if (ret && ret != ETIMEDOUT) {1315result = VK_ERROR_SURFACE_LOST_KHR;1316goto done;1317}1318}1319done:1320pthread_mutex_unlock(&wsi->wait_mutex);13211322if (result != VK_SUCCESS)1323return result;13241325return chain->status;1326}13271328/*1329* Check whether there are any other connectors driven by this crtc1330*/1331static bool1332wsi_display_crtc_solo(struct wsi_display *wsi,1333drmModeResPtr mode_res,1334drmModeConnectorPtr connector,1335uint32_t crtc_id)1336{1337/* See if any other connectors share the same encoder */1338for (int c = 0; c < mode_res->count_connectors; c++) {1339if (mode_res->connectors[c] == connector->connector_id)1340continue;13411342drmModeConnectorPtr other_connector =1343drmModeGetConnector(wsi->fd, mode_res->connectors[c]);13441345if (other_connector) {1346bool match = (other_connector->encoder_id == connector->encoder_id);1347drmModeFreeConnector(other_connector);1348if (match)1349return false;1350}1351}13521353/* See if any other encoders share the same crtc */1354for (int e = 0; e < mode_res->count_encoders; e++) {1355if (mode_res->encoders[e] == connector->encoder_id)1356continue;13571358drmModeEncoderPtr other_encoder =1359drmModeGetEncoder(wsi->fd, mode_res->encoders[e]);13601361if (other_encoder) {1362bool match = (other_encoder->crtc_id == crtc_id);1363drmModeFreeEncoder(other_encoder);1364if (match)1365return false;1366}1367}1368return true;1369}13701371/*1372* Pick a suitable CRTC to drive this connector. Prefer a CRTC which is1373* currently driving this connector and not any others. Settle for a CRTC1374* which is currently idle.1375*/1376static uint32_t1377wsi_display_select_crtc(const struct wsi_display_connector *connector,1378drmModeResPtr mode_res,1379drmModeConnectorPtr drm_connector)1380{1381struct wsi_display *wsi = connector->wsi;13821383/* See what CRTC is currently driving this connector */1384if (drm_connector->encoder_id) {1385drmModeEncoderPtr encoder =1386drmModeGetEncoder(wsi->fd, drm_connector->encoder_id);13871388if (encoder) {1389uint32_t crtc_id = encoder->crtc_id;1390drmModeFreeEncoder(encoder);1391if (crtc_id) {1392if (wsi_display_crtc_solo(wsi, mode_res, drm_connector, crtc_id))1393return crtc_id;1394}1395}1396}1397uint32_t crtc_id = 0;1398for (int c = 0; crtc_id == 0 && c < mode_res->count_crtcs; c++) {1399drmModeCrtcPtr crtc = drmModeGetCrtc(wsi->fd, mode_res->crtcs[c]);1400if (crtc && crtc->buffer_id == 0)1401crtc_id = crtc->crtc_id;1402drmModeFreeCrtc(crtc);1403}1404return crtc_id;1405}14061407static VkResult1408wsi_display_setup_connector(wsi_display_connector *connector,1409wsi_display_mode *display_mode)1410{1411struct wsi_display *wsi = connector->wsi;14121413if (connector->current_mode == display_mode && connector->crtc_id)1414return VK_SUCCESS;14151416VkResult result = VK_SUCCESS;14171418drmModeResPtr mode_res = drmModeGetResources(wsi->fd);1419if (!mode_res) {1420if (errno == ENOMEM)1421result = VK_ERROR_OUT_OF_HOST_MEMORY;1422else1423result = VK_ERROR_SURFACE_LOST_KHR;1424goto bail;1425}14261427drmModeConnectorPtr drm_connector =1428drmModeGetConnectorCurrent(wsi->fd, connector->id);14291430if (!drm_connector) {1431if (errno == ENOMEM)1432result = VK_ERROR_OUT_OF_HOST_MEMORY;1433else1434result = VK_ERROR_SURFACE_LOST_KHR;1435goto bail_mode_res;1436}14371438/* Pick a CRTC if we don't have one */1439if (!connector->crtc_id) {1440connector->crtc_id = wsi_display_select_crtc(connector,1441mode_res, drm_connector);1442if (!connector->crtc_id) {1443result = VK_ERROR_SURFACE_LOST_KHR;1444goto bail_connector;1445}1446}14471448if (connector->current_mode != display_mode) {14491450/* Find the drm mode corresponding to the requested VkDisplayMode */1451drmModeModeInfoPtr drm_mode = NULL;14521453for (int m = 0; m < drm_connector->count_modes; m++) {1454drm_mode = &drm_connector->modes[m];1455if (wsi_display_mode_matches_drm(display_mode, drm_mode))1456break;1457drm_mode = NULL;1458}14591460if (!drm_mode) {1461result = VK_ERROR_SURFACE_LOST_KHR;1462goto bail_connector;1463}14641465connector->current_mode = display_mode;1466connector->current_drm_mode = *drm_mode;1467}14681469bail_connector:1470drmModeFreeConnector(drm_connector);1471bail_mode_res:1472drmModeFreeResources(mode_res);1473bail:1474return result;14751476}14771478static VkResult1479wsi_display_fence_wait(struct wsi_fence *fence_wsi, uint64_t timeout)1480{1481const struct wsi_device *wsi_device = fence_wsi->wsi_device;1482struct wsi_display *wsi =1483(struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];1484struct wsi_display_fence *fence = (struct wsi_display_fence *) fence_wsi;14851486wsi_display_debug("%9lu wait fence %lu %ld\n",1487pthread_self(), fence->sequence,1488(int64_t) (timeout - wsi_common_get_current_time()));1489wsi_display_debug_code(uint64_t start_ns = wsi_common_get_current_time());1490pthread_mutex_lock(&wsi->wait_mutex);14911492VkResult result;1493int ret = 0;1494for (;;) {1495if (fence->event_received) {1496wsi_display_debug("%9lu fence %lu passed\n",1497pthread_self(), fence->sequence);1498result = VK_SUCCESS;1499break;1500}15011502if (ret == ETIMEDOUT) {1503wsi_display_debug("%9lu fence %lu timeout\n",1504pthread_self(), fence->sequence);1505result = VK_TIMEOUT;1506break;1507}15081509ret = wsi_display_wait_for_event(wsi, timeout);15101511if (ret && ret != ETIMEDOUT) {1512wsi_display_debug("%9lu fence %lu error\n",1513pthread_self(), fence->sequence);1514result = VK_ERROR_DEVICE_LOST;1515break;1516}1517}1518pthread_mutex_unlock(&wsi->wait_mutex);1519wsi_display_debug("%9lu fence wait %f ms\n",1520pthread_self(),1521((int64_t) (wsi_common_get_current_time() - start_ns)) /15221.0e6);1523return result;1524}15251526static void1527wsi_display_fence_check_free(struct wsi_display_fence *fence)1528{1529if (fence->event_received && fence->destroyed)1530vk_free(fence->base.alloc, fence);1531}15321533static void wsi_display_fence_event_handler(struct wsi_display_fence *fence)1534{1535struct wsi_display *wsi =1536(struct wsi_display *) fence->base.wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];15371538if (fence->syncobj) {1539(void) drmSyncobjSignal(wsi->fd, &fence->syncobj, 1);1540(void) drmSyncobjDestroy(wsi->fd, fence->syncobj);1541}15421543fence->event_received = true;1544wsi_display_fence_check_free(fence);1545}15461547static void1548wsi_display_fence_destroy(struct wsi_fence *fence_wsi)1549{1550struct wsi_display_fence *fence = (struct wsi_display_fence *) fence_wsi;15511552assert(!fence->destroyed);1553fence->destroyed = true;1554wsi_display_fence_check_free(fence);1555}15561557static struct wsi_display_fence *1558wsi_display_fence_alloc(VkDevice device,1559const struct wsi_device *wsi_device,1560VkDisplayKHR display,1561const VkAllocationCallbacks *allocator,1562int sync_fd)1563{1564struct wsi_display *wsi =1565(struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];1566struct wsi_display_fence *fence =1567vk_zalloc2(wsi->alloc, allocator, sizeof (*fence),15688, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);15691570if (!fence)1571return NULL;15721573if (sync_fd >= 0) {1574int ret = drmSyncobjFDToHandle(wsi->fd, sync_fd, &fence->syncobj);1575if (ret) {1576vk_free2(wsi->alloc, allocator, fence);1577return NULL;1578}1579}15801581fence->base.device = device;1582fence->base.display = display;1583fence->base.wsi_device = wsi_device;1584fence->base.alloc = allocator ? allocator : wsi->alloc;1585fence->base.wait = wsi_display_fence_wait;1586fence->base.destroy = wsi_display_fence_destroy;1587fence->event_received = false;1588fence->destroyed = false;1589fence->sequence = ++fence_sequence;1590return fence;1591}15921593static VkResult1594wsi_register_vblank_event(struct wsi_display_fence *fence,1595const struct wsi_device *wsi_device,1596VkDisplayKHR display,1597uint32_t flags,1598uint64_t frame_requested,1599uint64_t *frame_queued)1600{1601struct wsi_display *wsi =1602(struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];1603struct wsi_display_connector *connector =1604wsi_display_connector_from_handle(display);16051606if (wsi->fd < 0)1607return VK_ERROR_INITIALIZATION_FAILED;16081609for (;;) {1610int ret = drmCrtcQueueSequence(wsi->fd, connector->crtc_id,1611flags,1612frame_requested,1613frame_queued,1614(uintptr_t) fence);16151616if (!ret)1617return VK_SUCCESS;16181619if (errno != ENOMEM) {16201621/* Something unexpected happened. Pause for a moment so the1622* application doesn't just spin and then return a failure indication1623*/16241625wsi_display_debug("queue vblank event %lu failed\n", fence->sequence);1626struct timespec delay = {1627.tv_sec = 0,1628.tv_nsec = 100000000ull,1629};1630nanosleep(&delay, NULL);1631return VK_ERROR_OUT_OF_HOST_MEMORY;1632}16331634/* The kernel event queue is full. Wait for some events to be1635* processed and try again1636*/16371638pthread_mutex_lock(&wsi->wait_mutex);1639ret = wsi_display_wait_for_event(wsi, wsi_rel_to_abs_time(100000000ull));1640pthread_mutex_unlock(&wsi->wait_mutex);16411642if (ret) {1643wsi_display_debug("vblank queue full, event wait failed\n");1644return VK_ERROR_OUT_OF_HOST_MEMORY;1645}1646}1647}16481649/*1650* Check to see if the kernel has no flip queued and if there's an image1651* waiting to be displayed.1652*/1653static VkResult1654_wsi_display_queue_next(struct wsi_swapchain *drv_chain)1655{1656struct wsi_display_swapchain *chain =1657(struct wsi_display_swapchain *) drv_chain;1658struct wsi_display *wsi = chain->wsi;1659VkIcdSurfaceDisplay *surface = chain->surface;1660wsi_display_mode *display_mode =1661wsi_display_mode_from_handle(surface->displayMode);1662wsi_display_connector *connector = display_mode->connector;16631664if (wsi->fd < 0)1665return VK_ERROR_SURFACE_LOST_KHR;16661667if (display_mode != connector->current_mode)1668connector->active = false;16691670for (;;) {16711672/* Check to see if there is an image to display, or if some image is1673* already queued */16741675struct wsi_display_image *image = NULL;16761677for (uint32_t i = 0; i < chain->base.image_count; i++) {1678struct wsi_display_image *tmp_image = &chain->images[i];16791680switch (tmp_image->state) {1681case WSI_IMAGE_FLIPPING:1682/* already flipping, don't send another to the kernel yet */1683return VK_SUCCESS;1684case WSI_IMAGE_QUEUED:1685/* find the oldest queued */1686if (!image || tmp_image->flip_sequence < image->flip_sequence)1687image = tmp_image;1688break;1689default:1690break;1691}1692}16931694if (!image)1695return VK_SUCCESS;16961697int ret;1698if (connector->active) {1699ret = drmModePageFlip(wsi->fd, connector->crtc_id, image->fb_id,1700DRM_MODE_PAGE_FLIP_EVENT, image);1701if (ret == 0) {1702image->state = WSI_IMAGE_FLIPPING;1703return VK_SUCCESS;1704}1705wsi_display_debug("page flip err %d %s\n", ret, strerror(-ret));1706} else {1707ret = -EINVAL;1708}17091710if (ret == -EINVAL) {1711VkResult result = wsi_display_setup_connector(connector, display_mode);17121713if (result != VK_SUCCESS) {1714image->state = WSI_IMAGE_IDLE;1715return result;1716}17171718/* XXX allow setting of position */1719ret = drmModeSetCrtc(wsi->fd, connector->crtc_id,1720image->fb_id, 0, 0,1721&connector->id, 1,1722&connector->current_drm_mode);1723if (ret == 0) {1724/* Disable the HW cursor as the app doesn't have a mechanism1725* to control it.1726* Refer to question 12 of the VK_KHR_display spec.1727*/1728ret = drmModeSetCursor(wsi->fd, connector->crtc_id, 0, 0, 0 );1729if (ret != 0) {1730wsi_display_debug("failed to hide cursor err %d %s\n", ret, strerror(-ret));1731}17321733/* Assume that the mode set is synchronous and that any1734* previous image is now idle.1735*/1736image->state = WSI_IMAGE_DISPLAYING;1737wsi_display_idle_old_displaying(image);1738connector->active = true;1739return VK_SUCCESS;1740}1741}17421743if (ret != -EACCES) {1744connector->active = false;1745image->state = WSI_IMAGE_IDLE;1746return VK_ERROR_SURFACE_LOST_KHR;1747}17481749/* Some other VT is currently active. Sit here waiting for1750* our VT to become active again by polling once a second1751*/1752usleep(1000 * 1000);1753connector->active = false;1754}1755}17561757static VkResult1758wsi_display_queue_present(struct wsi_swapchain *drv_chain,1759uint32_t image_index,1760const VkPresentRegionKHR *damage)1761{1762struct wsi_display_swapchain *chain =1763(struct wsi_display_swapchain *) drv_chain;1764struct wsi_display *wsi = chain->wsi;1765struct wsi_display_image *image = &chain->images[image_index];1766VkResult result;17671768/* Bail early if the swapchain is broken */1769if (chain->status != VK_SUCCESS)1770return chain->status;17711772assert(image->state == WSI_IMAGE_DRAWING);1773wsi_display_debug("present %d\n", image_index);17741775pthread_mutex_lock(&wsi->wait_mutex);17761777image->flip_sequence = ++chain->flip_sequence;1778image->state = WSI_IMAGE_QUEUED;17791780result = _wsi_display_queue_next(drv_chain);1781if (result != VK_SUCCESS)1782chain->status = result;17831784pthread_mutex_unlock(&wsi->wait_mutex);17851786if (result != VK_SUCCESS)1787return result;17881789return chain->status;1790}17911792static VkResult1793wsi_display_surface_create_swapchain(1794VkIcdSurfaceBase *icd_surface,1795VkDevice device,1796struct wsi_device *wsi_device,1797const VkSwapchainCreateInfoKHR *create_info,1798const VkAllocationCallbacks *allocator,1799struct wsi_swapchain **swapchain_out)1800{1801struct wsi_display *wsi =1802(struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];18031804assert(create_info->sType == VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR);18051806const unsigned num_images = create_info->minImageCount;1807struct wsi_display_swapchain *chain =1808vk_zalloc(allocator,1809sizeof(*chain) + num_images * sizeof(chain->images[0]),18108, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);18111812if (chain == NULL)1813return VK_ERROR_OUT_OF_HOST_MEMORY;18141815VkResult result = wsi_swapchain_init(wsi_device, &chain->base, device,1816create_info, allocator);1817if (result != VK_SUCCESS) {1818vk_free(allocator, chain);1819return result;1820}18211822chain->base.destroy = wsi_display_swapchain_destroy;1823chain->base.get_wsi_image = wsi_display_get_wsi_image;1824chain->base.acquire_next_image = wsi_display_acquire_next_image;1825chain->base.queue_present = wsi_display_queue_present;1826chain->base.present_mode = wsi_swapchain_get_present_mode(wsi_device, create_info);1827chain->base.image_count = num_images;18281829chain->wsi = wsi;1830chain->status = VK_SUCCESS;18311832chain->surface = (VkIcdSurfaceDisplay *) icd_surface;18331834for (uint32_t image = 0; image < chain->base.image_count; image++) {1835result = wsi_display_image_init(device, &chain->base,1836create_info, allocator,1837&chain->images[image]);1838if (result != VK_SUCCESS) {1839while (image > 0) {1840--image;1841wsi_display_image_finish(&chain->base, allocator,1842&chain->images[image]);1843}1844vk_free(allocator, chain);1845goto fail_init_images;1846}1847}18481849*swapchain_out = &chain->base;18501851return VK_SUCCESS;18521853fail_init_images:1854return result;1855}18561857static bool1858wsi_init_pthread_cond_monotonic(pthread_cond_t *cond)1859{1860pthread_condattr_t condattr;1861bool ret = false;18621863if (pthread_condattr_init(&condattr) != 0)1864goto fail_attr_init;18651866if (pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC) != 0)1867goto fail_attr_set;18681869if (pthread_cond_init(cond, &condattr) != 0)1870goto fail_cond_init;18711872ret = true;18731874fail_cond_init:1875fail_attr_set:1876pthread_condattr_destroy(&condattr);1877fail_attr_init:1878return ret;1879}188018811882/*1883* Local version fo the libdrm helper. Added to avoid depending on bleeding1884* edge version of the library.1885*/1886static int1887local_drmIsMaster(int fd)1888{1889/* Detect master by attempting something that requires master.1890*1891* Authenticating magic tokens requires master and 0 is an1892* internal kernel detail which we could use. Attempting this on1893* a master fd would fail therefore fail with EINVAL because 01894* is invalid.1895*1896* A non-master fd will fail with EACCES, as the kernel checks1897* for master before attempting to do anything else.1898*1899* Since we don't want to leak implementation details, use1900* EACCES.1901*/1902return drmAuthMagic(fd, 0) != -EACCES;1903}19041905VkResult1906wsi_display_init_wsi(struct wsi_device *wsi_device,1907const VkAllocationCallbacks *alloc,1908int display_fd)1909{1910struct wsi_display *wsi = vk_zalloc(alloc, sizeof(*wsi), 8,1911VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);1912VkResult result;19131914if (!wsi) {1915result = VK_ERROR_OUT_OF_HOST_MEMORY;1916goto fail;1917}19181919wsi->fd = display_fd;1920if (wsi->fd != -1 && !local_drmIsMaster(wsi->fd))1921wsi->fd = -1;19221923wsi->alloc = alloc;19241925list_inithead(&wsi->connectors);19261927int ret = pthread_mutex_init(&wsi->wait_mutex, NULL);1928if (ret) {1929result = VK_ERROR_OUT_OF_HOST_MEMORY;1930goto fail_mutex;1931}19321933if (!wsi_init_pthread_cond_monotonic(&wsi->wait_cond)) {1934result = VK_ERROR_OUT_OF_HOST_MEMORY;1935goto fail_cond;1936}19371938wsi->base.get_support = wsi_display_surface_get_support;1939wsi->base.get_capabilities2 = wsi_display_surface_get_capabilities2;1940wsi->base.get_formats = wsi_display_surface_get_formats;1941wsi->base.get_formats2 = wsi_display_surface_get_formats2;1942wsi->base.get_present_modes = wsi_display_surface_get_present_modes;1943wsi->base.get_present_rectangles = wsi_display_surface_get_present_rectangles;1944wsi->base.create_swapchain = wsi_display_surface_create_swapchain;19451946wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY] = &wsi->base;19471948return VK_SUCCESS;19491950fail_cond:1951pthread_mutex_destroy(&wsi->wait_mutex);1952fail_mutex:1953vk_free(alloc, wsi);1954fail:1955return result;1956}19571958void1959wsi_display_finish_wsi(struct wsi_device *wsi_device,1960const VkAllocationCallbacks *alloc)1961{1962struct wsi_display *wsi =1963(struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];19641965if (wsi) {1966wsi_for_each_connector(connector, wsi) {1967wsi_for_each_display_mode(mode, connector) {1968vk_free(wsi->alloc, mode);1969}1970vk_free(wsi->alloc, connector);1971}19721973wsi_display_stop_wait_thread(wsi);1974pthread_mutex_destroy(&wsi->wait_mutex);1975pthread_cond_destroy(&wsi->wait_cond);19761977vk_free(alloc, wsi);1978}1979}19801981/*1982* Implement vkReleaseDisplay1983*/1984VkResult1985wsi_release_display(VkPhysicalDevice physical_device,1986struct wsi_device *wsi_device,1987VkDisplayKHR display)1988{1989struct wsi_display *wsi =1990(struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];19911992if (wsi->fd >= 0) {1993wsi_display_stop_wait_thread(wsi);19941995close(wsi->fd);1996wsi->fd = -1;1997}19981999#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT2000wsi_display_connector_from_handle(display)->output = None;2001#endif20022003return VK_SUCCESS;2004}20052006#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT20072008static struct wsi_display_connector *2009wsi_display_find_output(struct wsi_device *wsi_device,2010xcb_randr_output_t output)2011{2012struct wsi_display *wsi =2013(struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];20142015wsi_for_each_connector(connector, wsi) {2016if (connector->output == output)2017return connector;2018}20192020return NULL;2021}20222023/*2024* Given a RandR output, find the associated kernel connector_id by2025* looking at the CONNECTOR_ID property provided by the X server2026*/20272028static uint32_t2029wsi_display_output_to_connector_id(xcb_connection_t *connection,2030xcb_atom_t *connector_id_atom_p,2031xcb_randr_output_t output)2032{2033uint32_t connector_id = 0;2034xcb_atom_t connector_id_atom = *connector_id_atom_p;20352036if (connector_id_atom == 0) {2037/* Go dig out the CONNECTOR_ID property */2038xcb_intern_atom_cookie_t ia_c = xcb_intern_atom(connection,2039true,204012,2041"CONNECTOR_ID");2042xcb_intern_atom_reply_t *ia_r = xcb_intern_atom_reply(connection,2043ia_c,2044NULL);2045if (ia_r) {2046*connector_id_atom_p = connector_id_atom = ia_r->atom;2047free(ia_r);2048}2049}20502051/* If there's an CONNECTOR_ID atom in the server, then there may be a2052* CONNECTOR_ID property. Otherwise, there will not be and we don't even2053* need to bother.2054*/2055if (connector_id_atom) {20562057xcb_randr_query_version_cookie_t qv_c =2058xcb_randr_query_version(connection, 1, 6);2059xcb_randr_get_output_property_cookie_t gop_c =2060xcb_randr_get_output_property(connection,2061output,2062connector_id_atom,20630,20640,20650xffffffffUL,20660,20670);2068xcb_randr_query_version_reply_t *qv_r =2069xcb_randr_query_version_reply(connection, qv_c, NULL);2070free(qv_r);2071xcb_randr_get_output_property_reply_t *gop_r =2072xcb_randr_get_output_property_reply(connection, gop_c, NULL);2073if (gop_r) {2074if (gop_r->num_items == 1 && gop_r->format == 32)2075memcpy(&connector_id, xcb_randr_get_output_property_data(gop_r), 4);2076free(gop_r);2077}2078}2079return connector_id;2080}20812082static bool2083wsi_display_check_randr_version(xcb_connection_t *connection)2084{2085xcb_randr_query_version_cookie_t qv_c =2086xcb_randr_query_version(connection, 1, 6);2087xcb_randr_query_version_reply_t *qv_r =2088xcb_randr_query_version_reply(connection, qv_c, NULL);2089bool ret = false;20902091if (!qv_r)2092return false;20932094/* Check for version 1.6 or newer */2095ret = (qv_r->major_version > 1 ||2096(qv_r->major_version == 1 && qv_r->minor_version >= 6));20972098free(qv_r);2099return ret;2100}21012102/*2103* Given a kernel connector id, find the associated RandR output using the2104* CONNECTOR_ID property2105*/21062107static xcb_randr_output_t2108wsi_display_connector_id_to_output(xcb_connection_t *connection,2109uint32_t connector_id)2110{2111if (!wsi_display_check_randr_version(connection))2112return 0;21132114const xcb_setup_t *setup = xcb_get_setup(connection);21152116xcb_atom_t connector_id_atom = 0;2117xcb_randr_output_t output = 0;21182119/* Search all of the screens for the provided output */2120xcb_screen_iterator_t iter;2121for (iter = xcb_setup_roots_iterator(setup);2122output == 0 && iter.rem;2123xcb_screen_next(&iter))2124{2125xcb_randr_get_screen_resources_cookie_t gsr_c =2126xcb_randr_get_screen_resources(connection, iter.data->root);2127xcb_randr_get_screen_resources_reply_t *gsr_r =2128xcb_randr_get_screen_resources_reply(connection, gsr_c, NULL);21292130if (!gsr_r)2131return 0;21322133xcb_randr_output_t *ro = xcb_randr_get_screen_resources_outputs(gsr_r);2134int o;21352136for (o = 0; o < gsr_r->num_outputs; o++) {2137if (wsi_display_output_to_connector_id(connection,2138&connector_id_atom, ro[o])2139== connector_id)2140{2141output = ro[o];2142break;2143}2144}2145free(gsr_r);2146}2147return output;2148}21492150/*2151* Given a RandR output, find out which screen it's associated with2152*/2153static xcb_window_t2154wsi_display_output_to_root(xcb_connection_t *connection,2155xcb_randr_output_t output)2156{2157if (!wsi_display_check_randr_version(connection))2158return 0;21592160const xcb_setup_t *setup = xcb_get_setup(connection);2161xcb_window_t root = 0;21622163/* Search all of the screens for the provided output */2164for (xcb_screen_iterator_t iter = xcb_setup_roots_iterator(setup);2165root == 0 && iter.rem;2166xcb_screen_next(&iter))2167{2168xcb_randr_get_screen_resources_cookie_t gsr_c =2169xcb_randr_get_screen_resources(connection, iter.data->root);2170xcb_randr_get_screen_resources_reply_t *gsr_r =2171xcb_randr_get_screen_resources_reply(connection, gsr_c, NULL);21722173if (!gsr_r)2174return 0;21752176xcb_randr_output_t *ro = xcb_randr_get_screen_resources_outputs(gsr_r);21772178for (int o = 0; o < gsr_r->num_outputs; o++) {2179if (ro[o] == output) {2180root = iter.data->root;2181break;2182}2183}2184free(gsr_r);2185}2186return root;2187}21882189static bool2190wsi_display_mode_matches_x(struct wsi_display_mode *wsi,2191xcb_randr_mode_info_t *xcb)2192{2193return wsi->clock == (xcb->dot_clock + 500) / 1000 &&2194wsi->hdisplay == xcb->width &&2195wsi->hsync_start == xcb->hsync_start &&2196wsi->hsync_end == xcb->hsync_end &&2197wsi->htotal == xcb->htotal &&2198wsi->hskew == xcb->hskew &&2199wsi->vdisplay == xcb->height &&2200wsi->vsync_start == xcb->vsync_start &&2201wsi->vsync_end == xcb->vsync_end &&2202wsi->vtotal == xcb->vtotal &&2203wsi->vscan <= 1 &&2204wsi->flags == xcb->mode_flags;2205}22062207static struct wsi_display_mode *2208wsi_display_find_x_mode(struct wsi_device *wsi_device,2209struct wsi_display_connector *connector,2210xcb_randr_mode_info_t *mode)2211{2212wsi_for_each_display_mode(display_mode, connector) {2213if (wsi_display_mode_matches_x(display_mode, mode))2214return display_mode;2215}2216return NULL;2217}22182219static VkResult2220wsi_display_register_x_mode(struct wsi_device *wsi_device,2221struct wsi_display_connector *connector,2222xcb_randr_mode_info_t *x_mode,2223bool preferred)2224{2225struct wsi_display *wsi =2226(struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];2227struct wsi_display_mode *display_mode =2228wsi_display_find_x_mode(wsi_device, connector, x_mode);22292230if (display_mode) {2231display_mode->valid = true;2232return VK_SUCCESS;2233}22342235display_mode = vk_zalloc(wsi->alloc, sizeof (struct wsi_display_mode),22368, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);2237if (!display_mode)2238return VK_ERROR_OUT_OF_HOST_MEMORY;22392240display_mode->connector = connector;2241display_mode->valid = true;2242display_mode->preferred = preferred;2243display_mode->clock = (x_mode->dot_clock + 500) / 1000; /* kHz */2244display_mode->hdisplay = x_mode->width;2245display_mode->hsync_start = x_mode->hsync_start;2246display_mode->hsync_end = x_mode->hsync_end;2247display_mode->htotal = x_mode->htotal;2248display_mode->hskew = x_mode->hskew;2249display_mode->vdisplay = x_mode->height;2250display_mode->vsync_start = x_mode->vsync_start;2251display_mode->vsync_end = x_mode->vsync_end;2252display_mode->vtotal = x_mode->vtotal;2253display_mode->vscan = 0;2254display_mode->flags = x_mode->mode_flags;22552256list_addtail(&display_mode->list, &connector->display_modes);2257return VK_SUCCESS;2258}22592260static struct wsi_display_connector *2261wsi_display_get_output(struct wsi_device *wsi_device,2262xcb_connection_t *connection,2263xcb_randr_output_t output)2264{2265struct wsi_display *wsi =2266(struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];2267struct wsi_display_connector *connector;2268uint32_t connector_id;22692270xcb_window_t root = wsi_display_output_to_root(connection, output);2271if (!root)2272return NULL;22732274/* See if we already have a connector for this output */2275connector = wsi_display_find_output(wsi_device, output);22762277if (!connector) {2278xcb_atom_t connector_id_atom = 0;22792280/*2281* Go get the kernel connector ID for this X output2282*/2283connector_id = wsi_display_output_to_connector_id(connection,2284&connector_id_atom,2285output);22862287/* Any X server with lease support will have this atom */2288if (!connector_id) {2289return NULL;2290}22912292/* See if we already have a connector for this id */2293connector = wsi_display_find_connector(wsi_device, connector_id);22942295if (connector == NULL) {2296connector = wsi_display_alloc_connector(wsi, connector_id);2297if (!connector) {2298return NULL;2299}2300list_addtail(&connector->list, &wsi->connectors);2301}2302connector->output = output;2303}23042305xcb_randr_get_screen_resources_cookie_t src =2306xcb_randr_get_screen_resources(connection, root);2307xcb_randr_get_output_info_cookie_t oic =2308xcb_randr_get_output_info(connection, output, XCB_CURRENT_TIME);2309xcb_randr_get_screen_resources_reply_t *srr =2310xcb_randr_get_screen_resources_reply(connection, src, NULL);2311xcb_randr_get_output_info_reply_t *oir =2312xcb_randr_get_output_info_reply(connection, oic, NULL);23132314if (oir && srr) {2315/* Get X modes and add them */23162317connector->connected =2318oir->connection != XCB_RANDR_CONNECTION_DISCONNECTED;23192320wsi_display_invalidate_connector_modes(wsi_device, connector);23212322xcb_randr_mode_t *x_modes = xcb_randr_get_output_info_modes(oir);2323for (int m = 0; m < oir->num_modes; m++) {2324xcb_randr_mode_info_iterator_t i =2325xcb_randr_get_screen_resources_modes_iterator(srr);2326while (i.rem) {2327xcb_randr_mode_info_t *mi = i.data;2328if (mi->id == x_modes[m]) {2329VkResult result = wsi_display_register_x_mode(2330wsi_device, connector, mi, m < oir->num_preferred);2331if (result != VK_SUCCESS) {2332free(oir);2333free(srr);2334return NULL;2335}2336break;2337}2338xcb_randr_mode_info_next(&i);2339}2340}2341}23422343free(oir);2344free(srr);2345return connector;2346}23472348static xcb_randr_crtc_t2349wsi_display_find_crtc_for_output(xcb_connection_t *connection,2350xcb_window_t root,2351xcb_randr_output_t output)2352{2353xcb_randr_get_screen_resources_cookie_t gsr_c =2354xcb_randr_get_screen_resources(connection, root);2355xcb_randr_get_screen_resources_reply_t *gsr_r =2356xcb_randr_get_screen_resources_reply(connection, gsr_c, NULL);23572358if (!gsr_r)2359return 0;23602361xcb_randr_crtc_t *rc = xcb_randr_get_screen_resources_crtcs(gsr_r);2362xcb_randr_crtc_t idle_crtc = 0;2363xcb_randr_crtc_t active_crtc = 0;23642365/* Find either a crtc already connected to the desired output or idle */2366for (int c = 0; active_crtc == 0 && c < gsr_r->num_crtcs; c++) {2367xcb_randr_get_crtc_info_cookie_t gci_c =2368xcb_randr_get_crtc_info(connection, rc[c], gsr_r->config_timestamp);2369xcb_randr_get_crtc_info_reply_t *gci_r =2370xcb_randr_get_crtc_info_reply(connection, gci_c, NULL);23712372if (gci_r) {2373if (gci_r->mode) {2374int num_outputs = xcb_randr_get_crtc_info_outputs_length(gci_r);2375xcb_randr_output_t *outputs =2376xcb_randr_get_crtc_info_outputs(gci_r);23772378if (num_outputs == 1 && outputs[0] == output)2379active_crtc = rc[c];23802381} else if (idle_crtc == 0) {2382int num_possible = xcb_randr_get_crtc_info_possible_length(gci_r);2383xcb_randr_output_t *possible =2384xcb_randr_get_crtc_info_possible(gci_r);23852386for (int p = 0; p < num_possible; p++)2387if (possible[p] == output) {2388idle_crtc = rc[c];2389break;2390}2391}2392free(gci_r);2393}2394}2395free(gsr_r);23962397if (active_crtc)2398return active_crtc;2399return idle_crtc;2400}24012402VkResult2403wsi_acquire_xlib_display(VkPhysicalDevice physical_device,2404struct wsi_device *wsi_device,2405Display *dpy,2406VkDisplayKHR display)2407{2408struct wsi_display *wsi =2409(struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];2410xcb_connection_t *connection = XGetXCBConnection(dpy);2411struct wsi_display_connector *connector =2412wsi_display_connector_from_handle(display);2413xcb_window_t root;24142415/* XXX no support for multiple leases yet */2416if (wsi->fd >= 0)2417return VK_ERROR_INITIALIZATION_FAILED;24182419if (!connector->output) {2420connector->output = wsi_display_connector_id_to_output(connection,2421connector->id);24222423/* Check and see if we found the output */2424if (!connector->output)2425return VK_ERROR_INITIALIZATION_FAILED;2426}24272428root = wsi_display_output_to_root(connection, connector->output);2429if (!root)2430return VK_ERROR_INITIALIZATION_FAILED;24312432xcb_randr_crtc_t crtc = wsi_display_find_crtc_for_output(connection,2433root,2434connector->output);24352436if (!crtc)2437return VK_ERROR_INITIALIZATION_FAILED;24382439#ifdef HAVE_DRI3_MODIFIERS2440xcb_randr_lease_t lease = xcb_generate_id(connection);2441xcb_randr_create_lease_cookie_t cl_c =2442xcb_randr_create_lease(connection, root, lease, 1, 1,2443&crtc, &connector->output);2444xcb_randr_create_lease_reply_t *cl_r =2445xcb_randr_create_lease_reply(connection, cl_c, NULL);2446if (!cl_r)2447return VK_ERROR_INITIALIZATION_FAILED;24482449int fd = -1;2450if (cl_r->nfd > 0) {2451int *rcl_f = xcb_randr_create_lease_reply_fds(connection, cl_r);24522453fd = rcl_f[0];2454}2455free (cl_r);2456if (fd < 0)2457return VK_ERROR_INITIALIZATION_FAILED;24582459wsi->fd = fd;2460#endif24612462return VK_SUCCESS;2463}24642465VkResult2466wsi_get_randr_output_display(VkPhysicalDevice physical_device,2467struct wsi_device *wsi_device,2468Display *dpy,2469RROutput output,2470VkDisplayKHR *display)2471{2472xcb_connection_t *connection = XGetXCBConnection(dpy);2473struct wsi_display_connector *connector =2474wsi_display_get_output(wsi_device, connection, (xcb_randr_output_t) output);24752476if (connector)2477*display = wsi_display_connector_to_handle(connector);2478else2479*display = VK_NULL_HANDLE;2480return VK_SUCCESS;2481}24822483#endif24842485/* VK_EXT_display_control */2486VkResult2487wsi_display_power_control(VkDevice device,2488struct wsi_device *wsi_device,2489VkDisplayKHR display,2490const VkDisplayPowerInfoEXT *display_power_info)2491{2492struct wsi_display *wsi =2493(struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];2494struct wsi_display_connector *connector =2495wsi_display_connector_from_handle(display);2496int mode;24972498if (wsi->fd < 0)2499return VK_ERROR_INITIALIZATION_FAILED;25002501switch (display_power_info->powerState) {2502case VK_DISPLAY_POWER_STATE_OFF_EXT:2503mode = DRM_MODE_DPMS_OFF;2504break;2505case VK_DISPLAY_POWER_STATE_SUSPEND_EXT:2506mode = DRM_MODE_DPMS_SUSPEND;2507break;2508default:2509mode = DRM_MODE_DPMS_ON;2510break;2511}2512drmModeConnectorSetProperty(wsi->fd,2513connector->id,2514connector->dpms_property,2515mode);2516return VK_SUCCESS;2517}25182519VkResult2520wsi_register_device_event(VkDevice device,2521struct wsi_device *wsi_device,2522const VkDeviceEventInfoEXT *device_event_info,2523const VkAllocationCallbacks *allocator,2524struct wsi_fence **fence_p,2525int sync_fd)2526{2527return VK_ERROR_FEATURE_NOT_PRESENT;2528}25292530VkResult2531wsi_register_display_event(VkDevice device,2532struct wsi_device *wsi_device,2533VkDisplayKHR display,2534const VkDisplayEventInfoEXT *display_event_info,2535const VkAllocationCallbacks *allocator,2536struct wsi_fence **fence_p,2537int sync_fd)2538{2539struct wsi_display *wsi =2540(struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];2541struct wsi_display_fence *fence;2542VkResult ret;25432544switch (display_event_info->displayEvent) {2545case VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT:25462547fence = wsi_display_fence_alloc(device, wsi_device, display, allocator, sync_fd);25482549if (!fence)2550return VK_ERROR_OUT_OF_HOST_MEMORY;25512552ret = wsi_register_vblank_event(fence, wsi_device, display,2553DRM_CRTC_SEQUENCE_RELATIVE, 1, NULL);25542555if (ret == VK_SUCCESS) {2556if (fence_p)2557*fence_p = &fence->base;2558else2559fence->base.destroy(&fence->base);2560} else if (fence != NULL) {2561if (fence->syncobj)2562drmSyncobjDestroy(wsi->fd, fence->syncobj);2563vk_free2(wsi->alloc, allocator, fence);2564}25652566break;2567default:2568ret = VK_ERROR_FEATURE_NOT_PRESENT;2569break;2570}25712572return ret;2573}257425752576VkResult2577wsi_get_swapchain_counter(VkDevice device,2578struct wsi_device *wsi_device,2579VkSwapchainKHR _swapchain,2580VkSurfaceCounterFlagBitsEXT flag_bits,2581uint64_t *value)2582{2583struct wsi_display *wsi =2584(struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];2585struct wsi_display_swapchain *swapchain =2586(struct wsi_display_swapchain *) wsi_swapchain_from_handle(_swapchain);2587struct wsi_display_connector *connector =2588wsi_display_mode_from_handle(swapchain->surface->displayMode)->connector;25892590if (wsi->fd < 0)2591return VK_ERROR_INITIALIZATION_FAILED;25922593if (!connector->active) {2594*value = 0;2595return VK_SUCCESS;2596}25972598int ret = drmCrtcGetSequence(wsi->fd, connector->crtc_id, value, NULL);2599if (ret)2600*value = 0;26012602return VK_SUCCESS;2603}26042605VkResult2606wsi_acquire_drm_display(VkPhysicalDevice pDevice,2607struct wsi_device *wsi_device,2608int drm_fd,2609VkDisplayKHR display)2610{2611if (!wsi_device_matches_drm_fd(wsi_device, drm_fd))2612return VK_ERROR_UNKNOWN;26132614struct wsi_display *wsi =2615(struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];26162617/* XXX no support for mulitple leases yet */2618if (wsi->fd >= 0 || !local_drmIsMaster(drm_fd))2619return VK_ERROR_INITIALIZATION_FAILED;26202621struct wsi_display_connector *connector =2622wsi_display_connector_from_handle(display);26232624drmModeConnectorPtr drm_connector =2625drmModeGetConnectorCurrent(drm_fd, connector->id);26262627if (!drm_connector)2628return VK_ERROR_INITIALIZATION_FAILED;26292630drmModeFreeConnector(drm_connector);26312632wsi->fd = drm_fd;2633return VK_SUCCESS;2634}26352636VkResult2637wsi_get_drm_display(VkPhysicalDevice pDevice,2638struct wsi_device *wsi_device,2639int drm_fd,2640int connector_id,2641VkDisplayKHR *display)2642{2643if (!wsi_device_matches_drm_fd(wsi_device, drm_fd))2644return VK_ERROR_UNKNOWN;26452646struct wsi_display_connector *connector =2647wsi_display_get_connector(wsi_device, drm_fd, connector_id);26482649if (!connector) {2650*display = VK_NULL_HANDLE;2651return VK_ERROR_UNKNOWN;2652}26532654*display = wsi_display_connector_to_handle(connector);2655return VK_SUCCESS;2656}2657265826592660