diff --git a/src/xrt/auxiliary/bindings/bindings.py b/src/xrt/auxiliary/bindings/bindings.py index 52ae8a2fb..f0d6c6473 100755 --- a/src/xrt/auxiliary/bindings/bindings.py +++ b/src/xrt/auxiliary/bindings/bindings.py @@ -441,7 +441,7 @@ header = '''// Copyright 2020-2022, Collabora, Ltd. func_start = ''' bool -{name}(const struct oxr_extension_status *exts, const char *str, size_t length) +{name}(const struct oxr_extension_status *exts, XrVersion openxr_version, const char *str, size_t length) {{ ''' @@ -459,6 +459,21 @@ if_strcmp = '''{exttab}if (strcmp(str, "{check}") == 0) {{ {exttab}\t\t\treturn true; {exttab}\t\t}} else ''' +def check_promoted(openxr_version_promoted): + # If required version is 0.0, we can skip checking that the instance uses a more recent version + return openxr_version_promoted is not None and openxr_version_promoted["major"] != '0' and openxr_version_promoted["minor"] != '0' + +def write_verify_switch_body(f, dict_of_lists, profile, profile_name, ext_name, tab_char): + """Generate function to check if a string is in a set of strings. + Input is a file to write the code into, a dict where keys are length and + the values are lists of strings of that length. And a suffix if any.""" + f.write(f"{tab_char}\tswitch (length) {{\n") + for length in dict_of_lists: + f.write(f"{tab_char}\tcase {str(length)}:\n\t\t") + for path in dict_of_lists[length]: + f.write(if_strcmp.format(exttab=tab_char, check=path)) + f.write(f"{tab_char}{{\n{tab_char}\t\t\tbreak;\n{tab_char}\t\t}}\n") + f.write(f"{tab_char}\tdefault: break;\n{tab_char}\t}}\n") def write_verify_func_switch(f, dict_of_lists, profile, profile_name, ext_name): """Generate function to check if a string is in a set of strings. @@ -467,29 +482,30 @@ def write_verify_func_switch(f, dict_of_lists, profile, profile_name, ext_name): if len(dict_of_lists) == 0: return - if profile.extension_name is not None: - f.write(f'#ifdef OXR_HAVE_{profile.extension_name}\n') + is_ext = ext_name is not None and len(ext_name) > 0 + is_promoted = check_promoted(profile.openxr_version_promoted) f.write(f"\t// generated from: {profile_name}\n") - is_ext = ext_name is not None and len(ext_name) > 0 - ext_tab = "" + + # Example: pico neo 3 can be enabled by either enabling XR_BD_controller_interaction ext or using OpenXR 1.1+. + # Disabling OXR_HAVE_BD_controller_interaction should NOT remove pico neo from OpenXR 1.1+ (it makes "exts->BD_controller_interaction" invalid C code). + # Therefore separate code blocks for ext and version checks generated to avoid ifdef hell. if is_ext: + f.write(f'#ifdef OXR_HAVE_{profile.extension_name}\n') f.write(f"\tif (exts->{ext_name}) {{\n") - ext_tab = "\t" + write_verify_switch_body(f, dict_of_lists, profile, profile_name, ext_name, '\t') + f.write("\t}\n") + f.write(f'#endif // OXR_HAVE_{profile.extension_name}\n') - f.write(f"{ext_tab}\tswitch (length) {{\n") - for length in dict_of_lists: - f.write(f"{ext_tab}\tcase {str(length)}:\n\t\t") - for path in dict_of_lists[length]: - f.write(if_strcmp.format(exttab=ext_tab, check=path)) - f.write(f"{ext_tab}{{\n{ext_tab}\t\t\tbreak;\n{ext_tab}\t\t}}\n") - f.write(f"{ext_tab}\tdefault: break;\n{ext_tab}\t}}\n") - - if is_ext: + # The split into "is_promoted" and "not is_ext and not is_promoted" cases is not strictly necessary as we could generate the version check for both cases. + # For the "not is_ext and not is_promoted" case this would generate "if (openxr_version >= XR_MAKE_VERSION(0, 0, 0))", which we avoid doing here by this split. + if is_promoted: + f.write(f'\tif (openxr_version >= XR_MAKE_VERSION({profile.openxr_version_promoted["major"]}, {profile.openxr_version_promoted["minor"]}, 0)) {{\n') + write_verify_switch_body(f, dict_of_lists, profile, profile_name, ext_name, '\t') f.write("\t}\n") - if profile.extension_name is not None: - f.write(f'#endif // OXR_HAVE_{profile.extension_name}\n') + if not is_ext and not is_promoted: + write_verify_switch_body(f, dict_of_lists, profile, profile_name, ext_name, '') def write_verify_func_body(f, profile, dict_name): if profile is None or dict_name is None or len(dict_name) == 0: @@ -516,8 +532,18 @@ def generate_verify_functions(f, profile): f.write(f''' void -oxr_verify_{profile.validation_func_name}_ext(const struct oxr_extension_status *extensions, bool *out_supported, bool *out_enabled) -{{''') +oxr_verify_{profile.validation_func_name}_ext(const struct oxr_extension_status *extensions, XrVersion openxr_version, bool *out_supported, bool *out_enabled) +{{ +''') + is_promoted = check_promoted(profile.openxr_version_promoted) + if is_promoted: + f.write(f'\tif (openxr_version >= XR_MAKE_VERSION({profile.openxr_version_promoted["major"]}, {profile.openxr_version_promoted["minor"]}, 0)) {{\n') + f.write(f'\t\t*out_supported = true;\n') + f.write(f'\t\t*out_enabled = true;\n') + f.write(f'\t\treturn;\n') + f.write(f'\t}}') + + if profile.extension_name is not None: f.write(f''' #ifdef OXR_HAVE_{profile.extension_name} @@ -776,6 +802,8 @@ def generate_bindings_h(file, b): #include "xrt/xrt_defines.h" typedef uint64_t XrPath; // OpenXR typedef +typedef uint64_t XrVersion; // OpenXR typedef + struct oxr_extension_status; /** @@ -792,8 +820,8 @@ void oxr_get_interaction_profile_path_cache(XrPath **out_path_cache[{len(b.profi for profile in b.profiles: for fn_suffix in fn_prefixes: f.write( - f"\nbool\noxr_verify_{profile.validation_func_name}{fn_suffix}(const struct oxr_extension_status *extensions, const char *str, size_t length);\n") - f.write(f'''\nvoid\noxr_verify_{profile.validation_func_name}_ext(const struct oxr_extension_status *extensions, bool *out_supported, bool *out_enabled);\n''') + f"\nbool\noxr_verify_{profile.validation_func_name}{fn_suffix}(const struct oxr_extension_status *extensions, XrVersion openxr_major_minor, const char *str, size_t length);\n") + f.write(f'''\nvoid\noxr_verify_{profile.validation_func_name}_ext(const struct oxr_extension_status *extensions, XrVersion openxr_version, bool *out_supported, bool *out_enabled);\n''') f.write(f''' #define PATHS_PER_BINDING_TEMPLATE 16 @@ -826,8 +854,8 @@ struct binding_template \tenum xrt_output_name output; }}; -typedef bool (*path_verify_fn_t)(const struct oxr_extension_status *extensions, const char *, size_t); -typedef void (*ext_verify_fn_t)(const struct oxr_extension_status *extensions, bool *out_supported, bool *out_enabled); +typedef bool (*path_verify_fn_t)(const struct oxr_extension_status *extensions, XrVersion openxr_version, const char *, size_t); +typedef void (*ext_verify_fn_t)(const struct oxr_extension_status *extensions, XrVersion openxr_version, bool *out_supported, bool *out_enabled); struct profile_template {{ diff --git a/src/xrt/state_trackers/oxr/oxr_api_action.c b/src/xrt/state_trackers/oxr/oxr_api_action.c index 5a15d67bb..ddd53ced5 100644 --- a/src/xrt/state_trackers/oxr/oxr_api_action.c +++ b/src/xrt/state_trackers/oxr/oxr_api_action.c @@ -53,7 +53,7 @@ process_dpad(struct oxr_logger *log, dpad->binding); } - if (!dpad_emulator_fn(&inst->extensions, str, length)) { + if (!dpad_emulator_fn(&inst->extensions, inst->openxr_version.major_minor, str, length)) { return oxr_error(log, XR_ERROR_PATH_UNSUPPORTED, "(%s->binding == \"%s\") is not a valid dpad binding path for profile \"%s\"", prefix, str, ip_str); @@ -248,7 +248,7 @@ oxr_xrSuggestInteractionProfileBindings(XrInstance instance, bool ext_supported; bool ext_enabled; - ext_verify_fn(&inst->extensions, &ext_supported, &ext_enabled); + ext_verify_fn(&inst->extensions, inst->openxr_version.major_minor, &ext_supported, &ext_enabled); if (!ext_supported) { return oxr_error( &log, XR_ERROR_PATH_UNSUPPORTED, @@ -286,12 +286,12 @@ oxr_xrSuggestInteractionProfileBindings(XrInstance instance, i, s->binding); } - if (subpath_fn(&inst->extensions, str, length)) { + if (subpath_fn(&inst->extensions, inst->openxr_version.major_minor, str, length)) { continue; } #ifdef XR_EXT_dpad_binding - if (dpad_path_fn(&inst->extensions, str, length)) { + if (dpad_path_fn(&inst->extensions, inst->openxr_version.major_minor, str, length)) { if (!has_dpad) { return oxr_error( &log, XR_ERROR_PATH_UNSUPPORTED,