mirror of
https://gitlab.freedesktop.org/monado/monado.git
synced 2025-01-01 12:46:12 +00:00
c/null: Add null compositor
Based on sample compositor code. Co-authored-by: Jakob Bornecrantz <jakob@collabora.com> Co-authored-by: Ryan Pavlik <ryan.pavlik@collabora.com>
This commit is contained in:
parent
8effde1d61
commit
c976c54008
|
@ -235,6 +235,7 @@ option_with_deps(XRT_HAVE_KIMERA_SLAM "Enable Kimera support" DEPENDS kimera_vio
|
||||||
# Feature configuration (sorted)
|
# Feature configuration (sorted)
|
||||||
option(XRT_FEATURE_COLOR_LOG "Enable logging in color on supported platforms" ON)
|
option(XRT_FEATURE_COLOR_LOG "Enable logging in color on supported platforms" ON)
|
||||||
option_with_deps(XRT_FEATURE_COMPOSITOR_MAIN "Build main compositor host functionality" DEPENDS XRT_HAVE_VULKAN "XRT_HAVE_WAYLAND OR XRT_HAVE_XCB OR ANDROID OR WIN32")
|
option_with_deps(XRT_FEATURE_COMPOSITOR_MAIN "Build main compositor host functionality" DEPENDS XRT_HAVE_VULKAN "XRT_HAVE_WAYLAND OR XRT_HAVE_XCB OR ANDROID OR WIN32")
|
||||||
|
option_with_deps(XRT_FEATURE_COMPOSITOR_NULL "Build testing null compositor" DEPENDS XRT_HAVE_VULKAN)
|
||||||
option_with_deps(XRT_FEATURE_IPC "Enable the build of the IPC layer" DEPENDS "NOT WIN32")
|
option_with_deps(XRT_FEATURE_IPC "Enable the build of the IPC layer" DEPENDS "NOT WIN32")
|
||||||
option_with_deps(XRT_FEATURE_OPENXR "Build OpenXR runtime target" DEPENDS XRT_FEATURE_COMPOSITOR_MAIN)
|
option_with_deps(XRT_FEATURE_OPENXR "Build OpenXR runtime target" DEPENDS XRT_FEATURE_COMPOSITOR_MAIN)
|
||||||
option_with_deps(XRT_FEATURE_RENDERDOC "Enable RenderDoc API" DEPENDS "RT_LIBRARY OR WIN32")
|
option_with_deps(XRT_FEATURE_RENDERDOC "Enable RenderDoc API" DEPENDS "RT_LIBRARY OR WIN32")
|
||||||
|
@ -287,7 +288,7 @@ option(XRT_BUILD_SAMPLES "Enable compiling sample code implementations that will
|
||||||
# cmake-format: on
|
# cmake-format: on
|
||||||
|
|
||||||
# Most users won't touch these.
|
# Most users won't touch these.
|
||||||
mark_as_advanced(XRT_FEATURE_COMPOSITOR_MAIN XRT_FEATURE_OPENXR)
|
mark_as_advanced(XRT_FEATURE_COMPOSITOR_MAIN XRT_FEATURE_COMPOSITOR_NULL XRT_FEATURE_OPENXR)
|
||||||
|
|
||||||
# Defaults for OpenXR layer support
|
# Defaults for OpenXR layer support
|
||||||
if(NOT DEFINED XRT_FEATURE_OPENXR_LAYER_DEPTH)
|
if(NOT DEFINED XRT_FEATURE_OPENXR_LAYER_DEPTH)
|
||||||
|
@ -462,6 +463,7 @@ message(STATUS "# XRANDR: ${XRT_HAVE_XRANDR}")
|
||||||
message(STATUS "#")
|
message(STATUS "#")
|
||||||
message(STATUS "# FEATURE_COLOR_LOG: ${XRT_FEATURE_COLOR_LOG}")
|
message(STATUS "# FEATURE_COLOR_LOG: ${XRT_FEATURE_COLOR_LOG}")
|
||||||
message(STATUS "# FEATURE_COMPOSITOR_MAIN: ${XRT_FEATURE_COMPOSITOR_MAIN}")
|
message(STATUS "# FEATURE_COMPOSITOR_MAIN: ${XRT_FEATURE_COMPOSITOR_MAIN}")
|
||||||
|
message(STATUS "# FEATURE_COMPOSITOR_NULL: ${XRT_FEATURE_COMPOSITOR_NULL}")
|
||||||
message(STATUS "# FEATURE_IPC: ${XRT_FEATURE_IPC}")
|
message(STATUS "# FEATURE_IPC: ${XRT_FEATURE_IPC}")
|
||||||
message(STATUS "# FEATURE_OPENXR: ${XRT_FEATURE_OPENXR}")
|
message(STATUS "# FEATURE_OPENXR: ${XRT_FEATURE_OPENXR}")
|
||||||
message(STATUS "# FEATURE_OPENXR_LAYER_CUBE: ${XRT_FEATURE_OPENXR_LAYER_CUBE}")
|
message(STATUS "# FEATURE_OPENXR_LAYER_CUBE: ${XRT_FEATURE_OPENXR_LAYER_CUBE}")
|
||||||
|
|
|
@ -259,6 +259,16 @@ if(XRT_FEATURE_COMPOSITOR_MAIN)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
###
|
||||||
|
# Null compositor
|
||||||
|
#
|
||||||
|
|
||||||
|
if(XRT_FEATURE_COMPOSITOR_NULL)
|
||||||
|
add_subdirectory(null)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
###
|
###
|
||||||
# Multi client compositor library
|
# Multi client compositor library
|
||||||
#
|
#
|
||||||
|
|
6
src/xrt/compositor/null/CMakeLists.txt
Normal file
6
src/xrt/compositor/null/CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# Copyright 2019-2022, Collabora, Ltd.
|
||||||
|
# SPDX-License-Identifier: BSL-1.0
|
||||||
|
|
||||||
|
add_library(comp_null STATIC null_compositor.c null_compositor.h)
|
||||||
|
target_link_libraries(comp_null PUBLIC xrt-interfaces PRIVATE aux_util aux_os aux_vk comp_util comp_multi)
|
||||||
|
target_include_directories(comp_null PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/..)
|
579
src/xrt/compositor/null/null_compositor.c
Normal file
579
src/xrt/compositor/null/null_compositor.c
Normal file
|
@ -0,0 +1,579 @@
|
||||||
|
// Copyright 2019-2022, Collabora, Ltd.
|
||||||
|
// SPDX-License-Identifier: BSL-1.0
|
||||||
|
/*!
|
||||||
|
* @file
|
||||||
|
* @brief Null compositor implementation.
|
||||||
|
*
|
||||||
|
* Based on src/xrt/compositor/main/comp_compositor.c
|
||||||
|
*
|
||||||
|
* @author Jakob Bornecrantz <jakob@collabora.com>
|
||||||
|
* @author Lubosz Sarnecki <lubosz.sarnecki@collabora.com>
|
||||||
|
* @author Ryan Pavlik <ryan.pavlik@collabora.com>
|
||||||
|
* @ingroup comp_null
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "xrt/xrt_gfx_native.h"
|
||||||
|
|
||||||
|
#include "os/os_time.h"
|
||||||
|
|
||||||
|
#include "util/u_misc.h"
|
||||||
|
#include "util/u_time.h"
|
||||||
|
#include "util/u_debug.h"
|
||||||
|
#include "util/u_verify.h"
|
||||||
|
#include "util/u_handles.h"
|
||||||
|
#include "util/u_trace_marker.h"
|
||||||
|
|
||||||
|
#include "util/comp_vulkan.h"
|
||||||
|
|
||||||
|
#include "multi/comp_multi_interface.h"
|
||||||
|
|
||||||
|
#include "null_compositor.h"
|
||||||
|
#include "null_interfaces.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
|
||||||
|
DEBUG_GET_ONCE_LOG_OPTION(log, "XRT_COMPOSITOR_LOG", U_LOGGING_INFO)
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Helper functions.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
static struct vk_bundle *
|
||||||
|
get_vk(struct null_compositor *c)
|
||||||
|
{
|
||||||
|
return &c->base.vk;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Vulkan functions.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const char *instance_extensions_common[] = {
|
||||||
|
VK_KHR_EXTERNAL_FENCE_CAPABILITIES_EXTENSION_NAME, //
|
||||||
|
VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME, //
|
||||||
|
VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME, //
|
||||||
|
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, //
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *required_device_extensions[] = {
|
||||||
|
VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME, //
|
||||||
|
VK_KHR_EXTERNAL_FENCE_EXTENSION_NAME, //
|
||||||
|
VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME, //
|
||||||
|
VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME, //
|
||||||
|
VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, //
|
||||||
|
|
||||||
|
// Platform version of "external_memory"
|
||||||
|
#if defined(XRT_GRAPHICS_BUFFER_HANDLE_IS_FD)
|
||||||
|
VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME,
|
||||||
|
|
||||||
|
#elif defined(XRT_GRAPHICS_BUFFER_HANDLE_IS_AHARDWAREBUFFER)
|
||||||
|
VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME,
|
||||||
|
|
||||||
|
#elif defined(XRT_GRAPHICS_BUFFER_HANDLE_IS_WIN32_HANDLE)
|
||||||
|
VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME,
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error "Need port!"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Platform version of "external_fence" and "external_semaphore"
|
||||||
|
#if defined(XRT_GRAPHICS_SYNC_HANDLE_IS_FD) // Optional
|
||||||
|
|
||||||
|
#elif defined(XRT_GRAPHICS_SYNC_HANDLE_IS_WIN32_HANDLE)
|
||||||
|
VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME, //
|
||||||
|
VK_KHR_EXTERNAL_FENCE_WIN32_EXTENSION_NAME, //
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error "Need port!"
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *optional_device_extensions[] = {
|
||||||
|
VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME, //
|
||||||
|
VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME, //
|
||||||
|
|
||||||
|
// Platform version of "external_fence" and "external_semaphore"
|
||||||
|
#if defined(XRT_GRAPHICS_SYNC_HANDLE_IS_FD) // Optional
|
||||||
|
VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, //
|
||||||
|
VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME, //
|
||||||
|
|
||||||
|
#elif defined(XRT_GRAPHICS_SYNC_HANDLE_IS_WIN32_HANDLE) // Not optional
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error "Need port!"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef VK_KHR_timeline_semaphore
|
||||||
|
VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME,
|
||||||
|
#endif
|
||||||
|
#ifdef VK_EXT_calibrated_timestamps
|
||||||
|
VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME,
|
||||||
|
#endif
|
||||||
|
#ifdef VK_EXT_robustness2
|
||||||
|
VK_EXT_ROBUSTNESS_2_EXTENSION_NAME,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
static VkResult
|
||||||
|
select_instances_extensions(struct null_compositor *c, struct u_string_list *required, struct u_string_list *optional)
|
||||||
|
{
|
||||||
|
#ifdef VK_EXT_display_surface_counter
|
||||||
|
u_string_list_append(optional, VK_EXT_DISPLAY_SURFACE_COUNTER_EXTENSION_NAME);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return VK_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
compositor_init_vulkan(struct null_compositor *c)
|
||||||
|
{
|
||||||
|
struct vk_bundle *vk = get_vk(c);
|
||||||
|
VkResult ret;
|
||||||
|
|
||||||
|
// every backend needs at least the common extensions
|
||||||
|
struct u_string_list *required_instance_ext_list =
|
||||||
|
u_string_list_create_from_array(instance_extensions_common, ARRAY_SIZE(instance_extensions_common));
|
||||||
|
|
||||||
|
struct u_string_list *optional_instance_ext_list = u_string_list_create();
|
||||||
|
|
||||||
|
ret = select_instances_extensions(c, required_instance_ext_list, optional_instance_ext_list);
|
||||||
|
if (ret != VK_SUCCESS) {
|
||||||
|
VK_ERROR(vk, "select_instances_extensions: %s\n\tFailed to select instance extensions.",
|
||||||
|
vk_result_string(ret));
|
||||||
|
u_string_list_destroy(&required_instance_ext_list);
|
||||||
|
u_string_list_destroy(&optional_instance_ext_list);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct u_string_list *required_device_extension_list =
|
||||||
|
u_string_list_create_from_array(required_device_extensions, ARRAY_SIZE(required_device_extensions));
|
||||||
|
|
||||||
|
struct u_string_list *optional_device_extension_list =
|
||||||
|
u_string_list_create_from_array(optional_device_extensions, ARRAY_SIZE(optional_device_extensions));
|
||||||
|
|
||||||
|
struct comp_vulkan_arguments vk_args = {
|
||||||
|
.get_instance_proc_address = vkGetInstanceProcAddr,
|
||||||
|
.required_instance_version = VK_MAKE_VERSION(1, 0, 0),
|
||||||
|
.required_instance_extensions = required_instance_ext_list,
|
||||||
|
.optional_instance_extensions = optional_instance_ext_list,
|
||||||
|
.required_device_extensions = required_device_extension_list,
|
||||||
|
.optional_device_extensions = optional_device_extension_list,
|
||||||
|
.log_level = c->settings.log_level,
|
||||||
|
.only_compute_queue = false, // Regular GFX
|
||||||
|
.selected_gpu_index = -1, // Auto
|
||||||
|
.client_gpu_index = -1, // Auto
|
||||||
|
.timeline_semaphore = true, // Flag is optional, not a hard requirement.
|
||||||
|
};
|
||||||
|
|
||||||
|
struct comp_vulkan_results vk_res = {0};
|
||||||
|
bool bundle_ret = comp_vulkan_init_bundle(vk, &vk_args, &vk_res);
|
||||||
|
|
||||||
|
u_string_list_destroy(&required_instance_ext_list);
|
||||||
|
u_string_list_destroy(&optional_instance_ext_list);
|
||||||
|
u_string_list_destroy(&required_device_extension_list);
|
||||||
|
u_string_list_destroy(&optional_device_extension_list);
|
||||||
|
|
||||||
|
if (!bundle_ret) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
static_assert(ARRAY_SIZE(vk_res.client_gpu_deviceUUID.data) == XRT_UUID_SIZE, "array size mismatch");
|
||||||
|
static_assert(ARRAY_SIZE(vk_res.selected_gpu_deviceUUID.data) == XRT_UUID_SIZE, "array size mismatch");
|
||||||
|
static_assert(ARRAY_SIZE(vk_res.client_gpu_deviceUUID.data) == ARRAY_SIZE(c->sys_info.client_vk_deviceUUID.data), "array size mismatch");
|
||||||
|
static_assert(ARRAY_SIZE(vk_res.selected_gpu_deviceUUID.data) == ARRAY_SIZE(c->sys_info.compositor_vk_deviceUUID.data), "array size mismatch");
|
||||||
|
static_assert(ARRAY_SIZE(vk_res.client_gpu_deviceLUID.data) == XRT_LUID_SIZE, "array size mismatch");
|
||||||
|
static_assert(ARRAY_SIZE(vk_res.client_gpu_deviceLUID.data) == ARRAY_SIZE(c->sys_info.client_d3d_deviceLUID.data), "array size mismatch");
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
c->sys_info.client_vk_deviceUUID = vk_res.client_gpu_deviceUUID;
|
||||||
|
c->sys_info.compositor_vk_deviceUUID = vk_res.selected_gpu_deviceUUID;
|
||||||
|
c->sys_info.client_d3d_deviceLUID = vk_res.client_gpu_deviceLUID;
|
||||||
|
c->sys_info.client_d3d_deviceLUID_valid = vk_res.client_gpu_deviceLUID_valid;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Other init functions.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
static bool
|
||||||
|
compositor_init_pacing(struct null_compositor *c)
|
||||||
|
{
|
||||||
|
xrt_result_t xret = u_pc_fake_create(c->settings.frame_interval_ns, os_monotonic_get_ns(), &c->upc);
|
||||||
|
if (xret != XRT_SUCCESS) {
|
||||||
|
NULL_ERROR(c, "Failed to create fake pacing helper!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
compositor_init_info(struct null_compositor *c)
|
||||||
|
{
|
||||||
|
struct xrt_compositor_info *info = &c->base.base.base.info;
|
||||||
|
|
||||||
|
struct comp_vulkan_formats formats = {0};
|
||||||
|
comp_vulkan_formats_check(get_vk(c), &formats);
|
||||||
|
comp_vulkan_formats_copy_to_info(&formats, info);
|
||||||
|
comp_vulkan_formats_log(c->settings.log_level, &formats);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
compositor_init_sys_info(struct null_compositor *c, struct xrt_device *xdev)
|
||||||
|
{
|
||||||
|
struct xrt_system_compositor_info *sys_info = &c->sys_info;
|
||||||
|
|
||||||
|
// Required by OpenXR spec.
|
||||||
|
sys_info->max_layers = 16;
|
||||||
|
|
||||||
|
// UUIDs and LUID already set in vk init.
|
||||||
|
(void)sys_info->compositor_vk_deviceUUID;
|
||||||
|
(void)sys_info->client_vk_deviceUUID;
|
||||||
|
(void)sys_info->client_d3d_deviceLUID;
|
||||||
|
(void)sys_info->client_d3d_deviceLUID_valid;
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
sys_info->views[0].recommended.width_pixels = 128;
|
||||||
|
sys_info->views[0].recommended.height_pixels = 128;
|
||||||
|
sys_info->views[0].recommended.sample_count = 1;
|
||||||
|
sys_info->views[0].max.width_pixels = 1024;
|
||||||
|
sys_info->views[0].max.height_pixels = 1024;
|
||||||
|
sys_info->views[0].max.sample_count = 1;
|
||||||
|
|
||||||
|
sys_info->views[1].recommended.width_pixels = 128;
|
||||||
|
sys_info->views[1].recommended.height_pixels = 128;
|
||||||
|
sys_info->views[1].recommended.sample_count = 1;
|
||||||
|
sys_info->views[1].max.width_pixels = 1024;
|
||||||
|
sys_info->views[1].max.height_pixels = 1024;
|
||||||
|
sys_info->views[1].max.sample_count = 1;
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
// Copy the list directly.
|
||||||
|
assert(xdev->hmd->blend_mode_count <= XRT_MAX_DEVICE_BLEND_MODES);
|
||||||
|
assert(xdev->hmd->blend_mode_count != 0);
|
||||||
|
assert(xdev->hmd->blend_mode_count <= ARRAY_SIZE(sys_info->supported_blend_modes));
|
||||||
|
for (size_t i = 0; i < xdev->hmd->blend_mode_count; ++i) {
|
||||||
|
assert(u_verify_blend_mode_valid(xdev->hmd->blend_modes[i]));
|
||||||
|
sys_info->supported_blend_modes[i] = xdev->hmd->blend_modes[i];
|
||||||
|
}
|
||||||
|
sys_info->supported_blend_mode_count = (uint8_t)xdev->hmd->blend_mode_count;
|
||||||
|
|
||||||
|
// Refresh rates.
|
||||||
|
sys_info->num_refresh_rates = 1;
|
||||||
|
sys_info->refresh_rates[0] = (float)(1. / time_ns_to_s(c->settings.frame_interval_ns));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Member functions.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
static xrt_result_t
|
||||||
|
null_compositor_begin_session(struct xrt_compositor *xc, enum xrt_view_type type)
|
||||||
|
{
|
||||||
|
struct null_compositor *c = null_compositor(xc);
|
||||||
|
NULL_DEBUG(c, "BEGIN_SESSION");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* No logic needed here for the null compositor, if using the null
|
||||||
|
* compositor as a base for a new compositor put desired logic here.
|
||||||
|
*/
|
||||||
|
|
||||||
|
return XRT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static xrt_result_t
|
||||||
|
null_compositor_end_session(struct xrt_compositor *xc)
|
||||||
|
{
|
||||||
|
struct null_compositor *c = null_compositor(xc);
|
||||||
|
NULL_DEBUG(c, "END_SESSION");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* No logic needed here for the null compositor, if using the null
|
||||||
|
* compositor as a base for a new compositor put desired logic here.
|
||||||
|
*/
|
||||||
|
|
||||||
|
return XRT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static xrt_result_t
|
||||||
|
null_compositor_predict_frame(struct xrt_compositor *xc,
|
||||||
|
int64_t *out_frame_id,
|
||||||
|
uint64_t *out_wake_time_ns,
|
||||||
|
uint64_t *out_predicted_gpu_time_ns,
|
||||||
|
uint64_t *out_predicted_display_time_ns,
|
||||||
|
uint64_t *out_predicted_display_period_ns)
|
||||||
|
{
|
||||||
|
COMP_TRACE_MARKER();
|
||||||
|
|
||||||
|
struct null_compositor *c = null_compositor(xc);
|
||||||
|
NULL_TRACE(c, "PREDICT_FRAME");
|
||||||
|
|
||||||
|
uint64_t now_ns = os_monotonic_get_ns();
|
||||||
|
uint64_t null_desired_present_time_ns = 0;
|
||||||
|
uint64_t null_present_slop_ns = 0;
|
||||||
|
uint64_t null_min_display_period_ns = 0;
|
||||||
|
|
||||||
|
u_pc_predict( //
|
||||||
|
c->upc, // upc
|
||||||
|
now_ns, // now_ns
|
||||||
|
out_frame_id, // out_frame_id
|
||||||
|
out_wake_time_ns, // out_wake_up_time_ns
|
||||||
|
&null_desired_present_time_ns, // out_desired_present_time_ns
|
||||||
|
&null_present_slop_ns, // out_present_slop_ns
|
||||||
|
out_predicted_display_time_ns, // out_predicted_display_time_ns
|
||||||
|
out_predicted_display_period_ns, // out_predicted_display_period_ns
|
||||||
|
&null_min_display_period_ns); // out_min_display_period_ns
|
||||||
|
|
||||||
|
return XRT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static xrt_result_t
|
||||||
|
null_compositor_mark_frame(struct xrt_compositor *xc,
|
||||||
|
int64_t frame_id,
|
||||||
|
enum xrt_compositor_frame_point point,
|
||||||
|
uint64_t when_ns)
|
||||||
|
{
|
||||||
|
COMP_TRACE_MARKER();
|
||||||
|
|
||||||
|
struct null_compositor *c = null_compositor(xc);
|
||||||
|
NULL_TRACE(c, "MARK_FRAME %i", point);
|
||||||
|
|
||||||
|
switch (point) {
|
||||||
|
case XRT_COMPOSITOR_FRAME_POINT_WOKE:
|
||||||
|
u_pc_mark_point(c->upc, U_TIMING_POINT_WAKE_UP, frame_id, when_ns);
|
||||||
|
return XRT_SUCCESS;
|
||||||
|
default: assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return XRT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static xrt_result_t
|
||||||
|
null_compositor_begin_frame(struct xrt_compositor *xc, int64_t frame_id)
|
||||||
|
{
|
||||||
|
struct null_compositor *c = null_compositor(xc);
|
||||||
|
NULL_TRACE(c, "BEGIN_FRAME");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* No logic needed here for the null compositor, if using the null
|
||||||
|
* compositor as a base for a new compositor put desired logic here.
|
||||||
|
*/
|
||||||
|
|
||||||
|
return XRT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static xrt_result_t
|
||||||
|
null_compositor_discard_frame(struct xrt_compositor *xc, int64_t frame_id)
|
||||||
|
{
|
||||||
|
struct null_compositor *c = null_compositor(xc);
|
||||||
|
NULL_TRACE(c, "DISCARD_FRAME");
|
||||||
|
|
||||||
|
// Shouldn't be called.
|
||||||
|
assert(false);
|
||||||
|
|
||||||
|
return XRT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static xrt_result_t
|
||||||
|
null_compositor_layer_commit(struct xrt_compositor *xc, int64_t frame_id, xrt_graphics_sync_handle_t sync_handle)
|
||||||
|
{
|
||||||
|
COMP_TRACE_MARKER();
|
||||||
|
|
||||||
|
struct null_compositor *c = null_compositor(xc);
|
||||||
|
NULL_TRACE(c, "LAYER_COMMIT");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The null compositor doesn't render and frames, but needs to do
|
||||||
|
* minimal bookkeeping and handling of arguments. If using the null
|
||||||
|
* compositor as a base for a new compositor this is where you render
|
||||||
|
* frames to be displayed to devices or remote clients.
|
||||||
|
*/
|
||||||
|
|
||||||
|
u_graphics_sync_unref(&sync_handle);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Time keeping needed to keep the pacer happy.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// When we begin rendering.
|
||||||
|
{
|
||||||
|
uint64_t now_ns = os_monotonic_get_ns();
|
||||||
|
u_pc_mark_point(c->upc, U_TIMING_POINT_BEGIN, frame_id, now_ns);
|
||||||
|
}
|
||||||
|
|
||||||
|
// When we are submitting to the GPU.
|
||||||
|
{
|
||||||
|
uint64_t now_ns = os_monotonic_get_ns();
|
||||||
|
u_pc_mark_point(c->upc, U_TIMING_POINT_SUBMIT, frame_id, now_ns);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now is a good point to garbage collect.
|
||||||
|
comp_swapchain_garbage_collect(&c->base.cscgc);
|
||||||
|
|
||||||
|
return XRT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static xrt_result_t
|
||||||
|
null_compositor_poll_events(struct xrt_compositor *xc, union xrt_compositor_event *out_xce)
|
||||||
|
{
|
||||||
|
struct null_compositor *c = null_compositor(xc);
|
||||||
|
NULL_TRACE(c, "POLL_EVENTS");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The null compositor does only minimal state keeping. If using the
|
||||||
|
* null compositor as a base for a new compositor this is where you can
|
||||||
|
* improve the state tracking. Note this is very often consumed only
|
||||||
|
* by the multi compositor.
|
||||||
|
*/
|
||||||
|
|
||||||
|
U_ZERO(out_xce);
|
||||||
|
|
||||||
|
switch (c->state) {
|
||||||
|
case NULL_COMP_STATE_UNINITIALIZED:
|
||||||
|
NULL_ERROR(c, "Polled uninitialized compositor");
|
||||||
|
out_xce->state.type = XRT_COMPOSITOR_EVENT_NONE;
|
||||||
|
break;
|
||||||
|
case NULL_COMP_STATE_READY: out_xce->state.type = XRT_COMPOSITOR_EVENT_NONE; break;
|
||||||
|
case NULL_COMP_STATE_PREPARED:
|
||||||
|
NULL_DEBUG(c, "PREPARED -> VISIBLE");
|
||||||
|
out_xce->state.type = XRT_COMPOSITOR_EVENT_STATE_CHANGE;
|
||||||
|
out_xce->state.visible = true;
|
||||||
|
c->state = NULL_COMP_STATE_VISIBLE;
|
||||||
|
break;
|
||||||
|
case NULL_COMP_STATE_VISIBLE:
|
||||||
|
NULL_DEBUG(c, "VISIBLE -> FOCUSED");
|
||||||
|
out_xce->state.type = XRT_COMPOSITOR_EVENT_STATE_CHANGE;
|
||||||
|
out_xce->state.visible = true;
|
||||||
|
out_xce->state.focused = true;
|
||||||
|
c->state = NULL_COMP_STATE_FOCUSED;
|
||||||
|
break;
|
||||||
|
case NULL_COMP_STATE_FOCUSED:
|
||||||
|
// No more transitions.
|
||||||
|
out_xce->state.type = XRT_COMPOSITOR_EVENT_NONE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return XRT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
null_compositor_destroy(struct xrt_compositor *xc)
|
||||||
|
{
|
||||||
|
struct null_compositor *c = null_compositor(xc);
|
||||||
|
struct vk_bundle *vk = get_vk(c);
|
||||||
|
|
||||||
|
NULL_DEBUG(c, "NULL_COMP_DESTROY");
|
||||||
|
|
||||||
|
// Make sure we don't have anything to destroy.
|
||||||
|
comp_swapchain_garbage_collect(&c->base.cscgc);
|
||||||
|
|
||||||
|
|
||||||
|
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_deinit_mutex(vk);
|
||||||
|
|
||||||
|
if (vk->instance != VK_NULL_HANDLE) {
|
||||||
|
vk->vkDestroyInstance(vk->instance, NULL);
|
||||||
|
vk->instance = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
comp_base_fini(&c->base);
|
||||||
|
|
||||||
|
u_pc_destroy(&c->upc);
|
||||||
|
|
||||||
|
free(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* 'Exported' functions.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
xrt_result_t
|
||||||
|
null_compositor_create_system(struct xrt_device *xdev, struct xrt_system_compositor **out_xsysc)
|
||||||
|
{
|
||||||
|
struct null_compositor *c = U_TYPED_CALLOC(struct null_compositor);
|
||||||
|
|
||||||
|
c->base.base.base.begin_session = null_compositor_begin_session;
|
||||||
|
c->base.base.base.end_session = null_compositor_end_session;
|
||||||
|
c->base.base.base.predict_frame = null_compositor_predict_frame;
|
||||||
|
c->base.base.base.mark_frame = null_compositor_mark_frame;
|
||||||
|
c->base.base.base.begin_frame = null_compositor_begin_frame;
|
||||||
|
c->base.base.base.discard_frame = null_compositor_discard_frame;
|
||||||
|
c->base.base.base.layer_commit = null_compositor_layer_commit;
|
||||||
|
c->base.base.base.poll_events = null_compositor_poll_events;
|
||||||
|
c->base.base.base.destroy = null_compositor_destroy;
|
||||||
|
c->settings.log_level = debug_get_log_option_log();
|
||||||
|
c->frame.waited.id = -1;
|
||||||
|
c->frame.rendering.id = -1;
|
||||||
|
c->state = NULL_COMP_STATE_READY;
|
||||||
|
c->settings.frame_interval_ns = U_TIME_1S_IN_NS / 20; // 20 FPS
|
||||||
|
c->xdev = xdev;
|
||||||
|
|
||||||
|
NULL_DEBUG(c, "Doing init %p", (void *)c);
|
||||||
|
|
||||||
|
NULL_INFO(c,
|
||||||
|
"\n"
|
||||||
|
"################################################################################\n"
|
||||||
|
"# Null compositor starting, if you intended to use the null compositor (for CI #\n"
|
||||||
|
"# integration) then everything is mostly likely setup correctly. But if you #\n"
|
||||||
|
"# intended to use Monado with real hardware it you probably built Monado #\n"
|
||||||
|
"# without the main compositor, please check your build config and make sure #\n"
|
||||||
|
"# that the main compositor is being built. Also make sure that the environment #\n"
|
||||||
|
"# variable XRT_COMPOSITOR_NULL is not set. #\n"
|
||||||
|
"################################################################################");
|
||||||
|
|
||||||
|
// Do this as early as possible
|
||||||
|
comp_base_init(&c->base);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Main init sequence.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!compositor_init_pacing(c) || //
|
||||||
|
!compositor_init_vulkan(c) || //
|
||||||
|
!compositor_init_sys_info(c, xdev) || //
|
||||||
|
!compositor_init_info(c)) { //
|
||||||
|
NULL_DEBUG(c, "Failed to init compositor %p", (void *)c);
|
||||||
|
c->base.base.base.destroy(&c->base.base.base);
|
||||||
|
|
||||||
|
return XRT_ERROR_VULKAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
NULL_DEBUG(c, "Done %p", (void *)c);
|
||||||
|
|
||||||
|
return comp_multi_create_system_compositor(&c->base.base, &c->sys_info, out_xsysc);
|
||||||
|
}
|
185
src/xrt/compositor/null/null_compositor.h
Normal file
185
src/xrt/compositor/null/null_compositor.h
Normal file
|
@ -0,0 +1,185 @@
|
||||||
|
// Copyright 2019-2022, Collabora, Ltd.
|
||||||
|
// SPDX-License-Identifier: BSL-1.0
|
||||||
|
/*!
|
||||||
|
* @file
|
||||||
|
* @brief Header for null compositor.
|
||||||
|
*
|
||||||
|
* Based on src/xrt/compositor/main/comp_compositor.h
|
||||||
|
*
|
||||||
|
* @author Jakob Bornecrantz <jakob@collabora.com>
|
||||||
|
* @author Lubosz Sarnecki <lubosz.sarnecki@collabora.com>
|
||||||
|
* @author Ryan Pavlik <ryan.pavlik@collabora.com>
|
||||||
|
* @ingroup comp_null
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "xrt/xrt_gfx_vk.h"
|
||||||
|
#include "xrt/xrt_instance.h"
|
||||||
|
|
||||||
|
#include "os/os_time.h"
|
||||||
|
|
||||||
|
#include "util/u_threading.h"
|
||||||
|
#include "util/u_logging.h"
|
||||||
|
#include "util/u_pacing.h"
|
||||||
|
|
||||||
|
#include "util/comp_base.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @defgroup comp_null Null compositor
|
||||||
|
* @ingroup xrt
|
||||||
|
* @brief A non-rendering alternate for the main compositor that still can
|
||||||
|
* support applications fully.
|
||||||
|
*
|
||||||
|
* Monado's design is highly modular, including allowing alternate compositors to
|
||||||
|
* be used. If you are looking to write an additional or alternate compositor to
|
||||||
|
* the one in `src/xrt/compositor/main`, this code is your starting point. It is
|
||||||
|
* the basic implementation of @ref xrt_compositor_native extracted from there,
|
||||||
|
* renamed, and with most implementations removed. Compare with similarly-named
|
||||||
|
* files there to see what was removed, and what helper functionality has been
|
||||||
|
* factored out and may be reusable. For example, you may be able to use @ref
|
||||||
|
* comp_renderer, @ref comp_resources, @ref comp_shaders, and @ref comp_target,
|
||||||
|
* among others.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Structs, enums and defines.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* State to emulate state transitions correctly.
|
||||||
|
*
|
||||||
|
* @ingroup comp_null
|
||||||
|
*/
|
||||||
|
enum null_comp_state
|
||||||
|
{
|
||||||
|
NULL_COMP_STATE_UNINITIALIZED = 0,
|
||||||
|
NULL_COMP_STATE_READY = 1,
|
||||||
|
NULL_COMP_STATE_PREPARED = 2,
|
||||||
|
NULL_COMP_STATE_VISIBLE = 3,
|
||||||
|
NULL_COMP_STATE_FOCUSED = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Tracking frame state.
|
||||||
|
*
|
||||||
|
* @ingroup comp_null
|
||||||
|
*/
|
||||||
|
struct null_comp_frame
|
||||||
|
{
|
||||||
|
int64_t id;
|
||||||
|
uint64_t predicted_display_time_ns;
|
||||||
|
uint64_t desired_present_time_ns;
|
||||||
|
uint64_t present_slop_ns;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Main compositor struct tying everything in the compositor together.
|
||||||
|
*
|
||||||
|
* @implements xrt_compositor_native, comp_base.
|
||||||
|
* @ingroup comp_null
|
||||||
|
*/
|
||||||
|
struct null_compositor
|
||||||
|
{
|
||||||
|
struct comp_base base;
|
||||||
|
|
||||||
|
//! The device we are displaying to.
|
||||||
|
struct xrt_device *xdev;
|
||||||
|
|
||||||
|
//! Pacing helper to drive us forward.
|
||||||
|
struct u_pacing_compositor *upc;
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
enum u_logging_level log_level;
|
||||||
|
|
||||||
|
//! Frame interval that we are using.
|
||||||
|
uint64_t frame_interval_ns;
|
||||||
|
} settings;
|
||||||
|
|
||||||
|
// Kept here for convenience.
|
||||||
|
struct xrt_system_compositor_info sys_info;
|
||||||
|
|
||||||
|
//! State for generating the correct set of events.
|
||||||
|
enum null_comp_state state;
|
||||||
|
|
||||||
|
//! @todo Insert your own required members here
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
struct null_comp_frame waited;
|
||||||
|
struct null_comp_frame rendering;
|
||||||
|
} frame;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Functions and helpers.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Convenience function to convert a xrt_compositor to a null_compositor.
|
||||||
|
* (Down-cast helper.)
|
||||||
|
*
|
||||||
|
* @private @memberof null_compositor
|
||||||
|
* @ingroup comp_null
|
||||||
|
*/
|
||||||
|
static inline struct null_compositor *
|
||||||
|
null_compositor(struct xrt_compositor *xc)
|
||||||
|
{
|
||||||
|
return (struct null_compositor *)xc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Spew level logging.
|
||||||
|
*
|
||||||
|
* @relates null_compositor
|
||||||
|
* @ingroup comp_null
|
||||||
|
*/
|
||||||
|
#define NULL_TRACE(c, ...) U_LOG_IFL_T(c->settings.log_level, __VA_ARGS__);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Debug level logging.
|
||||||
|
*
|
||||||
|
* @relates null_compositor
|
||||||
|
*/
|
||||||
|
#define NULL_DEBUG(c, ...) U_LOG_IFL_D(c->settings.log_level, __VA_ARGS__);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Info level logging.
|
||||||
|
*
|
||||||
|
* @relates null_compositor
|
||||||
|
* @ingroup comp_null
|
||||||
|
*/
|
||||||
|
#define NULL_INFO(c, ...) U_LOG_IFL_I(c->settings.log_level, __VA_ARGS__);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Warn level logging.
|
||||||
|
*
|
||||||
|
* @relates null_compositor
|
||||||
|
* @ingroup comp_null
|
||||||
|
*/
|
||||||
|
#define NULL_WARN(c, ...) U_LOG_IFL_W(c->settings.log_level, __VA_ARGS__);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Error level logging.
|
||||||
|
*
|
||||||
|
* @relates null_compositor
|
||||||
|
* @ingroup comp_null
|
||||||
|
*/
|
||||||
|
#define NULL_ERROR(c, ...) U_LOG_IFL_E(c->settings.log_level, __VA_ARGS__);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
36
src/xrt/compositor/null/null_interfaces.h
Normal file
36
src/xrt/compositor/null/null_interfaces.h
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
// Copyright 2019-2022, Collabora, Ltd.
|
||||||
|
// SPDX-License-Identifier: BSL-1.0
|
||||||
|
/*!
|
||||||
|
* @file
|
||||||
|
* @brief Header for null compositor interfaces.
|
||||||
|
*
|
||||||
|
* @author Jakob Bornecrantz <jakob@collabora.com>
|
||||||
|
* @ingroup comp_null
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "xrt/xrt_compiler.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
struct xrt_device;
|
||||||
|
struct xrt_system_compositor;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Creates a @ref null_compositor.
|
||||||
|
*
|
||||||
|
* @ingroup comp_null
|
||||||
|
*/
|
||||||
|
xrt_result_t
|
||||||
|
null_compositor_create_system(struct xrt_device *xdev, struct xrt_system_compositor **out_xsysc);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
#cmakedefine XRT_FEATURE_COLOR_LOG
|
#cmakedefine XRT_FEATURE_COLOR_LOG
|
||||||
#cmakedefine XRT_FEATURE_COMPOSITOR_MAIN
|
#cmakedefine XRT_FEATURE_COMPOSITOR_MAIN
|
||||||
|
#cmakedefine XRT_FEATURE_COMPOSITOR_NULL
|
||||||
#cmakedefine XRT_FEATURE_IPC
|
#cmakedefine XRT_FEATURE_IPC
|
||||||
#cmakedefine XRT_FEATURE_OPENXR
|
#cmakedefine XRT_FEATURE_OPENXR
|
||||||
#cmakedefine XRT_FEATURE_OPENXR_DEBUG_UTILS
|
#cmakedefine XRT_FEATURE_OPENXR_DEBUG_UTILS
|
||||||
|
|
Loading…
Reference in a new issue