diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d2539130..3859d8b1d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -250,7 +250,9 @@ endif() if(WIN32) set(VK_USE_PLATFORM_WIN32_KHR TRUE) endif() - +if (XRT_HAVE_VULKAN AND NOT ANDROID) + set(VK_USE_PLATFORM_DISPLAY_KHR TRUE) +endif() if(NOT MSVC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic -Wall -Wextra -Wno-unused-parameter") diff --git a/meson.build b/meson.build index 0ba5ad1ea..a559e8271 100644 --- a/meson.build +++ b/meson.build @@ -131,6 +131,9 @@ endif build_xcb_xrandr_direct = build_xcb and build_xlib and xcb_randr.found() and x11_xcb.found() +# requires only vulkan +build_vk_khr_display = true + build_wayland = false if get_option('wayland').enabled() or get_option('wayland').auto() build_wayland = wayland.found() and wayland_protos.found() and wayland_scanner.found() diff --git a/src/xrt/compositor/CMakeLists.txt b/src/xrt/compositor/CMakeLists.txt index 5a90f4f03..94fcfa548 100644 --- a/src/xrt/compositor/CMakeLists.txt +++ b/src/xrt/compositor/CMakeLists.txt @@ -119,7 +119,11 @@ if(XRT_FEATURE_COMPOSITOR_MAIN) main/comp_window_mswin.c ) endif() - + if (VK_USE_PLATFORM_DISPLAY_KHR) + list(APPEND MAIN_SOURCE_FILES + main/comp_window_vk_display.c + ) + endif() # generate wayland protocols if(XRT_HAVE_WAYLAND) diff --git a/src/xrt/compositor/main/comp_compositor.c b/src/xrt/compositor/main/comp_compositor.c index fb96c46ce..b9c1d79d6 100644 --- a/src/xrt/compositor/main/comp_compositor.c +++ b/src/xrt/compositor/main/comp_compositor.c @@ -694,6 +694,13 @@ static const char *instance_extensions_direct_mode[] = { }; #endif +#ifdef VK_USE_PLATFORM_DISPLAY_KHR +static const char *instance_extensions_vk_display[] = { + COMP_INSTANCE_EXTENSIONS_COMMON, + VK_KHR_DISPLAY_EXTENSION_NAME, +}; +#endif + #ifdef VK_USE_PLATFORM_ANDROID_KHR static const char *instance_extensions_android[] = {COMP_INSTANCE_EXTENSIONS_COMMON, VK_KHR_ANDROID_SURFACE_EXTENSION_NAME}; @@ -785,6 +792,12 @@ select_instances_extensions(struct comp_compositor *c, const char ***out_exts, u *out_exts = instance_extensions_windows; *out_num = ARRAY_SIZE(instance_extensions_windows); break; +#endif +#ifdef VK_USE_PLATFORM_DISPLAY_KHR + case WINDOW_VK_DISPLAY: + *out_exts = instance_extensions_vk_display; + *out_num = ARRAY_SIZE(instance_extensions_vk_display); + break; #endif default: return VK_ERROR_INITIALIZATION_FAILED; } @@ -1106,8 +1119,8 @@ compositor_try_window(struct comp_compositor *c, struct comp_target *ct) static bool compositor_init_window_pre_vulkan(struct comp_compositor *c) { - // Nothing to do for nvidia. - if (c->settings.window_type == WINDOW_DIRECT_NVIDIA) { + // Nothing to do for nvidia and vk_display. + if (c->settings.window_type == WINDOW_DIRECT_NVIDIA || c->settings.window_type == WINDOW_VK_DISPLAY) { return true; } @@ -1197,16 +1210,25 @@ compositor_init_window_pre_vulkan(struct comp_compositor *c) static bool compositor_init_window_post_vulkan(struct comp_compositor *c) { - if (c->settings.window_type != WINDOW_DIRECT_NVIDIA) { - return true; + if (c->settings.window_type == WINDOW_DIRECT_NVIDIA) { +#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT + return compositor_try_window(c, comp_window_direct_nvidia_create(c)); +#else + assert(false && "NVIDIA direct mode depends on the xlib/xrandr direct mode."); + return false; +#endif } -#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT - return compositor_try_window(c, comp_window_direct_nvidia_create(c)); + if (c->settings.window_type == WINDOW_VK_DISPLAY) { +#ifdef VK_USE_PLATFORM_DISPLAY_KHR + return compositor_try_window(c, comp_window_vk_display_create(c)); #else - assert(false && "NVIDIA direct mode depends on the xlib/xrandr direct mode."); - return false; + assert(false && "VkDisplayKHR direct mode depends on VK_USE_PLATFORM_DISPLAY_KHR."); + return false; #endif + } + + return true; } static bool diff --git a/src/xrt/compositor/main/comp_settings.c b/src/xrt/compositor/main/comp_settings.c index a1548a552..33474dfef 100644 --- a/src/xrt/compositor/main/comp_settings.c +++ b/src/xrt/compositor/main/comp_settings.c @@ -16,6 +16,7 @@ DEBUG_GET_ONCE_BOOL_OPTION(print_modes, "XRT_COMPOSITOR_PRINT_MODES", false) DEBUG_GET_ONCE_BOOL_OPTION(force_randr, "XRT_COMPOSITOR_FORCE_RANDR", false) DEBUG_GET_ONCE_BOOL_OPTION(force_nvidia, "XRT_COMPOSITOR_FORCE_NVIDIA", false) DEBUG_GET_ONCE_OPTION(nvidia_display, "XRT_COMPOSITOR_FORCE_NVIDIA_DISPLAY", NULL) +DEBUG_GET_ONCE_NUM_OPTION(vk_display, "XRT_COMPOSITOR_FORCE_VK_DISPLAY", -1) DEBUG_GET_ONCE_BOOL_OPTION(force_xcb, "XRT_COMPOSITOR_FORCE_XCB", false) DEBUG_GET_ONCE_BOOL_OPTION(force_wayland, "XRT_COMPOSITOR_FORCE_WAYLAND", false) DEBUG_GET_ONCE_BOOL_OPTION(wireframe, "XRT_COMPOSITOR_WIREFRAME", false) @@ -60,6 +61,10 @@ comp_settings_init(struct comp_settings *s, struct xrt_device *xdev) } s->nvidia_display = debug_get_option_nvidia_display(); + s->vk_display = debug_get_num_option_vk_display(); + if (s->vk_display >= 0) { + s->window_type = WINDOW_VK_DISPLAY; + } if (debug_get_bool_option_force_randr()) { s->window_type = WINDOW_DIRECT_RANDR; diff --git a/src/xrt/compositor/main/comp_settings.h b/src/xrt/compositor/main/comp_settings.h index 4fc28463a..52c6c451b 100644 --- a/src/xrt/compositor/main/comp_settings.h +++ b/src/xrt/compositor/main/comp_settings.h @@ -48,6 +48,7 @@ enum window_type WINDOW_DIRECT_NVIDIA, WINDOW_ANDROID, WINDOW_MSWIN, + WINDOW_VK_DISPLAY, }; @@ -70,6 +71,9 @@ struct comp_settings //! display string forced by user or NULL const char *nvidia_display; + //! vk display number to use when forcing vk_display + int vk_display; + struct { uint32_t width; diff --git a/src/xrt/compositor/main/comp_window.h b/src/xrt/compositor/main/comp_window.h index 060f382ce..bb799e80e 100644 --- a/src/xrt/compositor/main/comp_window.h +++ b/src/xrt/compositor/main/comp_window.h @@ -68,6 +68,14 @@ struct comp_target * comp_window_direct_nvidia_create(struct comp_compositor *c); #endif +/*! + * Create a direct surface to an HMD on VkDisplay. + * + * @ingroup comp_main + * @public @memberof comp_window_direct_vk_display + */ +struct comp_target * +comp_window_vk_display_create(struct comp_compositor *c); #ifdef XRT_OS_ANDROID diff --git a/src/xrt/compositor/main/comp_window_vk_display.c b/src/xrt/compositor/main/comp_window_vk_display.c new file mode 100644 index 000000000..52058cb5f --- /dev/null +++ b/src/xrt/compositor/main/comp_window_vk_display.c @@ -0,0 +1,248 @@ +// Copyright 2019-2021, Collabora, Ltd. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Direct mode on PLATFORM_DISPLAY_KHR code. + * @author Christoph Haag + * @author Lubosz Sarnecki + * @author Jakob Bornecrantz + * @ingroup comp_main + */ + +#include "util/u_misc.h" + +#include "main/comp_window_direct.h" + +/* + * + * Private structs + * + */ + +/*! + * Probed display. + */ +struct vk_display +{ + VkDisplayPropertiesKHR display_properties; + VkDisplayKHR display; +}; + +/*! + * Direct mode "window" into a device, using PLATFORM_DISPLAY_KHR. + * + * @implements comp_target_swapchain + */ +struct comp_window_vk_display +{ + struct comp_target_swapchain base; + + struct vk_display *displays; + uint16_t num_displays; +}; + +/* + * + * Forward declare functions + * + */ + +static void +comp_window_vk_display_destroy(struct comp_target *ct); + +static bool +comp_window_vk_display_init(struct comp_target *ct); + +static struct vk_display * +comp_window_vk_display_current_display(struct comp_window_vk_display *w); + +static bool +comp_window_vk_display_init_swapchain(struct comp_target *ct, uint32_t width, uint32_t height); + + +/* + * + * Functions. + * + */ + +static void +_flush(struct comp_target *ct) +{ + (void)ct; +} + +static void +_update_window_title(struct comp_target *ct, const char *title) +{ + (void)ct; + (void)title; +} + +struct comp_target * +comp_window_vk_display_create(struct comp_compositor *c) +{ + struct comp_window_vk_display *w = U_TYPED_CALLOC(struct comp_window_vk_display); + + comp_target_swapchain_init_set_fnptrs(&w->base); + + w->base.base.name = "VkDisplayKHR"; + w->base.base.destroy = comp_window_vk_display_destroy; + w->base.base.flush = _flush; + w->base.base.init_pre_vulkan = comp_window_vk_display_init; + w->base.base.init_post_vulkan = comp_window_vk_display_init_swapchain; + w->base.base.set_title = _update_window_title; + w->base.base.c = c; + + return &w->base.base; +} + +static void +comp_window_vk_display_destroy(struct comp_target *ct) +{ + struct comp_window_vk_display *w_direct = (struct comp_window_vk_display *)ct; + + comp_target_swapchain_cleanup(&w_direct->base); + + for (uint32_t i = 0; i < w_direct->num_displays; i++) { + struct vk_display *d = &w_direct->displays[i]; + d->display = VK_NULL_HANDLE; + } + + if (w_direct->displays != NULL) + free(w_direct->displays); + + free(ct); +} + +static bool +append_vk_display_entry(struct comp_window_vk_display *w, struct VkDisplayPropertiesKHR *disp) +{ + w->base.base.c->settings.preferred.width = disp->physicalResolution.width; + w->base.base.c->settings.preferred.height = disp->physicalResolution.height; + struct vk_display d = {.display_properties = *disp, .display = disp->display}; + + w->num_displays += 1; + + U_ARRAY_REALLOC_OR_FREE(w->displays, struct vk_display, w->num_displays); + + if (w->displays == NULL) + COMP_ERROR(w->base.base.c, "Unable to reallocate vk_display displays"); + + w->displays[w->num_displays - 1] = d; + + return true; +} + +static void +print_found_displays(struct comp_compositor *c, struct VkDisplayPropertiesKHR *display_props, uint32_t display_count) +{ + COMP_ERROR(c, "== Found Displays =="); + for (uint32_t i = 0; i < display_count; i++) { + struct VkDisplayPropertiesKHR *p = &display_props[i]; + + COMP_ERROR(c, "[%d] %s with resolution %dx%d, dims %dx%d", i, p->displayName, + p->physicalResolution.width, p->physicalResolution.height, p->physicalDimensions.width, + p->physicalDimensions.height); + } +} + +static bool +comp_window_vk_display_init(struct comp_target *ct) +{ + struct comp_window_vk_display *w_direct = (struct comp_window_vk_display *)ct; + + // Sanity check. + if (ct->c->vk.instance == VK_NULL_HANDLE) { + COMP_ERROR(ct->c, "Vulkan not initialized before vk display init!"); + return false; + } + + struct vk_bundle comp_vk = ct->c->vk; + + uint32_t display_count; + if (comp_vk.vkGetPhysicalDeviceDisplayPropertiesKHR(comp_vk.physical_device, &display_count, NULL) != + VK_SUCCESS) { + COMP_ERROR(ct->c, "Failed to get vulkan display count"); + return false; + } + + if (display_count == 0) { + COMP_ERROR(ct->c, "No Vulkan displays found."); + return false; + } + + struct VkDisplayPropertiesKHR *display_props = U_TYPED_ARRAY_CALLOC(VkDisplayPropertiesKHR, display_count); + + if (display_props && comp_vk.vkGetPhysicalDeviceDisplayPropertiesKHR(comp_vk.physical_device, &display_count, + display_props) != VK_SUCCESS) { + COMP_ERROR(ct->c, "Failed to get display properties"); + free(display_props); + return false; + } + + if (ct->c->settings.vk_display > (int)display_count) { + COMP_ERROR(ct->c, "Requested display %d, but only %d found.", ct->c->settings.vk_display, + display_count); + print_found_displays(ct->c, display_props, display_count); + free(display_props); + return false; + } + + append_vk_display_entry(w_direct, &display_props[ct->c->settings.vk_display]); + + struct vk_display *d = comp_window_vk_display_current_display(w_direct); + if (!d) { + COMP_ERROR(ct->c, "display not found!"); + print_found_displays(ct->c, display_props, display_count); + free(display_props); + return false; + } + + free(display_props); + + return true; +} + +static struct vk_display * +comp_window_vk_display_current_display(struct comp_window_vk_display *w) +{ + int index = w->base.base.c->settings.display; + if (index == -1) + index = 0; + + if (w->num_displays <= (uint32_t)index) + return NULL; + + return &w->displays[index]; +} + +static bool +init_swapchain(struct comp_target_swapchain *cts, VkDisplayKHR display, uint32_t width, uint32_t height) +{ + VkResult ret; + + ret = comp_window_direct_create_surface(cts, display, width, height); + if (ret != VK_SUCCESS) { + COMP_ERROR(cts->base.c, "Failed to create surface! '%s'", vk_result_string(ret)); + return false; + } + + return true; +} + +static bool +comp_window_vk_display_init_swapchain(struct comp_target *ct, uint32_t width, uint32_t height) +{ + struct comp_window_vk_display *w_direct = (struct comp_window_vk_display *)ct; + + struct vk_display *d = comp_window_vk_display_current_display(w_direct); + if (!d) { + COMP_ERROR(ct->c, "display not found."); + return false; + } + + COMP_DEBUG(ct->c, "Will use display: %s", d->display_properties.displayName); + + return init_swapchain(&w_direct->base, d->display, width, height); +} diff --git a/src/xrt/compositor/meson.build b/src/xrt/compositor/meson.build index 3379bb293..3ff8b758a 100644 --- a/src/xrt/compositor/meson.build +++ b/src/xrt/compositor/meson.build @@ -36,6 +36,10 @@ compositor_srcs = [ compile_args = [] +if build_vk_khr_display + compositor_srcs += ['main/comp_window_vk_display.c'] +endif + if build_xcb compositor_srcs += ['main/comp_window_xcb.c'] compositor_deps += [xcb] diff --git a/src/xrt/include/xrt/meson.build b/src/xrt/include/xrt/meson.build index e2e836df7..bab700cd8 100644 --- a/src/xrt/include/xrt/meson.build +++ b/src/xrt/include/xrt/meson.build @@ -151,6 +151,9 @@ endif if build_xcb_xrandr_direct vulkan_conf.set('VK_USE_PLATFORM_XLIB_XRANDR_EXT', true) endif +if build_vk_khr_display + vulkan_conf.set('VK_USE_PLATFORM_DISPLAY_KHR', true) +endif xrt_config_vulkan_h = configure_file( output: 'xrt_config_vulkan.h', diff --git a/src/xrt/include/xrt/xrt_config_vulkan.h.cmake_in b/src/xrt/include/xrt/xrt_config_vulkan.h.cmake_in index f364a3024..d9bbf959a 100644 --- a/src/xrt/include/xrt/xrt_config_vulkan.h.cmake_in +++ b/src/xrt/include/xrt/xrt_config_vulkan.h.cmake_in @@ -14,3 +14,4 @@ #cmakedefine VK_USE_PLATFORM_XCB_KHR #cmakedefine VK_USE_PLATFORM_XLIB_XRANDR_EXT #cmakedefine VK_USE_PLATFORM_WIN32_KHR +#cmakedefine VK_USE_PLATFORM_DISPLAY_KHR