The open source OpenXR runtime
1// Copyright 2019, Collabora, Ltd.
2// SPDX-License-Identifier: BSL-1.0
3/*!
4 * @file
5 * @brief Common direct mode window code.
6 * @author Lubosz Sarnecki <lubosz.sarnecki@collabora.com>
7 * @author Jakob Bornecrantz <jakob@collabora.com>
8 * @ingroup comp_main
9 */
10
11
12#include "comp_window_direct.h"
13
14#include "util/u_misc.h"
15
16
17static inline struct vk_bundle *
18get_vk(struct comp_target_swapchain *cts)
19{
20 return &cts->base.c->base.vk;
21}
22
23static int
24choose_best_vk_mode_auto(struct comp_target *ct, VkDisplayModePropertiesKHR *mode_properties, int mode_count)
25{
26 if (mode_count == 1)
27 return 0;
28
29 int best_index = 0;
30
31 VkDisplayModeParametersKHR *current = &mode_properties[0].parameters;
32
33 COMP_DEBUG(ct->c, "Available Vk direct mode %d: %dx%d@%.2f", 0, current->visibleRegion.width,
34 current->visibleRegion.height, (float)current->refreshRate / 1000.);
35
36 // First priority: choose mode that maximizes rendered pixels.
37 // Second priority: choose mode with highest refresh rate.
38 for (int i = 1; i < mode_count; i++) {
39 current = &mode_properties[i].parameters;
40
41 COMP_DEBUG(ct->c, "Available Vk direct mode %d: %dx%d@%.2f", i, current->visibleRegion.width,
42 current->visibleRegion.height, (float)current->refreshRate / 1000.);
43
44
45 VkDisplayModeParametersKHR best = mode_properties[best_index].parameters;
46
47 int best_pixels = best.visibleRegion.width * best.visibleRegion.height;
48 int pixels = current->visibleRegion.width * current->visibleRegion.height;
49 if (pixels > best_pixels) {
50 best_index = i;
51 } else if (pixels == best_pixels && current->refreshRate > best.refreshRate) {
52 best_index = i;
53 }
54 }
55 VkDisplayModeParametersKHR best = mode_properties[best_index].parameters;
56 COMP_DEBUG(ct->c, "Auto choosing Vk direct mode %d: %dx%d@%.2f", best_index, best.visibleRegion.width,
57 best.visibleRegion.height, (float)best.refreshRate / 1000.f);
58 return best_index;
59}
60
61static void
62print_modes(struct comp_target *ct, VkDisplayModePropertiesKHR *mode_properties, int mode_count)
63{
64 COMP_PRINT_MODE(ct->c, "Available Vk modes for direct mode");
65 for (int i = 0; i < mode_count; i++) {
66 VkDisplayModePropertiesKHR *props = &mode_properties[i];
67 uint32_t width = props->parameters.visibleRegion.width;
68 uint32_t height = props->parameters.visibleRegion.height;
69 float refresh = (float)props->parameters.refreshRate / 1000.f;
70
71 COMP_PRINT_MODE(ct->c, "| %2d | %dx%d@%.2f", i, width, height, refresh);
72 }
73 COMP_PRINT_MODE(ct->c, "Listed %d modes", mode_count);
74}
75
76static VkDisplayModeKHR
77get_primary_display_mode(struct comp_target_swapchain *cts,
78 VkDisplayKHR display,
79 uint32_t *out_width,
80 uint32_t *out_height)
81{
82 struct vk_bundle *vk = get_vk(cts);
83 struct comp_target *ct = &cts->base;
84 VkDisplayModePropertiesKHR *mode_properties = NULL;
85 uint32_t mode_count = 0;
86 VkResult ret;
87
88 // Get plane properties
89 ret = vk_enumerate_display_mode_properties( //
90 vk, //
91 vk->physical_device, //
92 display, //
93 &mode_count, //
94 &mode_properties); //
95 if (ret != VK_SUCCESS) {
96 COMP_ERROR(ct->c, "vk_enumerate_display_mode_properties: %s", vk_result_string(ret));
97 return VK_NULL_HANDLE;
98 }
99
100
101 /*
102 * Debug information.
103 */
104
105 COMP_DEBUG(ct->c, "Found %d modes", mode_count);
106 print_modes(ct, mode_properties, mode_count);
107
108
109 /*
110 * Select the mode.
111 */
112
113 int chosen_mode = 0;
114
115 int desired_mode = ct->c->settings.desired_mode;
116 if (desired_mode + 1 > (int)mode_count) {
117 COMP_ERROR(ct->c,
118 "Requested mode index %d, but max is %d. Falling "
119 "back to automatic mode selection",
120 desired_mode, mode_count);
121 chosen_mode = choose_best_vk_mode_auto(ct, mode_properties, mode_count);
122 } else if (desired_mode < 0) {
123 chosen_mode = choose_best_vk_mode_auto(ct, mode_properties, mode_count);
124 } else {
125 COMP_DEBUG(ct->c, "Using manually chosen mode %d", desired_mode);
126 chosen_mode = desired_mode;
127 }
128
129 VkDisplayModePropertiesKHR props = mode_properties[chosen_mode];
130
131 COMP_DEBUG(ct->c, "found display mode %dx%d@%.2f", props.parameters.visibleRegion.width,
132 props.parameters.visibleRegion.height, (float)props.parameters.refreshRate / 1000.);
133
134 int64_t new_frame_interval = (int64_t)(1000. * 1000. * 1000. * 1000. / props.parameters.refreshRate);
135
136 COMP_DEBUG(ct->c, "Updating compositor frame interval from %" PRIu64 " (%f Hz) to %" PRIu64 " (%f Hz)",
137 ct->c->frame_interval_ns, 1000. * 1000. * 1000. / (float)ct->c->frame_interval_ns,
138 new_frame_interval, (float)props.parameters.refreshRate / 1000.);
139
140 ct->c->frame_interval_ns = new_frame_interval;
141
142 free(mode_properties);
143
144 *out_width = props.parameters.visibleRegion.width;
145 *out_height = props.parameters.visibleRegion.height;
146
147 return props.displayMode;
148}
149
150static VkDisplayPlaneAlphaFlagBitsKHR
151choose_alpha_mode(VkDisplayPlaneAlphaFlagsKHR flags)
152{
153 if (flags & VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_PREMULTIPLIED_BIT_KHR) {
154 return VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_PREMULTIPLIED_BIT_KHR;
155 }
156 if (flags & VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_BIT_KHR) {
157 return VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_BIT_KHR;
158 }
159 return VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR;
160}
161
162
163/*
164 *
165 * 'Exported' functions.
166 *
167 */
168
169VkResult
170comp_window_direct_create_surface(struct comp_target_swapchain *cts,
171 VkDisplayKHR display,
172 uint32_t width,
173 uint32_t height)
174{
175 struct vk_bundle *vk = get_vk(cts);
176 VkDisplayPlanePropertiesKHR *plane_properties = NULL;
177 uint32_t plane_property_count = 0;
178 VkResult ret;
179
180 // Get plane properties
181 ret = vk_enumerate_physical_display_plane_properties( //
182 vk, //
183 vk->physical_device, //
184 &plane_property_count, //
185 &plane_properties); //
186 if (ret != VK_SUCCESS) {
187 COMP_ERROR(cts->base.c, "vk_enumerate_physical_display_plane_properties: %s", vk_result_string(ret));
188 return VK_ERROR_INITIALIZATION_FAILED;
189 }
190
191 // Select the plane.
192 //! @todo actually select the plane.
193 uint32_t plane_index = 0;
194 uint32_t plane_stack_index = plane_properties[plane_index].currentStackIndex;
195
196 free(plane_properties);
197 plane_properties = NULL;
198
199 // Select the mode.
200 uint32_t mode_width = 0, mode_height = 0;
201 VkDisplayModeKHR display_mode = get_primary_display_mode( //
202 cts, //
203 display, //
204 &mode_width, //
205 &mode_height); //
206
207 if (display_mode == VK_NULL_HANDLE) {
208 COMP_ERROR(cts->base.c, "Failed to find display mode!");
209 return VK_ERROR_INITIALIZATION_FAILED;
210 }
211
212 /*
213 * This fixes a bug on NVIDIA Jetson. Note this isn't so much the NVIDIA
214 * Jetson fault, while the code was working on desktop, Monado did
215 * something wrong. What happned was that Monado would select a mode
216 * with one size, while then creating a VkSurface/VkSwapchain of a
217 * different size. This would work on hardware with scalers/panning
218 * modes. The NVIDIA Jetson apparently doesn't have support for that so
219 * failed when presenting. This patch makes sure that the VkSurface &
220 * VkSwapchain extents match the mode for all direct mode targets.
221 */
222 if (mode_width != width || mode_height != height) {
223 COMP_INFO(cts->base.c,
224 "Ignoring given extent %dx%d and using %dx%d from mode, bugs could happen otherwise.",
225 width, //
226 height, //
227 mode_width, //
228 mode_height); //
229 }
230
231 // We need the capabilities of the selected plane.
232 VkDisplayPlaneCapabilitiesKHR plane_caps;
233 vk->vkGetDisplayPlaneCapabilitiesKHR(vk->physical_device, display_mode, plane_index, &plane_caps);
234
235 VkDisplaySurfaceCreateInfoKHR surface_info = {
236 .sType = VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR,
237 .pNext = NULL,
238 .flags = 0,
239 .displayMode = display_mode,
240 .planeIndex = plane_index,
241 .planeStackIndex = plane_stack_index,
242 .transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
243 .globalAlpha = 1.0,
244 .alphaMode = choose_alpha_mode(plane_caps.supportedAlpha),
245 .imageExtent =
246 {
247 .width = mode_width,
248 .height = mode_height,
249 },
250 };
251
252 // This function is called seldom so ok to always print.
253 vk_print_display_surface_create_info(vk, &surface_info, U_LOGGING_INFO);
254
255 // Everything decided and logged, do the creation.
256 VkSurfaceKHR surface = VK_NULL_HANDLE;
257 ret = vk->vkCreateDisplayPlaneSurfaceKHR( //
258 vk->instance, //
259 &surface_info, //
260 NULL, //
261 &surface); //
262 if (ret != VK_SUCCESS) {
263 COMP_ERROR(cts->base.c, "vkCreateDisplayPlaneSurfaceKHR: %s", vk_result_string(ret));
264 return ret;
265 }
266
267 VK_NAME_SURFACE(vk, surface, "comp_target_swapchain direct surface");
268 cts->surface.handle = surface;
269
270 return VK_SUCCESS;
271}
272
273#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT
274
275int
276comp_window_direct_connect(struct comp_target_swapchain *cts, Display **dpy)
277{
278 *dpy = XOpenDisplay(NULL);
279 if (*dpy == NULL) {
280 COMP_ERROR(cts->base.c, "Could not open X display.");
281 return false;
282 }
283 return true;
284}
285
286VkResult
287comp_window_direct_acquire_xlib_display(struct comp_target_swapchain *cts, Display *dpy, VkDisplayKHR display)
288{
289 struct vk_bundle *vk = get_vk(cts);
290 VkResult ret;
291
292 ret = vk->vkAcquireXlibDisplayEXT(vk->physical_device, dpy, display);
293 if (ret != VK_SUCCESS) {
294 COMP_ERROR(cts->base.c, "vkAcquireXlibDisplayEXT: %s (0x%016" PRIx64 ")", vk_result_string(ret),
295 (uint64_t)display);
296 }
297 if (ret == VK_ERROR_INITIALIZATION_FAILED) {
298 COMP_ERROR(
299 cts->base.c,
300 "If you are using the NVIDIA proprietary driver the above error can be caused by the AllowHMD "
301 "xorg.conf option. Please make sure that AllowHMD is not set (like in '99-HMD.conf' from OpenHMD) "
302 "and that the desktop is not currently extended to this display.");
303 }
304 return ret;
305}
306
307bool
308comp_window_direct_init_swapchain(
309 struct comp_target_swapchain *cts, Display *dpy, VkDisplayKHR display, uint32_t width, uint32_t height)
310{
311 VkResult ret;
312 ret = comp_window_direct_acquire_xlib_display(cts, dpy, display);
313
314 if (ret != VK_SUCCESS) {
315 return false;
316 }
317
318 ret = comp_window_direct_create_surface(cts, display, width, height);
319 if (ret != VK_SUCCESS) {
320 COMP_ERROR(cts->base.c, "Failed to create surface! '%s'", vk_result_string(ret));
321 return false;
322 }
323
324 return true;
325}
326
327#endif