Commit a36f643e authored by Jakob Bornecrantz's avatar Jakob Bornecrantz

Merge branch 'master' into pb_multiapps_merge

parents ff176511 5443e3a0
Pipeline #162172 passed with stages
in 1 minute and 40 seconds
......@@ -143,6 +143,7 @@ format-and-spellcheck:
# List build options
- grep "^XRT_" CMakeCache.txt
- ninja
- ctest --output-on-failure
# "Base" job for a Meson build
.monado.base-job.build-meson:
......
......@@ -68,6 +68,9 @@ else()
find_package(OpenGL)
endif()
# This one is named differently because that's what CTest uses
option(BUILD_TESTING "Enable building of the test suite?" ON)
cmake_dependent_option(CMAKE_INTERPROCEDURAL_OPTIMIZATION "Enable inter-procedural (link-time) optimization" OFF "HAS_IPO" OFF)
cmake_dependent_option(XRT_HAVE_WAYLAND "Enable Wayland support" ON "WAYLAND_FOUND AND WAYLAND_SCANNER_FOUND AND WAYLAND_PROTOCOLS_FOUND" OFF)
cmake_dependent_option(XRT_HAVE_XLIB "Enable xlib support" ON "X11_FOUND" OFF)
......@@ -168,3 +171,8 @@ endif()
add_subdirectory(src)
add_subdirectory(doc)
if(BUILD_TESTING)
include(CTest)
add_subdirectory(tests)
endif()
OpenXR: Transform input types in a somewhat flexible, composable way. Also, do conversion at sync time, and use the transformed values to evaluate if the input has changed, per the spec.
......@@ -16,3 +16,6 @@ endif()
add_library(xrt-external-flexkalman INTERFACE)
target_include_directories(xrt-external-flexkalman INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/flexkalman)
add_library(xrt-external-catch2 INTERFACE)
target_include_directories(xrt-external-catch2 INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/Catch2)
This diff is collapsed.
......@@ -166,6 +166,18 @@ math_quat_validate(const struct xrt_quat *quat);
void
math_quat_normalize(struct xrt_quat *inout);
/*!
* Normalizes a quaternion if it has accumulated float precision errors.
* Returns true if the quaternion was already normalized or was normalized after
* being found within a small float precision tolerance.
* Returns false if the quaternion was not at all normalized.
*
* @relates xrt_quat
* @ingroup aux_math
*/
bool
math_quat_ensure_normalized(struct xrt_quat *inout);
/*!
* Rotate a vector.
*
......
......@@ -183,6 +183,28 @@ math_quat_normalize(struct xrt_quat *inout)
map_quat(*inout).normalize();
}
extern "C" bool
math_quat_ensure_normalized(struct xrt_quat *inout)
{
assert(inout != NULL);
if (math_quat_validate(inout))
return true;
const float FLOAT_EPSILON = Eigen::NumTraits<float>::epsilon();
const float TOLERANCE = FLOAT_EPSILON * 5;
auto rot = copy(*inout);
auto norm = rot.norm();
if (norm > 1.0f + TOLERANCE || norm < 1.0f - TOLERANCE) {
return false;
}
map_quat(*inout).normalize();
return true;
}
extern "C" void
math_quat_rotate(const struct xrt_quat *left,
const struct xrt_quat *right,
......
......@@ -55,7 +55,7 @@ if sdl2.found()
have_conf.set('XRT_HAVE_SDL2', true)
endif
if v4l2.found()
if v4l2.found() and 'v4l2' in drivers
have_conf.set('XRT_HAVE_V4L2', true)
endif
......
......@@ -189,13 +189,6 @@ struct xrt_input
union xrt_input_value value;
};
enum xrt_source_value_redirect
{
INPUT_REDIRECT_DEFAULT = 0,
INPUT_REDIRECT_VEC2_X_TO_VEC1,
INPUT_REDIRECT_VEC2_Y_TO_VEC1
};
struct xrt_output
{
enum xrt_output_name name;
......
......@@ -42,6 +42,8 @@ set(OXR_SOURCE_FILES
oxr_extension_support.h
oxr_handle_base.c
oxr_input.c
oxr_input_transform.c
oxr_input_transform.h
oxr_instance.c
oxr_logger.c
oxr_logger.h
......@@ -85,6 +87,8 @@ target_link_libraries(st_oxr PRIVATE
Vulkan::Vulkan
comp_client
)
target_include_directories(st_oxr PRIVATE
target_include_directories(st_oxr
PRIVATE
${CMAKE_CURRENT_BINARY_DIR}
)
INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/..)
......@@ -55,6 +55,8 @@ lib_st_oxr = static_library(
'oxr_extension_support.h',
'oxr_handle_base.c',
'oxr_input.c',
'oxr_input_transform.c',
'oxr_input_transform.h',
'oxr_instance.c',
'oxr_logger.c',
'oxr_logger.h',
......
......@@ -16,6 +16,7 @@
#include "oxr_objects.h"
#include "oxr_logger.h"
#include "oxr_handle.h"
#include "oxr_input_transform.h"
#include <math.h>
#include <stdio.h>
......@@ -50,9 +51,6 @@ oxr_action_cache_update(struct oxr_logger *log,
int64_t time,
bool select);
/*!
* @private @memberof oxr_action_attachment
*/
static void
oxr_action_attachment_update(struct oxr_logger *log,
struct oxr_session *sess,
......@@ -82,6 +80,12 @@ oxr_action_bind_inputs(struct oxr_logger *log,
static void
oxr_action_cache_teardown(struct oxr_action_cache *cache)
{
// Clean up input transforms
for (uint32_t i = 0; i < cache->num_inputs; i++) {
struct oxr_action_input *action_input = &cache->inputs[i];
oxr_input_transform_destroy(&(action_input->transforms));
action_input->num_transforms = 0;
}
free(cache->inputs);
cache->inputs = NULL;
free(cache->outputs);
......@@ -547,54 +551,6 @@ do_io_bindings(struct oxr_binding *b,
return found;
}
static bool
ends_with(const char *str, const char *suffix)
{
int len = strlen(str);
int suffix_len = strlen(suffix);
return (len >= suffix_len) &&
(0 == strcmp(str + (len - suffix_len), suffix));
}
static void
oxr_action_cache_determine_redirect(struct oxr_logger *log,
struct oxr_session *sess,
struct oxr_action *act,
struct oxr_action_cache *cache,
XrPath bound_path)
{
cache->redirect = INPUT_REDIRECT_DEFAULT;
struct oxr_action_input *input = &cache->inputs[0];
if (input == NULL)
return;
const char *str;
size_t length;
oxr_path_get_string(log, sess->sys->inst, bound_path, &str, &length);
enum xrt_input_type t = XRT_GET_INPUT_TYPE(input->input->name);
// trackpad/thumbstick data is kept in vec2f values.
// When a float action binds to ../trackpad/x or ../thumbstick/y, store
// the information which vec2f component to read.
if (act->data->action_type == XR_ACTION_TYPE_FLOAT_INPUT &&
t == XRT_INPUT_TYPE_VEC2_MINUS_ONE_TO_ONE) {
if (ends_with(str, "/x")) {
cache->redirect = INPUT_REDIRECT_VEC2_X_TO_VEC1;
} else if (ends_with(str, "/y")) {
cache->redirect = INPUT_REDIRECT_VEC2_Y_TO_VEC1;
} else {
oxr_log(log,
"No rule to get float from vec2f for action "
"%s, binding %s\n",
act->data->name, str);
}
}
}
static XrPath
get_matched_xrpath(struct oxr_binding *b, struct oxr_action *act)
{
......@@ -732,7 +688,8 @@ oxr_action_attachment_bind(struct oxr_logger *log,
if (act_ref->sub_paths.user || act_ref->sub_paths.any) {
#if 0
oxr_action_bind_inputs(log, slog, sess, act, &act_attached->user, user,
oxr_action_bind_inputs(log, &slog, sess, act,
&act_attached->user, user,
OXR_SUB_ACTION_PATH_USER);
#endif
}
......@@ -791,6 +748,11 @@ oxr_action_cache_stop_output(struct oxr_logger *log,
}
}
/*!
* Called during xrSyncActions.
*
* @private @memberof oxr_action_cache
*/
static void
oxr_action_cache_update(struct oxr_logger *log,
struct oxr_session *sess,
......@@ -832,40 +794,58 @@ oxr_action_cache_update(struct oxr_logger *log,
cache->current.active = true;
/*!
* @todo Combine multiple sources for a single subaction path
* and convert type as required.
* @todo Combine multiple sources for a single subaction path.
*/
struct xrt_input *input = cache->inputs[0].input;
struct oxr_action_input *action_input = &(cache->inputs[0]);
struct xrt_input *input = action_input->input;
struct oxr_input_value_tagged raw_input = {
.type = XRT_GET_INPUT_TYPE(input->name),
.value = input->value,
};
struct oxr_input_value_tagged transformed = {0};
if (!oxr_input_transform_process(action_input->transforms,
action_input->num_transforms,
&raw_input, &transformed)) {
// We couldn't transform, how strange. Reset all state.
// At this level we don't know what action this is, etc.
// so a warning message isn't very helpful.
U_ZERO(&cache->current);
return;
}
int64_t timestamp = input->timestamp;
bool changed = false;
switch (XRT_GET_INPUT_TYPE(input->name)) {
switch (transformed.type) {
case XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE:
case XRT_INPUT_TYPE_VEC1_MINUS_ONE_TO_ONE: {
changed = (input->value.vec1.x != last.value.vec1.x);
cache->current.value = input->value;
changed =
(transformed.value.vec1.x != last.value.vec1.x);
cache->current.value.vec1.x = transformed.value.vec1.x;
break;
}
case XRT_INPUT_TYPE_VEC2_MINUS_ONE_TO_ONE: {
changed = (input->value.vec2.x != last.value.vec2.x) ||
(input->value.vec2.y != last.value.vec2.y);
cache->current.value = input->value;
changed =
(transformed.value.vec2.x != last.value.vec2.x) ||
(transformed.value.vec2.y != last.value.vec2.y);
cache->current.value.vec2.x = transformed.value.vec2.x;
cache->current.value.vec2.y = transformed.value.vec2.y;
break;
}
#if 0
case XRT_INPUT_TYPE_VEC3_MINUS_ONE_TO_ONE: {
changed = (input->value.vec3.x != last.value.vec3.x) ||
(input->value.vec3.y != last.value.vec3.y) ||
(input->value.vec3.z != last.value.vec3.z);
cache->current.value.vec3.x = input->value.vec3.x;
cache->current.value.vec3.y = input->value.vec3.y;
cache->current.value.vec3.z = input->value.vec3.z;
changed = (transformed.value.vec3.x != last.vec3.x) ||
(transformed.value.vec3.y != last.vec3.y) ||
(transformed.value.vec3.z != last.vec3.z);
cache->current.vec3.x = transformed.value.vec3.x;
cache->current.vec3.y = transformed.value.vec3.y;
cache->current.vec3.z = transformed.value.vec3.z;
break;
}
#endif
case XRT_INPUT_TYPE_BOOLEAN: {
changed = (input->value.boolean != last.value.boolean);
cache->current.value.boolean = input->value.boolean;
changed =
(transformed.value.boolean != last.value.boolean);
cache->current.value.boolean =
transformed.value.boolean;
break;
}
case XRT_INPUT_TYPE_POSE: return;
......@@ -915,6 +895,11 @@ oxr_action_cache_update(struct oxr_logger *log,
} \
}
/*!
* Called during each xrSyncActions.
*
* @private @memberof oxr_action_attachment
*/
static void
oxr_action_attachment_update(struct oxr_logger *log,
struct oxr_session *sess,
......@@ -1019,6 +1004,35 @@ oxr_action_attachment_update(struct oxr_logger *log,
act_attached->any_state.active = true;
}
}
/*!
* Try to produce a transform chain to convert the available input into the
* desired input type.
*
* Populates @p action_input->transforms and @p action_input->num_transforms on
* success.
*
* @returns false if it could not, true if it could
*/
static bool
oxr_action_populate_input_transform(struct oxr_logger *log,
struct oxr_sink_logger *slog,
struct oxr_session *sess,
struct oxr_action *act,
struct oxr_action_input *action_input,
XrPath bound_path)
{
assert(action_input->transforms == NULL);
assert(action_input->num_transforms == 0);
const char *str;
size_t length;
oxr_path_get_string(log, sess->sys->inst, bound_path, &str, &length);
enum xrt_input_type t = XRT_GET_INPUT_TYPE(action_input->input->name);
return oxr_input_transform_create_chain(
log, slog, t, act->data->action_type, act->data->name, str,
&action_input->transforms, &action_input->num_transforms);
}
static void
oxr_action_bind_inputs(struct oxr_logger *log,
......@@ -1034,7 +1048,7 @@ oxr_action_bind_inputs(struct oxr_logger *log,
struct oxr_action_output outputs[16] = {0};
uint32_t num_outputs = 0;
//! @todo Should this be asserted to be none-null?
//! @todo Should this be asserted to be non-null?
XrPath bound_path = XR_NULL_PATH;
get_binding(log, slog, sess, act, profile, sub_path, inputs,
&num_inputs, outputs, &num_outputs, &bound_path);
......@@ -1046,6 +1060,19 @@ oxr_action_bind_inputs(struct oxr_logger *log,
cache->inputs =
U_TYPED_ARRAY_CALLOC(struct oxr_action_input, num_inputs);
for (uint32_t i = 0; i < num_inputs; i++) {
if (!oxr_action_populate_input_transform(
log, slog, sess, act, &(inputs[i]),
bound_path)) {
/*!
* @todo de-populate this element if we couldn't
* get a transform?
*/
oxr_slog(
slog,
"Could not populate a transform for %s "
"despite it being bound!\n",
act->data->name);
}
cache->inputs[i] = inputs[i];
}
cache->num_inputs = num_inputs;
......@@ -1060,8 +1087,6 @@ oxr_action_bind_inputs(struct oxr_logger *log,
}
cache->num_outputs = num_outputs;
}
oxr_action_cache_determine_redirect(log, sess, act, cache, bound_path);
}
......@@ -1303,40 +1328,29 @@ oxr_action_sync_data(struct oxr_logger *log,
static void
get_state_from_state_bool(struct oxr_action_state *state,
XrActionStateBoolean *data,
enum xrt_source_value_redirect redirect)
XrActionStateBoolean *data)
{
data->currentState = state->value.boolean;
data->lastChangeTime = state->timestamp;
data->changedSinceLastSync = state->changed;
data->isActive = XR_TRUE;
data->isActive = state->active;
//! @todo
// data->isActive = XR_TRUE;
}
static void
get_state_from_state_vec1(struct oxr_action_state *state,
XrActionStateFloat *data,
enum xrt_source_value_redirect redirect)
XrActionStateFloat *data)
{
switch (redirect) {
case INPUT_REDIRECT_VEC2_X_TO_VEC1:
data->currentState = state->value.vec2.x;
break;
case INPUT_REDIRECT_VEC2_Y_TO_VEC1:
data->currentState = state->value.vec2.y;
break;
case INPUT_REDIRECT_DEFAULT:
default: data->currentState = state->value.vec1.x; break;
}
data->currentState = state->value.vec1.x;
data->lastChangeTime = state->timestamp;
data->changedSinceLastSync = state->changed;
data->isActive = XR_TRUE;
data->isActive = state->active;
}
static void
get_state_from_state_vec2(struct oxr_action_state *state,
XrActionStateVector2f *data,
enum xrt_source_value_redirect redirect)
XrActionStateVector2f *data)
{
data->currentState.x = state->value.vec2.x;
data->currentState.y = state->value.vec2.y;
......@@ -1347,30 +1361,27 @@ get_state_from_state_vec2(struct oxr_action_state *state,
#define OXR_ACTION_GET_FILLER(TYPE) \
if (sub_paths.any && act_attached->any_state.active) { \
get_state_from_state_##TYPE(&act_attached->any_state, data, \
INPUT_REDIRECT_DEFAULT); \
get_state_from_state_##TYPE(&act_attached->any_state, data); \
} \
if (sub_paths.user && act_attached->user.current.active) { \
get_state_from_state_##TYPE(&act_attached->user.current, data, \
act_attached->user.redirect); \
get_state_from_state_##TYPE(&act_attached->user.current, \
data); \
} \
if (sub_paths.head && act_attached->head.current.active) { \
get_state_from_state_##TYPE(&act_attached->head.current, data, \
act_attached->head.redirect); \
get_state_from_state_##TYPE(&act_attached->head.current, \
data); \
} \
if (sub_paths.left && act_attached->left.current.active) { \
get_state_from_state_##TYPE(&act_attached->left.current, data, \
act_attached->left.redirect); \
get_state_from_state_##TYPE(&act_attached->left.current, \
data); \
} \
if (sub_paths.right && act_attached->right.current.active) { \
get_state_from_state_##TYPE(&act_attached->right.current, \
data, \
act_attached->right.redirect); \
data); \
} \
if (sub_paths.gamepad && act_attached->gamepad.current.active) { \
get_state_from_state_##TYPE(&act_attached->gamepad.current, \
data, \
act_attached->gamepad.redirect); \
data); \
}
......
// Copyright 2018-2020, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Handles transformation/filtering of input data.
* @author Ryan Pavlik <ryan.pavlik@collabora.com>
* @author Jakob Bornecrantz <jakob@collabora.com>
* @ingroup oxr_input_transform
*/
#include "oxr_input_transform.h"
#include "oxr_logger.h"
#include "oxr_objects.h"
#include "util/u_misc.h"
#include <string.h>
#include <assert.h>
/*!
* Arbitrary but larger than required.
*/
#define OXR_MAX_INPUT_TRANSFORMS 5
void
oxr_input_transform_destroy(struct oxr_input_transform **transform_ptr)
{
struct oxr_input_transform *xform = *transform_ptr;
if (xform == NULL) {
return;
}
free(xform);
*transform_ptr = NULL;
}
bool
oxr_input_transform_init_root(struct oxr_input_transform *transform,
enum xrt_input_type input_type)
{
assert(transform != NULL);
U_ZERO(transform);
transform->type = INPUT_TRANSFORM_IDENTITY;
transform->result_type = input_type;
return true;
}
bool
oxr_input_transform_init_vec2_get_x(struct oxr_input_transform *transform,
const struct oxr_input_transform *parent)
{
assert(transform != NULL);
assert(parent != NULL);
assert(parent->result_type == XRT_INPUT_TYPE_VEC2_MINUS_ONE_TO_ONE);
U_ZERO(transform);
transform->type = INPUT_TRANSFORM_VEC2_GET_X;
transform->result_type = XRT_INPUT_TYPE_VEC1_MINUS_ONE_TO_ONE;
return true;
}
bool
oxr_input_transform_init_vec2_get_y(struct oxr_input_transform *transform,
const struct oxr_input_transform *parent)
{
assert(transform != NULL);
assert(parent != NULL);
assert(parent->result_type == XRT_INPUT_TYPE_VEC2_MINUS_ONE_TO_ONE);
U_ZERO(transform);
transform->type = INPUT_TRANSFORM_VEC2_GET_Y;
transform->result_type = XRT_INPUT_TYPE_VEC1_MINUS_ONE_TO_ONE;
return true;
}
bool
oxr_input_transform_init_threshold(struct oxr_input_transform *transform,
const struct oxr_input_transform *parent,
float threshold,
bool invert)
{
assert(transform != NULL);
assert(parent != NULL);
assert((parent->result_type == XRT_INPUT_TYPE_VEC1_MINUS_ONE_TO_ONE) ||
(parent->result_type == XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE));
U_ZERO(transform);
transform->type = INPUT_TRANSFORM_THRESHOLD;
transform->result_type = XRT_INPUT_TYPE_BOOLEAN;
transform->data.threshold.threshold = threshold;
transform->data.threshold.invert = invert;
return true;
}
bool
oxr_input_transform_init_bool_to_vec1(struct oxr_input_transform *transform,
const struct oxr_input_transform *parent,
enum xrt_input_type result_type,
float true_val,
float false_val)
{
assert(transform != NULL);
assert(parent != NULL);
assert(parent->result_type == XRT_INPUT_TYPE_BOOLEAN);
assert((result_type == XRT_INPUT_TYPE_VEC1_MINUS_ONE_TO_ONE) ||
(result_type == XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE));
U_ZERO(transform);
transform->type = INPUT_TRANSFORM_BOOL_TO_VEC1;
transform->result_type = result_type;
transform->data.bool_to_vec1.true_val = true_val;
transform->data.bool_to_vec1.false_val = false_val;
return true;
}
bool
oxr_input_transform_process(const struct oxr_input_transform *transform,
size_t num_transforms,
const struct oxr_input_value_tagged *input,
struct oxr_input_value_tagged *out)
{
if (transform == NULL) {
return false;
}
struct oxr_input_value_tagged data = *input;
for (size_t i = 0; i < num_transforms; ++i) {
const struct oxr_input_transform *xform = &(transform[i]);
switch (xform->type) {
case INPUT_TRANSFORM_IDENTITY:
// do nothing
break;
case INPUT_TRANSFORM_VEC2_GET_X:
data.value.vec1.