1b8e80941Smrg/* 2b8e80941Smrg * Copyright © 2017 Keith Packard 3b8e80941Smrg * 4b8e80941Smrg * Permission to use, copy, modify, distribute, and sell this software and its 5b8e80941Smrg * documentation for any purpose is hereby granted without fee, provided that 6b8e80941Smrg * the above copyright notice appear in all copies and that both that copyright 7b8e80941Smrg * notice and this permission notice appear in supporting documentation, and 8b8e80941Smrg * that the name of the copyright holders not be used in advertising or 9b8e80941Smrg * publicity pertaining to distribution of the software without specific, 10b8e80941Smrg * written prior permission. The copyright holders make no representations 11b8e80941Smrg * about the suitability of this software for any purpose. It is provided "as 12b8e80941Smrg * is" without express or implied warranty. 13b8e80941Smrg * 14b8e80941Smrg * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 15b8e80941Smrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 16b8e80941Smrg * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 17b8e80941Smrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 18b8e80941Smrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 19b8e80941Smrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 20b8e80941Smrg * OF THIS SOFTWARE. 21b8e80941Smrg */ 22b8e80941Smrg 23b8e80941Smrg#include "util/macros.h" 24b8e80941Smrg#include <stdlib.h> 25b8e80941Smrg#include <stdio.h> 26b8e80941Smrg#include <unistd.h> 27b8e80941Smrg#include <errno.h> 28b8e80941Smrg#include <string.h> 29b8e80941Smrg#include <fcntl.h> 30b8e80941Smrg#include <poll.h> 31b8e80941Smrg#include <stdbool.h> 32b8e80941Smrg#include <math.h> 33b8e80941Smrg#include <xf86drm.h> 34b8e80941Smrg#include <xf86drmMode.h> 35b8e80941Smrg#include "drm-uapi/drm_fourcc.h" 36b8e80941Smrg#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT 37b8e80941Smrg#include <xcb/randr.h> 38b8e80941Smrg#include <X11/Xlib-xcb.h> 39b8e80941Smrg#endif 40b8e80941Smrg#include "util/hash_table.h" 41b8e80941Smrg#include "util/list.h" 42b8e80941Smrg 43b8e80941Smrg#include "vk_util.h" 44b8e80941Smrg#include "wsi_common_private.h" 45b8e80941Smrg#include "wsi_common_display.h" 46b8e80941Smrg#include "wsi_common_queue.h" 47b8e80941Smrg 48b8e80941Smrg#if 0 49b8e80941Smrg#define wsi_display_debug(...) fprintf(stderr, __VA_ARGS__) 50b8e80941Smrg#define wsi_display_debug_code(...) __VA_ARGS__ 51b8e80941Smrg#else 52b8e80941Smrg#define wsi_display_debug(...) 53b8e80941Smrg#define wsi_display_debug_code(...) 54b8e80941Smrg#endif 55b8e80941Smrg 56b8e80941Smrg/* These have lifetime equal to the instance, so they effectively 57b8e80941Smrg * never go away. This means we must keep track of them separately 58b8e80941Smrg * from all other resources. 59b8e80941Smrg */ 60b8e80941Smrgtypedef struct wsi_display_mode { 61b8e80941Smrg struct list_head list; 62b8e80941Smrg struct wsi_display_connector *connector; 63b8e80941Smrg bool valid; /* was found in most recent poll */ 64b8e80941Smrg bool preferred; 65b8e80941Smrg uint32_t clock; /* in kHz */ 66b8e80941Smrg uint16_t hdisplay, hsync_start, hsync_end, htotal, hskew; 67b8e80941Smrg uint16_t vdisplay, vsync_start, vsync_end, vtotal, vscan; 68b8e80941Smrg uint32_t flags; 69b8e80941Smrg} wsi_display_mode; 70b8e80941Smrg 71b8e80941Smrgtypedef struct wsi_display_connector { 72b8e80941Smrg struct list_head list; 73b8e80941Smrg struct wsi_display *wsi; 74b8e80941Smrg uint32_t id; 75b8e80941Smrg uint32_t crtc_id; 76b8e80941Smrg char *name; 77b8e80941Smrg bool connected; 78b8e80941Smrg bool active; 79b8e80941Smrg struct list_head display_modes; 80b8e80941Smrg wsi_display_mode *current_mode; 81b8e80941Smrg drmModeModeInfo current_drm_mode; 82b8e80941Smrg uint32_t dpms_property; 83b8e80941Smrg#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT 84b8e80941Smrg xcb_randr_output_t output; 85b8e80941Smrg#endif 86b8e80941Smrg} wsi_display_connector; 87b8e80941Smrg 88b8e80941Smrgstruct wsi_display { 89b8e80941Smrg struct wsi_interface base; 90b8e80941Smrg 91b8e80941Smrg const VkAllocationCallbacks *alloc; 92b8e80941Smrg 93b8e80941Smrg int fd; 94b8e80941Smrg 95b8e80941Smrg pthread_mutex_t wait_mutex; 96b8e80941Smrg pthread_cond_t wait_cond; 97b8e80941Smrg pthread_t wait_thread; 98b8e80941Smrg 99b8e80941Smrg struct list_head connectors; /* list of all discovered connectors */ 100b8e80941Smrg}; 101b8e80941Smrg 102b8e80941Smrg#define wsi_for_each_display_mode(_mode, _conn) \ 103b8e80941Smrg list_for_each_entry_safe(struct wsi_display_mode, _mode, \ 104b8e80941Smrg &(_conn)->display_modes, list) 105b8e80941Smrg 106b8e80941Smrg#define wsi_for_each_connector(_conn, _dev) \ 107b8e80941Smrg list_for_each_entry_safe(struct wsi_display_connector, _conn, \ 108b8e80941Smrg &(_dev)->connectors, list) 109b8e80941Smrg 110b8e80941Smrgenum wsi_image_state { 111b8e80941Smrg WSI_IMAGE_IDLE, 112b8e80941Smrg WSI_IMAGE_DRAWING, 113b8e80941Smrg WSI_IMAGE_QUEUED, 114b8e80941Smrg WSI_IMAGE_FLIPPING, 115b8e80941Smrg WSI_IMAGE_DISPLAYING 116b8e80941Smrg}; 117b8e80941Smrg 118b8e80941Smrgstruct wsi_display_image { 119b8e80941Smrg struct wsi_image base; 120b8e80941Smrg struct wsi_display_swapchain *chain; 121b8e80941Smrg enum wsi_image_state state; 122b8e80941Smrg uint32_t fb_id; 123b8e80941Smrg uint32_t buffer[4]; 124b8e80941Smrg uint64_t flip_sequence; 125b8e80941Smrg}; 126b8e80941Smrg 127b8e80941Smrgstruct wsi_display_swapchain { 128b8e80941Smrg struct wsi_swapchain base; 129b8e80941Smrg struct wsi_display *wsi; 130b8e80941Smrg VkIcdSurfaceDisplay *surface; 131b8e80941Smrg uint64_t flip_sequence; 132b8e80941Smrg VkResult status; 133b8e80941Smrg struct wsi_display_image images[0]; 134b8e80941Smrg}; 135b8e80941Smrg 136b8e80941Smrgstruct wsi_display_fence { 137b8e80941Smrg struct wsi_fence base; 138b8e80941Smrg bool event_received; 139b8e80941Smrg bool destroyed; 140b8e80941Smrg uint64_t sequence; 141b8e80941Smrg}; 142b8e80941Smrg 143b8e80941Smrgstatic uint64_t fence_sequence; 144b8e80941Smrg 145b8e80941SmrgICD_DEFINE_NONDISP_HANDLE_CASTS(wsi_display_mode, VkDisplayModeKHR) 146b8e80941SmrgICD_DEFINE_NONDISP_HANDLE_CASTS(wsi_display_connector, VkDisplayKHR) 147b8e80941Smrg 148b8e80941Smrgstatic bool 149b8e80941Smrgwsi_display_mode_matches_drm(wsi_display_mode *wsi, 150b8e80941Smrg drmModeModeInfoPtr drm) 151b8e80941Smrg{ 152b8e80941Smrg return wsi->clock == drm->clock && 153b8e80941Smrg wsi->hdisplay == drm->hdisplay && 154b8e80941Smrg wsi->hsync_start == drm->hsync_start && 155b8e80941Smrg wsi->hsync_end == drm->hsync_end && 156b8e80941Smrg wsi->htotal == drm->htotal && 157b8e80941Smrg wsi->hskew == drm->hskew && 158b8e80941Smrg wsi->vdisplay == drm->vdisplay && 159b8e80941Smrg wsi->vsync_start == drm->vsync_start && 160b8e80941Smrg wsi->vsync_end == drm->vsync_end && 161b8e80941Smrg wsi->vtotal == drm->vtotal && 162b8e80941Smrg MAX2(wsi->vscan, 1) == MAX2(drm->vscan, 1) && 163b8e80941Smrg wsi->flags == drm->flags; 164b8e80941Smrg} 165b8e80941Smrg 166b8e80941Smrgstatic double 167b8e80941Smrgwsi_display_mode_refresh(struct wsi_display_mode *wsi) 168b8e80941Smrg{ 169b8e80941Smrg return (double) wsi->clock * 1000.0 / ((double) wsi->htotal * 170b8e80941Smrg (double) wsi->vtotal * 171b8e80941Smrg (double) MAX2(wsi->vscan, 1)); 172b8e80941Smrg} 173b8e80941Smrg 174b8e80941Smrgstatic uint64_t wsi_rel_to_abs_time(uint64_t rel_time) 175b8e80941Smrg{ 176b8e80941Smrg uint64_t current_time = wsi_common_get_current_time(); 177b8e80941Smrg 178b8e80941Smrg /* check for overflow */ 179b8e80941Smrg if (rel_time > UINT64_MAX - current_time) 180b8e80941Smrg return UINT64_MAX; 181b8e80941Smrg 182b8e80941Smrg return current_time + rel_time; 183b8e80941Smrg} 184b8e80941Smrg 185b8e80941Smrgstatic struct wsi_display_mode * 186b8e80941Smrgwsi_display_find_drm_mode(struct wsi_device *wsi_device, 187b8e80941Smrg struct wsi_display_connector *connector, 188b8e80941Smrg drmModeModeInfoPtr mode) 189b8e80941Smrg{ 190b8e80941Smrg wsi_for_each_display_mode(display_mode, connector) { 191b8e80941Smrg if (wsi_display_mode_matches_drm(display_mode, mode)) 192b8e80941Smrg return display_mode; 193b8e80941Smrg } 194b8e80941Smrg return NULL; 195b8e80941Smrg} 196b8e80941Smrg 197b8e80941Smrgstatic void 198b8e80941Smrgwsi_display_invalidate_connector_modes(struct wsi_device *wsi_device, 199b8e80941Smrg struct wsi_display_connector *connector) 200b8e80941Smrg{ 201b8e80941Smrg wsi_for_each_display_mode(display_mode, connector) { 202b8e80941Smrg display_mode->valid = false; 203b8e80941Smrg } 204b8e80941Smrg} 205b8e80941Smrg 206b8e80941Smrgstatic VkResult 207b8e80941Smrgwsi_display_register_drm_mode(struct wsi_device *wsi_device, 208b8e80941Smrg struct wsi_display_connector *connector, 209b8e80941Smrg drmModeModeInfoPtr drm_mode) 210b8e80941Smrg{ 211b8e80941Smrg struct wsi_display *wsi = 212b8e80941Smrg (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; 213b8e80941Smrg struct wsi_display_mode *display_mode = 214b8e80941Smrg wsi_display_find_drm_mode(wsi_device, connector, drm_mode); 215b8e80941Smrg 216b8e80941Smrg if (display_mode) { 217b8e80941Smrg display_mode->valid = true; 218b8e80941Smrg return VK_SUCCESS; 219b8e80941Smrg } 220b8e80941Smrg 221b8e80941Smrg display_mode = vk_zalloc(wsi->alloc, sizeof (struct wsi_display_mode), 222b8e80941Smrg 8, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); 223b8e80941Smrg if (!display_mode) 224b8e80941Smrg return VK_ERROR_OUT_OF_HOST_MEMORY; 225b8e80941Smrg 226b8e80941Smrg display_mode->connector = connector; 227b8e80941Smrg display_mode->valid = true; 228b8e80941Smrg display_mode->preferred = (drm_mode->type & DRM_MODE_TYPE_PREFERRED) != 0; 229b8e80941Smrg display_mode->clock = drm_mode->clock; /* kHz */ 230b8e80941Smrg display_mode->hdisplay = drm_mode->hdisplay; 231b8e80941Smrg display_mode->hsync_start = drm_mode->hsync_start; 232b8e80941Smrg display_mode->hsync_end = drm_mode->hsync_end; 233b8e80941Smrg display_mode->htotal = drm_mode->htotal; 234b8e80941Smrg display_mode->hskew = drm_mode->hskew; 235b8e80941Smrg display_mode->vdisplay = drm_mode->vdisplay; 236b8e80941Smrg display_mode->vsync_start = drm_mode->vsync_start; 237b8e80941Smrg display_mode->vsync_end = drm_mode->vsync_end; 238b8e80941Smrg display_mode->vtotal = drm_mode->vtotal; 239b8e80941Smrg display_mode->vscan = drm_mode->vscan; 240b8e80941Smrg display_mode->flags = drm_mode->flags; 241b8e80941Smrg 242b8e80941Smrg list_addtail(&display_mode->list, &connector->display_modes); 243b8e80941Smrg return VK_SUCCESS; 244b8e80941Smrg} 245b8e80941Smrg 246b8e80941Smrg/* 247b8e80941Smrg * Update our information about a specific connector 248b8e80941Smrg */ 249b8e80941Smrg 250b8e80941Smrgstatic struct wsi_display_connector * 251b8e80941Smrgwsi_display_find_connector(struct wsi_device *wsi_device, 252b8e80941Smrg uint32_t connector_id) 253b8e80941Smrg{ 254b8e80941Smrg struct wsi_display *wsi = 255b8e80941Smrg (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; 256b8e80941Smrg 257b8e80941Smrg wsi_for_each_connector(connector, wsi) { 258b8e80941Smrg if (connector->id == connector_id) 259b8e80941Smrg return connector; 260b8e80941Smrg } 261b8e80941Smrg 262b8e80941Smrg return NULL; 263b8e80941Smrg} 264b8e80941Smrg 265b8e80941Smrgstatic struct wsi_display_connector * 266b8e80941Smrgwsi_display_alloc_connector(struct wsi_display *wsi, 267b8e80941Smrg uint32_t connector_id) 268b8e80941Smrg{ 269b8e80941Smrg struct wsi_display_connector *connector = 270b8e80941Smrg vk_zalloc(wsi->alloc, sizeof (struct wsi_display_connector), 271b8e80941Smrg 8, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); 272b8e80941Smrg 273b8e80941Smrg connector->id = connector_id; 274b8e80941Smrg connector->wsi = wsi; 275b8e80941Smrg connector->active = false; 276b8e80941Smrg /* XXX use EDID name */ 277b8e80941Smrg connector->name = "monitor"; 278b8e80941Smrg list_inithead(&connector->display_modes); 279b8e80941Smrg return connector; 280b8e80941Smrg} 281b8e80941Smrg 282b8e80941Smrgstatic struct wsi_display_connector * 283b8e80941Smrgwsi_display_get_connector(struct wsi_device *wsi_device, 284b8e80941Smrg uint32_t connector_id) 285b8e80941Smrg{ 286b8e80941Smrg struct wsi_display *wsi = 287b8e80941Smrg (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; 288b8e80941Smrg 289b8e80941Smrg if (wsi->fd < 0) 290b8e80941Smrg return NULL; 291b8e80941Smrg 292b8e80941Smrg drmModeConnectorPtr drm_connector = 293b8e80941Smrg drmModeGetConnector(wsi->fd, connector_id); 294b8e80941Smrg 295b8e80941Smrg if (!drm_connector) 296b8e80941Smrg return NULL; 297b8e80941Smrg 298b8e80941Smrg struct wsi_display_connector *connector = 299b8e80941Smrg wsi_display_find_connector(wsi_device, connector_id); 300b8e80941Smrg 301b8e80941Smrg if (!connector) { 302b8e80941Smrg connector = wsi_display_alloc_connector(wsi, connector_id); 303b8e80941Smrg if (!connector) { 304b8e80941Smrg drmModeFreeConnector(drm_connector); 305b8e80941Smrg return NULL; 306b8e80941Smrg } 307b8e80941Smrg list_addtail(&connector->list, &wsi->connectors); 308b8e80941Smrg } 309b8e80941Smrg 310b8e80941Smrg connector->connected = drm_connector->connection != DRM_MODE_DISCONNECTED; 311b8e80941Smrg 312b8e80941Smrg /* Look for a DPMS property if we haven't already found one */ 313b8e80941Smrg for (int p = 0; connector->dpms_property == 0 && 314b8e80941Smrg p < drm_connector->count_props; p++) 315b8e80941Smrg { 316b8e80941Smrg drmModePropertyPtr prop = drmModeGetProperty(wsi->fd, 317b8e80941Smrg drm_connector->props[p]); 318b8e80941Smrg if (!prop) 319b8e80941Smrg continue; 320b8e80941Smrg if (prop->flags & DRM_MODE_PROP_ENUM) { 321b8e80941Smrg if (!strcmp(prop->name, "DPMS")) 322b8e80941Smrg connector->dpms_property = drm_connector->props[p]; 323b8e80941Smrg } 324b8e80941Smrg drmModeFreeProperty(prop); 325b8e80941Smrg } 326b8e80941Smrg 327b8e80941Smrg /* Mark all connector modes as invalid */ 328b8e80941Smrg wsi_display_invalidate_connector_modes(wsi_device, connector); 329b8e80941Smrg 330b8e80941Smrg /* 331b8e80941Smrg * List current modes, adding new ones and marking existing ones as 332b8e80941Smrg * valid 333b8e80941Smrg */ 334b8e80941Smrg for (int m = 0; m < drm_connector->count_modes; m++) { 335b8e80941Smrg VkResult result = wsi_display_register_drm_mode(wsi_device, 336b8e80941Smrg connector, 337b8e80941Smrg &drm_connector->modes[m]); 338b8e80941Smrg if (result != VK_SUCCESS) { 339b8e80941Smrg drmModeFreeConnector(drm_connector); 340b8e80941Smrg return NULL; 341b8e80941Smrg } 342b8e80941Smrg } 343b8e80941Smrg 344b8e80941Smrg drmModeFreeConnector(drm_connector); 345b8e80941Smrg 346b8e80941Smrg return connector; 347b8e80941Smrg} 348b8e80941Smrg 349b8e80941Smrg#define MM_PER_PIXEL (1.0/96.0 * 25.4) 350b8e80941Smrg 351b8e80941Smrgstatic uint32_t 352b8e80941Smrgmode_size(struct wsi_display_mode *mode) 353b8e80941Smrg{ 354b8e80941Smrg /* fortunately, these are both uint16_t, so this is easy */ 355b8e80941Smrg return (uint32_t) mode->hdisplay * (uint32_t) mode->vdisplay; 356b8e80941Smrg} 357b8e80941Smrg 358b8e80941Smrgstatic void 359b8e80941Smrgwsi_display_fill_in_display_properties(struct wsi_device *wsi_device, 360b8e80941Smrg struct wsi_display_connector *connector, 361b8e80941Smrg VkDisplayProperties2KHR *properties2) 362b8e80941Smrg{ 363b8e80941Smrg assert(properties2->sType == VK_STRUCTURE_TYPE_DISPLAY_PROPERTIES_2_KHR); 364b8e80941Smrg VkDisplayPropertiesKHR *properties = &properties2->displayProperties; 365b8e80941Smrg 366b8e80941Smrg properties->display = wsi_display_connector_to_handle(connector); 367b8e80941Smrg properties->displayName = connector->name; 368b8e80941Smrg 369b8e80941Smrg /* Find the first preferred mode and assume that's the physical 370b8e80941Smrg * resolution. If there isn't a preferred mode, find the largest mode and 371b8e80941Smrg * use that. 372b8e80941Smrg */ 373b8e80941Smrg 374b8e80941Smrg struct wsi_display_mode *preferred_mode = NULL, *largest_mode = NULL; 375b8e80941Smrg wsi_for_each_display_mode(display_mode, connector) { 376b8e80941Smrg if (!display_mode->valid) 377b8e80941Smrg continue; 378b8e80941Smrg if (display_mode->preferred) { 379b8e80941Smrg preferred_mode = display_mode; 380b8e80941Smrg break; 381b8e80941Smrg } 382b8e80941Smrg if (largest_mode == NULL || 383b8e80941Smrg mode_size(display_mode) > mode_size(largest_mode)) 384b8e80941Smrg { 385b8e80941Smrg largest_mode = display_mode; 386b8e80941Smrg } 387b8e80941Smrg } 388b8e80941Smrg 389b8e80941Smrg if (preferred_mode) { 390b8e80941Smrg properties->physicalResolution.width = preferred_mode->hdisplay; 391b8e80941Smrg properties->physicalResolution.height = preferred_mode->vdisplay; 392b8e80941Smrg } else if (largest_mode) { 393b8e80941Smrg properties->physicalResolution.width = largest_mode->hdisplay; 394b8e80941Smrg properties->physicalResolution.height = largest_mode->vdisplay; 395b8e80941Smrg } else { 396b8e80941Smrg properties->physicalResolution.width = 1024; 397b8e80941Smrg properties->physicalResolution.height = 768; 398b8e80941Smrg } 399b8e80941Smrg 400b8e80941Smrg /* Make up physical size based on 96dpi */ 401b8e80941Smrg properties->physicalDimensions.width = 402b8e80941Smrg floor(properties->physicalResolution.width * MM_PER_PIXEL + 0.5); 403b8e80941Smrg properties->physicalDimensions.height = 404b8e80941Smrg floor(properties->physicalResolution.height * MM_PER_PIXEL + 0.5); 405b8e80941Smrg 406b8e80941Smrg properties->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; 407b8e80941Smrg properties->planeReorderPossible = VK_FALSE; 408b8e80941Smrg properties->persistentContent = VK_FALSE; 409b8e80941Smrg} 410b8e80941Smrg 411b8e80941Smrg/* 412b8e80941Smrg * Implement vkGetPhysicalDeviceDisplayPropertiesKHR (VK_KHR_display) 413b8e80941Smrg */ 414b8e80941SmrgVkResult 415b8e80941Smrgwsi_display_get_physical_device_display_properties( 416b8e80941Smrg VkPhysicalDevice physical_device, 417b8e80941Smrg struct wsi_device *wsi_device, 418b8e80941Smrg uint32_t *property_count, 419b8e80941Smrg VkDisplayPropertiesKHR *properties) 420b8e80941Smrg{ 421b8e80941Smrg struct wsi_display *wsi = 422b8e80941Smrg (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; 423b8e80941Smrg 424b8e80941Smrg if (properties == NULL) { 425b8e80941Smrg return wsi_display_get_physical_device_display_properties2( 426b8e80941Smrg physical_device, wsi_device, property_count, NULL); 427b8e80941Smrg } else { 428b8e80941Smrg /* If we're actually returning properties, allocate a temporary array of 429b8e80941Smrg * VkDisplayProperties2KHR structs, call properties2 to fill them out, 430b8e80941Smrg * and then copy them to the client. This seems a bit expensive but 431b8e80941Smrg * wsi_display_get_physical_device_display_properties2() calls 432b8e80941Smrg * drmModeGetResources() which does an ioctl and then a bunch of 433b8e80941Smrg * allocations so this should get lost in the noise. 434b8e80941Smrg */ 435b8e80941Smrg VkDisplayProperties2KHR *props2 = 436b8e80941Smrg vk_zalloc(wsi->alloc, sizeof(*props2) * *property_count, 8, 437b8e80941Smrg VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); 438b8e80941Smrg if (props2 == NULL) 439b8e80941Smrg return VK_ERROR_OUT_OF_HOST_MEMORY; 440b8e80941Smrg 441b8e80941Smrg for (uint32_t i = 0; i < *property_count; i++) 442b8e80941Smrg props2[i].sType = VK_STRUCTURE_TYPE_DISPLAY_PROPERTIES_2_KHR; 443b8e80941Smrg 444b8e80941Smrg VkResult result = wsi_display_get_physical_device_display_properties2( 445b8e80941Smrg physical_device, wsi_device, property_count, props2); 446b8e80941Smrg 447b8e80941Smrg if (result == VK_SUCCESS || result == VK_INCOMPLETE) { 448b8e80941Smrg for (uint32_t i = 0; i < *property_count; i++) 449b8e80941Smrg properties[i] = props2[i].displayProperties; 450b8e80941Smrg } 451b8e80941Smrg 452b8e80941Smrg vk_free(wsi->alloc, props2); 453b8e80941Smrg 454b8e80941Smrg return result; 455b8e80941Smrg } 456b8e80941Smrg} 457b8e80941Smrg 458b8e80941SmrgVkResult 459b8e80941Smrgwsi_display_get_physical_device_display_properties2( 460b8e80941Smrg VkPhysicalDevice physical_device, 461b8e80941Smrg struct wsi_device *wsi_device, 462b8e80941Smrg uint32_t *property_count, 463b8e80941Smrg VkDisplayProperties2KHR *properties) 464b8e80941Smrg{ 465b8e80941Smrg struct wsi_display *wsi = 466b8e80941Smrg (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; 467b8e80941Smrg 468b8e80941Smrg if (wsi->fd < 0) 469b8e80941Smrg goto bail; 470b8e80941Smrg 471b8e80941Smrg drmModeResPtr mode_res = drmModeGetResources(wsi->fd); 472b8e80941Smrg 473b8e80941Smrg if (!mode_res) 474b8e80941Smrg goto bail; 475b8e80941Smrg 476b8e80941Smrg VK_OUTARRAY_MAKE(conn, properties, property_count); 477b8e80941Smrg 478b8e80941Smrg /* Get current information */ 479b8e80941Smrg 480b8e80941Smrg for (int c = 0; c < mode_res->count_connectors; c++) { 481b8e80941Smrg struct wsi_display_connector *connector = 482b8e80941Smrg wsi_display_get_connector(wsi_device, mode_res->connectors[c]); 483b8e80941Smrg 484b8e80941Smrg if (!connector) { 485b8e80941Smrg drmModeFreeResources(mode_res); 486b8e80941Smrg return VK_ERROR_OUT_OF_HOST_MEMORY; 487b8e80941Smrg } 488b8e80941Smrg 489b8e80941Smrg if (connector->connected) { 490b8e80941Smrg vk_outarray_append(&conn, prop) { 491b8e80941Smrg wsi_display_fill_in_display_properties(wsi_device, 492b8e80941Smrg connector, 493b8e80941Smrg prop); 494b8e80941Smrg } 495b8e80941Smrg } 496b8e80941Smrg } 497b8e80941Smrg 498b8e80941Smrg drmModeFreeResources(mode_res); 499b8e80941Smrg 500b8e80941Smrg return vk_outarray_status(&conn); 501b8e80941Smrg 502b8e80941Smrgbail: 503b8e80941Smrg *property_count = 0; 504b8e80941Smrg return VK_SUCCESS; 505b8e80941Smrg} 506b8e80941Smrg 507b8e80941Smrg/* 508b8e80941Smrg * Implement vkGetPhysicalDeviceDisplayPlanePropertiesKHR (VK_KHR_display 509b8e80941Smrg */ 510b8e80941Smrgstatic void 511b8e80941Smrgwsi_display_fill_in_display_plane_properties( 512b8e80941Smrg struct wsi_device *wsi_device, 513b8e80941Smrg struct wsi_display_connector *connector, 514b8e80941Smrg VkDisplayPlaneProperties2KHR *properties) 515b8e80941Smrg{ 516b8e80941Smrg assert(properties->sType == VK_STRUCTURE_TYPE_DISPLAY_PLANE_PROPERTIES_2_KHR); 517b8e80941Smrg VkDisplayPlanePropertiesKHR *prop = &properties->displayPlaneProperties; 518b8e80941Smrg 519b8e80941Smrg if (connector && connector->active) { 520b8e80941Smrg prop->currentDisplay = wsi_display_connector_to_handle(connector); 521b8e80941Smrg prop->currentStackIndex = 0; 522b8e80941Smrg } else { 523b8e80941Smrg prop->currentDisplay = VK_NULL_HANDLE; 524b8e80941Smrg prop->currentStackIndex = 0; 525b8e80941Smrg } 526b8e80941Smrg} 527b8e80941Smrg 528b8e80941SmrgVkResult 529b8e80941Smrgwsi_display_get_physical_device_display_plane_properties( 530b8e80941Smrg VkPhysicalDevice physical_device, 531b8e80941Smrg struct wsi_device *wsi_device, 532b8e80941Smrg uint32_t *property_count, 533b8e80941Smrg VkDisplayPlanePropertiesKHR *properties) 534b8e80941Smrg{ 535b8e80941Smrg struct wsi_display *wsi = 536b8e80941Smrg (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; 537b8e80941Smrg 538b8e80941Smrg VK_OUTARRAY_MAKE(conn, properties, property_count); 539b8e80941Smrg 540b8e80941Smrg wsi_for_each_connector(connector, wsi) { 541b8e80941Smrg vk_outarray_append(&conn, prop) { 542b8e80941Smrg VkDisplayPlaneProperties2KHR prop2 = { 543b8e80941Smrg .sType = VK_STRUCTURE_TYPE_DISPLAY_PLANE_PROPERTIES_2_KHR, 544b8e80941Smrg }; 545b8e80941Smrg wsi_display_fill_in_display_plane_properties(wsi_device, connector, 546b8e80941Smrg &prop2); 547b8e80941Smrg *prop = prop2.displayPlaneProperties; 548b8e80941Smrg } 549b8e80941Smrg } 550b8e80941Smrg return vk_outarray_status(&conn); 551b8e80941Smrg} 552b8e80941Smrg 553b8e80941SmrgVkResult 554b8e80941Smrgwsi_display_get_physical_device_display_plane_properties2( 555b8e80941Smrg VkPhysicalDevice physical_device, 556b8e80941Smrg struct wsi_device *wsi_device, 557b8e80941Smrg uint32_t *property_count, 558b8e80941Smrg VkDisplayPlaneProperties2KHR *properties) 559b8e80941Smrg{ 560b8e80941Smrg struct wsi_display *wsi = 561b8e80941Smrg (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; 562b8e80941Smrg 563b8e80941Smrg VK_OUTARRAY_MAKE(conn, properties, property_count); 564b8e80941Smrg 565b8e80941Smrg wsi_for_each_connector(connector, wsi) { 566b8e80941Smrg vk_outarray_append(&conn, prop) { 567b8e80941Smrg wsi_display_fill_in_display_plane_properties(wsi_device, connector, 568b8e80941Smrg prop); 569b8e80941Smrg } 570b8e80941Smrg } 571b8e80941Smrg return vk_outarray_status(&conn); 572b8e80941Smrg} 573b8e80941Smrg 574b8e80941Smrg/* 575b8e80941Smrg * Implement vkGetDisplayPlaneSupportedDisplaysKHR (VK_KHR_display) 576b8e80941Smrg */ 577b8e80941Smrg 578b8e80941SmrgVkResult 579b8e80941Smrgwsi_display_get_display_plane_supported_displays( 580b8e80941Smrg VkPhysicalDevice physical_device, 581b8e80941Smrg struct wsi_device *wsi_device, 582b8e80941Smrg uint32_t plane_index, 583b8e80941Smrg uint32_t *display_count, 584b8e80941Smrg VkDisplayKHR *displays) 585b8e80941Smrg{ 586b8e80941Smrg struct wsi_display *wsi = 587b8e80941Smrg (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; 588b8e80941Smrg 589b8e80941Smrg VK_OUTARRAY_MAKE(conn, displays, display_count); 590b8e80941Smrg 591b8e80941Smrg int c = 0; 592b8e80941Smrg 593b8e80941Smrg wsi_for_each_connector(connector, wsi) { 594b8e80941Smrg if (c == plane_index && connector->connected) { 595b8e80941Smrg vk_outarray_append(&conn, display) { 596b8e80941Smrg *display = wsi_display_connector_to_handle(connector); 597b8e80941Smrg } 598b8e80941Smrg } 599b8e80941Smrg c++; 600b8e80941Smrg } 601b8e80941Smrg return vk_outarray_status(&conn); 602b8e80941Smrg} 603b8e80941Smrg 604b8e80941Smrg/* 605b8e80941Smrg * Implement vkGetDisplayModePropertiesKHR (VK_KHR_display) 606b8e80941Smrg */ 607b8e80941Smrg 608b8e80941Smrgstatic void 609b8e80941Smrgwsi_display_fill_in_display_mode_properties( 610b8e80941Smrg struct wsi_device *wsi_device, 611b8e80941Smrg struct wsi_display_mode *display_mode, 612b8e80941Smrg VkDisplayModeProperties2KHR *properties) 613b8e80941Smrg{ 614b8e80941Smrg assert(properties->sType == VK_STRUCTURE_TYPE_DISPLAY_MODE_PROPERTIES_2_KHR); 615b8e80941Smrg VkDisplayModePropertiesKHR *prop = &properties->displayModeProperties; 616b8e80941Smrg 617b8e80941Smrg prop->displayMode = wsi_display_mode_to_handle(display_mode); 618b8e80941Smrg prop->parameters.visibleRegion.width = display_mode->hdisplay; 619b8e80941Smrg prop->parameters.visibleRegion.height = display_mode->vdisplay; 620b8e80941Smrg prop->parameters.refreshRate = 621b8e80941Smrg (uint32_t) (wsi_display_mode_refresh(display_mode) * 1000 + 0.5); 622b8e80941Smrg} 623b8e80941Smrg 624b8e80941SmrgVkResult 625b8e80941Smrgwsi_display_get_display_mode_properties(VkPhysicalDevice physical_device, 626b8e80941Smrg struct wsi_device *wsi_device, 627b8e80941Smrg VkDisplayKHR display, 628b8e80941Smrg uint32_t *property_count, 629b8e80941Smrg VkDisplayModePropertiesKHR *properties) 630b8e80941Smrg{ 631b8e80941Smrg struct wsi_display_connector *connector = 632b8e80941Smrg wsi_display_connector_from_handle(display); 633b8e80941Smrg 634b8e80941Smrg VK_OUTARRAY_MAKE(conn, properties, property_count); 635b8e80941Smrg 636b8e80941Smrg wsi_for_each_display_mode(display_mode, connector) { 637b8e80941Smrg if (!display_mode->valid) 638b8e80941Smrg continue; 639b8e80941Smrg 640b8e80941Smrg vk_outarray_append(&conn, prop) { 641b8e80941Smrg VkDisplayModeProperties2KHR prop2 = { 642b8e80941Smrg .sType = VK_STRUCTURE_TYPE_DISPLAY_MODE_PROPERTIES_2_KHR, 643b8e80941Smrg }; 644b8e80941Smrg wsi_display_fill_in_display_mode_properties(wsi_device, 645b8e80941Smrg display_mode, &prop2); 646b8e80941Smrg *prop = prop2.displayModeProperties; 647b8e80941Smrg } 648b8e80941Smrg } 649b8e80941Smrg return vk_outarray_status(&conn); 650b8e80941Smrg} 651b8e80941Smrg 652b8e80941SmrgVkResult 653b8e80941Smrgwsi_display_get_display_mode_properties2(VkPhysicalDevice physical_device, 654b8e80941Smrg struct wsi_device *wsi_device, 655b8e80941Smrg VkDisplayKHR display, 656b8e80941Smrg uint32_t *property_count, 657b8e80941Smrg VkDisplayModeProperties2KHR *properties) 658b8e80941Smrg{ 659b8e80941Smrg struct wsi_display_connector *connector = 660b8e80941Smrg wsi_display_connector_from_handle(display); 661b8e80941Smrg 662b8e80941Smrg VK_OUTARRAY_MAKE(conn, properties, property_count); 663b8e80941Smrg 664b8e80941Smrg wsi_for_each_display_mode(display_mode, connector) { 665b8e80941Smrg if (!display_mode->valid) 666b8e80941Smrg continue; 667b8e80941Smrg 668b8e80941Smrg vk_outarray_append(&conn, prop) { 669b8e80941Smrg wsi_display_fill_in_display_mode_properties(wsi_device, 670b8e80941Smrg display_mode, prop); 671b8e80941Smrg } 672b8e80941Smrg } 673b8e80941Smrg return vk_outarray_status(&conn); 674b8e80941Smrg} 675b8e80941Smrg 676b8e80941Smrgstatic bool 677b8e80941Smrgwsi_display_mode_matches_vk(wsi_display_mode *wsi, 678b8e80941Smrg const VkDisplayModeParametersKHR *vk) 679b8e80941Smrg{ 680b8e80941Smrg return (vk->visibleRegion.width == wsi->hdisplay && 681b8e80941Smrg vk->visibleRegion.height == wsi->vdisplay && 682b8e80941Smrg fabs(wsi_display_mode_refresh(wsi) * 1000.0 - vk->refreshRate) < 10); 683b8e80941Smrg} 684b8e80941Smrg 685b8e80941Smrg/* 686b8e80941Smrg * Implement vkCreateDisplayModeKHR (VK_KHR_display) 687b8e80941Smrg */ 688b8e80941SmrgVkResult 689b8e80941Smrgwsi_display_create_display_mode(VkPhysicalDevice physical_device, 690b8e80941Smrg struct wsi_device *wsi_device, 691b8e80941Smrg VkDisplayKHR display, 692b8e80941Smrg const VkDisplayModeCreateInfoKHR *create_info, 693b8e80941Smrg const VkAllocationCallbacks *allocator, 694b8e80941Smrg VkDisplayModeKHR *mode) 695b8e80941Smrg{ 696b8e80941Smrg struct wsi_display_connector *connector = 697b8e80941Smrg wsi_display_connector_from_handle(display); 698b8e80941Smrg 699b8e80941Smrg if (create_info->flags != 0) 700b8e80941Smrg return VK_ERROR_INITIALIZATION_FAILED; 701b8e80941Smrg 702b8e80941Smrg /* Check and see if the requested mode happens to match an existing one and 703b8e80941Smrg * return that. This makes the conformance suite happy. Doing more than 704b8e80941Smrg * this would involve embedding the CVT function into the driver, which seems 705b8e80941Smrg * excessive. 706b8e80941Smrg */ 707b8e80941Smrg wsi_for_each_display_mode(display_mode, connector) { 708b8e80941Smrg if (display_mode->valid) { 709b8e80941Smrg if (wsi_display_mode_matches_vk(display_mode, &create_info->parameters)) { 710b8e80941Smrg *mode = wsi_display_mode_to_handle(display_mode); 711b8e80941Smrg return VK_SUCCESS; 712b8e80941Smrg } 713b8e80941Smrg } 714b8e80941Smrg } 715b8e80941Smrg return VK_ERROR_INITIALIZATION_FAILED; 716b8e80941Smrg} 717b8e80941Smrg 718b8e80941Smrg/* 719b8e80941Smrg * Implement vkGetDisplayPlaneCapabilities 720b8e80941Smrg */ 721b8e80941SmrgVkResult 722b8e80941Smrgwsi_get_display_plane_capabilities(VkPhysicalDevice physical_device, 723b8e80941Smrg struct wsi_device *wsi_device, 724b8e80941Smrg VkDisplayModeKHR mode_khr, 725b8e80941Smrg uint32_t plane_index, 726b8e80941Smrg VkDisplayPlaneCapabilitiesKHR *capabilities) 727b8e80941Smrg{ 728b8e80941Smrg struct wsi_display_mode *mode = wsi_display_mode_from_handle(mode_khr); 729b8e80941Smrg 730b8e80941Smrg /* XXX use actual values */ 731b8e80941Smrg capabilities->supportedAlpha = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR; 732b8e80941Smrg capabilities->minSrcPosition.x = 0; 733b8e80941Smrg capabilities->minSrcPosition.y = 0; 734b8e80941Smrg capabilities->maxSrcPosition.x = 0; 735b8e80941Smrg capabilities->maxSrcPosition.y = 0; 736b8e80941Smrg capabilities->minSrcExtent.width = mode->hdisplay; 737b8e80941Smrg capabilities->minSrcExtent.height = mode->vdisplay; 738b8e80941Smrg capabilities->maxSrcExtent.width = mode->hdisplay; 739b8e80941Smrg capabilities->maxSrcExtent.height = mode->vdisplay; 740b8e80941Smrg capabilities->minDstPosition.x = 0; 741b8e80941Smrg capabilities->minDstPosition.y = 0; 742b8e80941Smrg capabilities->maxDstPosition.x = 0; 743b8e80941Smrg capabilities->maxDstPosition.y = 0; 744b8e80941Smrg capabilities->minDstExtent.width = mode->hdisplay; 745b8e80941Smrg capabilities->minDstExtent.height = mode->vdisplay; 746b8e80941Smrg capabilities->maxDstExtent.width = mode->hdisplay; 747b8e80941Smrg capabilities->maxDstExtent.height = mode->vdisplay; 748b8e80941Smrg return VK_SUCCESS; 749b8e80941Smrg} 750b8e80941Smrg 751b8e80941SmrgVkResult 752b8e80941Smrgwsi_get_display_plane_capabilities2( 753b8e80941Smrg VkPhysicalDevice physical_device, 754b8e80941Smrg struct wsi_device *wsi_device, 755b8e80941Smrg const VkDisplayPlaneInfo2KHR *pDisplayPlaneInfo, 756b8e80941Smrg VkDisplayPlaneCapabilities2KHR *capabilities) 757b8e80941Smrg{ 758b8e80941Smrg assert(capabilities->sType == 759b8e80941Smrg VK_STRUCTURE_TYPE_DISPLAY_PLANE_CAPABILITIES_2_KHR); 760b8e80941Smrg 761b8e80941Smrg VkResult result = 762b8e80941Smrg wsi_get_display_plane_capabilities(physical_device, wsi_device, 763b8e80941Smrg pDisplayPlaneInfo->mode, 764b8e80941Smrg pDisplayPlaneInfo->planeIndex, 765b8e80941Smrg &capabilities->capabilities); 766b8e80941Smrg 767b8e80941Smrg vk_foreach_struct(ext, capabilities->pNext) { 768b8e80941Smrg switch (ext->sType) { 769b8e80941Smrg case VK_STRUCTURE_TYPE_SURFACE_PROTECTED_CAPABILITIES_KHR: { 770b8e80941Smrg VkSurfaceProtectedCapabilitiesKHR *protected = (void *)ext; 771b8e80941Smrg protected->supportsProtected = VK_FALSE; 772b8e80941Smrg break; 773b8e80941Smrg } 774b8e80941Smrg 775b8e80941Smrg default: 776b8e80941Smrg /* Ignored */ 777b8e80941Smrg break; 778b8e80941Smrg } 779b8e80941Smrg } 780b8e80941Smrg 781b8e80941Smrg return result; 782b8e80941Smrg} 783b8e80941Smrg 784b8e80941SmrgVkResult 785b8e80941Smrgwsi_create_display_surface(VkInstance instance, 786b8e80941Smrg const VkAllocationCallbacks *allocator, 787b8e80941Smrg const VkDisplaySurfaceCreateInfoKHR *create_info, 788b8e80941Smrg VkSurfaceKHR *surface_khr) 789b8e80941Smrg{ 790b8e80941Smrg VkIcdSurfaceDisplay *surface = vk_zalloc(allocator, sizeof *surface, 8, 791b8e80941Smrg VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); 792b8e80941Smrg 793b8e80941Smrg if (surface == NULL) 794b8e80941Smrg return VK_ERROR_OUT_OF_HOST_MEMORY; 795b8e80941Smrg 796b8e80941Smrg surface->base.platform = VK_ICD_WSI_PLATFORM_DISPLAY; 797b8e80941Smrg 798b8e80941Smrg surface->displayMode = create_info->displayMode; 799b8e80941Smrg surface->planeIndex = create_info->planeIndex; 800b8e80941Smrg surface->planeStackIndex = create_info->planeStackIndex; 801b8e80941Smrg surface->transform = create_info->transform; 802b8e80941Smrg surface->globalAlpha = create_info->globalAlpha; 803b8e80941Smrg surface->alphaMode = create_info->alphaMode; 804b8e80941Smrg surface->imageExtent = create_info->imageExtent; 805b8e80941Smrg 806b8e80941Smrg *surface_khr = VkIcdSurfaceBase_to_handle(&surface->base); 807b8e80941Smrg return VK_SUCCESS; 808b8e80941Smrg} 809b8e80941Smrg 810b8e80941Smrg 811b8e80941Smrgstatic VkResult 812b8e80941Smrgwsi_display_surface_get_support(VkIcdSurfaceBase *surface, 813b8e80941Smrg struct wsi_device *wsi_device, 814b8e80941Smrg uint32_t queueFamilyIndex, 815b8e80941Smrg VkBool32* pSupported) 816b8e80941Smrg{ 817b8e80941Smrg *pSupported = VK_TRUE; 818b8e80941Smrg return VK_SUCCESS; 819b8e80941Smrg} 820b8e80941Smrg 821b8e80941Smrgstatic VkResult 822b8e80941Smrgwsi_display_surface_get_capabilities(VkIcdSurfaceBase *surface_base, 823b8e80941Smrg struct wsi_device *wsi_device, 824b8e80941Smrg VkSurfaceCapabilitiesKHR* caps) 825b8e80941Smrg{ 826b8e80941Smrg VkIcdSurfaceDisplay *surface = (VkIcdSurfaceDisplay *) surface_base; 827b8e80941Smrg wsi_display_mode *mode = wsi_display_mode_from_handle(surface->displayMode); 828b8e80941Smrg 829b8e80941Smrg caps->currentExtent.width = mode->hdisplay; 830b8e80941Smrg caps->currentExtent.height = mode->vdisplay; 831b8e80941Smrg 832b8e80941Smrg caps->minImageExtent = (VkExtent2D) { 1, 1 }; 833b8e80941Smrg caps->maxImageExtent = (VkExtent2D) { 834b8e80941Smrg wsi_device->maxImageDimension2D, 835b8e80941Smrg wsi_device->maxImageDimension2D, 836b8e80941Smrg }; 837b8e80941Smrg 838b8e80941Smrg caps->supportedCompositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; 839b8e80941Smrg 840b8e80941Smrg caps->minImageCount = 2; 841b8e80941Smrg caps->maxImageCount = 0; 842b8e80941Smrg 843b8e80941Smrg caps->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; 844b8e80941Smrg caps->currentTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; 845b8e80941Smrg caps->maxImageArrayLayers = 1; 846b8e80941Smrg caps->supportedUsageFlags = 847b8e80941Smrg VK_IMAGE_USAGE_TRANSFER_SRC_BIT | 848b8e80941Smrg VK_IMAGE_USAGE_SAMPLED_BIT | 849b8e80941Smrg VK_IMAGE_USAGE_TRANSFER_DST_BIT | 850b8e80941Smrg VK_IMAGE_USAGE_STORAGE_BIT | 851b8e80941Smrg VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; 852b8e80941Smrg 853b8e80941Smrg return VK_SUCCESS; 854b8e80941Smrg} 855b8e80941Smrg 856b8e80941Smrgstatic VkResult 857b8e80941Smrgwsi_display_surface_get_surface_counters( 858b8e80941Smrg VkIcdSurfaceBase *surface_base, 859b8e80941Smrg VkSurfaceCounterFlagsEXT *counters) 860b8e80941Smrg{ 861b8e80941Smrg *counters = VK_SURFACE_COUNTER_VBLANK_EXT; 862b8e80941Smrg return VK_SUCCESS; 863b8e80941Smrg} 864b8e80941Smrg 865b8e80941Smrgstatic VkResult 866b8e80941Smrgwsi_display_surface_get_capabilities2(VkIcdSurfaceBase *icd_surface, 867b8e80941Smrg struct wsi_device *wsi_device, 868b8e80941Smrg const void *info_next, 869b8e80941Smrg VkSurfaceCapabilities2KHR *caps) 870b8e80941Smrg{ 871b8e80941Smrg assert(caps->sType == VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR); 872b8e80941Smrg VkResult result; 873b8e80941Smrg 874b8e80941Smrg result = wsi_display_surface_get_capabilities(icd_surface, wsi_device, 875b8e80941Smrg &caps->surfaceCapabilities); 876b8e80941Smrg if (result != VK_SUCCESS) 877b8e80941Smrg return result; 878b8e80941Smrg 879b8e80941Smrg struct wsi_surface_supported_counters *counters = 880b8e80941Smrg vk_find_struct( caps->pNext, WSI_SURFACE_SUPPORTED_COUNTERS_MESA); 881b8e80941Smrg 882b8e80941Smrg if (counters) { 883b8e80941Smrg result = wsi_display_surface_get_surface_counters( 884b8e80941Smrg icd_surface, 885b8e80941Smrg &counters->supported_surface_counters); 886b8e80941Smrg } 887b8e80941Smrg 888b8e80941Smrg return result; 889b8e80941Smrg} 890b8e80941Smrg 891b8e80941Smrgstatic const struct { 892b8e80941Smrg VkFormat format; 893b8e80941Smrg uint32_t drm_format; 894b8e80941Smrg} available_surface_formats[] = { 895b8e80941Smrg { .format = VK_FORMAT_B8G8R8A8_SRGB, .drm_format = DRM_FORMAT_XRGB8888 }, 896b8e80941Smrg { .format = VK_FORMAT_B8G8R8A8_UNORM, .drm_format = DRM_FORMAT_XRGB8888 }, 897b8e80941Smrg}; 898b8e80941Smrg 899b8e80941Smrgstatic VkResult 900b8e80941Smrgwsi_display_surface_get_formats(VkIcdSurfaceBase *icd_surface, 901b8e80941Smrg struct wsi_device *wsi_device, 902b8e80941Smrg uint32_t *surface_format_count, 903b8e80941Smrg VkSurfaceFormatKHR *surface_formats) 904b8e80941Smrg{ 905b8e80941Smrg VK_OUTARRAY_MAKE(out, surface_formats, surface_format_count); 906b8e80941Smrg 907b8e80941Smrg for (unsigned i = 0; i < ARRAY_SIZE(available_surface_formats); i++) { 908b8e80941Smrg vk_outarray_append(&out, f) { 909b8e80941Smrg f->format = available_surface_formats[i].format; 910b8e80941Smrg f->colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; 911b8e80941Smrg } 912b8e80941Smrg } 913b8e80941Smrg 914b8e80941Smrg return vk_outarray_status(&out); 915b8e80941Smrg} 916b8e80941Smrg 917b8e80941Smrgstatic VkResult 918b8e80941Smrgwsi_display_surface_get_formats2(VkIcdSurfaceBase *surface, 919b8e80941Smrg struct wsi_device *wsi_device, 920b8e80941Smrg const void *info_next, 921b8e80941Smrg uint32_t *surface_format_count, 922b8e80941Smrg VkSurfaceFormat2KHR *surface_formats) 923b8e80941Smrg{ 924b8e80941Smrg VK_OUTARRAY_MAKE(out, surface_formats, surface_format_count); 925b8e80941Smrg 926b8e80941Smrg for (unsigned i = 0; i < ARRAY_SIZE(available_surface_formats); i++) { 927b8e80941Smrg vk_outarray_append(&out, f) { 928b8e80941Smrg assert(f->sType == VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR); 929b8e80941Smrg f->surfaceFormat.format = available_surface_formats[i].format; 930b8e80941Smrg f->surfaceFormat.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; 931b8e80941Smrg } 932b8e80941Smrg } 933b8e80941Smrg 934b8e80941Smrg return vk_outarray_status(&out); 935b8e80941Smrg} 936b8e80941Smrg 937b8e80941Smrgstatic VkResult 938b8e80941Smrgwsi_display_surface_get_present_modes(VkIcdSurfaceBase *surface, 939b8e80941Smrg uint32_t *present_mode_count, 940b8e80941Smrg VkPresentModeKHR *present_modes) 941b8e80941Smrg{ 942b8e80941Smrg VK_OUTARRAY_MAKE(conn, present_modes, present_mode_count); 943b8e80941Smrg 944b8e80941Smrg vk_outarray_append(&conn, present) { 945b8e80941Smrg *present = VK_PRESENT_MODE_FIFO_KHR; 946b8e80941Smrg } 947b8e80941Smrg 948b8e80941Smrg return vk_outarray_status(&conn); 949b8e80941Smrg} 950b8e80941Smrg 951b8e80941Smrgstatic VkResult 952b8e80941Smrgwsi_display_surface_get_present_rectangles(VkIcdSurfaceBase *surface_base, 953b8e80941Smrg struct wsi_device *wsi_device, 954b8e80941Smrg uint32_t* pRectCount, 955b8e80941Smrg VkRect2D* pRects) 956b8e80941Smrg{ 957b8e80941Smrg VkIcdSurfaceDisplay *surface = (VkIcdSurfaceDisplay *) surface_base; 958b8e80941Smrg wsi_display_mode *mode = wsi_display_mode_from_handle(surface->displayMode); 959b8e80941Smrg VK_OUTARRAY_MAKE(out, pRects, pRectCount); 960b8e80941Smrg 961b8e80941Smrg if (wsi_device_matches_drm_fd(wsi_device, mode->connector->wsi->fd)) { 962b8e80941Smrg vk_outarray_append(&out, rect) { 963b8e80941Smrg *rect = (VkRect2D) { 964b8e80941Smrg .offset = { 0, 0 }, 965b8e80941Smrg .extent = { mode->hdisplay, mode->vdisplay }, 966b8e80941Smrg }; 967b8e80941Smrg } 968b8e80941Smrg } 969b8e80941Smrg 970b8e80941Smrg return vk_outarray_status(&out); 971b8e80941Smrg} 972b8e80941Smrg 973b8e80941Smrgstatic void 974b8e80941Smrgwsi_display_destroy_buffer(struct wsi_display *wsi, 975b8e80941Smrg uint32_t buffer) 976b8e80941Smrg{ 977b8e80941Smrg (void) drmIoctl(wsi->fd, DRM_IOCTL_GEM_CLOSE, 978b8e80941Smrg &((struct drm_gem_close) { .handle = buffer })); 979b8e80941Smrg} 980b8e80941Smrg 981b8e80941Smrgstatic VkResult 982b8e80941Smrgwsi_display_image_init(VkDevice device_h, 983b8e80941Smrg struct wsi_swapchain *drv_chain, 984b8e80941Smrg const VkSwapchainCreateInfoKHR *create_info, 985b8e80941Smrg const VkAllocationCallbacks *allocator, 986b8e80941Smrg struct wsi_display_image *image) 987b8e80941Smrg{ 988b8e80941Smrg struct wsi_display_swapchain *chain = 989b8e80941Smrg (struct wsi_display_swapchain *) drv_chain; 990b8e80941Smrg struct wsi_display *wsi = chain->wsi; 991b8e80941Smrg uint32_t drm_format = 0; 992b8e80941Smrg 993b8e80941Smrg for (unsigned i = 0; i < ARRAY_SIZE(available_surface_formats); i++) { 994b8e80941Smrg if (create_info->imageFormat == available_surface_formats[i].format) { 995b8e80941Smrg drm_format = available_surface_formats[i].drm_format; 996b8e80941Smrg break; 997b8e80941Smrg } 998b8e80941Smrg } 999b8e80941Smrg 1000b8e80941Smrg /* the application provided an invalid format, bail */ 1001b8e80941Smrg if (drm_format == 0) 1002b8e80941Smrg return VK_ERROR_DEVICE_LOST; 1003b8e80941Smrg 1004b8e80941Smrg VkResult result = wsi_create_native_image(&chain->base, create_info, 1005b8e80941Smrg 0, NULL, NULL, 1006b8e80941Smrg &image->base); 1007b8e80941Smrg if (result != VK_SUCCESS) 1008b8e80941Smrg return result; 1009b8e80941Smrg 1010b8e80941Smrg memset(image->buffer, 0, sizeof (image->buffer)); 1011b8e80941Smrg 1012b8e80941Smrg for (unsigned int i = 0; i < image->base.num_planes; i++) { 1013b8e80941Smrg int ret = drmPrimeFDToHandle(wsi->fd, image->base.fds[i], 1014b8e80941Smrg &image->buffer[i]); 1015b8e80941Smrg 1016b8e80941Smrg close(image->base.fds[i]); 1017b8e80941Smrg image->base.fds[i] = -1; 1018b8e80941Smrg if (ret < 0) 1019b8e80941Smrg goto fail_handle; 1020b8e80941Smrg } 1021b8e80941Smrg 1022b8e80941Smrg image->chain = chain; 1023b8e80941Smrg image->state = WSI_IMAGE_IDLE; 1024b8e80941Smrg image->fb_id = 0; 1025b8e80941Smrg 1026b8e80941Smrg int ret = drmModeAddFB2(wsi->fd, 1027b8e80941Smrg create_info->imageExtent.width, 1028b8e80941Smrg create_info->imageExtent.height, 1029b8e80941Smrg drm_format, 1030b8e80941Smrg image->buffer, 1031b8e80941Smrg image->base.row_pitches, 1032b8e80941Smrg image->base.offsets, 1033b8e80941Smrg &image->fb_id, 0); 1034b8e80941Smrg 1035b8e80941Smrg if (ret) 1036b8e80941Smrg goto fail_fb; 1037b8e80941Smrg 1038b8e80941Smrg return VK_SUCCESS; 1039b8e80941Smrg 1040b8e80941Smrgfail_fb: 1041b8e80941Smrgfail_handle: 1042b8e80941Smrg for (unsigned int i = 0; i < image->base.num_planes; i++) { 1043b8e80941Smrg if (image->buffer[i]) 1044b8e80941Smrg wsi_display_destroy_buffer(wsi, image->buffer[i]); 1045b8e80941Smrg if (image->base.fds[i] != -1) { 1046b8e80941Smrg close(image->base.fds[i]); 1047b8e80941Smrg image->base.fds[i] = -1; 1048b8e80941Smrg } 1049b8e80941Smrg } 1050b8e80941Smrg 1051b8e80941Smrg wsi_destroy_image(&chain->base, &image->base); 1052b8e80941Smrg 1053b8e80941Smrg return VK_ERROR_OUT_OF_HOST_MEMORY; 1054b8e80941Smrg} 1055b8e80941Smrg 1056b8e80941Smrgstatic void 1057b8e80941Smrgwsi_display_image_finish(struct wsi_swapchain *drv_chain, 1058b8e80941Smrg const VkAllocationCallbacks *allocator, 1059b8e80941Smrg struct wsi_display_image *image) 1060b8e80941Smrg{ 1061b8e80941Smrg struct wsi_display_swapchain *chain = 1062b8e80941Smrg (struct wsi_display_swapchain *) drv_chain; 1063b8e80941Smrg struct wsi_display *wsi = chain->wsi; 1064b8e80941Smrg 1065b8e80941Smrg drmModeRmFB(wsi->fd, image->fb_id); 1066b8e80941Smrg for (unsigned int i = 0; i < image->base.num_planes; i++) 1067b8e80941Smrg wsi_display_destroy_buffer(wsi, image->buffer[i]); 1068b8e80941Smrg wsi_destroy_image(&chain->base, &image->base); 1069b8e80941Smrg} 1070b8e80941Smrg 1071b8e80941Smrgstatic VkResult 1072b8e80941Smrgwsi_display_swapchain_destroy(struct wsi_swapchain *drv_chain, 1073b8e80941Smrg const VkAllocationCallbacks *allocator) 1074b8e80941Smrg{ 1075b8e80941Smrg struct wsi_display_swapchain *chain = 1076b8e80941Smrg (struct wsi_display_swapchain *) drv_chain; 1077b8e80941Smrg 1078b8e80941Smrg for (uint32_t i = 0; i < chain->base.image_count; i++) 1079b8e80941Smrg wsi_display_image_finish(drv_chain, allocator, &chain->images[i]); 1080b8e80941Smrg 1081b8e80941Smrg wsi_swapchain_finish(&chain->base); 1082b8e80941Smrg vk_free(allocator, chain); 1083b8e80941Smrg return VK_SUCCESS; 1084b8e80941Smrg} 1085b8e80941Smrg 1086b8e80941Smrgstatic struct wsi_image * 1087b8e80941Smrgwsi_display_get_wsi_image(struct wsi_swapchain *drv_chain, 1088b8e80941Smrg uint32_t image_index) 1089b8e80941Smrg{ 1090b8e80941Smrg struct wsi_display_swapchain *chain = 1091b8e80941Smrg (struct wsi_display_swapchain *) drv_chain; 1092b8e80941Smrg 1093b8e80941Smrg return &chain->images[image_index].base; 1094b8e80941Smrg} 1095b8e80941Smrg 1096b8e80941Smrgstatic void 1097b8e80941Smrgwsi_display_idle_old_displaying(struct wsi_display_image *active_image) 1098b8e80941Smrg{ 1099b8e80941Smrg struct wsi_display_swapchain *chain = active_image->chain; 1100b8e80941Smrg 1101b8e80941Smrg wsi_display_debug("idle everyone but %ld\n", 1102b8e80941Smrg active_image - &(chain->images[0])); 1103b8e80941Smrg for (uint32_t i = 0; i < chain->base.image_count; i++) 1104b8e80941Smrg if (chain->images[i].state == WSI_IMAGE_DISPLAYING && 1105b8e80941Smrg &chain->images[i] != active_image) 1106b8e80941Smrg { 1107b8e80941Smrg wsi_display_debug("idle %d\n", i); 1108b8e80941Smrg chain->images[i].state = WSI_IMAGE_IDLE; 1109b8e80941Smrg } 1110b8e80941Smrg} 1111b8e80941Smrg 1112b8e80941Smrgstatic VkResult 1113b8e80941Smrg_wsi_display_queue_next(struct wsi_swapchain *drv_chain); 1114b8e80941Smrg 1115b8e80941Smrgstatic void 1116b8e80941Smrgwsi_display_page_flip_handler2(int fd, 1117b8e80941Smrg unsigned int frame, 1118b8e80941Smrg unsigned int sec, 1119b8e80941Smrg unsigned int usec, 1120b8e80941Smrg uint32_t crtc_id, 1121b8e80941Smrg void *data) 1122b8e80941Smrg{ 1123b8e80941Smrg struct wsi_display_image *image = data; 1124b8e80941Smrg struct wsi_display_swapchain *chain = image->chain; 1125b8e80941Smrg 1126b8e80941Smrg wsi_display_debug("image %ld displayed at %d\n", 1127b8e80941Smrg image - &(image->chain->images[0]), frame); 1128b8e80941Smrg image->state = WSI_IMAGE_DISPLAYING; 1129b8e80941Smrg wsi_display_idle_old_displaying(image); 1130b8e80941Smrg VkResult result = _wsi_display_queue_next(&(chain->base)); 1131b8e80941Smrg if (result != VK_SUCCESS) 1132b8e80941Smrg chain->status = result; 1133b8e80941Smrg} 1134b8e80941Smrg 1135b8e80941Smrgstatic void wsi_display_fence_event_handler(struct wsi_display_fence *fence); 1136b8e80941Smrg 1137b8e80941Smrgstatic void wsi_display_page_flip_handler(int fd, 1138b8e80941Smrg unsigned int frame, 1139b8e80941Smrg unsigned int sec, 1140b8e80941Smrg unsigned int usec, 1141b8e80941Smrg void *data) 1142b8e80941Smrg{ 1143b8e80941Smrg wsi_display_page_flip_handler2(fd, frame, sec, usec, 0, data); 1144b8e80941Smrg} 1145b8e80941Smrg 1146b8e80941Smrgstatic void wsi_display_vblank_handler(int fd, unsigned int frame, 1147b8e80941Smrg unsigned int sec, unsigned int usec, 1148b8e80941Smrg void *data) 1149b8e80941Smrg{ 1150b8e80941Smrg struct wsi_display_fence *fence = data; 1151b8e80941Smrg 1152b8e80941Smrg wsi_display_fence_event_handler(fence); 1153b8e80941Smrg} 1154b8e80941Smrg 1155b8e80941Smrgstatic void wsi_display_sequence_handler(int fd, uint64_t frame, 1156b8e80941Smrg uint64_t nsec, uint64_t user_data) 1157b8e80941Smrg{ 1158b8e80941Smrg struct wsi_display_fence *fence = 1159b8e80941Smrg (struct wsi_display_fence *) (uintptr_t) user_data; 1160b8e80941Smrg 1161b8e80941Smrg wsi_display_fence_event_handler(fence); 1162b8e80941Smrg} 1163b8e80941Smrg 1164b8e80941Smrgstatic drmEventContext event_context = { 1165b8e80941Smrg .version = DRM_EVENT_CONTEXT_VERSION, 1166b8e80941Smrg .page_flip_handler = wsi_display_page_flip_handler, 1167b8e80941Smrg#if DRM_EVENT_CONTEXT_VERSION >= 3 1168b8e80941Smrg .page_flip_handler2 = wsi_display_page_flip_handler2, 1169b8e80941Smrg#endif 1170b8e80941Smrg .vblank_handler = wsi_display_vblank_handler, 1171b8e80941Smrg .sequence_handler = wsi_display_sequence_handler, 1172b8e80941Smrg}; 1173b8e80941Smrg 1174b8e80941Smrgstatic void * 1175b8e80941Smrgwsi_display_wait_thread(void *data) 1176b8e80941Smrg{ 1177b8e80941Smrg struct wsi_display *wsi = data; 1178b8e80941Smrg struct pollfd pollfd = { 1179b8e80941Smrg .fd = wsi->fd, 1180b8e80941Smrg .events = POLLIN 1181b8e80941Smrg }; 1182b8e80941Smrg 1183b8e80941Smrg pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); 1184b8e80941Smrg for (;;) { 1185b8e80941Smrg int ret = poll(&pollfd, 1, -1); 1186b8e80941Smrg if (ret > 0) { 1187b8e80941Smrg pthread_mutex_lock(&wsi->wait_mutex); 1188b8e80941Smrg (void) drmHandleEvent(wsi->fd, &event_context); 1189b8e80941Smrg pthread_mutex_unlock(&wsi->wait_mutex); 1190b8e80941Smrg pthread_cond_broadcast(&wsi->wait_cond); 1191b8e80941Smrg } 1192b8e80941Smrg } 1193b8e80941Smrg return NULL; 1194b8e80941Smrg} 1195b8e80941Smrg 1196b8e80941Smrgstatic int 1197b8e80941Smrgwsi_display_start_wait_thread(struct wsi_display *wsi) 1198b8e80941Smrg{ 1199b8e80941Smrg if (!wsi->wait_thread) { 1200b8e80941Smrg int ret = pthread_create(&wsi->wait_thread, NULL, 1201b8e80941Smrg wsi_display_wait_thread, wsi); 1202b8e80941Smrg if (ret) 1203b8e80941Smrg return ret; 1204b8e80941Smrg } 1205b8e80941Smrg return 0; 1206b8e80941Smrg} 1207b8e80941Smrg 1208b8e80941Smrg/* 1209b8e80941Smrg * Wait for at least one event from the kernel to be processed. 1210b8e80941Smrg * Call with wait_mutex held 1211b8e80941Smrg */ 1212b8e80941Smrgstatic int 1213b8e80941Smrgwsi_display_wait_for_event(struct wsi_display *wsi, 1214b8e80941Smrg uint64_t timeout_ns) 1215b8e80941Smrg{ 1216b8e80941Smrg int ret; 1217b8e80941Smrg 1218b8e80941Smrg ret = wsi_display_start_wait_thread(wsi); 1219b8e80941Smrg 1220b8e80941Smrg if (ret) 1221b8e80941Smrg return ret; 1222b8e80941Smrg 1223b8e80941Smrg struct timespec abs_timeout = { 1224b8e80941Smrg .tv_sec = timeout_ns / 1000000000ULL, 1225b8e80941Smrg .tv_nsec = timeout_ns % 1000000000ULL, 1226b8e80941Smrg }; 1227b8e80941Smrg 1228b8e80941Smrg ret = pthread_cond_timedwait(&wsi->wait_cond, &wsi->wait_mutex, 1229b8e80941Smrg &abs_timeout); 1230b8e80941Smrg 1231b8e80941Smrg wsi_display_debug("%9ld done waiting for event %d\n", pthread_self(), ret); 1232b8e80941Smrg return ret; 1233b8e80941Smrg} 1234b8e80941Smrg 1235b8e80941Smrgstatic VkResult 1236b8e80941Smrgwsi_display_acquire_next_image(struct wsi_swapchain *drv_chain, 1237b8e80941Smrg const VkAcquireNextImageInfoKHR *info, 1238b8e80941Smrg uint32_t *image_index) 1239b8e80941Smrg{ 1240b8e80941Smrg struct wsi_display_swapchain *chain = 1241b8e80941Smrg (struct wsi_display_swapchain *)drv_chain; 1242b8e80941Smrg struct wsi_display *wsi = chain->wsi; 1243b8e80941Smrg int ret = 0; 1244b8e80941Smrg VkResult result = VK_SUCCESS; 1245b8e80941Smrg 1246b8e80941Smrg /* Bail early if the swapchain is broken */ 1247b8e80941Smrg if (chain->status != VK_SUCCESS) 1248b8e80941Smrg return chain->status; 1249b8e80941Smrg 1250b8e80941Smrg uint64_t timeout = info->timeout; 1251b8e80941Smrg if (timeout != 0 && timeout != UINT64_MAX) 1252b8e80941Smrg timeout = wsi_rel_to_abs_time(timeout); 1253b8e80941Smrg 1254b8e80941Smrg pthread_mutex_lock(&wsi->wait_mutex); 1255b8e80941Smrg for (;;) { 1256b8e80941Smrg for (uint32_t i = 0; i < chain->base.image_count; i++) { 1257b8e80941Smrg if (chain->images[i].state == WSI_IMAGE_IDLE) { 1258b8e80941Smrg *image_index = i; 1259b8e80941Smrg wsi_display_debug("image %d available\n", i); 1260b8e80941Smrg chain->images[i].state = WSI_IMAGE_DRAWING; 1261b8e80941Smrg result = VK_SUCCESS; 1262b8e80941Smrg goto done; 1263b8e80941Smrg } 1264b8e80941Smrg wsi_display_debug("image %d state %d\n", i, chain->images[i].state); 1265b8e80941Smrg } 1266b8e80941Smrg 1267b8e80941Smrg if (ret == ETIMEDOUT) { 1268b8e80941Smrg result = VK_TIMEOUT; 1269b8e80941Smrg goto done; 1270b8e80941Smrg } 1271b8e80941Smrg 1272b8e80941Smrg ret = wsi_display_wait_for_event(wsi, timeout); 1273b8e80941Smrg 1274b8e80941Smrg if (ret && ret != ETIMEDOUT) { 1275b8e80941Smrg result = VK_ERROR_SURFACE_LOST_KHR; 1276b8e80941Smrg goto done; 1277b8e80941Smrg } 1278b8e80941Smrg } 1279b8e80941Smrgdone: 1280b8e80941Smrg pthread_mutex_unlock(&wsi->wait_mutex); 1281b8e80941Smrg 1282b8e80941Smrg if (result != VK_SUCCESS) 1283b8e80941Smrg return result; 1284b8e80941Smrg 1285b8e80941Smrg return chain->status; 1286b8e80941Smrg} 1287b8e80941Smrg 1288b8e80941Smrg/* 1289b8e80941Smrg * Check whether there are any other connectors driven by this crtc 1290b8e80941Smrg */ 1291b8e80941Smrgstatic bool 1292b8e80941Smrgwsi_display_crtc_solo(struct wsi_display *wsi, 1293b8e80941Smrg drmModeResPtr mode_res, 1294b8e80941Smrg drmModeConnectorPtr connector, 1295b8e80941Smrg uint32_t crtc_id) 1296b8e80941Smrg{ 1297b8e80941Smrg /* See if any other connectors share the same encoder */ 1298b8e80941Smrg for (int c = 0; c < mode_res->count_connectors; c++) { 1299b8e80941Smrg if (mode_res->connectors[c] == connector->connector_id) 1300b8e80941Smrg continue; 1301b8e80941Smrg 1302b8e80941Smrg drmModeConnectorPtr other_connector = 1303b8e80941Smrg drmModeGetConnector(wsi->fd, mode_res->connectors[c]); 1304b8e80941Smrg 1305b8e80941Smrg if (other_connector) { 1306b8e80941Smrg bool match = (other_connector->encoder_id == connector->encoder_id); 1307b8e80941Smrg drmModeFreeConnector(other_connector); 1308b8e80941Smrg if (match) 1309b8e80941Smrg return false; 1310b8e80941Smrg } 1311b8e80941Smrg } 1312b8e80941Smrg 1313b8e80941Smrg /* See if any other encoders share the same crtc */ 1314b8e80941Smrg for (int e = 0; e < mode_res->count_encoders; e++) { 1315b8e80941Smrg if (mode_res->encoders[e] == connector->encoder_id) 1316b8e80941Smrg continue; 1317b8e80941Smrg 1318b8e80941Smrg drmModeEncoderPtr other_encoder = 1319b8e80941Smrg drmModeGetEncoder(wsi->fd, mode_res->encoders[e]); 1320b8e80941Smrg 1321b8e80941Smrg if (other_encoder) { 1322b8e80941Smrg bool match = (other_encoder->crtc_id == crtc_id); 1323b8e80941Smrg drmModeFreeEncoder(other_encoder); 1324b8e80941Smrg if (match) 1325b8e80941Smrg return false; 1326b8e80941Smrg } 1327b8e80941Smrg } 1328b8e80941Smrg return true; 1329b8e80941Smrg} 1330b8e80941Smrg 1331b8e80941Smrg/* 1332b8e80941Smrg * Pick a suitable CRTC to drive this connector. Prefer a CRTC which is 1333b8e80941Smrg * currently driving this connector and not any others. Settle for a CRTC 1334b8e80941Smrg * which is currently idle. 1335b8e80941Smrg */ 1336b8e80941Smrgstatic uint32_t 1337b8e80941Smrgwsi_display_select_crtc(const struct wsi_display_connector *connector, 1338b8e80941Smrg drmModeResPtr mode_res, 1339b8e80941Smrg drmModeConnectorPtr drm_connector) 1340b8e80941Smrg{ 1341b8e80941Smrg struct wsi_display *wsi = connector->wsi; 1342b8e80941Smrg 1343b8e80941Smrg /* See what CRTC is currently driving this connector */ 1344b8e80941Smrg if (drm_connector->encoder_id) { 1345b8e80941Smrg drmModeEncoderPtr encoder = 1346b8e80941Smrg drmModeGetEncoder(wsi->fd, drm_connector->encoder_id); 1347b8e80941Smrg 1348b8e80941Smrg if (encoder) { 1349b8e80941Smrg uint32_t crtc_id = encoder->crtc_id; 1350b8e80941Smrg drmModeFreeEncoder(encoder); 1351b8e80941Smrg if (crtc_id) { 1352b8e80941Smrg if (wsi_display_crtc_solo(wsi, mode_res, drm_connector, crtc_id)) 1353b8e80941Smrg return crtc_id; 1354b8e80941Smrg } 1355b8e80941Smrg } 1356b8e80941Smrg } 1357b8e80941Smrg uint32_t crtc_id = 0; 1358b8e80941Smrg for (int c = 0; crtc_id == 0 && c < mode_res->count_crtcs; c++) { 1359b8e80941Smrg drmModeCrtcPtr crtc = drmModeGetCrtc(wsi->fd, mode_res->crtcs[c]); 1360b8e80941Smrg if (crtc && crtc->buffer_id == 0) 1361b8e80941Smrg crtc_id = crtc->crtc_id; 1362b8e80941Smrg drmModeFreeCrtc(crtc); 1363b8e80941Smrg } 1364b8e80941Smrg return crtc_id; 1365b8e80941Smrg} 1366b8e80941Smrg 1367b8e80941Smrgstatic VkResult 1368b8e80941Smrgwsi_display_setup_connector(wsi_display_connector *connector, 1369b8e80941Smrg wsi_display_mode *display_mode) 1370b8e80941Smrg{ 1371b8e80941Smrg struct wsi_display *wsi = connector->wsi; 1372b8e80941Smrg 1373b8e80941Smrg if (connector->current_mode == display_mode && connector->crtc_id) 1374b8e80941Smrg return VK_SUCCESS; 1375b8e80941Smrg 1376b8e80941Smrg VkResult result = VK_SUCCESS; 1377b8e80941Smrg 1378b8e80941Smrg drmModeResPtr mode_res = drmModeGetResources(wsi->fd); 1379b8e80941Smrg if (!mode_res) { 1380b8e80941Smrg if (errno == ENOMEM) 1381b8e80941Smrg result = VK_ERROR_OUT_OF_HOST_MEMORY; 1382b8e80941Smrg else 1383b8e80941Smrg result = VK_ERROR_SURFACE_LOST_KHR; 1384b8e80941Smrg goto bail; 1385b8e80941Smrg } 1386b8e80941Smrg 1387b8e80941Smrg drmModeConnectorPtr drm_connector = 1388b8e80941Smrg drmModeGetConnectorCurrent(wsi->fd, connector->id); 1389b8e80941Smrg 1390b8e80941Smrg if (!drm_connector) { 1391b8e80941Smrg if (errno == ENOMEM) 1392b8e80941Smrg result = VK_ERROR_OUT_OF_HOST_MEMORY; 1393b8e80941Smrg else 1394b8e80941Smrg result = VK_ERROR_SURFACE_LOST_KHR; 1395b8e80941Smrg goto bail_mode_res; 1396b8e80941Smrg } 1397b8e80941Smrg 1398b8e80941Smrg /* Pick a CRTC if we don't have one */ 1399b8e80941Smrg if (!connector->crtc_id) { 1400b8e80941Smrg connector->crtc_id = wsi_display_select_crtc(connector, 1401b8e80941Smrg mode_res, drm_connector); 1402b8e80941Smrg if (!connector->crtc_id) { 1403b8e80941Smrg result = VK_ERROR_SURFACE_LOST_KHR; 1404b8e80941Smrg goto bail_connector; 1405b8e80941Smrg } 1406b8e80941Smrg } 1407b8e80941Smrg 1408b8e80941Smrg if (connector->current_mode != display_mode) { 1409b8e80941Smrg 1410b8e80941Smrg /* Find the drm mode corresponding to the requested VkDisplayMode */ 1411b8e80941Smrg drmModeModeInfoPtr drm_mode = NULL; 1412b8e80941Smrg 1413b8e80941Smrg for (int m = 0; m < drm_connector->count_modes; m++) { 1414b8e80941Smrg drm_mode = &drm_connector->modes[m]; 1415b8e80941Smrg if (wsi_display_mode_matches_drm(display_mode, drm_mode)) 1416b8e80941Smrg break; 1417b8e80941Smrg drm_mode = NULL; 1418b8e80941Smrg } 1419b8e80941Smrg 1420b8e80941Smrg if (!drm_mode) { 1421b8e80941Smrg result = VK_ERROR_SURFACE_LOST_KHR; 1422b8e80941Smrg goto bail_connector; 1423b8e80941Smrg } 1424b8e80941Smrg 1425b8e80941Smrg connector->current_mode = display_mode; 1426b8e80941Smrg connector->current_drm_mode = *drm_mode; 1427b8e80941Smrg } 1428b8e80941Smrg 1429b8e80941Smrgbail_connector: 1430b8e80941Smrg drmModeFreeConnector(drm_connector); 1431b8e80941Smrgbail_mode_res: 1432b8e80941Smrg drmModeFreeResources(mode_res); 1433b8e80941Smrgbail: 1434b8e80941Smrg return result; 1435b8e80941Smrg 1436b8e80941Smrg} 1437b8e80941Smrg 1438b8e80941Smrgstatic VkResult 1439b8e80941Smrgwsi_display_fence_wait(struct wsi_fence *fence_wsi, uint64_t timeout) 1440b8e80941Smrg{ 1441b8e80941Smrg const struct wsi_device *wsi_device = fence_wsi->wsi_device; 1442b8e80941Smrg struct wsi_display *wsi = 1443b8e80941Smrg (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; 1444b8e80941Smrg struct wsi_display_fence *fence = (struct wsi_display_fence *) fence_wsi; 1445b8e80941Smrg 1446b8e80941Smrg wsi_display_debug("%9lu wait fence %lu %ld\n", 1447b8e80941Smrg pthread_self(), fence->sequence, 1448b8e80941Smrg (int64_t) (timeout - wsi_common_get_current_time())); 1449b8e80941Smrg wsi_display_debug_code(uint64_t start_ns = wsi_common_get_current_time()); 1450b8e80941Smrg pthread_mutex_lock(&wsi->wait_mutex); 1451b8e80941Smrg 1452b8e80941Smrg VkResult result; 1453b8e80941Smrg int ret = 0; 1454b8e80941Smrg for (;;) { 1455b8e80941Smrg if (fence->event_received) { 1456b8e80941Smrg wsi_display_debug("%9lu fence %lu passed\n", 1457b8e80941Smrg pthread_self(), fence->sequence); 1458b8e80941Smrg result = VK_SUCCESS; 1459b8e80941Smrg break; 1460b8e80941Smrg } 1461b8e80941Smrg 1462b8e80941Smrg if (ret == ETIMEDOUT) { 1463b8e80941Smrg wsi_display_debug("%9lu fence %lu timeout\n", 1464b8e80941Smrg pthread_self(), fence->sequence); 1465b8e80941Smrg result = VK_TIMEOUT; 1466b8e80941Smrg break; 1467b8e80941Smrg } 1468b8e80941Smrg 1469b8e80941Smrg ret = wsi_display_wait_for_event(wsi, timeout); 1470b8e80941Smrg 1471b8e80941Smrg if (ret && ret != ETIMEDOUT) { 1472b8e80941Smrg wsi_display_debug("%9lu fence %lu error\n", 1473b8e80941Smrg pthread_self(), fence->sequence); 1474b8e80941Smrg result = VK_ERROR_DEVICE_LOST; 1475b8e80941Smrg break; 1476b8e80941Smrg } 1477b8e80941Smrg } 1478b8e80941Smrg pthread_mutex_unlock(&wsi->wait_mutex); 1479b8e80941Smrg wsi_display_debug("%9lu fence wait %f ms\n", 1480b8e80941Smrg pthread_self(), 1481b8e80941Smrg ((int64_t) (wsi_common_get_current_time() - start_ns)) / 1482b8e80941Smrg 1.0e6); 1483b8e80941Smrg return result; 1484b8e80941Smrg} 1485b8e80941Smrg 1486b8e80941Smrgstatic void 1487b8e80941Smrgwsi_display_fence_check_free(struct wsi_display_fence *fence) 1488b8e80941Smrg{ 1489b8e80941Smrg if (fence->event_received && fence->destroyed) 1490b8e80941Smrg vk_free(fence->base.alloc, fence); 1491b8e80941Smrg} 1492b8e80941Smrg 1493b8e80941Smrgstatic void wsi_display_fence_event_handler(struct wsi_display_fence *fence) 1494b8e80941Smrg{ 1495b8e80941Smrg fence->event_received = true; 1496b8e80941Smrg wsi_display_fence_check_free(fence); 1497b8e80941Smrg} 1498b8e80941Smrg 1499b8e80941Smrgstatic void 1500b8e80941Smrgwsi_display_fence_destroy(struct wsi_fence *fence_wsi) 1501b8e80941Smrg{ 1502b8e80941Smrg struct wsi_display_fence *fence = (struct wsi_display_fence *) fence_wsi; 1503b8e80941Smrg 1504b8e80941Smrg assert(!fence->destroyed); 1505b8e80941Smrg fence->destroyed = true; 1506b8e80941Smrg wsi_display_fence_check_free(fence); 1507b8e80941Smrg} 1508b8e80941Smrg 1509b8e80941Smrgstatic struct wsi_display_fence * 1510b8e80941Smrgwsi_display_fence_alloc(VkDevice device, 1511b8e80941Smrg const struct wsi_device *wsi_device, 1512b8e80941Smrg VkDisplayKHR display, 1513b8e80941Smrg const VkAllocationCallbacks *allocator) 1514b8e80941Smrg{ 1515b8e80941Smrg struct wsi_display *wsi = 1516b8e80941Smrg (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; 1517b8e80941Smrg struct wsi_display_fence *fence = 1518b8e80941Smrg vk_zalloc2(wsi->alloc, allocator, sizeof (*fence), 1519b8e80941Smrg 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); 1520b8e80941Smrg 1521b8e80941Smrg if (!fence) 1522b8e80941Smrg return NULL; 1523b8e80941Smrg 1524b8e80941Smrg fence->base.device = device; 1525b8e80941Smrg fence->base.display = display; 1526b8e80941Smrg fence->base.wsi_device = wsi_device; 1527b8e80941Smrg fence->base.alloc = allocator ? allocator : wsi->alloc; 1528b8e80941Smrg fence->base.wait = wsi_display_fence_wait; 1529b8e80941Smrg fence->base.destroy = wsi_display_fence_destroy; 1530b8e80941Smrg fence->event_received = false; 1531b8e80941Smrg fence->destroyed = false; 1532b8e80941Smrg fence->sequence = ++fence_sequence; 1533b8e80941Smrg return fence; 1534b8e80941Smrg} 1535b8e80941Smrg 1536b8e80941Smrgstatic VkResult 1537b8e80941Smrgwsi_register_vblank_event(struct wsi_display_fence *fence, 1538b8e80941Smrg const struct wsi_device *wsi_device, 1539b8e80941Smrg VkDisplayKHR display, 1540b8e80941Smrg uint32_t flags, 1541b8e80941Smrg uint64_t frame_requested, 1542b8e80941Smrg uint64_t *frame_queued) 1543b8e80941Smrg{ 1544b8e80941Smrg struct wsi_display *wsi = 1545b8e80941Smrg (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; 1546b8e80941Smrg struct wsi_display_connector *connector = 1547b8e80941Smrg wsi_display_connector_from_handle(display); 1548b8e80941Smrg 1549b8e80941Smrg if (wsi->fd < 0) 1550b8e80941Smrg return VK_ERROR_INITIALIZATION_FAILED; 1551b8e80941Smrg 1552b8e80941Smrg for (;;) { 1553b8e80941Smrg int ret = drmCrtcQueueSequence(wsi->fd, connector->crtc_id, 1554b8e80941Smrg flags, 1555b8e80941Smrg frame_requested, 1556b8e80941Smrg frame_queued, 1557b8e80941Smrg (uintptr_t) fence); 1558b8e80941Smrg 1559b8e80941Smrg if (!ret) 1560b8e80941Smrg return VK_SUCCESS; 1561b8e80941Smrg 1562b8e80941Smrg if (errno != ENOMEM) { 1563b8e80941Smrg 1564b8e80941Smrg /* Something unexpected happened. Pause for a moment so the 1565b8e80941Smrg * application doesn't just spin and then return a failure indication 1566b8e80941Smrg */ 1567b8e80941Smrg 1568b8e80941Smrg wsi_display_debug("queue vblank event %lu failed\n", fence->sequence); 1569b8e80941Smrg struct timespec delay = { 1570b8e80941Smrg .tv_sec = 0, 1571b8e80941Smrg .tv_nsec = 100000000ull, 1572b8e80941Smrg }; 1573b8e80941Smrg nanosleep(&delay, NULL); 1574b8e80941Smrg return VK_ERROR_OUT_OF_HOST_MEMORY; 1575b8e80941Smrg } 1576b8e80941Smrg 1577b8e80941Smrg /* The kernel event queue is full. Wait for some events to be 1578b8e80941Smrg * processed and try again 1579b8e80941Smrg */ 1580b8e80941Smrg 1581b8e80941Smrg pthread_mutex_lock(&wsi->wait_mutex); 1582b8e80941Smrg ret = wsi_display_wait_for_event(wsi, wsi_rel_to_abs_time(100000000ull)); 1583b8e80941Smrg pthread_mutex_unlock(&wsi->wait_mutex); 1584b8e80941Smrg 1585b8e80941Smrg if (ret) { 1586b8e80941Smrg wsi_display_debug("vblank queue full, event wait failed\n"); 1587b8e80941Smrg return VK_ERROR_OUT_OF_HOST_MEMORY; 1588b8e80941Smrg } 1589b8e80941Smrg } 1590b8e80941Smrg} 1591b8e80941Smrg 1592b8e80941Smrg/* 1593b8e80941Smrg * Check to see if the kernel has no flip queued and if there's an image 1594b8e80941Smrg * waiting to be displayed. 1595b8e80941Smrg */ 1596b8e80941Smrgstatic VkResult 1597b8e80941Smrg_wsi_display_queue_next(struct wsi_swapchain *drv_chain) 1598b8e80941Smrg{ 1599b8e80941Smrg struct wsi_display_swapchain *chain = 1600b8e80941Smrg (struct wsi_display_swapchain *) drv_chain; 1601b8e80941Smrg struct wsi_display *wsi = chain->wsi; 1602b8e80941Smrg VkIcdSurfaceDisplay *surface = chain->surface; 1603b8e80941Smrg wsi_display_mode *display_mode = 1604b8e80941Smrg wsi_display_mode_from_handle(surface->displayMode); 1605b8e80941Smrg wsi_display_connector *connector = display_mode->connector; 1606b8e80941Smrg 1607b8e80941Smrg if (wsi->fd < 0) 1608b8e80941Smrg return VK_ERROR_SURFACE_LOST_KHR; 1609b8e80941Smrg 1610b8e80941Smrg if (display_mode != connector->current_mode) 1611b8e80941Smrg connector->active = false; 1612b8e80941Smrg 1613b8e80941Smrg for (;;) { 1614b8e80941Smrg 1615b8e80941Smrg /* Check to see if there is an image to display, or if some image is 1616b8e80941Smrg * already queued */ 1617b8e80941Smrg 1618b8e80941Smrg struct wsi_display_image *image = NULL; 1619b8e80941Smrg 1620b8e80941Smrg for (uint32_t i = 0; i < chain->base.image_count; i++) { 1621b8e80941Smrg struct wsi_display_image *tmp_image = &chain->images[i]; 1622b8e80941Smrg 1623b8e80941Smrg switch (tmp_image->state) { 1624b8e80941Smrg case WSI_IMAGE_FLIPPING: 1625b8e80941Smrg /* already flipping, don't send another to the kernel yet */ 1626b8e80941Smrg return VK_SUCCESS; 1627b8e80941Smrg case WSI_IMAGE_QUEUED: 1628b8e80941Smrg /* find the oldest queued */ 1629b8e80941Smrg if (!image || tmp_image->flip_sequence < image->flip_sequence) 1630b8e80941Smrg image = tmp_image; 1631b8e80941Smrg break; 1632b8e80941Smrg default: 1633b8e80941Smrg break; 1634b8e80941Smrg } 1635b8e80941Smrg } 1636b8e80941Smrg 1637b8e80941Smrg if (!image) 1638b8e80941Smrg return VK_SUCCESS; 1639b8e80941Smrg 1640b8e80941Smrg int ret; 1641b8e80941Smrg if (connector->active) { 1642b8e80941Smrg ret = drmModePageFlip(wsi->fd, connector->crtc_id, image->fb_id, 1643b8e80941Smrg DRM_MODE_PAGE_FLIP_EVENT, image); 1644b8e80941Smrg if (ret == 0) { 1645b8e80941Smrg image->state = WSI_IMAGE_FLIPPING; 1646b8e80941Smrg return VK_SUCCESS; 1647b8e80941Smrg } 1648b8e80941Smrg wsi_display_debug("page flip err %d %s\n", ret, strerror(-ret)); 1649b8e80941Smrg } else { 1650b8e80941Smrg ret = -EINVAL; 1651b8e80941Smrg } 1652b8e80941Smrg 1653b8e80941Smrg if (ret == -EINVAL) { 1654b8e80941Smrg VkResult result = wsi_display_setup_connector(connector, display_mode); 1655b8e80941Smrg 1656b8e80941Smrg if (result != VK_SUCCESS) { 1657b8e80941Smrg image->state = WSI_IMAGE_IDLE; 1658b8e80941Smrg return result; 1659b8e80941Smrg } 1660b8e80941Smrg 1661b8e80941Smrg /* XXX allow setting of position */ 1662b8e80941Smrg ret = drmModeSetCrtc(wsi->fd, connector->crtc_id, 1663b8e80941Smrg image->fb_id, 0, 0, 1664b8e80941Smrg &connector->id, 1, 1665b8e80941Smrg &connector->current_drm_mode); 1666b8e80941Smrg if (ret == 0) { 1667b8e80941Smrg /* Assume that the mode set is synchronous and that any 1668b8e80941Smrg * previous image is now idle. 1669b8e80941Smrg */ 1670b8e80941Smrg image->state = WSI_IMAGE_DISPLAYING; 1671b8e80941Smrg wsi_display_idle_old_displaying(image); 1672b8e80941Smrg connector->active = true; 1673b8e80941Smrg return VK_SUCCESS; 1674b8e80941Smrg } 1675b8e80941Smrg } 1676b8e80941Smrg 1677b8e80941Smrg if (ret != -EACCES) { 1678b8e80941Smrg connector->active = false; 1679b8e80941Smrg image->state = WSI_IMAGE_IDLE; 1680b8e80941Smrg return VK_ERROR_SURFACE_LOST_KHR; 1681b8e80941Smrg } 1682b8e80941Smrg 1683b8e80941Smrg /* Some other VT is currently active. Sit here waiting for 1684b8e80941Smrg * our VT to become active again by polling once a second 1685b8e80941Smrg */ 1686b8e80941Smrg usleep(1000 * 1000); 1687b8e80941Smrg connector->active = false; 1688b8e80941Smrg } 1689b8e80941Smrg} 1690b8e80941Smrg 1691b8e80941Smrgstatic VkResult 1692b8e80941Smrgwsi_display_queue_present(struct wsi_swapchain *drv_chain, 1693b8e80941Smrg uint32_t image_index, 1694b8e80941Smrg const VkPresentRegionKHR *damage) 1695b8e80941Smrg{ 1696b8e80941Smrg struct wsi_display_swapchain *chain = 1697b8e80941Smrg (struct wsi_display_swapchain *) drv_chain; 1698b8e80941Smrg struct wsi_display *wsi = chain->wsi; 1699b8e80941Smrg struct wsi_display_image *image = &chain->images[image_index]; 1700b8e80941Smrg VkResult result; 1701b8e80941Smrg 1702b8e80941Smrg /* Bail early if the swapchain is broken */ 1703b8e80941Smrg if (chain->status != VK_SUCCESS) 1704b8e80941Smrg return chain->status; 1705b8e80941Smrg 1706b8e80941Smrg assert(image->state == WSI_IMAGE_DRAWING); 1707b8e80941Smrg wsi_display_debug("present %d\n", image_index); 1708b8e80941Smrg 1709b8e80941Smrg pthread_mutex_lock(&wsi->wait_mutex); 1710b8e80941Smrg 1711b8e80941Smrg image->flip_sequence = ++chain->flip_sequence; 1712b8e80941Smrg image->state = WSI_IMAGE_QUEUED; 1713b8e80941Smrg 1714b8e80941Smrg result = _wsi_display_queue_next(drv_chain); 1715b8e80941Smrg if (result != VK_SUCCESS) 1716b8e80941Smrg chain->status = result; 1717b8e80941Smrg 1718b8e80941Smrg pthread_mutex_unlock(&wsi->wait_mutex); 1719b8e80941Smrg 1720b8e80941Smrg if (result != VK_SUCCESS) 1721b8e80941Smrg return result; 1722b8e80941Smrg 1723b8e80941Smrg return chain->status; 1724b8e80941Smrg} 1725b8e80941Smrg 1726b8e80941Smrgstatic VkResult 1727b8e80941Smrgwsi_display_surface_create_swapchain( 1728b8e80941Smrg VkIcdSurfaceBase *icd_surface, 1729b8e80941Smrg VkDevice device, 1730b8e80941Smrg struct wsi_device *wsi_device, 1731b8e80941Smrg const VkSwapchainCreateInfoKHR *create_info, 1732b8e80941Smrg const VkAllocationCallbacks *allocator, 1733b8e80941Smrg struct wsi_swapchain **swapchain_out) 1734b8e80941Smrg{ 1735b8e80941Smrg struct wsi_display *wsi = 1736b8e80941Smrg (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; 1737b8e80941Smrg 1738b8e80941Smrg assert(create_info->sType == VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR); 1739b8e80941Smrg 1740b8e80941Smrg const unsigned num_images = create_info->minImageCount; 1741b8e80941Smrg struct wsi_display_swapchain *chain = 1742b8e80941Smrg vk_zalloc(allocator, 1743b8e80941Smrg sizeof(*chain) + num_images * sizeof(chain->images[0]), 1744b8e80941Smrg 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); 1745b8e80941Smrg 1746b8e80941Smrg if (chain == NULL) 1747b8e80941Smrg return VK_ERROR_OUT_OF_HOST_MEMORY; 1748b8e80941Smrg 1749b8e80941Smrg VkResult result = wsi_swapchain_init(wsi_device, &chain->base, device, 1750b8e80941Smrg create_info, allocator); 1751b8e80941Smrg if (result != VK_SUCCESS) { 1752b8e80941Smrg vk_free(allocator, chain); 1753b8e80941Smrg return result; 1754b8e80941Smrg } 1755b8e80941Smrg 1756b8e80941Smrg chain->base.destroy = wsi_display_swapchain_destroy; 1757b8e80941Smrg chain->base.get_wsi_image = wsi_display_get_wsi_image; 1758b8e80941Smrg chain->base.acquire_next_image = wsi_display_acquire_next_image; 1759b8e80941Smrg chain->base.queue_present = wsi_display_queue_present; 1760b8e80941Smrg chain->base.present_mode = wsi_swapchain_get_present_mode(wsi_device, create_info); 1761b8e80941Smrg chain->base.image_count = num_images; 1762b8e80941Smrg 1763b8e80941Smrg chain->wsi = wsi; 1764b8e80941Smrg chain->status = VK_SUCCESS; 1765b8e80941Smrg 1766b8e80941Smrg chain->surface = (VkIcdSurfaceDisplay *) icd_surface; 1767b8e80941Smrg 1768b8e80941Smrg for (uint32_t image = 0; image < chain->base.image_count; image++) { 1769b8e80941Smrg result = wsi_display_image_init(device, &chain->base, 1770b8e80941Smrg create_info, allocator, 1771b8e80941Smrg &chain->images[image]); 1772b8e80941Smrg if (result != VK_SUCCESS) { 1773b8e80941Smrg while (image > 0) { 1774b8e80941Smrg --image; 1775b8e80941Smrg wsi_display_image_finish(&chain->base, allocator, 1776b8e80941Smrg &chain->images[image]); 1777b8e80941Smrg } 1778b8e80941Smrg vk_free(allocator, chain); 1779b8e80941Smrg goto fail_init_images; 1780b8e80941Smrg } 1781b8e80941Smrg } 1782b8e80941Smrg 1783b8e80941Smrg *swapchain_out = &chain->base; 1784b8e80941Smrg 1785b8e80941Smrg return VK_SUCCESS; 1786b8e80941Smrg 1787b8e80941Smrgfail_init_images: 1788b8e80941Smrg return result; 1789b8e80941Smrg} 1790b8e80941Smrg 1791b8e80941Smrgstatic bool 1792b8e80941Smrgwsi_init_pthread_cond_monotonic(pthread_cond_t *cond) 1793b8e80941Smrg{ 1794b8e80941Smrg pthread_condattr_t condattr; 1795b8e80941Smrg bool ret = false; 1796b8e80941Smrg 1797b8e80941Smrg if (pthread_condattr_init(&condattr) != 0) 1798b8e80941Smrg goto fail_attr_init; 1799b8e80941Smrg 1800b8e80941Smrg if (pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC) != 0) 1801b8e80941Smrg goto fail_attr_set; 1802b8e80941Smrg 1803b8e80941Smrg if (pthread_cond_init(cond, &condattr) != 0) 1804b8e80941Smrg goto fail_cond_init; 1805b8e80941Smrg 1806b8e80941Smrg ret = true; 1807b8e80941Smrg 1808b8e80941Smrgfail_cond_init: 1809b8e80941Smrgfail_attr_set: 1810b8e80941Smrg pthread_condattr_destroy(&condattr); 1811b8e80941Smrgfail_attr_init: 1812b8e80941Smrg return ret; 1813b8e80941Smrg} 1814b8e80941Smrg 1815b8e80941Smrg 1816b8e80941Smrg/* 1817b8e80941Smrg * Local version fo the libdrm helper. Added to avoid depending on bleeding 1818b8e80941Smrg * edge version of the library. 1819b8e80941Smrg */ 1820b8e80941Smrgstatic int 1821b8e80941Smrglocal_drmIsMaster(int fd) 1822b8e80941Smrg{ 1823b8e80941Smrg /* Detect master by attempting something that requires master. 1824b8e80941Smrg * 1825b8e80941Smrg * Authenticating magic tokens requires master and 0 is an 1826b8e80941Smrg * internal kernel detail which we could use. Attempting this on 1827b8e80941Smrg * a master fd would fail therefore fail with EINVAL because 0 1828b8e80941Smrg * is invalid. 1829b8e80941Smrg * 1830b8e80941Smrg * A non-master fd will fail with EACCES, as the kernel checks 1831b8e80941Smrg * for master before attempting to do anything else. 1832b8e80941Smrg * 1833b8e80941Smrg * Since we don't want to leak implementation details, use 1834b8e80941Smrg * EACCES. 1835b8e80941Smrg */ 1836b8e80941Smrg return drmAuthMagic(fd, 0) != -EACCES; 1837b8e80941Smrg} 1838b8e80941Smrg 1839b8e80941SmrgVkResult 1840b8e80941Smrgwsi_display_init_wsi(struct wsi_device *wsi_device, 1841b8e80941Smrg const VkAllocationCallbacks *alloc, 1842b8e80941Smrg int display_fd) 1843b8e80941Smrg{ 1844b8e80941Smrg struct wsi_display *wsi = vk_zalloc(alloc, sizeof(*wsi), 8, 1845b8e80941Smrg VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); 1846b8e80941Smrg VkResult result; 1847b8e80941Smrg 1848b8e80941Smrg if (!wsi) { 1849b8e80941Smrg result = VK_ERROR_OUT_OF_HOST_MEMORY; 1850b8e80941Smrg goto fail; 1851b8e80941Smrg } 1852b8e80941Smrg 1853b8e80941Smrg wsi->fd = display_fd; 1854b8e80941Smrg if (wsi->fd != -1 && !local_drmIsMaster(wsi->fd)) 1855b8e80941Smrg wsi->fd = -1; 1856b8e80941Smrg 1857b8e80941Smrg wsi->alloc = alloc; 1858b8e80941Smrg 1859b8e80941Smrg list_inithead(&wsi->connectors); 1860b8e80941Smrg 1861b8e80941Smrg int ret = pthread_mutex_init(&wsi->wait_mutex, NULL); 1862b8e80941Smrg if (ret) { 1863b8e80941Smrg result = VK_ERROR_OUT_OF_HOST_MEMORY; 1864b8e80941Smrg goto fail_mutex; 1865b8e80941Smrg } 1866b8e80941Smrg 1867b8e80941Smrg if (!wsi_init_pthread_cond_monotonic(&wsi->wait_cond)) { 1868b8e80941Smrg result = VK_ERROR_OUT_OF_HOST_MEMORY; 1869b8e80941Smrg goto fail_cond; 1870b8e80941Smrg } 1871b8e80941Smrg 1872b8e80941Smrg wsi->base.get_support = wsi_display_surface_get_support; 1873b8e80941Smrg wsi->base.get_capabilities2 = wsi_display_surface_get_capabilities2; 1874b8e80941Smrg wsi->base.get_formats = wsi_display_surface_get_formats; 1875b8e80941Smrg wsi->base.get_formats2 = wsi_display_surface_get_formats2; 1876b8e80941Smrg wsi->base.get_present_modes = wsi_display_surface_get_present_modes; 1877b8e80941Smrg wsi->base.get_present_rectangles = wsi_display_surface_get_present_rectangles; 1878b8e80941Smrg wsi->base.create_swapchain = wsi_display_surface_create_swapchain; 1879b8e80941Smrg 1880b8e80941Smrg wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY] = &wsi->base; 1881b8e80941Smrg 1882b8e80941Smrg return VK_SUCCESS; 1883b8e80941Smrg 1884b8e80941Smrgfail_cond: 1885b8e80941Smrg pthread_mutex_destroy(&wsi->wait_mutex); 1886b8e80941Smrgfail_mutex: 1887b8e80941Smrg vk_free(alloc, wsi); 1888b8e80941Smrgfail: 1889b8e80941Smrg return result; 1890b8e80941Smrg} 1891b8e80941Smrg 1892b8e80941Smrgvoid 1893b8e80941Smrgwsi_display_finish_wsi(struct wsi_device *wsi_device, 1894b8e80941Smrg const VkAllocationCallbacks *alloc) 1895b8e80941Smrg{ 1896b8e80941Smrg struct wsi_display *wsi = 1897b8e80941Smrg (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; 1898b8e80941Smrg 1899b8e80941Smrg if (wsi) { 1900b8e80941Smrg wsi_for_each_connector(connector, wsi) { 1901b8e80941Smrg wsi_for_each_display_mode(mode, connector) { 1902b8e80941Smrg vk_free(wsi->alloc, mode); 1903b8e80941Smrg } 1904b8e80941Smrg vk_free(wsi->alloc, connector); 1905b8e80941Smrg } 1906b8e80941Smrg 1907b8e80941Smrg pthread_mutex_lock(&wsi->wait_mutex); 1908b8e80941Smrg if (wsi->wait_thread) { 1909b8e80941Smrg pthread_cancel(wsi->wait_thread); 1910b8e80941Smrg pthread_join(wsi->wait_thread, NULL); 1911b8e80941Smrg } 1912b8e80941Smrg pthread_mutex_unlock(&wsi->wait_mutex); 1913b8e80941Smrg pthread_mutex_destroy(&wsi->wait_mutex); 1914b8e80941Smrg pthread_cond_destroy(&wsi->wait_cond); 1915b8e80941Smrg 1916b8e80941Smrg vk_free(alloc, wsi); 1917b8e80941Smrg } 1918b8e80941Smrg} 1919b8e80941Smrg 1920b8e80941Smrg/* 1921b8e80941Smrg * Implement vkReleaseDisplay 1922b8e80941Smrg */ 1923b8e80941SmrgVkResult 1924b8e80941Smrgwsi_release_display(VkPhysicalDevice physical_device, 1925b8e80941Smrg struct wsi_device *wsi_device, 1926b8e80941Smrg VkDisplayKHR display) 1927b8e80941Smrg{ 1928b8e80941Smrg struct wsi_display *wsi = 1929b8e80941Smrg (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; 1930b8e80941Smrg 1931b8e80941Smrg if (wsi->fd >= 0) { 1932b8e80941Smrg close(wsi->fd); 1933b8e80941Smrg wsi->fd = -1; 1934b8e80941Smrg } 1935b8e80941Smrg#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT 1936b8e80941Smrg wsi_display_connector_from_handle(display)->output = None; 1937b8e80941Smrg#endif 1938b8e80941Smrg 1939b8e80941Smrg return VK_SUCCESS; 1940b8e80941Smrg} 1941b8e80941Smrg 1942b8e80941Smrg#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT 1943b8e80941Smrg 1944b8e80941Smrgstatic struct wsi_display_connector * 1945b8e80941Smrgwsi_display_find_output(struct wsi_device *wsi_device, 1946b8e80941Smrg xcb_randr_output_t output) 1947b8e80941Smrg{ 1948b8e80941Smrg struct wsi_display *wsi = 1949b8e80941Smrg (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; 1950b8e80941Smrg 1951b8e80941Smrg wsi_for_each_connector(connector, wsi) { 1952b8e80941Smrg if (connector->output == output) 1953b8e80941Smrg return connector; 1954b8e80941Smrg } 1955b8e80941Smrg 1956b8e80941Smrg return NULL; 1957b8e80941Smrg} 1958b8e80941Smrg 1959b8e80941Smrg/* 1960b8e80941Smrg * Given a RandR output, find the associated kernel connector_id by 1961b8e80941Smrg * looking at the CONNECTOR_ID property provided by the X server 1962b8e80941Smrg */ 1963b8e80941Smrg 1964b8e80941Smrgstatic uint32_t 1965b8e80941Smrgwsi_display_output_to_connector_id(xcb_connection_t *connection, 1966b8e80941Smrg xcb_atom_t *connector_id_atom_p, 1967b8e80941Smrg xcb_randr_output_t output) 1968b8e80941Smrg{ 1969b8e80941Smrg uint32_t connector_id = 0; 1970b8e80941Smrg xcb_atom_t connector_id_atom = *connector_id_atom_p; 1971b8e80941Smrg 1972b8e80941Smrg if (connector_id_atom == 0) { 1973b8e80941Smrg /* Go dig out the CONNECTOR_ID property */ 1974b8e80941Smrg xcb_intern_atom_cookie_t ia_c = xcb_intern_atom(connection, 1975b8e80941Smrg true, 1976b8e80941Smrg 12, 1977b8e80941Smrg "CONNECTOR_ID"); 1978b8e80941Smrg xcb_intern_atom_reply_t *ia_r = xcb_intern_atom_reply(connection, 1979b8e80941Smrg ia_c, 1980b8e80941Smrg NULL); 1981b8e80941Smrg if (ia_r) { 1982b8e80941Smrg *connector_id_atom_p = connector_id_atom = ia_r->atom; 1983b8e80941Smrg free(ia_r); 1984b8e80941Smrg } 1985b8e80941Smrg } 1986b8e80941Smrg 1987b8e80941Smrg /* If there's an CONNECTOR_ID atom in the server, then there may be a 1988b8e80941Smrg * CONNECTOR_ID property. Otherwise, there will not be and we don't even 1989b8e80941Smrg * need to bother. 1990b8e80941Smrg */ 1991b8e80941Smrg if (connector_id_atom) { 1992b8e80941Smrg 1993b8e80941Smrg xcb_randr_query_version_cookie_t qv_c = 1994b8e80941Smrg xcb_randr_query_version(connection, 1, 6); 1995b8e80941Smrg xcb_randr_get_output_property_cookie_t gop_c = 1996b8e80941Smrg xcb_randr_get_output_property(connection, 1997b8e80941Smrg output, 1998b8e80941Smrg connector_id_atom, 1999b8e80941Smrg 0, 2000b8e80941Smrg 0, 2001b8e80941Smrg 0xffffffffUL, 2002b8e80941Smrg 0, 2003b8e80941Smrg 0); 2004b8e80941Smrg xcb_randr_query_version_reply_t *qv_r = 2005b8e80941Smrg xcb_randr_query_version_reply(connection, qv_c, NULL); 2006b8e80941Smrg free(qv_r); 2007b8e80941Smrg xcb_randr_get_output_property_reply_t *gop_r = 2008b8e80941Smrg xcb_randr_get_output_property_reply(connection, gop_c, NULL); 2009b8e80941Smrg if (gop_r) { 2010b8e80941Smrg if (gop_r->num_items == 1 && gop_r->format == 32) 2011b8e80941Smrg memcpy(&connector_id, xcb_randr_get_output_property_data(gop_r), 4); 2012b8e80941Smrg free(gop_r); 2013b8e80941Smrg } 2014b8e80941Smrg } 2015b8e80941Smrg return connector_id; 2016b8e80941Smrg} 2017b8e80941Smrg 2018b8e80941Smrgstatic bool 2019b8e80941Smrgwsi_display_check_randr_version(xcb_connection_t *connection) 2020b8e80941Smrg{ 2021b8e80941Smrg xcb_randr_query_version_cookie_t qv_c = 2022b8e80941Smrg xcb_randr_query_version(connection, 1, 6); 2023b8e80941Smrg xcb_randr_query_version_reply_t *qv_r = 2024b8e80941Smrg xcb_randr_query_version_reply(connection, qv_c, NULL); 2025b8e80941Smrg bool ret = false; 2026b8e80941Smrg 2027b8e80941Smrg if (!qv_r) 2028b8e80941Smrg return false; 2029b8e80941Smrg 2030b8e80941Smrg /* Check for version 1.6 or newer */ 2031b8e80941Smrg ret = (qv_r->major_version > 1 || 2032b8e80941Smrg (qv_r->major_version == 1 && qv_r->minor_version >= 6)); 2033b8e80941Smrg 2034b8e80941Smrg free(qv_r); 2035b8e80941Smrg return ret; 2036b8e80941Smrg} 2037b8e80941Smrg 2038b8e80941Smrg/* 2039b8e80941Smrg * Given a kernel connector id, find the associated RandR output using the 2040b8e80941Smrg * CONNECTOR_ID property 2041b8e80941Smrg */ 2042b8e80941Smrg 2043b8e80941Smrgstatic xcb_randr_output_t 2044b8e80941Smrgwsi_display_connector_id_to_output(xcb_connection_t *connection, 2045b8e80941Smrg uint32_t connector_id) 2046b8e80941Smrg{ 2047b8e80941Smrg if (!wsi_display_check_randr_version(connection)) 2048b8e80941Smrg return 0; 2049b8e80941Smrg 2050b8e80941Smrg const xcb_setup_t *setup = xcb_get_setup(connection); 2051b8e80941Smrg 2052b8e80941Smrg xcb_atom_t connector_id_atom = 0; 2053b8e80941Smrg xcb_randr_output_t output = 0; 2054b8e80941Smrg 2055b8e80941Smrg /* Search all of the screens for the provided output */ 2056b8e80941Smrg xcb_screen_iterator_t iter; 2057b8e80941Smrg for (iter = xcb_setup_roots_iterator(setup); 2058b8e80941Smrg output == 0 && iter.rem; 2059b8e80941Smrg xcb_screen_next(&iter)) 2060b8e80941Smrg { 2061b8e80941Smrg xcb_randr_get_screen_resources_cookie_t gsr_c = 2062b8e80941Smrg xcb_randr_get_screen_resources(connection, iter.data->root); 2063b8e80941Smrg xcb_randr_get_screen_resources_reply_t *gsr_r = 2064b8e80941Smrg xcb_randr_get_screen_resources_reply(connection, gsr_c, NULL); 2065b8e80941Smrg 2066b8e80941Smrg if (!gsr_r) 2067b8e80941Smrg return 0; 2068b8e80941Smrg 2069b8e80941Smrg xcb_randr_output_t *ro = xcb_randr_get_screen_resources_outputs(gsr_r); 2070b8e80941Smrg int o; 2071b8e80941Smrg 2072b8e80941Smrg for (o = 0; o < gsr_r->num_outputs; o++) { 2073b8e80941Smrg if (wsi_display_output_to_connector_id(connection, 2074b8e80941Smrg &connector_id_atom, ro[o]) 2075b8e80941Smrg == connector_id) 2076b8e80941Smrg { 2077b8e80941Smrg output = ro[o]; 2078b8e80941Smrg break; 2079b8e80941Smrg } 2080b8e80941Smrg } 2081b8e80941Smrg free(gsr_r); 2082b8e80941Smrg } 2083b8e80941Smrg return output; 2084b8e80941Smrg} 2085b8e80941Smrg 2086b8e80941Smrg/* 2087b8e80941Smrg * Given a RandR output, find out which screen it's associated with 2088b8e80941Smrg */ 2089b8e80941Smrgstatic xcb_window_t 2090b8e80941Smrgwsi_display_output_to_root(xcb_connection_t *connection, 2091b8e80941Smrg xcb_randr_output_t output) 2092b8e80941Smrg{ 2093b8e80941Smrg if (!wsi_display_check_randr_version(connection)) 2094b8e80941Smrg return 0; 2095b8e80941Smrg 2096b8e80941Smrg const xcb_setup_t *setup = xcb_get_setup(connection); 2097b8e80941Smrg xcb_window_t root = 0; 2098b8e80941Smrg 2099b8e80941Smrg /* Search all of the screens for the provided output */ 2100b8e80941Smrg for (xcb_screen_iterator_t iter = xcb_setup_roots_iterator(setup); 2101b8e80941Smrg root == 0 && iter.rem; 2102b8e80941Smrg xcb_screen_next(&iter)) 2103b8e80941Smrg { 2104b8e80941Smrg xcb_randr_get_screen_resources_cookie_t gsr_c = 2105b8e80941Smrg xcb_randr_get_screen_resources(connection, iter.data->root); 2106b8e80941Smrg xcb_randr_get_screen_resources_reply_t *gsr_r = 2107b8e80941Smrg xcb_randr_get_screen_resources_reply(connection, gsr_c, NULL); 2108b8e80941Smrg 2109b8e80941Smrg if (!gsr_r) 2110b8e80941Smrg return 0; 2111b8e80941Smrg 2112b8e80941Smrg xcb_randr_output_t *ro = xcb_randr_get_screen_resources_outputs(gsr_r); 2113b8e80941Smrg 2114b8e80941Smrg for (int o = 0; o < gsr_r->num_outputs; o++) { 2115b8e80941Smrg if (ro[o] == output) { 2116b8e80941Smrg root = iter.data->root; 2117b8e80941Smrg break; 2118b8e80941Smrg } 2119b8e80941Smrg } 2120b8e80941Smrg free(gsr_r); 2121b8e80941Smrg } 2122b8e80941Smrg return root; 2123b8e80941Smrg} 2124b8e80941Smrg 2125b8e80941Smrgstatic bool 2126b8e80941Smrgwsi_display_mode_matches_x(struct wsi_display_mode *wsi, 2127b8e80941Smrg xcb_randr_mode_info_t *xcb) 2128b8e80941Smrg{ 2129b8e80941Smrg return wsi->clock == (xcb->dot_clock + 500) / 1000 && 2130b8e80941Smrg wsi->hdisplay == xcb->width && 2131b8e80941Smrg wsi->hsync_start == xcb->hsync_start && 2132b8e80941Smrg wsi->hsync_end == xcb->hsync_end && 2133b8e80941Smrg wsi->htotal == xcb->htotal && 2134b8e80941Smrg wsi->hskew == xcb->hskew && 2135b8e80941Smrg wsi->vdisplay == xcb->height && 2136b8e80941Smrg wsi->vsync_start == xcb->vsync_start && 2137b8e80941Smrg wsi->vsync_end == xcb->vsync_end && 2138b8e80941Smrg wsi->vtotal == xcb->vtotal && 2139b8e80941Smrg wsi->vscan <= 1 && 2140b8e80941Smrg wsi->flags == xcb->mode_flags; 2141b8e80941Smrg} 2142b8e80941Smrg 2143b8e80941Smrgstatic struct wsi_display_mode * 2144b8e80941Smrgwsi_display_find_x_mode(struct wsi_device *wsi_device, 2145b8e80941Smrg struct wsi_display_connector *connector, 2146b8e80941Smrg xcb_randr_mode_info_t *mode) 2147b8e80941Smrg{ 2148b8e80941Smrg wsi_for_each_display_mode(display_mode, connector) { 2149b8e80941Smrg if (wsi_display_mode_matches_x(display_mode, mode)) 2150b8e80941Smrg return display_mode; 2151b8e80941Smrg } 2152b8e80941Smrg return NULL; 2153b8e80941Smrg} 2154b8e80941Smrg 2155b8e80941Smrgstatic VkResult 2156b8e80941Smrgwsi_display_register_x_mode(struct wsi_device *wsi_device, 2157b8e80941Smrg struct wsi_display_connector *connector, 2158b8e80941Smrg xcb_randr_mode_info_t *x_mode, 2159b8e80941Smrg bool preferred) 2160b8e80941Smrg{ 2161b8e80941Smrg struct wsi_display *wsi = 2162b8e80941Smrg (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; 2163b8e80941Smrg struct wsi_display_mode *display_mode = 2164b8e80941Smrg wsi_display_find_x_mode(wsi_device, connector, x_mode); 2165b8e80941Smrg 2166b8e80941Smrg if (display_mode) { 2167b8e80941Smrg display_mode->valid = true; 2168b8e80941Smrg return VK_SUCCESS; 2169b8e80941Smrg } 2170b8e80941Smrg 2171b8e80941Smrg display_mode = vk_zalloc(wsi->alloc, sizeof (struct wsi_display_mode), 2172b8e80941Smrg 8, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); 2173b8e80941Smrg if (!display_mode) 2174b8e80941Smrg return VK_ERROR_OUT_OF_HOST_MEMORY; 2175b8e80941Smrg 2176b8e80941Smrg display_mode->connector = connector; 2177b8e80941Smrg display_mode->valid = true; 2178b8e80941Smrg display_mode->preferred = preferred; 2179b8e80941Smrg display_mode->clock = (x_mode->dot_clock + 500) / 1000; /* kHz */ 2180b8e80941Smrg display_mode->hdisplay = x_mode->width; 2181b8e80941Smrg display_mode->hsync_start = x_mode->hsync_start; 2182b8e80941Smrg display_mode->hsync_end = x_mode->hsync_end; 2183b8e80941Smrg display_mode->htotal = x_mode->htotal; 2184b8e80941Smrg display_mode->hskew = x_mode->hskew; 2185b8e80941Smrg display_mode->vdisplay = x_mode->height; 2186b8e80941Smrg display_mode->vsync_start = x_mode->vsync_start; 2187b8e80941Smrg display_mode->vsync_end = x_mode->vsync_end; 2188b8e80941Smrg display_mode->vtotal = x_mode->vtotal; 2189b8e80941Smrg display_mode->vscan = 0; 2190b8e80941Smrg display_mode->flags = x_mode->mode_flags; 2191b8e80941Smrg 2192b8e80941Smrg list_addtail(&display_mode->list, &connector->display_modes); 2193b8e80941Smrg return VK_SUCCESS; 2194b8e80941Smrg} 2195b8e80941Smrg 2196b8e80941Smrgstatic struct wsi_display_connector * 2197b8e80941Smrgwsi_display_get_output(struct wsi_device *wsi_device, 2198b8e80941Smrg xcb_connection_t *connection, 2199b8e80941Smrg xcb_randr_output_t output) 2200b8e80941Smrg{ 2201b8e80941Smrg struct wsi_display *wsi = 2202b8e80941Smrg (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; 2203b8e80941Smrg struct wsi_display_connector *connector; 2204b8e80941Smrg uint32_t connector_id; 2205b8e80941Smrg 2206b8e80941Smrg xcb_window_t root = wsi_display_output_to_root(connection, output); 2207b8e80941Smrg if (!root) 2208b8e80941Smrg return NULL; 2209b8e80941Smrg 2210b8e80941Smrg /* See if we already have a connector for this output */ 2211b8e80941Smrg connector = wsi_display_find_output(wsi_device, output); 2212b8e80941Smrg 2213b8e80941Smrg if (!connector) { 2214b8e80941Smrg xcb_atom_t connector_id_atom = 0; 2215b8e80941Smrg 2216b8e80941Smrg /* 2217b8e80941Smrg * Go get the kernel connector ID for this X output 2218b8e80941Smrg */ 2219b8e80941Smrg connector_id = wsi_display_output_to_connector_id(connection, 2220b8e80941Smrg &connector_id_atom, 2221b8e80941Smrg output); 2222b8e80941Smrg 2223b8e80941Smrg /* Any X server with lease support will have this atom */ 2224b8e80941Smrg if (!connector_id) { 2225b8e80941Smrg return NULL; 2226b8e80941Smrg } 2227b8e80941Smrg 2228b8e80941Smrg /* See if we already have a connector for this id */ 2229b8e80941Smrg connector = wsi_display_find_connector(wsi_device, connector_id); 2230b8e80941Smrg 2231b8e80941Smrg if (connector == NULL) { 2232b8e80941Smrg connector = wsi_display_alloc_connector(wsi, connector_id); 2233b8e80941Smrg if (!connector) { 2234b8e80941Smrg return NULL; 2235b8e80941Smrg } 2236b8e80941Smrg list_addtail(&connector->list, &wsi->connectors); 2237b8e80941Smrg } 2238b8e80941Smrg connector->output = output; 2239b8e80941Smrg } 2240b8e80941Smrg 2241b8e80941Smrg xcb_randr_get_screen_resources_cookie_t src = 2242b8e80941Smrg xcb_randr_get_screen_resources(connection, root); 2243b8e80941Smrg xcb_randr_get_output_info_cookie_t oic = 2244b8e80941Smrg xcb_randr_get_output_info(connection, output, XCB_CURRENT_TIME); 2245b8e80941Smrg xcb_randr_get_screen_resources_reply_t *srr = 2246b8e80941Smrg xcb_randr_get_screen_resources_reply(connection, src, NULL); 2247b8e80941Smrg xcb_randr_get_output_info_reply_t *oir = 2248b8e80941Smrg xcb_randr_get_output_info_reply(connection, oic, NULL); 2249b8e80941Smrg 2250b8e80941Smrg if (oir && srr) { 2251b8e80941Smrg /* Get X modes and add them */ 2252b8e80941Smrg 2253b8e80941Smrg connector->connected = 2254b8e80941Smrg oir->connection != XCB_RANDR_CONNECTION_DISCONNECTED; 2255b8e80941Smrg 2256b8e80941Smrg wsi_display_invalidate_connector_modes(wsi_device, connector); 2257b8e80941Smrg 2258b8e80941Smrg xcb_randr_mode_t *x_modes = xcb_randr_get_output_info_modes(oir); 2259b8e80941Smrg for (int m = 0; m < oir->num_modes; m++) { 2260b8e80941Smrg xcb_randr_mode_info_iterator_t i = 2261b8e80941Smrg xcb_randr_get_screen_resources_modes_iterator(srr); 2262b8e80941Smrg while (i.rem) { 2263b8e80941Smrg xcb_randr_mode_info_t *mi = i.data; 2264b8e80941Smrg if (mi->id == x_modes[m]) { 2265b8e80941Smrg VkResult result = wsi_display_register_x_mode( 2266b8e80941Smrg wsi_device, connector, mi, m < oir->num_preferred); 2267b8e80941Smrg if (result != VK_SUCCESS) { 2268b8e80941Smrg free(oir); 2269b8e80941Smrg free(srr); 2270b8e80941Smrg return NULL; 2271b8e80941Smrg } 2272b8e80941Smrg break; 2273b8e80941Smrg } 2274b8e80941Smrg xcb_randr_mode_info_next(&i); 2275b8e80941Smrg } 2276b8e80941Smrg } 2277b8e80941Smrg } 2278b8e80941Smrg 2279b8e80941Smrg free(oir); 2280b8e80941Smrg free(srr); 2281b8e80941Smrg return connector; 2282b8e80941Smrg} 2283b8e80941Smrg 2284b8e80941Smrgstatic xcb_randr_crtc_t 2285b8e80941Smrgwsi_display_find_crtc_for_output(xcb_connection_t *connection, 2286b8e80941Smrg xcb_window_t root, 2287b8e80941Smrg xcb_randr_output_t output) 2288b8e80941Smrg{ 2289b8e80941Smrg xcb_randr_get_screen_resources_cookie_t gsr_c = 2290b8e80941Smrg xcb_randr_get_screen_resources(connection, root); 2291b8e80941Smrg xcb_randr_get_screen_resources_reply_t *gsr_r = 2292b8e80941Smrg xcb_randr_get_screen_resources_reply(connection, gsr_c, NULL); 2293b8e80941Smrg 2294b8e80941Smrg if (!gsr_r) 2295b8e80941Smrg return 0; 2296b8e80941Smrg 2297b8e80941Smrg xcb_randr_crtc_t *rc = xcb_randr_get_screen_resources_crtcs(gsr_r); 2298b8e80941Smrg xcb_randr_crtc_t idle_crtc = 0; 2299b8e80941Smrg xcb_randr_crtc_t active_crtc = 0; 2300b8e80941Smrg 2301b8e80941Smrg /* Find either a crtc already connected to the desired output or idle */ 2302b8e80941Smrg for (int c = 0; active_crtc == 0 && c < gsr_r->num_crtcs; c++) { 2303b8e80941Smrg xcb_randr_get_crtc_info_cookie_t gci_c = 2304b8e80941Smrg xcb_randr_get_crtc_info(connection, rc[c], gsr_r->config_timestamp); 2305b8e80941Smrg xcb_randr_get_crtc_info_reply_t *gci_r = 2306b8e80941Smrg xcb_randr_get_crtc_info_reply(connection, gci_c, NULL); 2307b8e80941Smrg 2308b8e80941Smrg if (gci_r) { 2309b8e80941Smrg if (gci_r->mode) { 2310b8e80941Smrg int num_outputs = xcb_randr_get_crtc_info_outputs_length(gci_r); 2311b8e80941Smrg xcb_randr_output_t *outputs = 2312b8e80941Smrg xcb_randr_get_crtc_info_outputs(gci_r); 2313b8e80941Smrg 2314b8e80941Smrg if (num_outputs == 1 && outputs[0] == output) 2315b8e80941Smrg active_crtc = rc[c]; 2316b8e80941Smrg 2317b8e80941Smrg } else if (idle_crtc == 0) { 2318b8e80941Smrg int num_possible = xcb_randr_get_crtc_info_possible_length(gci_r); 2319b8e80941Smrg xcb_randr_output_t *possible = 2320b8e80941Smrg xcb_randr_get_crtc_info_possible(gci_r); 2321b8e80941Smrg 2322b8e80941Smrg for (int p = 0; p < num_possible; p++) 2323b8e80941Smrg if (possible[p] == output) { 2324b8e80941Smrg idle_crtc = rc[c]; 2325b8e80941Smrg break; 2326b8e80941Smrg } 2327b8e80941Smrg } 2328b8e80941Smrg free(gci_r); 2329b8e80941Smrg } 2330b8e80941Smrg } 2331b8e80941Smrg free(gsr_r); 2332b8e80941Smrg 2333b8e80941Smrg if (active_crtc) 2334b8e80941Smrg return active_crtc; 2335b8e80941Smrg return idle_crtc; 2336b8e80941Smrg} 2337b8e80941Smrg 2338b8e80941SmrgVkResult 2339b8e80941Smrgwsi_acquire_xlib_display(VkPhysicalDevice physical_device, 2340b8e80941Smrg struct wsi_device *wsi_device, 2341b8e80941Smrg Display *dpy, 2342b8e80941Smrg VkDisplayKHR display) 2343b8e80941Smrg{ 2344b8e80941Smrg struct wsi_display *wsi = 2345b8e80941Smrg (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; 2346b8e80941Smrg xcb_connection_t *connection = XGetXCBConnection(dpy); 2347b8e80941Smrg struct wsi_display_connector *connector = 2348b8e80941Smrg wsi_display_connector_from_handle(display); 2349b8e80941Smrg xcb_window_t root; 2350b8e80941Smrg 2351b8e80941Smrg /* XXX no support for multiple leases yet */ 2352b8e80941Smrg if (wsi->fd >= 0) 2353b8e80941Smrg return VK_ERROR_INITIALIZATION_FAILED; 2354b8e80941Smrg 2355b8e80941Smrg if (!connector->output) { 2356b8e80941Smrg connector->output = wsi_display_connector_id_to_output(connection, 2357b8e80941Smrg connector->id); 2358b8e80941Smrg 2359b8e80941Smrg /* Check and see if we found the output */ 2360b8e80941Smrg if (!connector->output) 2361b8e80941Smrg return VK_ERROR_INITIALIZATION_FAILED; 2362b8e80941Smrg } 2363b8e80941Smrg 2364b8e80941Smrg root = wsi_display_output_to_root(connection, connector->output); 2365b8e80941Smrg if (!root) 2366b8e80941Smrg return VK_ERROR_INITIALIZATION_FAILED; 2367b8e80941Smrg 2368b8e80941Smrg xcb_randr_crtc_t crtc = wsi_display_find_crtc_for_output(connection, 2369b8e80941Smrg root, 2370b8e80941Smrg connector->output); 2371b8e80941Smrg 2372b8e80941Smrg if (!crtc) 2373b8e80941Smrg return VK_ERROR_INITIALIZATION_FAILED; 2374b8e80941Smrg 2375b8e80941Smrg#ifdef HAVE_DRI3_MODIFIERS 2376b8e80941Smrg xcb_randr_lease_t lease = xcb_generate_id(connection); 2377b8e80941Smrg xcb_randr_create_lease_cookie_t cl_c = 2378b8e80941Smrg xcb_randr_create_lease(connection, root, lease, 1, 1, 2379b8e80941Smrg &crtc, &connector->output); 2380b8e80941Smrg xcb_randr_create_lease_reply_t *cl_r = 2381b8e80941Smrg xcb_randr_create_lease_reply(connection, cl_c, NULL); 2382b8e80941Smrg if (!cl_r) 2383b8e80941Smrg return VK_ERROR_INITIALIZATION_FAILED; 2384b8e80941Smrg 2385b8e80941Smrg int fd = -1; 2386b8e80941Smrg if (cl_r->nfd > 0) { 2387b8e80941Smrg int *rcl_f = xcb_randr_create_lease_reply_fds(connection, cl_r); 2388b8e80941Smrg 2389b8e80941Smrg fd = rcl_f[0]; 2390b8e80941Smrg } 2391b8e80941Smrg free (cl_r); 2392b8e80941Smrg if (fd < 0) 2393b8e80941Smrg return VK_ERROR_INITIALIZATION_FAILED; 2394b8e80941Smrg 2395b8e80941Smrg wsi->fd = fd; 2396b8e80941Smrg#endif 2397b8e80941Smrg 2398b8e80941Smrg return VK_SUCCESS; 2399b8e80941Smrg} 2400b8e80941Smrg 2401b8e80941SmrgVkResult 2402b8e80941Smrgwsi_get_randr_output_display(VkPhysicalDevice physical_device, 2403b8e80941Smrg struct wsi_device *wsi_device, 2404b8e80941Smrg Display *dpy, 2405b8e80941Smrg RROutput output, 2406b8e80941Smrg VkDisplayKHR *display) 2407b8e80941Smrg{ 2408b8e80941Smrg xcb_connection_t *connection = XGetXCBConnection(dpy); 2409b8e80941Smrg struct wsi_display_connector *connector = 2410b8e80941Smrg wsi_display_get_output(wsi_device, connection, (xcb_randr_output_t) output); 2411b8e80941Smrg 2412b8e80941Smrg if (connector) 2413b8e80941Smrg *display = wsi_display_connector_to_handle(connector); 2414b8e80941Smrg else 2415b8e80941Smrg *display = VK_NULL_HANDLE; 2416b8e80941Smrg return VK_SUCCESS; 2417b8e80941Smrg} 2418b8e80941Smrg 2419b8e80941Smrg#endif 2420b8e80941Smrg 2421b8e80941Smrg/* VK_EXT_display_control */ 2422b8e80941SmrgVkResult 2423b8e80941Smrgwsi_display_power_control(VkDevice device, 2424b8e80941Smrg struct wsi_device *wsi_device, 2425b8e80941Smrg VkDisplayKHR display, 2426b8e80941Smrg const VkDisplayPowerInfoEXT *display_power_info) 2427b8e80941Smrg{ 2428b8e80941Smrg struct wsi_display *wsi = 2429b8e80941Smrg (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; 2430b8e80941Smrg struct wsi_display_connector *connector = 2431b8e80941Smrg wsi_display_connector_from_handle(display); 2432b8e80941Smrg int mode; 2433b8e80941Smrg 2434b8e80941Smrg if (wsi->fd < 0) 2435b8e80941Smrg return VK_ERROR_INITIALIZATION_FAILED; 2436b8e80941Smrg 2437b8e80941Smrg switch (display_power_info->powerState) { 2438b8e80941Smrg case VK_DISPLAY_POWER_STATE_OFF_EXT: 2439b8e80941Smrg mode = DRM_MODE_DPMS_OFF; 2440b8e80941Smrg break; 2441b8e80941Smrg case VK_DISPLAY_POWER_STATE_SUSPEND_EXT: 2442b8e80941Smrg mode = DRM_MODE_DPMS_SUSPEND; 2443b8e80941Smrg break; 2444b8e80941Smrg default: 2445b8e80941Smrg mode = DRM_MODE_DPMS_ON; 2446b8e80941Smrg break; 2447b8e80941Smrg } 2448b8e80941Smrg drmModeConnectorSetProperty(wsi->fd, 2449b8e80941Smrg connector->id, 2450b8e80941Smrg connector->dpms_property, 2451b8e80941Smrg mode); 2452b8e80941Smrg return VK_SUCCESS; 2453b8e80941Smrg} 2454b8e80941Smrg 2455b8e80941SmrgVkResult 2456b8e80941Smrgwsi_register_device_event(VkDevice device, 2457b8e80941Smrg struct wsi_device *wsi_device, 2458b8e80941Smrg const VkDeviceEventInfoEXT *device_event_info, 2459b8e80941Smrg const VkAllocationCallbacks *allocator, 2460b8e80941Smrg struct wsi_fence **fence_p) 2461b8e80941Smrg{ 2462b8e80941Smrg return VK_ERROR_FEATURE_NOT_PRESENT; 2463b8e80941Smrg} 2464b8e80941Smrg 2465b8e80941SmrgVkResult 2466b8e80941Smrgwsi_register_display_event(VkDevice device, 2467b8e80941Smrg struct wsi_device *wsi_device, 2468b8e80941Smrg VkDisplayKHR display, 2469b8e80941Smrg const VkDisplayEventInfoEXT *display_event_info, 2470b8e80941Smrg const VkAllocationCallbacks *allocator, 2471b8e80941Smrg struct wsi_fence **fence_p) 2472b8e80941Smrg{ 2473b8e80941Smrg struct wsi_display *wsi = 2474b8e80941Smrg (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; 2475b8e80941Smrg struct wsi_display_fence *fence; 2476b8e80941Smrg VkResult ret; 2477b8e80941Smrg 2478b8e80941Smrg switch (display_event_info->displayEvent) { 2479b8e80941Smrg case VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT: 2480b8e80941Smrg 2481b8e80941Smrg fence = wsi_display_fence_alloc(device, wsi_device, display, allocator); 2482b8e80941Smrg 2483b8e80941Smrg if (!fence) 2484b8e80941Smrg return VK_ERROR_OUT_OF_HOST_MEMORY; 2485b8e80941Smrg 2486b8e80941Smrg ret = wsi_register_vblank_event(fence, wsi_device, display, 2487b8e80941Smrg DRM_CRTC_SEQUENCE_RELATIVE, 1, NULL); 2488b8e80941Smrg 2489b8e80941Smrg if (ret == VK_SUCCESS) 2490b8e80941Smrg *fence_p = &fence->base; 2491b8e80941Smrg else if (fence != NULL) 2492b8e80941Smrg vk_free2(wsi->alloc, allocator, fence); 2493b8e80941Smrg 2494b8e80941Smrg break; 2495b8e80941Smrg default: 2496b8e80941Smrg ret = VK_ERROR_FEATURE_NOT_PRESENT; 2497b8e80941Smrg break; 2498b8e80941Smrg } 2499b8e80941Smrg 2500b8e80941Smrg return ret; 2501b8e80941Smrg} 2502b8e80941Smrg 2503b8e80941Smrg 2504b8e80941SmrgVkResult 2505b8e80941Smrgwsi_get_swapchain_counter(VkDevice device, 2506b8e80941Smrg struct wsi_device *wsi_device, 2507b8e80941Smrg VkSwapchainKHR _swapchain, 2508b8e80941Smrg VkSurfaceCounterFlagBitsEXT flag_bits, 2509b8e80941Smrg uint64_t *value) 2510b8e80941Smrg{ 2511b8e80941Smrg struct wsi_display *wsi = 2512b8e80941Smrg (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; 2513b8e80941Smrg struct wsi_display_swapchain *swapchain = 2514b8e80941Smrg (struct wsi_display_swapchain *) wsi_swapchain_from_handle(_swapchain); 2515b8e80941Smrg struct wsi_display_connector *connector = 2516b8e80941Smrg wsi_display_mode_from_handle(swapchain->surface->displayMode)->connector; 2517b8e80941Smrg 2518b8e80941Smrg if (wsi->fd < 0) 2519b8e80941Smrg return VK_ERROR_INITIALIZATION_FAILED; 2520b8e80941Smrg 2521b8e80941Smrg if (!connector->active) { 2522b8e80941Smrg *value = 0; 2523b8e80941Smrg return VK_SUCCESS; 2524b8e80941Smrg } 2525b8e80941Smrg 2526b8e80941Smrg int ret = drmCrtcGetSequence(wsi->fd, connector->crtc_id, value, NULL); 2527b8e80941Smrg if (ret) 2528b8e80941Smrg *value = 0; 2529b8e80941Smrg 2530b8e80941Smrg return VK_SUCCESS; 2531b8e80941Smrg} 2532b8e80941Smrg 2533