The open source OpenXR runtime
at prediction-2 327 lines 11 kB view raw
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