2 * Copyright © 2017 Keith Packard
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.
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
23 #include "util/macros.h"
34 #include <xf86drmMode.h>
35 #include <drm_fourcc.h>
36 #include "util/hash_table.h"
37 #include "util/list.h"
40 #include "wsi_common_private.h"
41 #include "wsi_common_display.h"
42 #include "wsi_common_queue.h"
45 #define wsi_display_debug(...) fprintf(stderr, __VA_ARGS__)
46 #define wsi_display_debug_code(...) __VA_ARGS__
48 #define wsi_display_debug(...)
49 #define wsi_display_debug_code(...)
52 /* These have lifetime equal to the instance, so they effectively
53 * never go away. This means we must keep track of them separately
54 * from all other resources.
56 typedef struct wsi_display_mode
{
57 struct list_head list
;
58 struct wsi_display_connector
*connector
;
59 bool valid
; /* was found in most recent poll */
61 uint32_t clock
; /* in kHz */
62 uint16_t hdisplay
, hsync_start
, hsync_end
, htotal
, hskew
;
63 uint16_t vdisplay
, vsync_start
, vsync_end
, vtotal
, vscan
;
67 typedef struct wsi_display_connector
{
68 struct list_head list
;
69 struct wsi_display
*wsi
;
75 struct list_head display_modes
;
76 wsi_display_mode
*current_mode
;
77 drmModeModeInfo current_drm_mode
;
78 } wsi_display_connector
;
81 struct wsi_interface base
;
83 const VkAllocationCallbacks
*alloc
;
87 pthread_mutex_t wait_mutex
;
88 pthread_cond_t wait_cond
;
89 pthread_t wait_thread
;
91 struct list_head connectors
;
94 #define wsi_for_each_display_mode(_mode, _conn) \
95 list_for_each_entry_safe(struct wsi_display_mode, _mode, \
96 &(_conn)->display_modes, list)
98 #define wsi_for_each_connector(_conn, _dev) \
99 list_for_each_entry_safe(struct wsi_display_connector, _conn, \
100 &(_dev)->connectors, list)
102 enum wsi_image_state
{
110 struct wsi_display_image
{
111 struct wsi_image base
;
112 struct wsi_display_swapchain
*chain
;
113 enum wsi_image_state state
;
116 uint64_t flip_sequence
;
119 struct wsi_display_swapchain
{
120 struct wsi_swapchain base
;
121 struct wsi_display
*wsi
;
122 VkIcdSurfaceDisplay
*surface
;
123 uint64_t flip_sequence
;
125 struct wsi_display_image images
[0];
128 ICD_DEFINE_NONDISP_HANDLE_CASTS(wsi_display_mode
, VkDisplayModeKHR
)
129 ICD_DEFINE_NONDISP_HANDLE_CASTS(wsi_display_connector
, VkDisplayKHR
)
132 wsi_display_mode_matches_drm(wsi_display_mode
*wsi
,
133 drmModeModeInfoPtr drm
)
135 return wsi
->clock
== drm
->clock
&&
136 wsi
->hdisplay
== drm
->hdisplay
&&
137 wsi
->hsync_start
== drm
->hsync_start
&&
138 wsi
->hsync_end
== drm
->hsync_end
&&
139 wsi
->htotal
== drm
->htotal
&&
140 wsi
->hskew
== drm
->hskew
&&
141 wsi
->vdisplay
== drm
->vdisplay
&&
142 wsi
->vsync_start
== drm
->vsync_start
&&
143 wsi
->vsync_end
== drm
->vsync_end
&&
144 wsi
->vtotal
== drm
->vtotal
&&
145 MAX2(wsi
->vscan
, 1) == MAX2(drm
->vscan
, 1) &&
146 wsi
->flags
== drm
->flags
;
150 wsi_display_mode_refresh(struct wsi_display_mode
*wsi
)
152 return (double) wsi
->clock
* 1000.0 / ((double) wsi
->htotal
*
153 (double) wsi
->vtotal
*
154 (double) MAX2(wsi
->vscan
, 1));
157 static uint64_t wsi_get_current_monotonic(void)
161 clock_gettime(CLOCK_MONOTONIC
, &tv
);
162 return tv
.tv_nsec
+ tv
.tv_sec
*1000000000ull;
165 static uint64_t wsi_rel_to_abs_time(uint64_t rel_time
)
167 uint64_t current_time
= wsi_get_current_monotonic();
169 /* check for overflow */
170 if (rel_time
> UINT64_MAX
- current_time
)
173 return current_time
+ rel_time
;
176 static struct wsi_display_mode
*
177 wsi_display_find_drm_mode(struct wsi_device
*wsi_device
,
178 struct wsi_display_connector
*connector
,
179 drmModeModeInfoPtr mode
)
181 wsi_for_each_display_mode(display_mode
, connector
) {
182 if (wsi_display_mode_matches_drm(display_mode
, mode
))
189 wsi_display_invalidate_connector_modes(struct wsi_device
*wsi_device
,
190 struct wsi_display_connector
*connector
)
192 wsi_for_each_display_mode(display_mode
, connector
) {
193 display_mode
->valid
= false;
198 wsi_display_register_drm_mode(struct wsi_device
*wsi_device
,
199 struct wsi_display_connector
*connector
,
200 drmModeModeInfoPtr drm_mode
)
202 struct wsi_display
*wsi
=
203 (struct wsi_display
*) wsi_device
->wsi
[VK_ICD_WSI_PLATFORM_DISPLAY
];
204 struct wsi_display_mode
*display_mode
=
205 wsi_display_find_drm_mode(wsi_device
, connector
, drm_mode
);
208 display_mode
->valid
= true;
212 display_mode
= vk_zalloc(wsi
->alloc
, sizeof (struct wsi_display_mode
),
213 8, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE
);
215 return VK_ERROR_OUT_OF_HOST_MEMORY
;
217 display_mode
->connector
= connector
;
218 display_mode
->valid
= true;
219 display_mode
->preferred
= (drm_mode
->type
& DRM_MODE_TYPE_PREFERRED
) != 0;
220 display_mode
->clock
= drm_mode
->clock
; /* kHz */
221 display_mode
->hdisplay
= drm_mode
->hdisplay
;
222 display_mode
->hsync_start
= drm_mode
->hsync_start
;
223 display_mode
->hsync_end
= drm_mode
->hsync_end
;
224 display_mode
->htotal
= drm_mode
->htotal
;
225 display_mode
->hskew
= drm_mode
->hskew
;
226 display_mode
->vdisplay
= drm_mode
->vdisplay
;
227 display_mode
->vsync_start
= drm_mode
->vsync_start
;
228 display_mode
->vsync_end
= drm_mode
->vsync_end
;
229 display_mode
->vtotal
= drm_mode
->vtotal
;
230 display_mode
->vscan
= drm_mode
->vscan
;
231 display_mode
->flags
= drm_mode
->flags
;
233 list_addtail(&display_mode
->list
, &connector
->display_modes
);
238 * Update our information about a specific connector
241 static struct wsi_display_connector
*
242 wsi_display_find_connector(struct wsi_device
*wsi_device
,
243 uint32_t connector_id
)
245 struct wsi_display
*wsi
=
246 (struct wsi_display
*) wsi_device
->wsi
[VK_ICD_WSI_PLATFORM_DISPLAY
];
248 wsi_for_each_connector(connector
, wsi
) {
249 if (connector
->id
== connector_id
)
256 static struct wsi_display_connector
*
257 wsi_display_alloc_connector(struct wsi_display
*wsi
,
258 uint32_t connector_id
)
260 struct wsi_display_connector
*connector
=
261 vk_zalloc(wsi
->alloc
, sizeof (struct wsi_display_connector
),
262 8, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE
);
264 connector
->id
= connector_id
;
265 connector
->wsi
= wsi
;
266 connector
->active
= false;
267 /* XXX use EDID name */
268 connector
->name
= "monitor";
269 list_inithead(&connector
->display_modes
);
273 static struct wsi_display_connector
*
274 wsi_display_get_connector(struct wsi_device
*wsi_device
,
275 uint32_t connector_id
)
277 struct wsi_display
*wsi
=
278 (struct wsi_display
*) wsi_device
->wsi
[VK_ICD_WSI_PLATFORM_DISPLAY
];
283 drmModeConnectorPtr drm_connector
=
284 drmModeGetConnector(wsi
->fd
, connector_id
);
289 struct wsi_display_connector
*connector
=
290 wsi_display_find_connector(wsi_device
, connector_id
);
293 connector
= wsi_display_alloc_connector(wsi
, connector_id
);
295 drmModeFreeConnector(drm_connector
);
298 list_addtail(&connector
->list
, &wsi
->connectors
);
301 connector
->connected
= drm_connector
->connection
!= DRM_MODE_DISCONNECTED
;
303 /* Mark all connector modes as invalid */
304 wsi_display_invalidate_connector_modes(wsi_device
, connector
);
307 * List current modes, adding new ones and marking existing ones as
310 for (int m
= 0; m
< drm_connector
->count_modes
; m
++) {
311 VkResult result
= wsi_display_register_drm_mode(wsi_device
,
313 &drm_connector
->modes
[m
]);
314 if (result
!= VK_SUCCESS
) {
315 drmModeFreeConnector(drm_connector
);
320 drmModeFreeConnector(drm_connector
);
325 #define MM_PER_PIXEL (1.0/96.0 * 25.4)
328 mode_size(struct wsi_display_mode
*mode
)
330 /* fortunately, these are both uint16_t, so this is easy */
331 return (uint32_t) mode
->hdisplay
* (uint32_t) mode
->vdisplay
;
335 wsi_display_fill_in_display_properties(struct wsi_device
*wsi_device
,
336 struct wsi_display_connector
*connector
,
337 VkDisplayPropertiesKHR
*properties
)
339 properties
->display
= wsi_display_connector_to_handle(connector
);
340 properties
->displayName
= connector
->name
;
342 /* Find the first preferred mode and assume that's the physical
343 * resolution. If there isn't a preferred mode, find the largest mode and
347 struct wsi_display_mode
*preferred_mode
= NULL
, *largest_mode
= NULL
;
348 wsi_for_each_display_mode(display_mode
, connector
) {
349 if (!display_mode
->valid
)
351 if (display_mode
->preferred
) {
352 preferred_mode
= display_mode
;
355 if (largest_mode
== NULL
||
356 mode_size(display_mode
) > mode_size(largest_mode
))
358 largest_mode
= display_mode
;
362 if (preferred_mode
) {
363 properties
->physicalResolution
.width
= preferred_mode
->hdisplay
;
364 properties
->physicalResolution
.height
= preferred_mode
->vdisplay
;
365 } else if (largest_mode
) {
366 properties
->physicalResolution
.width
= largest_mode
->hdisplay
;
367 properties
->physicalResolution
.height
= largest_mode
->vdisplay
;
369 properties
->physicalResolution
.width
= 1024;
370 properties
->physicalResolution
.height
= 768;
373 /* Make up physical size based on 96dpi */
374 properties
->physicalDimensions
.width
=
375 floor(properties
->physicalResolution
.width
* MM_PER_PIXEL
+ 0.5);
376 properties
->physicalDimensions
.height
=
377 floor(properties
->physicalResolution
.height
* MM_PER_PIXEL
+ 0.5);
379 properties
->supportedTransforms
= VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR
;
380 properties
->planeReorderPossible
= VK_FALSE
;
381 properties
->persistentContent
= VK_FALSE
;
385 * Implement vkGetPhysicalDeviceDisplayPropertiesKHR (VK_KHR_display)
388 wsi_display_get_physical_device_display_properties(
389 VkPhysicalDevice physical_device
,
390 struct wsi_device
*wsi_device
,
391 uint32_t *property_count
,
392 VkDisplayPropertiesKHR
*properties
)
394 struct wsi_display
*wsi
=
395 (struct wsi_display
*) wsi_device
->wsi
[VK_ICD_WSI_PLATFORM_DISPLAY
];
400 drmModeResPtr mode_res
= drmModeGetResources(wsi
->fd
);
405 VK_OUTARRAY_MAKE(conn
, properties
, property_count
);
407 /* Get current information */
409 for (int c
= 0; c
< mode_res
->count_connectors
; c
++) {
410 struct wsi_display_connector
*connector
=
411 wsi_display_get_connector(wsi_device
, mode_res
->connectors
[c
]);
414 drmModeFreeResources(mode_res
);
415 return VK_ERROR_OUT_OF_HOST_MEMORY
;
418 if (connector
->connected
) {
419 vk_outarray_append(&conn
, prop
) {
420 wsi_display_fill_in_display_properties(wsi_device
,
427 drmModeFreeResources(mode_res
);
429 return vk_outarray_status(&conn
);
437 * Implement vkGetPhysicalDeviceDisplayPlanePropertiesKHR (VK_KHR_display
440 wsi_display_get_physical_device_display_plane_properties(
441 VkPhysicalDevice physical_device
,
442 struct wsi_device
*wsi_device
,
443 uint32_t *property_count
,
444 VkDisplayPlanePropertiesKHR
*properties
)
446 struct wsi_display
*wsi
=
447 (struct wsi_display
*) wsi_device
->wsi
[VK_ICD_WSI_PLATFORM_DISPLAY
];
449 VK_OUTARRAY_MAKE(conn
, properties
, property_count
);
451 wsi_for_each_connector(connector
, wsi
) {
452 vk_outarray_append(&conn
, prop
) {
453 if (connector
&& connector
->active
) {
454 prop
->currentDisplay
= wsi_display_connector_to_handle(connector
);
455 prop
->currentStackIndex
= 0;
457 prop
->currentDisplay
= VK_NULL_HANDLE
;
458 prop
->currentStackIndex
= 0;
462 return vk_outarray_status(&conn
);
466 * Implement vkGetDisplayPlaneSupportedDisplaysKHR (VK_KHR_display)
470 wsi_display_get_display_plane_supported_displays(
471 VkPhysicalDevice physical_device
,
472 struct wsi_device
*wsi_device
,
473 uint32_t plane_index
,
474 uint32_t *display_count
,
475 VkDisplayKHR
*displays
)
477 struct wsi_display
*wsi
=
478 (struct wsi_display
*) wsi_device
->wsi
[VK_ICD_WSI_PLATFORM_DISPLAY
];
480 VK_OUTARRAY_MAKE(conn
, displays
, display_count
);
484 wsi_for_each_connector(connector
, wsi
) {
485 if (c
== plane_index
&& connector
->connected
) {
486 vk_outarray_append(&conn
, display
) {
487 *display
= wsi_display_connector_to_handle(connector
);
492 return vk_outarray_status(&conn
);
496 * Implement vkGetDisplayModePropertiesKHR (VK_KHR_display)
500 wsi_display_get_display_mode_properties(VkPhysicalDevice physical_device
,
501 struct wsi_device
*wsi_device
,
502 VkDisplayKHR display
,
503 uint32_t *property_count
,
504 VkDisplayModePropertiesKHR
*properties
)
506 struct wsi_display_connector
*connector
=
507 wsi_display_connector_from_handle(display
);
509 VK_OUTARRAY_MAKE(conn
, properties
, property_count
);
511 wsi_for_each_display_mode(display_mode
, connector
) {
512 if (display_mode
->valid
) {
513 vk_outarray_append(&conn
, prop
) {
514 prop
->displayMode
= wsi_display_mode_to_handle(display_mode
);
515 prop
->parameters
.visibleRegion
.width
= display_mode
->hdisplay
;
516 prop
->parameters
.visibleRegion
.height
= display_mode
->vdisplay
;
517 prop
->parameters
.refreshRate
=
518 (uint32_t) (wsi_display_mode_refresh(display_mode
) * 1000 + 0.5);
522 return vk_outarray_status(&conn
);
526 wsi_display_mode_matches_vk(wsi_display_mode
*wsi
,
527 const VkDisplayModeParametersKHR
*vk
)
529 return (vk
->visibleRegion
.width
== wsi
->hdisplay
&&
530 vk
->visibleRegion
.height
== wsi
->vdisplay
&&
531 fabs(wsi_display_mode_refresh(wsi
) * 1000.0 - vk
->refreshRate
) < 10);
535 * Implement vkCreateDisplayModeKHR (VK_KHR_display)
538 wsi_display_create_display_mode(VkPhysicalDevice physical_device
,
539 struct wsi_device
*wsi_device
,
540 VkDisplayKHR display
,
541 const VkDisplayModeCreateInfoKHR
*create_info
,
542 const VkAllocationCallbacks
*allocator
,
543 VkDisplayModeKHR
*mode
)
545 struct wsi_display_connector
*connector
=
546 wsi_display_connector_from_handle(display
);
548 if (create_info
->flags
!= 0)
549 return VK_ERROR_INITIALIZATION_FAILED
;
551 /* Check and see if the requested mode happens to match an existing one and
552 * return that. This makes the conformance suite happy. Doing more than
553 * this would involve embedding the CVT function into the driver, which seems
556 wsi_for_each_display_mode(display_mode
, connector
) {
557 if (display_mode
->valid
) {
558 if (wsi_display_mode_matches_vk(display_mode
, &create_info
->parameters
)) {
559 *mode
= wsi_display_mode_to_handle(display_mode
);
564 return VK_ERROR_INITIALIZATION_FAILED
;
568 * Implement vkGetDisplayPlaneCapabilities
571 wsi_get_display_plane_capabilities(VkPhysicalDevice physical_device
,
572 struct wsi_device
*wsi_device
,
573 VkDisplayModeKHR mode_khr
,
574 uint32_t plane_index
,
575 VkDisplayPlaneCapabilitiesKHR
*capabilities
)
577 struct wsi_display_mode
*mode
= wsi_display_mode_from_handle(mode_khr
);
579 /* XXX use actual values */
580 capabilities
->supportedAlpha
= VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR
;
581 capabilities
->minSrcPosition
.x
= 0;
582 capabilities
->minSrcPosition
.y
= 0;
583 capabilities
->maxSrcPosition
.x
= 0;
584 capabilities
->maxSrcPosition
.y
= 0;
585 capabilities
->minSrcExtent
.width
= mode
->hdisplay
;
586 capabilities
->minSrcExtent
.height
= mode
->vdisplay
;
587 capabilities
->maxSrcExtent
.width
= mode
->hdisplay
;
588 capabilities
->maxSrcExtent
.height
= mode
->vdisplay
;
589 capabilities
->minDstPosition
.x
= 0;
590 capabilities
->minDstPosition
.y
= 0;
591 capabilities
->maxDstPosition
.x
= 0;
592 capabilities
->maxDstPosition
.y
= 0;
593 capabilities
->minDstExtent
.width
= mode
->hdisplay
;
594 capabilities
->minDstExtent
.height
= mode
->vdisplay
;
595 capabilities
->maxDstExtent
.width
= mode
->hdisplay
;
596 capabilities
->maxDstExtent
.height
= mode
->vdisplay
;
601 wsi_create_display_surface(VkInstance instance
,
602 const VkAllocationCallbacks
*allocator
,
603 const VkDisplaySurfaceCreateInfoKHR
*create_info
,
604 VkSurfaceKHR
*surface_khr
)
606 VkIcdSurfaceDisplay
*surface
= vk_zalloc(allocator
, sizeof *surface
, 8,
607 VK_SYSTEM_ALLOCATION_SCOPE_OBJECT
);
610 return VK_ERROR_OUT_OF_HOST_MEMORY
;
612 surface
->base
.platform
= VK_ICD_WSI_PLATFORM_DISPLAY
;
614 surface
->displayMode
= create_info
->displayMode
;
615 surface
->planeIndex
= create_info
->planeIndex
;
616 surface
->planeStackIndex
= create_info
->planeStackIndex
;
617 surface
->transform
= create_info
->transform
;
618 surface
->globalAlpha
= create_info
->globalAlpha
;
619 surface
->alphaMode
= create_info
->alphaMode
;
620 surface
->imageExtent
= create_info
->imageExtent
;
622 *surface_khr
= VkIcdSurfaceBase_to_handle(&surface
->base
);
628 wsi_display_surface_get_support(VkIcdSurfaceBase
*surface
,
629 struct wsi_device
*wsi_device
,
630 const VkAllocationCallbacks
*allocator
,
631 uint32_t queueFamilyIndex
,
633 VkBool32
* pSupported
)
635 *pSupported
= VK_TRUE
;
640 wsi_display_surface_get_capabilities(VkIcdSurfaceBase
*surface_base
,
641 VkSurfaceCapabilitiesKHR
* caps
)
643 VkIcdSurfaceDisplay
*surface
= (VkIcdSurfaceDisplay
*) surface_base
;
644 wsi_display_mode
*mode
= wsi_display_mode_from_handle(surface
->displayMode
);
646 caps
->currentExtent
.width
= mode
->hdisplay
;
647 caps
->currentExtent
.height
= mode
->vdisplay
;
649 /* XXX Figure out extents based on driver capabilities */
650 caps
->maxImageExtent
= caps
->minImageExtent
= caps
->currentExtent
;
652 caps
->supportedCompositeAlpha
= VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR
;
654 caps
->minImageCount
= 2;
655 caps
->maxImageCount
= 0;
657 caps
->supportedTransforms
= VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR
;
658 caps
->currentTransform
= VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR
;
659 caps
->maxImageArrayLayers
= 1;
660 caps
->supportedUsageFlags
=
661 VK_IMAGE_USAGE_TRANSFER_SRC_BIT
|
662 VK_IMAGE_USAGE_SAMPLED_BIT
|
663 VK_IMAGE_USAGE_TRANSFER_DST_BIT
|
664 VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
;
670 wsi_display_surface_get_capabilities2(VkIcdSurfaceBase
*icd_surface
,
671 const void *info_next
,
672 VkSurfaceCapabilities2KHR
*caps
)
674 assert(caps
->sType
== VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR
);
676 return wsi_display_surface_get_capabilities(icd_surface
,
677 &caps
->surfaceCapabilities
);
680 static const struct {
683 } available_surface_formats
[] = {
684 { .format
= VK_FORMAT_B8G8R8A8_SRGB
, .drm_format
= DRM_FORMAT_XRGB8888
},
685 { .format
= VK_FORMAT_B8G8R8A8_UNORM
, .drm_format
= DRM_FORMAT_XRGB8888
},
689 wsi_display_surface_get_formats(VkIcdSurfaceBase
*icd_surface
,
690 struct wsi_device
*wsi_device
,
691 uint32_t *surface_format_count
,
692 VkSurfaceFormatKHR
*surface_formats
)
694 VK_OUTARRAY_MAKE(out
, surface_formats
, surface_format_count
);
696 for (unsigned i
= 0; i
< ARRAY_SIZE(available_surface_formats
); i
++) {
697 vk_outarray_append(&out
, f
) {
698 f
->format
= available_surface_formats
[i
].format
;
699 f
->colorSpace
= VK_COLORSPACE_SRGB_NONLINEAR_KHR
;
703 return vk_outarray_status(&out
);
707 wsi_display_surface_get_formats2(VkIcdSurfaceBase
*surface
,
708 struct wsi_device
*wsi_device
,
709 const void *info_next
,
710 uint32_t *surface_format_count
,
711 VkSurfaceFormat2KHR
*surface_formats
)
713 VK_OUTARRAY_MAKE(out
, surface_formats
, surface_format_count
);
715 for (unsigned i
= 0; i
< ARRAY_SIZE(available_surface_formats
); i
++) {
716 vk_outarray_append(&out
, f
) {
717 assert(f
->sType
== VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR
);
718 f
->surfaceFormat
.format
= available_surface_formats
[i
].format
;
719 f
->surfaceFormat
.colorSpace
= VK_COLORSPACE_SRGB_NONLINEAR_KHR
;
723 return vk_outarray_status(&out
);
727 wsi_display_surface_get_present_modes(VkIcdSurfaceBase
*surface
,
728 uint32_t *present_mode_count
,
729 VkPresentModeKHR
*present_modes
)
731 VK_OUTARRAY_MAKE(conn
, present_modes
, present_mode_count
);
733 vk_outarray_append(&conn
, present
) {
734 *present
= VK_PRESENT_MODE_FIFO_KHR
;
737 return vk_outarray_status(&conn
);
741 wsi_display_destroy_buffer(struct wsi_display
*wsi
,
744 (void) drmIoctl(wsi
->fd
, DRM_IOCTL_MODE_DESTROY_DUMB
,
745 &((struct drm_mode_destroy_dumb
) { .handle
= buffer
}));
749 wsi_display_image_init(VkDevice device_h
,
750 struct wsi_swapchain
*drv_chain
,
751 const VkSwapchainCreateInfoKHR
*create_info
,
752 const VkAllocationCallbacks
*allocator
,
753 struct wsi_display_image
*image
)
755 struct wsi_display_swapchain
*chain
=
756 (struct wsi_display_swapchain
*) drv_chain
;
757 struct wsi_display
*wsi
= chain
->wsi
;
758 uint32_t drm_format
= 0;
760 for (unsigned i
= 0; i
< ARRAY_SIZE(available_surface_formats
); i
++) {
761 if (create_info
->imageFormat
== available_surface_formats
[i
].format
) {
762 drm_format
= available_surface_formats
[i
].drm_format
;
767 /* the application provided an invalid format, bail */
769 return VK_ERROR_DEVICE_LOST
;
771 VkResult result
= wsi_create_native_image(&chain
->base
, create_info
,
774 if (result
!= VK_SUCCESS
)
777 memset(image
->buffer
, 0, sizeof (image
->buffer
));
779 for (unsigned int i
= 0; i
< image
->base
.num_planes
; i
++) {
780 int ret
= drmPrimeFDToHandle(wsi
->fd
, image
->base
.fds
[i
],
783 close(image
->base
.fds
[i
]);
784 image
->base
.fds
[i
] = -1;
789 image
->chain
= chain
;
790 image
->state
= WSI_IMAGE_IDLE
;
793 int ret
= drmModeAddFB2(wsi
->fd
,
794 create_info
->imageExtent
.width
,
795 create_info
->imageExtent
.height
,
798 image
->base
.row_pitches
,
809 for (unsigned int i
= 0; i
< image
->base
.num_planes
; i
++) {
810 if (image
->buffer
[i
])
811 wsi_display_destroy_buffer(wsi
, image
->buffer
[i
]);
812 if (image
->base
.fds
[i
] != -1) {
813 close(image
->base
.fds
[i
]);
814 image
->base
.fds
[i
] = -1;
818 wsi_destroy_image(&chain
->base
, &image
->base
);
820 return VK_ERROR_OUT_OF_HOST_MEMORY
;
824 wsi_display_image_finish(struct wsi_swapchain
*drv_chain
,
825 const VkAllocationCallbacks
*allocator
,
826 struct wsi_display_image
*image
)
828 struct wsi_display_swapchain
*chain
=
829 (struct wsi_display_swapchain
*) drv_chain
;
830 struct wsi_display
*wsi
= chain
->wsi
;
832 drmModeRmFB(wsi
->fd
, image
->fb_id
);
833 for (unsigned int i
= 0; i
< image
->base
.num_planes
; i
++)
834 wsi_display_destroy_buffer(wsi
, image
->buffer
[i
]);
835 wsi_destroy_image(&chain
->base
, &image
->base
);
839 wsi_display_swapchain_destroy(struct wsi_swapchain
*drv_chain
,
840 const VkAllocationCallbacks
*allocator
)
842 struct wsi_display_swapchain
*chain
=
843 (struct wsi_display_swapchain
*) drv_chain
;
845 for (uint32_t i
= 0; i
< chain
->base
.image_count
; i
++)
846 wsi_display_image_finish(drv_chain
, allocator
, &chain
->images
[i
]);
847 vk_free(allocator
, chain
);
851 static struct wsi_image
*
852 wsi_display_get_wsi_image(struct wsi_swapchain
*drv_chain
,
853 uint32_t image_index
)
855 struct wsi_display_swapchain
*chain
=
856 (struct wsi_display_swapchain
*) drv_chain
;
858 return &chain
->images
[image_index
].base
;
862 wsi_display_idle_old_displaying(struct wsi_display_image
*active_image
)
864 struct wsi_display_swapchain
*chain
= active_image
->chain
;
866 wsi_display_debug("idle everyone but %ld\n",
867 active_image
- &(chain
->images
[0]));
868 for (uint32_t i
= 0; i
< chain
->base
.image_count
; i
++)
869 if (chain
->images
[i
].state
== WSI_IMAGE_DISPLAYING
&&
870 &chain
->images
[i
] != active_image
)
872 wsi_display_debug("idle %d\n", i
);
873 chain
->images
[i
].state
= WSI_IMAGE_IDLE
;
878 _wsi_display_queue_next(struct wsi_swapchain
*drv_chain
);
881 wsi_display_page_flip_handler2(int fd
,
888 struct wsi_display_image
*image
= data
;
889 struct wsi_display_swapchain
*chain
= image
->chain
;
891 wsi_display_debug("image %ld displayed at %d\n",
892 image
- &(image
->chain
->images
[0]), frame
);
893 image
->state
= WSI_IMAGE_DISPLAYING
;
894 wsi_display_idle_old_displaying(image
);
895 VkResult result
= _wsi_display_queue_next(&(chain
->base
));
896 if (result
!= VK_SUCCESS
)
897 chain
->status
= result
;
900 static void wsi_display_page_flip_handler(int fd
,
906 wsi_display_page_flip_handler2(fd
, frame
, sec
, usec
, 0, data
);
909 static drmEventContext event_context
= {
910 .version
= DRM_EVENT_CONTEXT_VERSION
,
911 .page_flip_handler
= wsi_display_page_flip_handler
,
912 #if DRM_EVENT_CONTEXT_VERSION >= 3
913 .page_flip_handler2
= wsi_display_page_flip_handler2
,
918 wsi_display_wait_thread(void *data
)
920 struct wsi_display
*wsi
= data
;
921 struct pollfd pollfd
= {
926 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS
, NULL
);
928 int ret
= poll(&pollfd
, 1, -1);
930 pthread_mutex_lock(&wsi
->wait_mutex
);
931 (void) drmHandleEvent(wsi
->fd
, &event_context
);
932 pthread_mutex_unlock(&wsi
->wait_mutex
);
933 pthread_cond_broadcast(&wsi
->wait_cond
);
940 wsi_display_start_wait_thread(struct wsi_display
*wsi
)
942 if (!wsi
->wait_thread
) {
943 int ret
= pthread_create(&wsi
->wait_thread
, NULL
,
944 wsi_display_wait_thread
, wsi
);
952 * Wait for at least one event from the kernel to be processed.
953 * Call with wait_mutex held
956 wsi_display_wait_for_event(struct wsi_display
*wsi
,
961 ret
= wsi_display_start_wait_thread(wsi
);
966 struct timespec abs_timeout
= {
967 .tv_sec
= timeout_ns
/ 1000000000ULL,
968 .tv_nsec
= timeout_ns
% 1000000000ULL,
971 ret
= pthread_cond_timedwait(&wsi
->wait_cond
, &wsi
->wait_mutex
,
974 wsi_display_debug("%9ld done waiting for event %d\n", pthread_self(), ret
);
979 wsi_display_acquire_next_image(struct wsi_swapchain
*drv_chain
,
981 VkSemaphore semaphore
,
982 uint32_t *image_index
)
984 struct wsi_display_swapchain
*chain
=
985 (struct wsi_display_swapchain
*)drv_chain
;
986 struct wsi_display
*wsi
= chain
->wsi
;
988 VkResult result
= VK_SUCCESS
;
990 /* Bail early if the swapchain is broken */
991 if (chain
->status
!= VK_SUCCESS
)
992 return chain
->status
;
994 if (timeout
!= 0 && timeout
!= UINT64_MAX
)
995 timeout
= wsi_rel_to_abs_time(timeout
);
997 pthread_mutex_lock(&wsi
->wait_mutex
);
999 for (uint32_t i
= 0; i
< chain
->base
.image_count
; i
++) {
1000 if (chain
->images
[i
].state
== WSI_IMAGE_IDLE
) {
1002 wsi_display_debug("image %d available\n", i
);
1003 chain
->images
[i
].state
= WSI_IMAGE_DRAWING
;
1004 result
= VK_SUCCESS
;
1007 wsi_display_debug("image %d state %d\n", i
, chain
->images
[i
].state
);
1010 if (ret
== ETIMEDOUT
) {
1011 result
= VK_TIMEOUT
;
1015 ret
= wsi_display_wait_for_event(wsi
, timeout
);
1017 if (ret
&& ret
!= ETIMEDOUT
) {
1018 result
= VK_ERROR_OUT_OF_DATE_KHR
;
1023 pthread_mutex_unlock(&wsi
->wait_mutex
);
1025 if (result
!= VK_SUCCESS
)
1028 return chain
->status
;
1032 * Check whether there are any other connectors driven by this crtc
1035 wsi_display_crtc_solo(struct wsi_display
*wsi
,
1036 drmModeResPtr mode_res
,
1037 drmModeConnectorPtr connector
,
1040 /* See if any other connectors share the same encoder */
1041 for (int c
= 0; c
< mode_res
->count_connectors
; c
++) {
1042 if (mode_res
->connectors
[c
] == connector
->connector_id
)
1045 drmModeConnectorPtr other_connector
=
1046 drmModeGetConnector(wsi
->fd
, mode_res
->connectors
[c
]);
1048 if (other_connector
) {
1049 bool match
= (other_connector
->encoder_id
== connector
->encoder_id
);
1050 drmModeFreeConnector(other_connector
);
1056 /* See if any other encoders share the same crtc */
1057 for (int e
= 0; e
< mode_res
->count_encoders
; e
++) {
1058 if (mode_res
->encoders
[e
] == connector
->encoder_id
)
1061 drmModeEncoderPtr other_encoder
=
1062 drmModeGetEncoder(wsi
->fd
, mode_res
->encoders
[e
]);
1064 if (other_encoder
) {
1065 bool match
= (other_encoder
->crtc_id
== crtc_id
);
1066 drmModeFreeEncoder(other_encoder
);
1075 * Pick a suitable CRTC to drive this connector. Prefer a CRTC which is
1076 * currently driving this connector and not any others. Settle for a CRTC
1077 * which is currently idle.
1080 wsi_display_select_crtc(struct wsi_display_connector
*connector
,
1081 drmModeResPtr mode_res
,
1082 drmModeConnectorPtr drm_connector
)
1084 struct wsi_display
*wsi
= connector
->wsi
;
1086 /* See what CRTC is currently driving this connector */
1087 if (drm_connector
->encoder_id
) {
1088 drmModeEncoderPtr encoder
=
1089 drmModeGetEncoder(wsi
->fd
, drm_connector
->encoder_id
);
1092 uint32_t crtc_id
= encoder
->crtc_id
;
1093 drmModeFreeEncoder(encoder
);
1095 if (wsi_display_crtc_solo(wsi
, mode_res
, drm_connector
, crtc_id
))
1100 uint32_t crtc_id
= 0;
1101 for (int c
= 0; crtc_id
== 0 && c
< mode_res
->count_crtcs
; c
++) {
1102 drmModeCrtcPtr crtc
= drmModeGetCrtc(wsi
->fd
, mode_res
->crtcs
[c
]);
1103 if (crtc
&& crtc
->buffer_id
== 0)
1104 crtc_id
= crtc
->crtc_id
;
1105 drmModeFreeCrtc(crtc
);
1111 wsi_display_setup_connector(wsi_display_connector
*connector
,
1112 wsi_display_mode
*display_mode
)
1114 struct wsi_display
*wsi
= connector
->wsi
;
1116 if (connector
->current_mode
== display_mode
&& connector
->crtc_id
)
1119 VkResult result
= VK_SUCCESS
;
1121 drmModeResPtr mode_res
= drmModeGetResources(wsi
->fd
);
1123 if (errno
== ENOMEM
)
1124 result
= VK_ERROR_OUT_OF_HOST_MEMORY
;
1126 result
= VK_ERROR_OUT_OF_DATE_KHR
;
1130 drmModeConnectorPtr drm_connector
=
1131 drmModeGetConnectorCurrent(wsi
->fd
, connector
->id
);
1133 if (!drm_connector
) {
1134 if (errno
== ENOMEM
)
1135 result
= VK_ERROR_OUT_OF_HOST_MEMORY
;
1137 result
= VK_ERROR_OUT_OF_DATE_KHR
;
1141 /* Pick a CRTC if we don't have one */
1142 if (!connector
->crtc_id
) {
1143 connector
->crtc_id
= wsi_display_select_crtc(connector
,
1144 mode_res
, drm_connector
);
1145 if (!connector
->crtc_id
) {
1146 result
= VK_ERROR_OUT_OF_DATE_KHR
;
1147 goto bail_connector
;
1151 if (connector
->current_mode
!= display_mode
) {
1153 /* Find the drm mode corresponding to the requested VkDisplayMode */
1154 drmModeModeInfoPtr drm_mode
= NULL
;
1156 for (int m
= 0; m
< drm_connector
->count_modes
; m
++) {
1157 drm_mode
= &drm_connector
->modes
[m
];
1158 if (wsi_display_mode_matches_drm(display_mode
, drm_mode
))
1164 result
= VK_ERROR_OUT_OF_DATE_KHR
;
1165 goto bail_connector
;
1168 connector
->current_mode
= display_mode
;
1169 connector
->current_drm_mode
= *drm_mode
;
1173 drmModeFreeConnector(drm_connector
);
1175 drmModeFreeResources(mode_res
);
1182 * Check to see if the kernel has no flip queued and if there's an image
1183 * waiting to be displayed.
1186 _wsi_display_queue_next(struct wsi_swapchain
*drv_chain
)
1188 struct wsi_display_swapchain
*chain
=
1189 (struct wsi_display_swapchain
*) drv_chain
;
1190 struct wsi_display
*wsi
= chain
->wsi
;
1191 VkIcdSurfaceDisplay
*surface
= chain
->surface
;
1192 wsi_display_mode
*display_mode
=
1193 wsi_display_mode_from_handle(surface
->displayMode
);
1194 wsi_display_connector
*connector
= display_mode
->connector
;
1197 return VK_ERROR_OUT_OF_DATE_KHR
;
1199 if (display_mode
!= connector
->current_mode
)
1200 connector
->active
= false;
1204 /* Check to see if there is an image to display, or if some image is
1207 struct wsi_display_image
*image
= NULL
;
1209 for (uint32_t i
= 0; i
< chain
->base
.image_count
; i
++) {
1210 struct wsi_display_image
*tmp_image
= &chain
->images
[i
];
1212 switch (tmp_image
->state
) {
1213 case WSI_IMAGE_FLIPPING
:
1214 /* already flipping, don't send another to the kernel yet */
1216 case WSI_IMAGE_QUEUED
:
1217 /* find the oldest queued */
1218 if (!image
|| tmp_image
->flip_sequence
< image
->flip_sequence
)
1230 if (connector
->active
) {
1231 ret
= drmModePageFlip(wsi
->fd
, connector
->crtc_id
, image
->fb_id
,
1232 DRM_MODE_PAGE_FLIP_EVENT
, image
);
1234 image
->state
= WSI_IMAGE_FLIPPING
;
1237 wsi_display_debug("page flip err %d %s\n", ret
, strerror(-ret
));
1242 if (ret
== -EINVAL
) {
1243 VkResult result
= wsi_display_setup_connector(connector
, display_mode
);
1245 if (result
!= VK_SUCCESS
) {
1246 image
->state
= WSI_IMAGE_IDLE
;
1250 /* XXX allow setting of position */
1251 ret
= drmModeSetCrtc(wsi
->fd
, connector
->crtc_id
,
1254 &connector
->current_drm_mode
);
1256 /* Assume that the mode set is synchronous and that any
1257 * previous image is now idle.
1259 image
->state
= WSI_IMAGE_DISPLAYING
;
1260 wsi_display_idle_old_displaying(image
);
1261 connector
->active
= true;
1266 if (ret
!= -EACCES
) {
1267 connector
->active
= false;
1268 image
->state
= WSI_IMAGE_IDLE
;
1269 return VK_ERROR_OUT_OF_DATE_KHR
;
1272 /* Some other VT is currently active. Sit here waiting for
1273 * our VT to become active again by polling once a second
1275 usleep(1000 * 1000);
1276 connector
->active
= false;
1281 wsi_display_queue_present(struct wsi_swapchain
*drv_chain
,
1282 uint32_t image_index
,
1283 const VkPresentRegionKHR
*damage
)
1285 struct wsi_display_swapchain
*chain
=
1286 (struct wsi_display_swapchain
*) drv_chain
;
1287 struct wsi_display
*wsi
= chain
->wsi
;
1288 struct wsi_display_image
*image
= &chain
->images
[image_index
];
1291 /* Bail early if the swapchain is broken */
1292 if (chain
->status
!= VK_SUCCESS
)
1293 return chain
->status
;
1295 assert(image
->state
== WSI_IMAGE_DRAWING
);
1296 wsi_display_debug("present %d\n", image_index
);
1298 pthread_mutex_lock(&wsi
->wait_mutex
);
1300 image
->flip_sequence
= ++chain
->flip_sequence
;
1301 image
->state
= WSI_IMAGE_QUEUED
;
1303 result
= _wsi_display_queue_next(drv_chain
);
1304 if (result
!= VK_SUCCESS
)
1305 chain
->status
= result
;
1307 pthread_mutex_unlock(&wsi
->wait_mutex
);
1309 if (result
!= VK_SUCCESS
)
1312 return chain
->status
;
1316 wsi_display_surface_create_swapchain(
1317 VkIcdSurfaceBase
*icd_surface
,
1319 struct wsi_device
*wsi_device
,
1321 const VkSwapchainCreateInfoKHR
*create_info
,
1322 const VkAllocationCallbacks
*allocator
,
1323 struct wsi_swapchain
**swapchain_out
)
1325 struct wsi_display
*wsi
=
1326 (struct wsi_display
*) wsi_device
->wsi
[VK_ICD_WSI_PLATFORM_DISPLAY
];
1328 assert(create_info
->sType
== VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR
);
1330 const unsigned num_images
= create_info
->minImageCount
;
1331 struct wsi_display_swapchain
*chain
=
1332 vk_zalloc(allocator
,
1333 sizeof(*chain
) + num_images
* sizeof(chain
->images
[0]),
1334 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT
);
1337 return VK_ERROR_OUT_OF_HOST_MEMORY
;
1339 VkResult result
= wsi_swapchain_init(wsi_device
, &chain
->base
, device
,
1340 create_info
, allocator
);
1342 chain
->base
.destroy
= wsi_display_swapchain_destroy
;
1343 chain
->base
.get_wsi_image
= wsi_display_get_wsi_image
;
1344 chain
->base
.acquire_next_image
= wsi_display_acquire_next_image
;
1345 chain
->base
.queue_present
= wsi_display_queue_present
;
1346 chain
->base
.present_mode
= create_info
->presentMode
;
1347 chain
->base
.image_count
= num_images
;
1350 chain
->status
= VK_SUCCESS
;
1352 chain
->surface
= (VkIcdSurfaceDisplay
*) icd_surface
;
1354 for (uint32_t image
= 0; image
< chain
->base
.image_count
; image
++) {
1355 result
= wsi_display_image_init(device
, &chain
->base
,
1356 create_info
, allocator
,
1357 &chain
->images
[image
]);
1358 if (result
!= VK_SUCCESS
) {
1361 wsi_display_image_finish(&chain
->base
, allocator
,
1362 &chain
->images
[image
]);
1364 vk_free(allocator
, chain
);
1365 goto fail_init_images
;
1369 *swapchain_out
= &chain
->base
;
1378 wsi_init_pthread_cond_monotonic(pthread_cond_t
*cond
)
1380 pthread_condattr_t condattr
;
1383 if (pthread_condattr_init(&condattr
) != 0)
1384 goto fail_attr_init
;
1386 if (pthread_condattr_setclock(&condattr
, CLOCK_MONOTONIC
) != 0)
1389 if (pthread_cond_init(cond
, &condattr
) != 0)
1390 goto fail_cond_init
;
1396 pthread_condattr_destroy(&condattr
);
1402 wsi_display_init_wsi(struct wsi_device
*wsi_device
,
1403 const VkAllocationCallbacks
*alloc
,
1406 struct wsi_display
*wsi
= vk_zalloc(alloc
, sizeof(*wsi
), 8,
1407 VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE
);
1411 result
= VK_ERROR_OUT_OF_HOST_MEMORY
;
1415 wsi
->fd
= display_fd
;
1418 list_inithead(&wsi
->connectors
);
1420 int ret
= pthread_mutex_init(&wsi
->wait_mutex
, NULL
);
1422 result
= VK_ERROR_OUT_OF_HOST_MEMORY
;
1426 if (!wsi_init_pthread_cond_monotonic(&wsi
->wait_cond
)) {
1427 result
= VK_ERROR_OUT_OF_HOST_MEMORY
;
1431 wsi
->base
.get_support
= wsi_display_surface_get_support
;
1432 wsi
->base
.get_capabilities
= wsi_display_surface_get_capabilities
;
1433 wsi
->base
.get_capabilities2
= wsi_display_surface_get_capabilities2
;
1434 wsi
->base
.get_formats
= wsi_display_surface_get_formats
;
1435 wsi
->base
.get_formats2
= wsi_display_surface_get_formats2
;
1436 wsi
->base
.get_present_modes
= wsi_display_surface_get_present_modes
;
1437 wsi
->base
.create_swapchain
= wsi_display_surface_create_swapchain
;
1439 wsi_device
->wsi
[VK_ICD_WSI_PLATFORM_DISPLAY
] = &wsi
->base
;
1444 pthread_mutex_destroy(&wsi
->wait_mutex
);
1446 vk_free(alloc
, wsi
);
1452 wsi_display_finish_wsi(struct wsi_device
*wsi_device
,
1453 const VkAllocationCallbacks
*alloc
)
1455 struct wsi_display
*wsi
=
1456 (struct wsi_display
*) wsi_device
->wsi
[VK_ICD_WSI_PLATFORM_DISPLAY
];
1459 wsi_for_each_connector(connector
, wsi
) {
1460 wsi_for_each_display_mode(mode
, connector
) {
1461 vk_free(wsi
->alloc
, mode
);
1463 vk_free(wsi
->alloc
, connector
);
1466 pthread_mutex_lock(&wsi
->wait_mutex
);
1467 if (wsi
->wait_thread
) {
1468 pthread_cancel(wsi
->wait_thread
);
1469 pthread_join(wsi
->wait_thread
, NULL
);
1471 pthread_mutex_unlock(&wsi
->wait_mutex
);
1472 pthread_mutex_destroy(&wsi
->wait_mutex
);
1473 pthread_cond_destroy(&wsi
->wait_cond
);
1475 vk_free(alloc
, wsi
);
1480 * Implement vkReleaseDisplay
1483 wsi_release_display(VkPhysicalDevice physical_device
,
1484 struct wsi_device
*wsi_device
,
1485 VkDisplayKHR display
)
1487 struct wsi_display
*wsi
=
1488 (struct wsi_display
*) wsi_device
->wsi
[VK_ICD_WSI_PLATFORM_DISPLAY
];