// Copyright 2019, Collabora, Ltd. // SPDX-License-Identifier: BSL-1.0 /*! * @file * @brief Main compositor written using Vulkan implementation. * @author Jakob Bornecrantz * @author Lubosz Sarnecki * @ingroup comp */ #include #include #include #include "util/u_debug.h" #include "main/comp_compositor.h" #include "main/comp_client_interface.h" static void compositor_destroy(struct xrt_compositor *xc) { struct comp_compositor *c = comp_compositor(xc); struct vk_bundle *vk = &c->vk; COMP_DEBUG(c, "DESTROY"); if (c->r) { comp_renderer_destroy(c->r); c->r = NULL; } vk_swapchain_cleanup(&c->window->swapchain); if (c->window != NULL) { c->window->destroy(c->window); c->window = NULL; } if (vk->cmd_pool != VK_NULL_HANDLE) { vk->vkDestroyCommandPool(vk->device, vk->cmd_pool, NULL); vk->cmd_pool = VK_NULL_HANDLE; } if (vk->device != VK_NULL_HANDLE) { vk->vkDestroyDevice(vk->device, NULL); vk->device = VK_NULL_HANDLE; } vk_destroy_validation_callback(vk); if (vk->instance != VK_NULL_HANDLE) { vk->vkDestroyInstance(vk->instance, NULL); vk->instance = VK_NULL_HANDLE; } free(c); } static void compositor_begin_session(struct xrt_compositor *xc, enum xrt_view_type type) { struct comp_compositor *c = comp_compositor(xc); COMP_DEBUG(c, "BEGIN_SESSION"); } static void compositor_end_session(struct xrt_compositor *xc) { struct comp_compositor *c = comp_compositor(xc); COMP_DEBUG(c, "END_SESSION"); } static void compositor_wait_frame(struct xrt_compositor *xc, int64_t *predicted_display_time, int64_t *predicted_display_period) { struct comp_compositor *c = comp_compositor(xc); COMP_SPEW(c, "WAIT_FRAME"); //! @todo set *predicted_display_time // *predicted_display_time = 0; // *predicted_display_period = 0; } static void compositor_begin_frame(struct xrt_compositor *xc) { struct comp_compositor *c = comp_compositor(xc); COMP_SPEW(c, "BEGIN_FRAME"); } static void compositor_discard_frame(struct xrt_compositor *xc) { struct comp_compositor *c = comp_compositor(xc); COMP_SPEW(c, "DISCARD_FRAME"); } static void compositor_end_frame(struct xrt_compositor *xc, enum xrt_blend_mode blend_mode, struct xrt_swapchain **xscs, uint32_t *acquired_index, uint32_t num_swapchains) { struct comp_compositor *c = comp_compositor(xc); COMP_SPEW(c, "END_FRAME"); struct comp_swapchain_image *right; struct comp_swapchain_image *left; // Stereo! if (num_swapchains == 2) { left = &comp_swapchain(xscs[0])->images[acquired_index[0]]; right = &comp_swapchain(xscs[1])->images[acquired_index[1]]; comp_renderer_frame(c->r, left, right); } else { COMP_ERROR(c, "non-stereo rendering not supported"); } } /* * * Vulkan functions. * */ #define GET_PROC(name) (PFN_##name) c->vk.vkGetInstanceProcAddr(NULL, #name); #define GET_DEV_PROC(c, name) \ (PFN_##name) c->vk.vkGetDeviceProcAddr(c->vk.device, #name); #define GET_INS_PROC(c, name) \ (PFN_##name) c->vk.vkGetInstanceProcAddr(c->vk.instance, #name); #define GET_DEV_PROC(c, name) \ (PFN_##name) c->vk.vkGetDeviceProcAddr(c->vk.device, #name); VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr(VkInstance instance, const char *pName); static VkResult find_get_instance_proc_addr(struct comp_compositor *c) { //! @todo Do any library loading here. c->vk.vkGetInstanceProcAddr = vkGetInstanceProcAddr; // Fill in all loader functions. // clang-format off c->vk.vkCreateInstance = GET_PROC(vkCreateInstance); // clang-format on return VK_SUCCESS; } #ifdef XRT_ENABLE_VK_VALIDATION #define COMPOSITOR_DEBUG_VULKAN_EXTENSIONS VK_EXT_DEBUG_REPORT_EXTENSION_NAME, #else #define COMPOSITOR_DEBUG_VULKAN_EXTENSIONS #endif #define COMPOSITOR_COMMON_VULKAN_EXTENSIONS \ COMPOSITOR_DEBUG_VULKAN_EXTENSIONS \ VK_KHR_SURFACE_EXTENSION_NAME, \ VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, \ VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME, \ VK_KHR_EXTERNAL_FENCE_CAPABILITIES_EXTENSION_NAME, \ VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME static const char *instance_extensions_none[] = { COMPOSITOR_COMMON_VULKAN_EXTENSIONS}; #ifdef VK_USE_PLATFORM_XCB_KHR static const char *instance_extensions_xcb[] = { COMPOSITOR_COMMON_VULKAN_EXTENSIONS, VK_KHR_XCB_SURFACE_EXTENSION_NAME, }; #endif #ifdef VK_USE_PLATFORM_WAYLAND_KHR static const char *instance_extensions_wayland[] = { COMPOSITOR_COMMON_VULKAN_EXTENSIONS, VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME, }; #endif #ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT static const char *instance_extensions_direct_mode[] = { COMPOSITOR_COMMON_VULKAN_EXTENSIONS, VK_KHR_DISPLAY_EXTENSION_NAME, VK_EXT_DIRECT_MODE_DISPLAY_EXTENSION_NAME, VK_EXT_ACQUIRE_XLIB_DISPLAY_EXTENSION_NAME, }; #endif static VkResult select_instances_extensions(struct comp_compositor *c, const char ***out_exts, uint32_t *out_num) { switch (c->settings.window_type) { case WINDOW_NONE: *out_exts = instance_extensions_none; *out_num = ARRAY_SIZE(instance_extensions_none); break; #ifdef VK_USE_PLATFORM_XCB_KHR case WINDOW_XCB: *out_exts = instance_extensions_xcb; *out_num = ARRAY_SIZE(instance_extensions_xcb); break; #endif #ifdef VK_USE_PLATFORM_WAYLAND_KHR case WINDOW_WAYLAND: *out_exts = instance_extensions_wayland; *out_num = ARRAY_SIZE(instance_extensions_wayland); break; #endif #ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT case WINDOW_DIRECT_MODE: *out_exts = instance_extensions_direct_mode; *out_num = ARRAY_SIZE(instance_extensions_direct_mode); break; #endif default: return VK_ERROR_INITIALIZATION_FAILED; } return VK_SUCCESS; } static VkResult create_instance(struct comp_compositor *c) { const char **instance_extensions; uint32_t num_extensions; VkResult ret; VkApplicationInfo app_info = { .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .pApplicationName = "Collabora Compositor", .pEngineName = "Monado", .apiVersion = VK_MAKE_VERSION(1, 0, 2), }; ret = select_instances_extensions(c, &instance_extensions, &num_extensions); if (ret != VK_SUCCESS) { COMP_ERROR(c, "Failed to select instance extensions: %s", vk_result_string(ret)); return ret; } #ifdef XRT_ENABLE_VK_VALIDATION const char *instance_layers[] = { "VK_LAYER_LUNARG_standard_validation", }; #endif VkInstanceCreateInfo instance_info = { .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, .pApplicationInfo = &app_info, .enabledExtensionCount = num_extensions, .ppEnabledExtensionNames = instance_extensions, #ifdef XRT_ENABLE_VK_VALIDATION .enabledLayerCount = ARRAY_SIZE(instance_layers), .ppEnabledLayerNames = instance_layers, #endif }; ret = c->vk.vkCreateInstance(&instance_info, NULL, &c->vk.instance); if (ret != VK_SUCCESS) { COMP_ERROR(c, "vkCreateInstance: %s\n", vk_result_string(ret)); COMP_ERROR(c, "Failed to create Vulkan instance"); return ret; } ret = vk_get_instance_functions(&c->vk); if (ret != VK_SUCCESS) { COMP_ERROR(c, "Failed to get Vulkan instance functions: %s", vk_result_string(ret)); return ret; } #ifdef XRT_ENABLE_VK_VALIDATION vk_init_validation_callback(&c->vk); #endif return ret; } static bool compositor_init_vulkan(struct comp_compositor *c) { VkResult ret; ret = find_get_instance_proc_addr(c); if (ret != VK_SUCCESS) { return false; } ret = create_instance(c); if (ret != VK_SUCCESS) { return false; } ret = vk_create_device(&c->vk); if (ret != VK_SUCCESS) { return false; } ret = vk_init_cmd_pool(&c->vk); if (ret != VK_SUCCESS) { return false; } return true; } /* * * Other functions. * */ void comp_compositor_print(struct comp_compositor *c, const char *func, const char *fmt, ...) { fprintf(stderr, "%s - ", func); va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); fprintf(stderr, "\n"); } static bool compositor_try_window(struct comp_compositor *c, struct comp_window *window) { if (window == NULL) { return false; } if (!window->init(window)) { window->destroy(window); return false; } else { COMP_DEBUG(c, "Window backend %s initialized!", window->name); c->window = window; return true; } } static bool compositor_init_window(struct comp_compositor *c) { // Setup the initial width from the settings. c->current.width = c->settings.width; c->current.height = c->settings.height; switch (c->settings.window_type) { case WINDOW_AUTO: #ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT if (compositor_try_window(c, comp_window_direct_create(c))) { c->settings.window_type = WINDOW_DIRECT_MODE; return true; } #endif #ifdef VK_USE_PLATFORM_XCB_KHR if (compositor_try_window(c, comp_window_xcb_create(c))) { c->settings.window_type = WINDOW_XCB; return true; } #endif #ifdef VK_USE_PLATFORM_WAYLAND_KHR if (compositor_try_window(c, comp_window_wayland_create(c))) { c->settings.window_type = WINDOW_WAYLAND; return true; } #endif COMP_ERROR(c, "Failed to auto detect window support!"); break; case WINDOW_XCB: #ifdef VK_USE_PLATFORM_XCB_KHR compositor_try_window(c, comp_window_xcb_create(c)); #else COMP_ERROR(c, "XCB support not compiled in!"); #endif break; case WINDOW_WAYLAND: #ifdef VK_USE_PLATFORM_WAYLAND_KHR compositor_try_window(c, comp_window_wayland_create(c)); #else COMP_ERROR(c, "Wayland support not compiled in!"); #endif break; case WINDOW_DIRECT_MODE: #ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT compositor_try_window(c, comp_window_direct_create(c)); #else COMP_ERROR(c, "Direct mode support not compiled in!"); #endif break; default: COMP_ERROR(c, "Unknown window type!"); break; } // Failed to create? if (c->window == NULL) { return false; } return true; } static void _sc_dimension_cb(uint32_t width, uint32_t height, void *ptr) { struct comp_compositor *c = (struct comp_compositor *)ptr; COMP_DEBUG(c, "_sc_dimension_cb %dx%d", width, height); c->current.width = width; c->current.height = height; } static bool compositor_init_swapchain(struct comp_compositor *c) { //! @todo Make c->window->init_swachain call vk_swapchain_init and give //! _sc_dimension_cb to window or just have it call a function? vk_swapchain_init(&c->window->swapchain, &c->vk, _sc_dimension_cb, (void *)c); if (!c->window->init_swapchain(c->window, c->current.width, c->current.height)) { COMP_ERROR(c, "Window init_swapchain failed!"); goto err_destroy; } return true; // Error path. err_destroy: c->window->destroy(c->window); c->window = NULL; return false; } static bool compositor_init_renderer(struct comp_compositor *c) { c->r = comp_renderer_create(c); if (c->r == NULL) { return false; } return true; } struct xrt_compositor_fd * comp_compositor_create(struct xrt_device *xdev, bool flip_y) { struct comp_compositor *c = calloc(1, sizeof(struct comp_compositor)); c->base.base.create_swapchain = comp_swapchain_create; c->base.base.begin_session = compositor_begin_session; c->base.base.end_session = compositor_end_session; c->base.base.wait_frame = compositor_wait_frame; c->base.base.begin_frame = compositor_begin_frame; c->base.base.discard_frame = compositor_discard_frame; c->base.base.end_frame = compositor_end_frame; c->base.base.destroy = compositor_destroy; c->xdev = xdev; COMP_DEBUG(c, "Doing init %p", (void *)c); // Init the settings to default. comp_settings_init(&c->settings, xdev); c->settings.flip_y = flip_y; // Need to select window backend before creating Vulkan, then // swapchain will initialize the window fully and the swapchain, and // finally the renderer is created which renderers to window/swapchain. // clang-format off if (!compositor_init_window(c) || !compositor_init_vulkan(c) || !compositor_init_swapchain(c) || !compositor_init_renderer(c)) { COMP_DEBUG(c, "Failed to init compositor %p", (void *)c); c->base.base.destroy(&c->base.base); return NULL; } // clang-format on COMP_DEBUG(c, "Done %p", (void *)c); return &c->base; }