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