sdl_test: Add SDL test framework

This commit is contained in:
Jakob Bornecrantz 2022-06-01 20:16:18 +01:00
parent bea3569016
commit 0fedf6fe7d
11 changed files with 1809 additions and 0 deletions

View file

@ -32,3 +32,7 @@ endif()
if(XRT_FEATURE_STEAMVR_PLUGIN)
add_subdirectory(steamvr_drv)
endif()
if(XRT_FEATURE_SERVICE AND XRT_HAVE_SDL2 AND XRT_HAVE_VULKAN)
add_subdirectory(sdl_test)
endif()

View file

@ -0,0 +1,37 @@
# Copyright 2020-2022, Collabora, Ltd.
# SPDX-License-Identifier: BSL-1.0
add_executable(
sdl-test
sdl_compositor.c
sdl_device.c
sdl_hack_stubs.c
sdl_instance.c
sdl_internal.h
sdl_internal.hpp
sdl_main.c
sdl_program.cpp
sdl_swapchain.c
)
add_sanitizers(sdl-test)
target_link_libraries(
sdl-test
PRIVATE
xrt-external-imgui-sdl2
aux_os
aux_ogl
aux_util
comp_util
comp_multi
ipc_server
st_gui
drv_includes
drv_simulated
)
if(WIN32)
target_link_libraries(gui PRIVATE SDL2::SDL2main)
endif()
install(TARGETS sdl-test RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})

View file

@ -0,0 +1,608 @@
// Copyright 2019-2022, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief SDL compositor implementation.
*
* Based on src/xrt/compositor/null/null_compositor.c
*
* @author Jakob Bornecrantz <jakob@collabora.com>
* @author Lubosz Sarnecki <lubosz.sarnecki@collabora.com>
* @author Ryan Pavlik <ryan.pavlik@collabora.com>
* @ingroup sdl_test
*/
#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 "sdl_internal.h"
#include <stdio.h>
#include <stdarg.h>
/*
*
* Helper functions.
*
*/
static struct vk_bundle *
get_vk(struct sdl_compositor *c)
{
return &c->base.vk;
}
#define SC_TRACE(c, ...) U_LOG_IFL_T(c->base.vk.log_level, __VA_ARGS__);
#define SC_DEBUG(c, ...) U_LOG_IFL_D(c->base.vk.log_level, __VA_ARGS__);
#define SC_INFO(c, ...) U_LOG_IFL_I(c->base.vk.log_level, __VA_ARGS__);
#define SC_WARN(c, ...) U_LOG_IFL_W(c->base.vk.log_level, __VA_ARGS__);
#define SC_ERROR(c, ...) U_LOG_IFL_E(c->base.vk.log_level, __VA_ARGS__);
/*
*
* 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_image_format_list
VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME,
#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 sdl_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 sdl_compositor *c, enum u_logging_level log_level)
{
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 = 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 sdl_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) {
SC_ERROR(c, "Failed to create fake pacing helper!");
return false;
}
return true;
}
static bool
compositor_init_info(struct sdl_compositor *c)
{
struct vk_bundle *vk = get_vk(c);
struct xrt_compositor_info *info = &c->base.base.base.info;
struct comp_vulkan_formats formats = {0};
comp_vulkan_formats_check(vk, &formats);
comp_vulkan_formats_copy_to_info(&formats, info);
comp_vulkan_formats_log(c->base.vk.log_level, &formats);
return true;
}
static bool
compositor_init_sys_info(struct sdl_compositor *c, struct sdl_program *sp, 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;
// Get window size and set recommended size to it.
const int min = 128;
const int max = 16 * 1024;
int w = 0, h = 0;
SDL_GetWindowSize(sp->win, &w, &h);
if (w <= min || h <= min) {
U_LOG_W("Window size is %ix%i which is smaller then %ix%i upping size.", w, h, min, min);
w = min;
h = min;
}
// clang-format off
sys_info->views[0].recommended.width_pixels = w;
sys_info->views[0].recommended.height_pixels = h;
sys_info->views[0].recommended.sample_count = 1;
sys_info->views[0].max.width_pixels = max;
sys_info->views[0].max.height_pixels = max;
sys_info->views[0].max.sample_count = 1;
sys_info->views[1].recommended.width_pixels = min; // Second view is minimum
sys_info->views[1].recommended.height_pixels = min; // Second view is minimum
sys_info->views[1].recommended.sample_count = 1;
sys_info->views[1].max.width_pixels = max;
sys_info->views[1].max.height_pixels = max;
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
sdl_compositor_begin_session(struct xrt_compositor *xc, enum xrt_view_type type)
{
struct sdl_compositor *c = &from_comp(xc)->c;
SC_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
sdl_compositor_end_session(struct xrt_compositor *xc)
{
struct sdl_compositor *c = &from_comp(xc)->c;
SC_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
sdl_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 sdl_compositor *c = &from_comp(xc)->c;
SC_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
sdl_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 sdl_compositor *c = &from_comp(xc)->c;
SC_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
sdl_compositor_begin_frame(struct xrt_compositor *xc, int64_t frame_id)
{
struct sdl_compositor *c = &from_comp(xc)->c;
SC_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
sdl_compositor_discard_frame(struct xrt_compositor *xc, int64_t frame_id)
{
struct sdl_compositor *c = &from_comp(xc)->c;
SC_TRACE(c, "DISCARD_FRAME");
// Shouldn't be called.
assert(false);
return XRT_SUCCESS;
}
static xrt_result_t
sdl_compositor_layer_commit(struct xrt_compositor *xc, int64_t frame_id, xrt_graphics_sync_handle_t sync_handle)
{
COMP_TRACE_MARKER();
struct sdl_program *sp = from_comp(xc);
struct sdl_compositor *c = &sp->c;
SC_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);
}
// Render with SDL.
sdl_program_plus_render(sp->spp);
// 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
sdl_compositor_poll_events(struct xrt_compositor *xc, union xrt_compositor_event *out_xce)
{
struct sdl_compositor *c = &from_comp(xc)->c;
SC_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 SDL_COMP_STATE_UNINITIALIZED:
SC_ERROR(c, "Polled uninitialized compositor");
out_xce->state.type = XRT_COMPOSITOR_EVENT_NONE;
break;
case SDL_COMP_STATE_READY: out_xce->state.type = XRT_COMPOSITOR_EVENT_NONE; break;
case SDL_COMP_STATE_PREPARED:
SC_DEBUG(c, "PREPARED -> VISIBLE");
out_xce->state.type = XRT_COMPOSITOR_EVENT_STATE_CHANGE;
out_xce->state.visible = true;
c->state = SDL_COMP_STATE_VISIBLE;
break;
case SDL_COMP_STATE_VISIBLE:
SC_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 = SDL_COMP_STATE_FOCUSED;
break;
case SDL_COMP_STATE_FOCUSED:
// No more transitions.
out_xce->state.type = XRT_COMPOSITOR_EVENT_NONE;
break;
}
return XRT_SUCCESS;
}
static void
sdl_compositor_destroy(struct xrt_compositor *xc)
{
struct sdl_compositor *c = &from_comp(xc)->c;
struct vk_bundle *vk = get_vk(c);
SC_DEBUG(c, "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);
// Don't free as we are sub allocated.
}
/*
*
* 'Exported' functions.
*
*/
void
sdl_compositor_init(struct sdl_program *sp)
{
struct xrt_device *xdev = &sp->xdev_base;
enum u_logging_level log_level = sp->log_level;
struct sdl_compositor *c = &sp->c;
c->base.base.base.begin_session = sdl_compositor_begin_session;
c->base.base.base.end_session = sdl_compositor_end_session;
c->base.base.base.predict_frame = sdl_compositor_predict_frame;
c->base.base.base.mark_frame = sdl_compositor_mark_frame;
c->base.base.base.begin_frame = sdl_compositor_begin_frame;
c->base.base.base.discard_frame = sdl_compositor_discard_frame;
c->base.base.base.layer_commit = sdl_compositor_layer_commit;
c->base.base.base.poll_events = sdl_compositor_poll_events;
c->base.base.base.destroy = sdl_compositor_destroy;
c->base.vk.log_level = log_level;
c->frame.waited.id = -1;
c->frame.rendering.id = -1;
c->state = SDL_COMP_STATE_READY;
c->settings.frame_interval_ns = U_TIME_1S_IN_NS / 20; // 20 FPS
SC_DEBUG(c, "Doing init %p", (void *)c);
// Do this as early as possible
comp_base_init(&c->base);
// Override some comp_base functions.
c->base.base.base.create_swapchain = sdl_swapchain_create;
c->base.base.base.import_swapchain = sdl_swapchain_import;
/*
* Main init sequence.
*/
if (!compositor_init_pacing(c) || //
!compositor_init_vulkan(c, log_level) || //
!compositor_init_sys_info(c, sp, xdev) || //
!compositor_init_info(c)) { //
SC_DEBUG(c, "Failed to init compositor %p", (void *)c);
c->base.base.base.destroy(&c->base.base.base);
assert(false);
}
SC_DEBUG(c, "Done %p", (void *)c);
}
xrt_result_t
sdl_compositor_create_system(struct sdl_program *sp, struct xrt_system_compositor **out_xsysc)
{
// Standard app pacer.
struct u_pacing_app_factory *upaf = NULL;
xrt_result_t xret = u_pa_factory_create(&upaf);
assert(xret == XRT_SUCCESS && upaf != NULL);
return comp_multi_create_system_compositor(&sp->c.base.base, upaf, &sp->c.sys_info, out_xsysc);
}

View file

@ -0,0 +1,149 @@
// Copyright 2020-2022, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Shared default implementation of the device with compositor.
* @author Jakob Bornecrantz <jakob@collabora.com>
*/
#include "sdl_internal.h"
#include "util/u_device.h"
#include "util/u_distortion_mesh.h"
static void
sdl_hmd_update_inputs(struct xrt_device *xdev)
{
// Empty, you should put code to update the attached inputs fields.
}
static void
sdl_hmd_get_tracked_pose(struct xrt_device *xdev,
enum xrt_input_name name,
uint64_t at_timestamp_ns,
struct xrt_space_relation *out_relation)
{
struct sdl_program *sp = from_xdev(xdev);
if (name != XRT_INPUT_GENERIC_HEAD_POSE) {
U_LOG_E("Unknown input name");
return;
}
struct xrt_space_relation relation = XRT_SPACE_RELATION_ZERO;
relation.pose = sp->state.head.pose;
relation.relation_flags = //
XRT_SPACE_RELATION_POSITION_TRACKED_BIT | //
XRT_SPACE_RELATION_POSITION_VALID_BIT | //
XRT_SPACE_RELATION_ORIENTATION_VALID_BIT | //
XRT_SPACE_RELATION_ORIENTATION_TRACKED_BIT; //
*out_relation = relation;
}
static void
sdl_hmd_get_view_poses(struct xrt_device *xdev,
const struct xrt_vec3 *default_eye_relation,
uint64_t at_timestamp_ns,
uint32_t view_count,
struct xrt_space_relation *out_head_relation,
struct xrt_fov *out_fovs,
struct xrt_pose *out_poses)
{
u_device_get_view_poses( //
xdev, //
default_eye_relation, //
at_timestamp_ns, //
view_count, //
out_head_relation, //
out_fovs, //
out_poses); //
}
static void
sdl_hmd_destroy(struct xrt_device *xdev)
{
struct sdl_program *sp = from_xdev(xdev);
if (xdev->hmd->distortion.mesh.vertices) {
free(xdev->hmd->distortion.mesh.vertices);
xdev->hmd->distortion.mesh.vertices = NULL;
}
if (xdev->hmd->distortion.mesh.indices) {
free(xdev->hmd->distortion.mesh.indices);
xdev->hmd->distortion.mesh.indices = NULL;
}
(void)sp; // We are apart of sdl_program, do not free.
}
/*
*
* 'Exported' functions.
*
*/
void
sdl_device_init(struct sdl_program *sp)
{
struct xrt_device *xdev = &sp->xdev_base;
// Setup pointers.
xdev->inputs = sp->inputs;
xdev->input_count = ARRAY_SIZE(sp->inputs);
xdev->tracking_origin = &sp->origin;
xdev->hmd = &sp->hmd;
// Name and type.
xdev->name = XRT_DEVICE_GENERIC_HMD;
xdev->device_type = XRT_DEVICE_TYPE_HMD;
// Print name.
snprintf(xdev->str, XRT_DEVICE_NAME_LEN, "SDL HMD");
snprintf(xdev->serial, XRT_DEVICE_NAME_LEN, "SDL HMD");
// Input info.
xdev->inputs[0].name = XRT_INPUT_GENERIC_HEAD_POSE;
xdev->inputs[0].active = true;
// Function pointers.
xdev->update_inputs = sdl_hmd_update_inputs;
xdev->get_tracked_pose = sdl_hmd_get_tracked_pose;
xdev->get_view_poses = sdl_hmd_get_view_poses;
xdev->destroy = sdl_hmd_destroy;
// Minimum needed stuff.
struct u_device_simple_info info;
info.display.w_pixels = 1280;
info.display.h_pixels = 720;
info.display.w_meters = 0.13f;
info.display.h_meters = 0.07f;
info.lens_horizontal_separation_meters = 0.13f / 2.0f;
info.lens_vertical_position_meters = 0.07f / 2.0f;
info.fov[0] = 85.0f * ((float)(M_PI) / 180.0f);
info.fov[1] = 85.0f * ((float)(M_PI) / 180.0f);
if (!u_device_setup_split_side_by_side(xdev, &info)) {
U_LOG_E("Failed to setup basic device info");
return;
}
// Refresh rate.
xdev->hmd->screens[0].nominal_frame_interval_ns = time_s_to_ns(1.0f / 60.0f);
// Blend mode(s), setup after u_device_setup_split_side_by_side.
xdev->hmd->blend_modes[0] = XRT_BLEND_MODE_OPAQUE;
xdev->hmd->blend_mode_count = 1;
// Distortion information, fills in xdev->compute_distortion().
u_distortion_mesh_set_none(xdev);
// Tracking origin.
xdev->tracking_origin->offset = (struct xrt_pose)XRT_POSE_IDENTITY;
xdev->tracking_origin->type = XRT_TRACKING_TYPE_OTHER;
snprintf(xdev->tracking_origin->name, XRT_TRACKING_NAME_LEN, "SDL Tracking");
}

View file

@ -0,0 +1,31 @@
// Copyright 2019-2022, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Stubs needed to be able to link in the ipc layer.
* @author Jakob Bornecrantz <jakob@collabora.com>
*/
#include "xrt/xrt_compiler.h"
struct xrt_instance;
struct xrt_system_devices;
int
oxr_sdl2_hack_create(void **out_hack)
{
return 0;
}
void
oxr_sdl2_hack_start(void *hack, struct xrt_instance *xinst, struct xrt_system_devices *xsysd)
{
// Noop
}
void
oxr_sdl2_hack_stop(void **hack)
{
// Noop
}

View file

@ -0,0 +1,129 @@
// Copyright 2020-2022, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Shared default implementation of the instance with compositor.
* @author Jakob Bornecrantz <jakob@collabora.com>
*/
#include "xrt/xrt_system.h"
#include "xrt/xrt_instance.h"
#include "xrt/xrt_config_drivers.h"
#include "util/u_misc.h"
#include "util/u_trace_marker.h"
#include "sdl_internal.h"
#if 0 && defined(XRT_BUILD_DRIVER_SIMULATED)
#include "simulated/simulated_interface.h"
#define USE_SIMULATED
#endif
/*
*
* System devices functions.
*
*/
static void
sdl_system_devices_destroy(struct xrt_system_devices *xsysd)
{
struct sdl_program *sp = from_xsysd(xsysd);
for (size_t i = 0; i < xsysd->xdev_count; i++) {
xrt_device_destroy(&xsysd->xdevs[i]);
}
(void)sp; // We are apart of sdl_program, do not free.
}
/*
*
* Instance functions.
*
*/
static xrt_result_t
sdl_instance_get_prober(struct xrt_instance *xinst, struct xrt_prober **out_xp)
{
return XRT_ERROR_PROBER_NOT_SUPPORTED;
}
static xrt_result_t
sdl_instance_create_system(struct xrt_instance *xinst,
struct xrt_system_devices **out_xsysd,
struct xrt_system_compositor **out_xsysc)
{
assert(out_xsysd != NULL);
assert(*out_xsysd == NULL);
assert(out_xsysc == NULL || *out_xsysc == NULL);
struct sdl_program *sp = from_xinst(xinst);
*out_xsysd = &sp->xsysd_base;
// Early out if we only want devices.
if (out_xsysc == NULL) {
return XRT_SUCCESS;
}
sdl_compositor_create_system(sp, out_xsysc);
return XRT_SUCCESS;
}
static void
sdl_instance_destroy(struct xrt_instance *xinst)
{
struct sdl_program *sp = from_xinst(xinst);
// Frees struct.
sdl_program_plus_destroy(sp->spp);
}
/*
*
* Exported function(s).
*
*/
void
sdl_system_devices_init(struct sdl_program *sp)
{
sp->xsysd_base.destroy = sdl_system_devices_destroy;
#ifdef USE_SIMULATED
struct xrt_device *head = simulated_hmd_create();
#else
struct xrt_device *head = &sp->xdev_base;
#endif
// Setup the device base as the only device.
sp->xsysd_base.xdevs[0] = head;
sp->xsysd_base.xdev_count = 1;
sp->xsysd_base.roles.head = head;
}
void
sdl_instance_init(struct sdl_program *sp)
{
sp->xinst_base.create_system = sdl_instance_create_system;
sp->xinst_base.get_prober = sdl_instance_get_prober;
sp->xinst_base.destroy = sdl_instance_destroy;
}
xrt_result_t
xrt_instance_create(struct xrt_instance_info *ii, struct xrt_instance **out_xinst)
{
u_trace_marker_init();
struct sdl_program *sp = sdl_program_plus_create();
*out_xinst = &sp->xinst_base;
return XRT_SUCCESS;
}

View file

@ -0,0 +1,410 @@
// Copyright 2020-2022, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Internal header for SDL XR system.
* @author Jakob Bornecrantz <jakob@collabora.com>
* @ingroup sdl_test
*/
#include "xrt/xrt_system.h"
#include "xrt/xrt_device.h"
#include "xrt/xrt_instance.h"
#include "xrt/xrt_tracking.h"
#include "xrt/xrt_compositor.h"
#include "util/u_pacing.h"
#include "util/u_logging.h"
#include "util/comp_base.h"
#include "util/comp_swapchain.h"
#include "SDL2/SDL.h"
#include "ogl/ogl_api.h"
#ifdef __cplusplus
extern "C" {
#endif
struct sdl_program;
/*!
* Sub-class of @ref comp_swapchain, used to do OpenGL rendering.
*
* @ingroup sdl_test
*/
struct sdl_swapchain
{
struct comp_swapchain base;
//! Pointer back to main program.
struct sdl_program *sp;
//! Cached width and height.
int w, h;
//! Number of textures in base.base.base.image_count.
GLuint textures[XRT_MAX_SWAPCHAIN_IMAGES];
//! Same number of images as textures.
GLuint memory[XRT_MAX_SWAPCHAIN_IMAGES];
};
/*!
* State to emulate state transitions correctly.
*
* @ingroup sdl_test
*/
enum sdl_comp_state
{
SDL_COMP_STATE_UNINITIALIZED = 0,
SDL_COMP_STATE_READY = 1,
SDL_COMP_STATE_PREPARED = 2,
SDL_COMP_STATE_VISIBLE = 3,
SDL_COMP_STATE_FOCUSED = 4,
};
/*!
* Tracking frame state.
*
* @ingroup sdl_test
*/
struct sdl_comp_frame
{
int64_t id;
uint64_t predicted_display_time_ns;
uint64_t desired_present_time_ns;
uint64_t present_slop_ns;
};
/*!
* Split out for convinecne.
*
* @ingroup sdl_test
*/
struct sdl_compositor
{
//! Base native compositor.
struct comp_base base;
//! Pacing helper to drive us forward.
struct u_pacing_compositor *upc;
struct
{
//! 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 sdl_comp_state state;
//! @todo Insert your own required members here
struct
{
struct sdl_comp_frame waited;
struct sdl_comp_frame rendering;
} frame;
};
struct sdl_program_plus;
/*!
* C base class for the SDL program.
*
* @ingroup sdl_test
*/
struct sdl_program
{
//! Base class for devices.
struct xrt_device xdev_base;
//! Instance base.
struct xrt_instance xinst_base;
//! System devices base.
struct xrt_system_devices xsysd_base;
//! SDL compositor struct.
struct sdl_compositor c;
//! Created system compositor.
struct xrt_system_compositor *xsysc;
//! Inputs exposed by the SDL device.
struct xrt_input inputs[1];
//! HMD parts exposed by the SDL device to become a HMD.
struct xrt_hmd_parts hmd;
//! Tracking origin that the device is located in.
struct xrt_tracking_origin origin;
//! The current log level.
enum u_logging_level log_level;
struct
{
struct
{
//! The pose of the head, only used for view space.
struct xrt_pose pose;
} head;
struct
{
//! Pose of each individual eye.
struct xrt_pose pose;
//! Fov of each individual eye.
struct xrt_fov fov;
} left, right;
} state;
//! The main window.
SDL_Window *win;
//! Main OpenGL context.
SDL_GLContext ctx;
//! Protects the OpenGL context.
struct os_mutex current_mutex;
//! Pointer back to the C++ part of the program.
struct sdl_program_plus *spp;
};
static inline struct sdl_program *
from_xinst(struct xrt_instance *xinst)
{
return container_of(xinst, struct sdl_program, xinst_base);
}
static inline struct sdl_program *
from_xsysd(struct xrt_system_devices *xsysd)
{
return container_of(xsysd, struct sdl_program, xsysd_base);
}
static inline struct sdl_program *
from_xdev(struct xrt_device *xdev)
{
return container_of(xdev, struct sdl_program, xdev_base);
}
static inline struct sdl_program *
from_comp(struct xrt_compositor *xc)
{
return container_of(xc, struct sdl_program, c.base.base);
}
/*!
* Spew level logging.
*
* @relates sdl_program
* @ingroup sdl_test
*/
#define SP_TRACE(sp, ...) U_LOG_IFL_T(sp->log_level, __VA_ARGS__);
/*!
* Debug level logging.
*
* @relates sdl_program
*/
#define SP_DEBUG(sp, ...) U_LOG_IFL_D(sp->log_level, __VA_ARGS__);
/*!
* Info level logging.
*
* @relates sdl_program
* @ingroup sdl_test
*/
#define SP_INFO(sp, ...) U_LOG_IFL_I(sp->log_level, __VA_ARGS__);
/*!
* Warn level logging.
*
* @relates sdl_program
* @ingroup sdl_test
*/
#define SP_WARN(sp, ...) U_LOG_IFL_W(sp->log_level, __VA_ARGS__);
/*!
* Error level logging.
*
* @relates sdl_program
* @ingroup sdl_test
*/
#define SP_ERROR(sp, ...) U_LOG_IFL_E(sp->log_level, __VA_ARGS__);
/*!
* Check for OpenGL errors, context needs to be current.
*
* @ingroup sdl_test
*/
#define CHECK_GL() \
do { \
GLint err = glGetError(); \
if (err != 0) { \
U_LOG_RAW("%s:%u: error: 0x%04x", __func__, __LINE__, err); \
} \
} while (false)
/*!
* Makes the OpenGL context current in this thread, takes lock.
*
* @ingroup sdl_test
*/
static inline void
sdl_make_current(struct sdl_program *sp)
{
os_mutex_lock(&sp->current_mutex);
SDL_GL_MakeCurrent(sp->win, sp->ctx);
}
/*!
* Unmakes the any OpenGL context current in this thread, releases the lock.
*
* @ingroup sdl_test
*/
static inline void
sdl_make_uncurrent(struct sdl_program *sp)
{
SDL_GL_MakeCurrent(NULL, NULL);
os_mutex_unlock(&sp->current_mutex);
}
/*
*
* sdl_device.c
*
*/
/*!
* Init the @ref xrt_device sub struct.
*
* In sdl_device.c
*
* @ingroup sdl_test
*/
void
sdl_device_init(struct sdl_program *sp);
/*
*
* sdl_swapchain.c
*
*/
/*!
* Implementation of @ref xrt_compositor::create_swapchain.
*
* @ingroup sdl_test
*/
xrt_result_t
sdl_swapchain_create(struct xrt_compositor *xc,
const struct xrt_swapchain_create_info *info,
struct xrt_swapchain **out_xsc);
/*!
* Implementation of @ref xrt_compositor::import_swapchain.
*
* @ingroup sdl_test
*/
xrt_result_t
sdl_swapchain_import(struct xrt_compositor *xc,
const struct xrt_swapchain_create_info *info,
struct xrt_image_native *native_images,
uint32_t native_image_count,
struct xrt_swapchain **out_xsc);
/*
*
* sdl_compositor.c
*
*/
/*!
* Initializes the compositor part of the SDL program.
*
* @ingroup sdl_test
*/
void
sdl_compositor_init(struct sdl_program *sp);
/*!
* Creates the system compositor that wraps the native compositor.
*
* @ingroup sdl_test
*/
xrt_result_t
sdl_compositor_create_system(struct sdl_program *sp, struct xrt_system_compositor **out_xsysc);
/*
*
* sdl_instance.c
*
*/
/*!
* Init the @ref xrt_system_devices sub struct.
*
* @ingroup sdl_test
*/
void
sdl_system_devices_init(struct sdl_program *sp);
/*!
* Init the @ref xrt_instance sub struct.
*
* @ingroup sdl_test
*/
void
sdl_instance_init(struct sdl_program *sp);
/*
*
* sdl_program.cpp
*
*/
/*!
* Create the SDL program.
*
* @ingroup sdl_test
*/
struct sdl_program *
sdl_program_plus_create();
/*!
* Render a frame, called by the compositor when layers have been committed.
*
* @ingroup sdl_test
*/
void
sdl_program_plus_render(struct sdl_program_plus *spp);
/*!
* Destroy the SDL program.
*
* @ingroup sdl_test
*/
void
sdl_program_plus_destroy(struct sdl_program_plus *spp);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,21 @@
// Copyright 2020-2022, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Internal C++ header for SDL XR system.
* @author Jakob Bornecrantz <jakob@collabora.com>
* @ingroup sdl_test
*/
#pragma once
#ifndef __cplusplus
#error "This header is C++ only"
#endif
#include "sdl_internal.h"
struct sdl_program_plus : sdl_program
{
// CPP only things
};

View file

@ -0,0 +1,28 @@
// Copyright 2019-2022, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Main file for sdl compositor experiments.
* @author Jakob Bornecrantz <jakob@collabora.com>
*/
#include "util/u_trace_marker.h"
#include <SDL2/SDL.h>
#include <SDL2/SDL_main.h>
// Insert the on load constructor to init trace marker.
U_TRACE_TARGET_SETUP(U_TRACE_WHICH_SERVICE)
int
ipc_server_main(int argc, char *argv[]);
int
main(int argc, char *argv[])
{
u_trace_marker_init();
return ipc_server_main(argc, argv);
}

View file

@ -0,0 +1,178 @@
// Copyright 2020-2022, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief C++ program part for the SDL test.
* @author Jakob Bornecrantz <jakob@collabora.com>
* @ingroup sdl_test
*/
#include "ogl/ogl_api.h"
#include "util/u_misc.h"
#include "sdl_internal.hpp"
void
sdl_create_window(struct sdl_program *sp)
{
if (SDL_Init(SDL_INIT_EVERYTHING) < 0) {
assert(false);
}
char title[1024];
snprintf(title, sizeof(title), "Monado! ☃");
int x = SDL_WINDOWPOS_UNDEFINED;
int y = SDL_WINDOWPOS_UNDEFINED;
int w = 1920;
int h = 1080;
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
int window_flags = 0;
window_flags |= SDL_WINDOW_SHOWN;
window_flags |= SDL_WINDOW_OPENGL;
window_flags |= SDL_WINDOW_RESIZABLE;
window_flags |= SDL_WINDOW_ALLOW_HIGHDPI;
#if 0
window_flags |= SDL_WINDOW_MAXIMIZED;
#endif
sp->win = SDL_CreateWindow(title, x, y, w, h, window_flags);
if (sp->win == NULL) {
assert(false);
}
sp->ctx = SDL_GL_CreateContext(sp->win);
if (sp->ctx == NULL) {
assert(false);
}
// Make the context current in this thread for loading OpenGL.
sdl_make_current(sp);
SDL_GL_SetSwapInterval(1); // Enable vsync
// Setup OpenGL bindings.
bool err = gladLoadGL((GLADloadfunc)SDL_GL_GetProcAddress) == 0;
if (err) {
assert(false);
}
// We are going to render on a different thread, make sure to unbind it.
sdl_make_uncurrent(sp);
}
extern "C" struct sdl_program *
sdl_program_plus_create()
{
sdl_program_plus &spp = *new sdl_program_plus();
spp.spp = &spp;
os_mutex_init(&spp.current_mutex);
// Initial state.
spp.log_level = U_LOGGING_INFO;
spp.state.head.pose = XRT_POSE_IDENTITY;
// Create the window, init before sub components.
sdl_create_window(&spp);
// Init sub components.
sdl_instance_init(&spp);
sdl_system_devices_init(&spp);
sdl_device_init(&spp);
sdl_compositor_init(&spp); // Needs the window.
return &spp;
}
extern "C" void
sdl_program_plus_render(struct sdl_program_plus *spp_ptr)
{
auto &spp = *spp_ptr;
// Make context current
sdl_make_current(&spp);
// Flush the events.
SDL_Event e = {0};
while (SDL_PollEvent(&e)) {
// Nothing for now.
}
if (spp.c.base.slot.layer_count == 0) {
glClearColor(0.2f, 0.2f, 0.2f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
} else if (spp.c.base.slot.layers[0].data.type == XRT_LAYER_STEREO_PROJECTION ||
spp.c.base.slot.layers[0].data.type == XRT_LAYER_STEREO_PROJECTION_DEPTH) {
auto &l = spp.c.base.slot.layers[0];
auto &ssc = *(sdl_swapchain *)l.sc_array[0];
GLuint tex = ssc.textures[l.data.stereo.l.sub.image_index];
glClearColor(0.2f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
GLuint fbo = 0;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
CHECK_GL();
glFramebufferTexture2D( //
GL_READ_FRAMEBUFFER, // GLenum target
GL_COLOR_ATTACHMENT0, // GLenum attachment
GL_TEXTURE_2D, // GLenum textarget
tex, // GLuint texture
0); // GLint level
CHECK_GL();
int w, h;
SDL_GetWindowSize(spp.win, &w, &h);
glBlitFramebuffer( //
0, // GLint srcX0
0, // GLint srcY0
ssc.w, // GLint srcX1
ssc.h, // GLint srcY1
0, // GLint dstX0
0, // GLint dstY0
w, // GLint dstX1
h, // GLint dstY1
GL_COLOR_BUFFER_BIT, // GLbitfield mask
GL_NEAREST); // GLenum filter
CHECK_GL();
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
CHECK_GL();
glDeleteFramebuffers(1, &fbo);
} else {
glClearColor(1.0f, 0.0f, 1.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
}
// Display what we rendered.
SDL_GL_SwapWindow(spp.win);
// Will be used when creating swapchains, unbind it.
sdl_make_uncurrent(&spp);
}
extern "C" void
sdl_program_plus_destroy(struct sdl_program_plus *spp)
{
os_mutex_destroy(&spp->current_mutex);
delete spp;
}

View file

@ -0,0 +1,214 @@
// Copyright 2022, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Swapchain code for the sdl code.
* @author Jakob Bornecrantz <jakob@collabora.com>
* @ingroup sdl_test
*/
#include "sdl_internal.h"
#include "util/u_handles.h"
#include "ogl/ogl_helpers.h"
static int64_t
vk_format_to_gl(int64_t format)
{
switch (format) {
case 4 /* VK_FORMAT_R5G6B5_UNORM_PACK16 */: return 0; // GL_RGB565?
case 23 /* VK_FORMAT_R8G8B8_UNORM */: return GL_RGB8; // Should not be used, colour precision.
case 29 /* VK_FORMAT_R8G8B8_SRGB */: return GL_SRGB8;
case 30 /* VK_FORMAT_B8G8R8_UNORM */: return 0;
case 37 /* VK_FORMAT_R8G8B8A8_UNORM */: return GL_RGBA8; // Should not be used, colour precision.
case 43 /* VK_FORMAT_R8G8B8A8_SRGB */: return GL_SRGB8_ALPHA8;
case 44 /* VK_FORMAT_B8G8R8A8_UNORM */: return 0;
case 50 /* VK_FORMAT_B8G8R8A8_SRGB */: return 0;
case 64 /* VK_FORMAT_A2B10G10R10_UNORM_PACK32 */: return GL_RGB10_A2;
case 84 /* VK_FORMAT_R16G16B16_UNORM */: return GL_RGB16;
case 90 /* VK_FORMAT_R16G16B16_SFLOAT */: return GL_RGB16F;
case 91 /* VK_FORMAT_R16G16B16A16_UNORM */: return GL_RGBA16;
case 97 /* VK_FORMAT_R16G16B16A16_SFLOAT */: return GL_RGBA16F;
case 124 /* VK_FORMAT_D16_UNORM */: return GL_DEPTH_COMPONENT16;
case 125 /* VK_FORMAT_X8_D24_UNORM_PACK32 */: return 0; // GL_DEPTH_COMPONENT24?
case 126 /* VK_FORMAT_D32_SFLOAT */: return GL_DEPTH_COMPONENT32F;
case 127 /* VK_FORMAT_S8_UINT */: return 0; // GL_STENCIL_INDEX8?
case 129 /* VK_FORMAT_D24_UNORM_S8_UINT */: return GL_DEPTH24_STENCIL8;
case 130 /* VK_FORMAT_D32_SFLOAT_S8_UINT */: return GL_DEPTH32F_STENCIL8;
default: U_LOG_W("Cannot convert VK format %" PRIu64 " to GL format!", format); return 0;
}
}
static void
post_init_setup(struct sdl_swapchain *ssc, struct sdl_program *sp, const struct xrt_swapchain_create_info *info)
{
SP_DEBUG(sp, "CREATE");
// Setup fields
ssc->sp = sp;
ssc->w = (int)info->width;
ssc->h = (int)info->height;
sdl_make_current(sp);
GLuint binding_enum = 0;
GLuint tex_target = 0;
ogl_texture_target_for_swapchain_info(info, &tex_target, &binding_enum);
uint32_t image_count = ssc->base.base.base.image_count;
GLuint gl_format = vk_format_to_gl(info->format);
glCreateTextures(tex_target, image_count, ssc->textures);
CHECK_GL();
glCreateMemoryObjectsEXT(image_count, ssc->memory);
CHECK_GL();
for (uint32_t i = 0; i < image_count; i++) {
GLint dedicated = ssc->base.base.images[i].use_dedicated_allocation ? GL_TRUE : GL_FALSE;
glMemoryObjectParameterivEXT(ssc->memory[i], GL_DEDICATED_MEMORY_OBJECT_EXT, &dedicated);
CHECK_GL();
// The below function consumes the handle, need to reference it.
xrt_graphics_buffer_handle_t handle = u_graphics_buffer_ref(ssc->base.base.images[i].handle);
glImportMemoryFdEXT( //
ssc->memory[i], //
ssc->base.base.images[i].size, //
GL_HANDLE_TYPE_OPAQUE_FD_EXT, //
handle); //
CHECK_GL();
if (info->array_size == 1) {
glTextureStorageMem2DEXT( //
ssc->textures[i], //
info->mip_count, //
gl_format, //
info->width, //
info->height, //
ssc->memory[i], //
0); //
} else {
glTextureStorageMem3DEXT( //
ssc->textures[i], //
info->mip_count, //
gl_format, //
info->width, //
info->height, //
info->array_size, //
ssc->memory[i], //
0); //
}
CHECK_GL();
}
sdl_make_uncurrent(sp);
}
static void
really_destroy(struct comp_swapchain *sc)
{
struct sdl_swapchain *ssc = (struct sdl_swapchain *)sc;
struct sdl_program *sp = ssc->sp;
SP_DEBUG(sp, "DESTROY");
sdl_make_current(sp);
uint32_t image_count = ssc->base.base.base.image_count;
if (image_count > 0) {
glDeleteTextures(image_count, ssc->textures);
glDeleteMemoryObjectsEXT(image_count, ssc->memory);
U_ZERO_ARRAY(ssc->textures);
U_ZERO_ARRAY(ssc->memory);
}
sdl_make_uncurrent(sp);
// Teardown the base swapchain, freeing all Vulkan resources.
comp_swapchain_teardown(sc);
// Teardown does not free the struct itself.
free(ssc);
}
/*
*
* 'Exported' functions.
*
*/
xrt_result_t
sdl_swapchain_create(struct xrt_compositor *xc,
const struct xrt_swapchain_create_info *info,
struct xrt_swapchain **out_xsc)
{
struct sdl_program *sp = from_comp(xc);
xrt_result_t xret;
/*
* In case the default get properties function have been overridden
* make sure to correctly dispatch the call to get the properties.
*/
struct xrt_swapchain_create_properties xsccp = {0};
xrt_comp_get_swapchain_create_properties(xc, info, &xsccp);
struct sdl_swapchain *ssc = U_TYPED_CALLOC(struct sdl_swapchain);
xret = comp_swapchain_create_init( //
&ssc->base, //
really_destroy, //
&sp->c.base.vk, //
&sp->c.base.cscgc, //
info, //
&xsccp); //
if (xret != XRT_SUCCESS) {
free(ssc);
return xret;
}
// Init SDL fields and create OpenGL resources.
post_init_setup(ssc, sp, info);
// Correctly setup refcounts, init sets refcount to zero.
xrt_swapchain_reference(out_xsc, &ssc->base.base.base);
return xret;
}
xrt_result_t
sdl_swapchain_import(struct xrt_compositor *xc,
const struct xrt_swapchain_create_info *info,
struct xrt_image_native *native_images,
uint32_t native_image_count,
struct xrt_swapchain **out_xsc)
{
struct sdl_program *sp = from_comp(xc);
xrt_result_t xret;
struct sdl_swapchain *ssc = U_TYPED_CALLOC(struct sdl_swapchain);
xret = comp_swapchain_import_init( //
&ssc->base, //
really_destroy, //
&sp->c.base.vk, //
&sp->c.base.cscgc, //
info, //
native_images, //
native_image_count); //
if (xret != XRT_SUCCESS) {
free(ssc);
return xret;
}
// Init SDL fields and create OpenGL resources.
post_init_setup(ssc, sp, info);
// Correctly setup refcounts, init sets refcount to zero.
xrt_swapchain_reference(out_xsc, &ssc->base.base.base);
return xret;
}