From 9cdea50c4a145a23df416e8023413ae7622024e5 Mon Sep 17 00:00:00 2001 From: Bastiaan Olij Date: Wed, 16 Apr 2025 17:51:56 +1000 Subject: [PATCH 01/10] Add client matrix --- client_schema.json | 239 ++++++++++++++++++ client_schema.json.license | 2 + clients/godot.json | 133 ++++++++++ clients/godot.json.license | 2 + extension_support_report.py | 4 +- openxr_inventory/client_inventory.py | 138 ++++++++++ openxr_inventory/extensions.py | 112 ++++++-- openxr_inventory/inventory_data.py | 80 ++++++ openxr_inventory/runtime_inventory.py | 77 +----- .../templates/extension_support.jinja2.html | 171 ++++++++++++- schema.json => runtime_schema.json | 0 ...son.license => runtime_schema.json.license | 0 runtimes/bytedance_pico.json | 2 +- runtimes/bytedance_pico_4_ultra.json | 2 +- runtimes/htc_vive_cosmos.json | 2 +- runtimes/htc_vive_focus.json | 2 +- runtimes/magicleap_ml2.json | 2 +- runtimes/magicleap_remote_rendering.json | 2 +- runtimes/meta_pc.json | 2 +- runtimes/meta_pc_dev_mode.json | 2 +- runtimes/meta_quest_1_mobile.json | 2 +- runtimes/meta_quest_2_mobile.json | 2 +- runtimes/meta_quest_3_mobile.json | 2 +- runtimes/meta_quest_3s_mobile.json | 2 +- runtimes/meta_quest_pro_mobile.json | 2 +- runtimes/meta_xr_simulator.json | 2 +- runtimes/microsoft_hololens2.json | 2 +- runtimes/microsoft_pc.json | 2 +- runtimes/microsoft_remoting.json | 2 +- runtimes/monado_android.json | 2 +- runtimes/monado_linux.json | 2 +- runtimes/monado_windows.json | 2 +- runtimes/qualcomm.json | 2 +- runtimes/valve_steamvr.json | 2 +- runtimes/varjo.json | 2 +- verify_schema.sh | 9 +- 36 files changed, 882 insertions(+), 131 deletions(-) create mode 100644 client_schema.json create mode 100644 client_schema.json.license create mode 100644 clients/godot.json create mode 100644 clients/godot.json.license create mode 100644 openxr_inventory/client_inventory.py create mode 100644 openxr_inventory/inventory_data.py rename schema.json => runtime_schema.json (100%) rename schema.json.license => runtime_schema.json.license (100%) diff --git a/client_schema.json b/client_schema.json new file mode 100644 index 0000000..7247793 --- /dev/null +++ b/client_schema.json @@ -0,0 +1,239 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "definitions": { + "extension_name": { + "type": "string", + "description": "Name of an OpenXR extension", + "pattern": "^XR_([A-Z]+)(_([a-zA-Z0-9])+)+$" + }, + "extension_with_details": { + "type": "object", + "properties": { + "name": { + "allOf": [ + { + "title": "Extension name" + }, + { + "$ref": "#/definitions/extension_name" + } + ] + }, + "notes": { + "type": "string", + "title": "Notes", + "description": "Free-form text about support of this extension" + } + }, + "required": [ + "name", + "notes" + ] + }, + "extension": { + "allOf": [ + { + "title": "Supported Extension" + }, + { + "anyOf": [ + { + "$ref": "#/definitions/extension_name" + }, + { + "$ref": "#/definitions/extension_with_details" + } + ] + } + ], + "examples": [ + "XR_KHR_composition_layer_cylinder", + { + "name": "XR_EXT_hand_tracking", + "notes": "Currently in beta" + } + ] + }, + "platform": { + "type": "string", + "enum": [ + "Windows (Desktop)", + "Windows (HoloLens 2)", + "Linux (Desktop/Embedded)", + "Android (All-in-one)", + "Android (Phone/Installable)", + "MacOS (Desktop)" + ] + }, + "form_factor": { + "type": "string", + "title": "XrFormFactor", + "enum": [ + "XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY", + "XR_FORM_FACTOR_HANDHELD_DISPLAY" + ] + }, + "view_configuration": { + "type": "string", + "title": "XrViewConfigurationType", + "enum": [ + "XR_VIEW_CONFIGURATION_TYPE_PRIMARY_MONO", + "XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO", + "XR_VIEW_CONFIGURATION_TYPE_PRIMARY_QUAD_VARJO", + "XR_VIEW_CONFIGURATION_TYPE_SECONDARY_MONO_FIRST_PERSON_OBSERVER_MSFT" + ] + }, + "environment_blend_mode": { + "type": "string", + "enum": [ + "OPAQUE", + "ADDITIVE", + "ALPHA_BLEND" + ] + }, + "component": { + "type": "object", + "title": "Component", + "properties": { + "name": { + "title": "Name of this component", + "type": "string" + }, + "abbreviation": { + "title": "Abbreviation shown in matrix", + "type": "string" + }, + "color": { + "title": "Background color used in matrix", + "type": "string", + "enum": [ + "black", + "white", + "gray", + "silver", + "maroon", + "red", + "purple", + "fushsia", + "green", + "lime", + "olive", + "yellow", + "navy", + "blue", + "teal", + "aqua" + ] + }, + "extensions": { + "title": "Supported extensions", + "description": "A list of all supported extensions in this client, on this platform. Details or caveats may be included in 'notes'.", + "type": "array", + "items": { + "$ref": "#/definitions/extension" + } + } + }, + "required": [ + "name", + "abbreviation", + "color", + "extensions" + ] + }, + "view_configuration_data": { + "type": "object", + "title": "View Configuration Data", + "properties": { + "view_configuration": { + "$ref": "#/definitions/view_configuration" + }, + "secondary_msft": { + "title": "Secondary (MSFT) view configuration?", + "type": "boolean", + "default": false + }, + "environment_blend_modes": { + "title": "Supported environment blend modes in this view configuration", + "type": "array", + "items": { + "$ref": "#/definitions/environment_blend_mode" + }, + "minItems": 1 + } + }, + "required": [ + "view_configuration", + "environment_blend_modes" + ] + }, + "form_factor_data": { + "type": "object", + "title": "Form Factor Data", + "properties": { + "form_factor": { + "$ref": "#/definitions/form_factor" + }, + "view_configurations": { + "type": "array", + "items": { + "$ref": "#/definitions/view_configuration_data" + }, + "minItems": 1 + } + }, + "required": [ + "form_factor", + "view_configurations" + ] + } + }, + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "Client Name" + }, + "vendor": { + "type": "string", + "title": "Vendor" + }, + "platform": { + "$description": "Platforms this client supports.", + "type": "array", + "items": { + "$ref": "#/definitions/platform" + } + }, + "updated": { + "type": "string", + "title": "Last updated", + "description": "Date this document was last reflective of the reality" + }, + "notes": { + "type": "string", + "title": "Notes", + "description": "Freeform notes about this data file" + }, + "components": { + "title": "Components", + "description": "A list of all components for this client.", + "type": "array", + "items": { + "$ref": "#/definitions/component" + } + }, + "form_factors": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/form_factor_data" + } + } + }, + "required": [ + "name", + "vendor", + "components" + ] +} diff --git a/client_schema.json.license b/client_schema.json.license new file mode 100644 index 0000000..bb89a0e --- /dev/null +++ b/client_schema.json.license @@ -0,0 +1,2 @@ +Copyright 2025, The Khronos Group Inc. +SPDX-License-Identifier: CC-BY-4.0 diff --git a/clients/godot.json b/clients/godot.json new file mode 100644 index 0000000..48cce4c --- /dev/null +++ b/clients/godot.json @@ -0,0 +1,133 @@ +{ + "$schema": "../client_schema.json", + "name": "Godot Game Engine", + "vendor": "Godot Community", + "notes": "Some extensions require the use of the Godot OpenXR Vendors Plugin.", + "platforms": [ + "Windows (Desktop)", + "Linux (Desktop/Embedded)", + "Android (All-in-one)", + "Android (Phone/Installable)", + "MacOS (Desktop)" + ], + "components": [ + { + "name": "Godot Core", + "abbreviation": "Core", + "color": "green", + "extensions": [ + "XR_KHR_android_create_instance", + "XR_KHR_android_surface_swapchain", + "XR_KHR_binding_modification", + "XR_KHR_composition_layer_cylinder", + "XR_KHR_composition_layer_depth", + "XR_KHR_composition_layer_equirect2", + "XR_KHR_loader_init", + "XR_KHR_loader_init_android", + "XR_KHR_opengl_es_enable", + "XR_KHR_opengl_enable", + "XR_KHR_visibility_mask", + "XR_KHR_vulkan_enable2", + "XR_EXT_debug_utils", + "XR_EXT_dpad_binding", + "XR_EXT_eye_gaze_interaction", + "XR_EXT_future", + "XR_EXT_hand_interaction", + "XR_EXT_hand_tracking", + "XR_EXT_hand_joints_motion_range", + "XR_EXT_hand_tracking_data_source", + "XR_EXT_local_floor", + "XR_EXT_view_configuration_depth_range", + "XR_EXT_palm_pose", + "XR_EXT_performance_settings", + "XR_EXT_hp_mixed_reality_controller", + "XR_EXT_samsung_odyssey_controller", + "XR_BD_controller_interaction", + "XR_FB_touch_controller_proximity", + "XR_FB_touch_controller_pro", + "XR_META_touch_controller_plus", + "XR_HTC_vive_cosmos_controller_interaction", + "XR_HTC_vive_focus3_controller_interaction", + "XR_HTC_hand_interaction", + "XR_HTCX_vive_tracker_interaction", + "XR_ML_ml2_controller_interaction", + "XR_MSFT_hand_interaction" + ] + }, + { + "name": "Godot Vendors plugin", + "abbreviation": "Vendors", + "color": "yellow", + "extensions": [ + "XR_FB_body_tracking", + "XR_FB_composition_layer_alpha_blend", + "XR_FB_composition_layer_secure_content", + "XR_FB_composition_layer_settings", + "XR_FB_display_refresh_rate", + "XR_FB_face_tracking2", + "XR_FB_foveation", + "XR_FB_foveation_configuration", + "XR_FB_hand_tracking_aim", + "XR_FB_hand_tracking_capsules", + "XR_FB_hand_tracking_mesh", + "XR_FB_passthrough", + "XR_META_passthrough_preferences", + "XR_META_passthrough_color_lut", + "XR_META_spatial_entity_mesh", + "XR_FB_render_model", + "XR_FB_scene", + "XR_FB_scene_capture", + "XR_FB_spatial_entity_container", + "XR_FB_spatial_entity", + "XR_FB_spatial_entity_query", + "XR_FB_spatial_entity_sharing", + "XR_FB_spatial_entity_storage_batch", + "XR_FB_spatial_entity_storage", + "XR_FB_spatial_entity_user", + "XR_FB_swapchain_update_state", + "XR_FB_swapchain_update_state_vulkan", + "XR_FB_swapchain_update_state_opengl_es", + "XR_FB_triangle_mesh", + "XR_META_automatic_layer_filter", + "XR_HTC_facial_tracking", + "XR_HTC_passthrough" + ] + } + ], + "form_factors": [ + { + "form_factor": "XR_FORM_FACTOR_HANDHELD_DISPLAY", + "view_configurations": [ + { + "view_configuration": "XR_VIEW_CONFIGURATION_TYPE_PRIMARY_MONO", + "environment_blend_modes": [ + "OPAQUE", + "ADDITIVE", + "ALPHA_BLEND" + ] + } + ] + }, + { + "form_factor": "XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY", + "view_configurations": [ + { + "view_configuration": "XR_VIEW_CONFIGURATION_TYPE_PRIMARY_MONO", + "environment_blend_modes": [ + "OPAQUE", + "ADDITIVE", + "ALPHA_BLEND" + ] + }, + { + "view_configuration": "XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO", + "environment_blend_modes": [ + "OPAQUE", + "ADDITIVE", + "ALPHA_BLEND" + ] + } + ] + } + ] +} diff --git a/clients/godot.json.license b/clients/godot.json.license new file mode 100644 index 0000000..bb89a0e --- /dev/null +++ b/clients/godot.json.license @@ -0,0 +1,2 @@ +Copyright 2025, The Khronos Group Inc. +SPDX-License-Identifier: CC-BY-4.0 diff --git a/extension_support_report.py b/extension_support_report.py index 8796195..c20176f 100755 --- a/extension_support_report.py +++ b/extension_support_report.py @@ -5,7 +5,9 @@ from openxr_inventory.extensions import generate_report from openxr_inventory.runtime_inventory import load_all_runtimes +from openxr_inventory.client_inventory import load_all_clients if __name__ == "__main__": runtimes = load_all_runtimes() - generate_report(runtimes) + clients = load_all_clients() + generate_report(runtimes, clients) diff --git a/openxr_inventory/client_inventory.py b/openxr_inventory/client_inventory.py new file mode 100644 index 0000000..08e732c --- /dev/null +++ b/openxr_inventory/client_inventory.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python3 -i +# Copyright 2022, The Khronos Group Inc. +# +# SPDX-License-Identifier: Apache-2.0 + +import json +from dataclasses import dataclass +from pathlib import Path +from typing import Dict, List, Optional, Union + +from .inventory_data import ExtensionEntry, EnvironmentBlendModeEntry, ViewConfigurationEntry, FormFactorEntry + +@dataclass +class ComponentEntry: + """Data about component of a client""" + + name: str + """The name of the component""" + + abbreviation: str + """The abbreviation to use in the client matrix""" + + color: str + """The color to use in the client matrix""" + + extensions: List[ExtensionEntry] + """The supported extensions""" + + @classmethod + def from_json(self, d: Dict) -> "ComponentEntry": + exts = [ExtensionEntry.from_json(entry) for entry in d["extensions"]] + return ComponentEntry( + name=d["name"], + abbreviation=d["abbreviation"], + color=d["color"], + extensions=exts + ) + + +@dataclass(order=True) +class ClientData: + """Data about a single client, corresponds to a single JSON file in the inventory""" + + stub: str + """A short identifier suitable for use as an HTML anchor, file name stem, etc.""" + + name: str + """The name of the client""" + + notes: Optional[str] + """Free-form text with extra information""" + + vendor: str + """The vendor's name""" + + components: List[ComponentEntry] + """The components""" + + form_factors: List[FormFactorEntry] + """The supported form factors""" + + def get_component_for_extension(self, ext_name: str) -> Optional[ComponentEntry]: + for component in self.components: + for entry in component.extensions: + if entry.name == ext_name: + return component + + return None + + def get_extension_entry(self, ext_name: str) -> Optional[ExtensionEntry]: + """ + Get the entry for the named extension, if it exists. + + This can tell you if the runtime supports that extension, as well as any notes from the inventory. + """ + extension_entries = [] + + for component in self.components: + extension_entries += [ entry for entry in component.extensions if entry.name == ext_name ] + + if not extension_entries: + return + assert len(extension_entries) == 1 + return extension_entries[0] + + @property + def conformance_submission_url(self) -> Optional[str]: + if self.conformance_submission: + return "https://www.khronos.org/conformance/adopters/conformant-products/openxr#submission_{}".format( + self.conformance_submission + ) + + @classmethod + def from_json(cls, stub: str, d: Dict) -> "ClientData": + """ + Create an object from the data loaded from a json file. + + 'stub' should be the stem of the filename, typically. + """ + comps = [ComponentEntry.from_json(entry) for entry in d["components"]] + form_factors = [FormFactorEntry.from_json(entry) for entry in (d.get("form_factors", []))] + return ClientData( + stub=stub, + name=d["name"], + notes=d.get("notes"), + vendor=d["vendor"], + components=comps, + form_factors=form_factors, + ) + + +def load_all_clients(directory=None) -> List[ClientData]: + """Load all client inventory files.""" + if not directory: + directory = Path(__file__).parent.parent / "clients" + + failures = [] + results = [] + for f in directory.glob("*.json"): + with open(f, "r", encoding="utf-8") as fp: + data = json.load(fp) + try: + parsed = ClientData.from_json(f.stem, data) + results.append(parsed) + print("Loaded %s" % parsed.stub) + except KeyError as e: + print( + "Error loading %s (probably missing required property), skipping..." + % str(f) + ) + print(e) + failures.append(str(f)) + if failures: + print(failures) + raise RuntimeError( + "Could not parse some files, probably missing required properties" + ) + return results diff --git a/openxr_inventory/extensions.py b/openxr_inventory/extensions.py index ce24059..eb4997d 100644 --- a/openxr_inventory/extensions.py +++ b/openxr_inventory/extensions.py @@ -7,7 +7,9 @@ import re from typing import Dict, List, Tuple -from .runtime_inventory import ExtensionEntry, RuntimeData +from .inventory_data import ExtensionEntry +from .runtime_inventory import RuntimeData +from .client_inventory import ClientData _RE_IS_KHR = re.compile(r"^XR_KHR_.*") _RE_IS_EXT = re.compile(r"^XR_EXT_.*") @@ -83,11 +85,17 @@ def ext_name_key(ext_name: str): return (categorize_ext_name(ext_name), ext_name) -def compute_known_extensions(runtimes: List[RuntimeData]) -> List[str]: +def compute_known_extensions(runtimes: List[RuntimeData], clients: List[ClientData]) -> List[str]: """Compute a list of all known extensions, sorted as in the spec itself.""" known_extensions = set() + for runtime in runtimes: known_extensions.update(ext.name for ext in runtime.extensions) + + for client in clients: + for component in client.components: + known_extensions.update(ext.name for ext in component.extensions) + return list(sorted(known_extensions, key=ext_name_key)) @@ -99,9 +107,19 @@ def compute_runtime_support(runtimes: List[RuntimeData]) -> Dict[str, List[str]] runtime_support[runtime.name] = support return runtime_support - -def compute_known_form_factors(runtimes: List[RuntimeData]) -> List[str]: +def compute_client_support(clients: List[ClientData]) -> Dict[str, List[str]]: + """Compute a dictionary from client names to a list of supported extension names.""" + client_support = {} + for client in clients: + support = [] + for component in client.components: + support += [ext.name for ext in component.extensions] + client_support[client.name] = support + return client_support + +def compute_known_form_factors(runtimes: List[RuntimeData], clients: List[ClientData]) -> List[str]: known_form_factors = {} + for runtime in runtimes: for ff in runtime.form_factors: for vc in ff.view_configurations: @@ -109,46 +127,85 @@ def compute_known_form_factors(runtimes: List[RuntimeData]) -> List[str]: for ebm in vc.environment_blend_modes: known_form_factors[ff.name][vc.name] = known_form_factors[ff.name].get(vc.name, set()) known_form_factors[ff.name][vc.name].add(ebm.name) + + for client in clients: + for ff in client.form_factors: + for vc in ff.view_configurations: + known_form_factors[ff.name] = known_form_factors.get(ff.name, {}) + for ebm in vc.environment_blend_modes: + known_form_factors[ff.name][vc.name] = known_form_factors[ff.name].get(vc.name, set()) + known_form_factors[ff.name][vc.name].add(ebm.name) + return known_form_factors -def compute_form_factor_support(runtimes: List[RuntimeData]) -> Dict[str, List[str]]: - runtime_form_factor_support = {} +def compute_form_factor_support(runtimes: List[RuntimeData], clients: List[ClientData]) -> Dict[str, List[str]]: + form_factor_support = {} + for runtime in runtimes: if runtime.form_factors: - runtime_form_factor_support[runtime.name] = {} + form_factor_support[runtime.name] = {} for ff in runtime.form_factors: - runtime_form_factor_support[runtime.name][ff.name] = {} + form_factor_support[runtime.name][ff.name] = {} + + for vc in ff.view_configurations: + form_factor_support[runtime.name][ff.name][vc.name] = set() + + for ebm in vc.environment_blend_modes: + form_factor_support[runtime.name][ff.name][vc.name].add(ebm.name) + + for client in clients: + if client.form_factors: + form_factor_support[client.name] = {} + + for ff in client.form_factors: + form_factor_support[client.name][ff.name] = {} for vc in ff.view_configurations: - runtime_form_factor_support[runtime.name][ff.name][vc.name] = set() + form_factor_support[client.name][ff.name][vc.name] = set() for ebm in vc.environment_blend_modes: - runtime_form_factor_support[runtime.name][ff.name][vc.name].add(ebm.name) - return runtime_form_factor_support + form_factor_support[client.name][ff.name][vc.name].add(ebm.name) + + return form_factor_support + +class ExtensionSupport: + runtime_count: int + client_count: int + def __init__(self, runtime_count, client_count): + self.runtime_count = runtime_count + self.client_count = client_count def compute_extension_support( runtimes: List[RuntimeData], -) -> Dict[str, List[Tuple[RuntimeData, ExtensionEntry]]]: + clients: List[ClientData], +) -> Dict[str, ExtensionSupport]: """ For each extension, find all the runtimes that support it. Returns a dict with extension names as the keys, and a list of (RuntimeData, ExtensionEntry) tuples as the values. """ - known_extensions = compute_known_extensions(runtimes) + known_extensions = compute_known_extensions(runtimes, clients) extension_support = {} for extension_name in known_extensions: - # Get all the support - support = [ - (runtime, runtime.get_extension_entry(extension_name)) - for runtime in runtimes - ] + runtime_count = 0 + client_count = 0 + + # Count our runtime support + for runtime in runtimes: + if runtime.get_extension_entry(extension_name): + runtime_count += 1 + + # Get all the client support + for client in clients: + if client.get_extension_entry(extension_name): + client_count += 1 + # Filter out the empty ones - support = [(runtime, entry) for runtime, entry in support if entry] - extension_support[extension_name] = support + extension_support[extension_name] = ExtensionSupport(runtime_count, client_count) return extension_support @@ -157,6 +214,7 @@ def compute_extension_support( def generate_report( runtimes: List[RuntimeData], + clients: List[ClientData], template_filename: str = _FILENAME_STEM + ".jinja2.html", out_filename: str = "public/" + _FILENAME_STEM + ".html", ): @@ -173,12 +231,14 @@ def generate_report( template = env.get_template(template_filename) spec_url = "https://www.khronos.org/registry/OpenXR/specs/1.1/html/xrspec.html" contents = template.render( - extensions=compute_known_extensions(runtimes), - extension_support=compute_extension_support(runtimes), + extensions=compute_known_extensions(runtimes, clients), + extension_support=compute_extension_support(runtimes, clients), runtime_support=compute_runtime_support(runtimes), - known_form_factors=compute_known_form_factors(runtimes), - form_factor_support=compute_form_factor_support(runtimes), + client_support=compute_client_support(clients), + known_form_factors=compute_known_form_factors(runtimes, clients), + form_factor_support=compute_form_factor_support(runtimes, clients), runtimes=runtimes, + clients=clients, spec_url=spec_url, ) @@ -191,6 +251,8 @@ def generate_report( if __name__ == "__main__": from .runtime_inventory import load_all_runtimes + from .client_inventory import load_all_clients runtimes = load_all_runtimes() - generate_report(runtimes) + clients = load_all_clients() + generate_report(runtimes, clients) diff --git a/openxr_inventory/inventory_data.py b/openxr_inventory/inventory_data.py new file mode 100644 index 0000000..83ed878 --- /dev/null +++ b/openxr_inventory/inventory_data.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 -i +# Copyright 2022, The Khronos Group Inc. +# +# SPDX-License-Identifier: Apache-2.0 + +import json +from dataclasses import dataclass +from typing import Dict, List, Optional, Union + +@dataclass +class ExtensionEntry: + """ + An entry in the "extensions" array for a runtime or layer. + + Corresponds to the schema reference `#/definitions/extension`. + """ + + name: str + """Extension name""" + + notes: Optional[str] = None + """Optional notes about the support/use of this extension""" + + @classmethod + def from_json(cls, d: Union[Dict, str]) -> "ExtensionEntry": + """Create an ExtensionEntry from either a str or dict as you'd get from parsing the JSON.""" + if isinstance(d, str): + return ExtensionEntry(name=d) + return ExtensionEntry(name=d["name"], notes=d.get("notes")) + +@dataclass +class EnvironmentBlendModeEntry: + """ + An entry in the "environment_blend_modes" array for a runtime or layer. + + Corresponds to the schema reference `#/definitions/environment_blend_mode`. + """ + + name: str + """Environment blend mode name""" + + @classmethod + def from_json(cls, d: Union[Dict, str]) -> "EnvironmentBlendModeEntry": + return EnvironmentBlendModeEntry(name=d) + +@dataclass +class ViewConfigurationEntry: + """ + An entry in the "view_configurations" array for a runtime or layer. + + Corresponds to the schema reference `#/definitions/view_configuration`. + """ + + name: str + """View configuration name""" + + environment_blend_modes: List[EnvironmentBlendModeEntry] + """Environment blend modes supported for the view configuration""" + + @classmethod + def from_json(cls, d: Union[Dict, str]) -> "ViewConfigurationEntry": + return ViewConfigurationEntry(name=d["view_configuration"], environment_blend_modes=[EnvironmentBlendModeEntry.from_json(b) for b in d["environment_blend_modes"]]) + +@dataclass +class FormFactorEntry: + """ + An entry in the "form_factors" array for a runtime or layer. + + Corresponds to the schema reference `#/definitions/form_factor`. + """ + + name: str + """Form Factor name""" + + view_configurations: List[ViewConfigurationEntry] + """View configurations supported for the form factor""" + + @classmethod + def from_json(cls, d: Union[Dict, str]) -> "FormFactorEntry": + return FormFactorEntry(name=d["form_factor"], view_configurations=[ViewConfigurationEntry.from_json(v) for v in d["view_configurations"]]) diff --git a/openxr_inventory/runtime_inventory.py b/openxr_inventory/runtime_inventory.py index edb6d58..a377c98 100644 --- a/openxr_inventory/runtime_inventory.py +++ b/openxr_inventory/runtime_inventory.py @@ -8,82 +8,7 @@ from pathlib import Path from typing import Dict, List, Optional, Union - -@dataclass -class ExtensionEntry: - """ - An entry in the "extensions" array for a runtime or layer. - - Corresponds to the schema reference `#/definitions/extension`. - """ - - name: str - """Extension name""" - - notes: Optional[str] = None - """Optional notes about the support/use of this extension""" - - @classmethod - def from_json(cls, d: Union[Dict, str]) -> "ExtensionEntry": - """Create an ExtensionEntry from either a str or dict as you'd get from parsing the JSON.""" - if isinstance(d, str): - return ExtensionEntry(name=d) - return ExtensionEntry(name=d["name"], notes=d.get("notes")) - - -@dataclass -class EnvironmentBlendModeEntry: - """ - An entry in the "environment_blend_modes" array for a runtime or layer. - - Corresponds to the schema reference `#/definitions/environment_blend_mode`. - """ - - name: str - """Environment blend mode name""" - - @classmethod - def from_json(cls, d: Union[Dict, str]) -> "EnvironmentBlendModeEntry": - return EnvironmentBlendModeEntry(name=d) - - -@dataclass -class ViewConfigurationEntry: - """ - An entry in the "view_configurations" array for a runtime or layer. - - Corresponds to the schema reference `#/definitions/view_configuration`. - """ - - name: str - """View configuration name""" - - environment_blend_modes: List[EnvironmentBlendModeEntry] - """Environment blend modes supported for the view configuration""" - - @classmethod - def from_json(cls, d: Union[Dict, str]) -> "ViewConfigurationEntry": - return ViewConfigurationEntry(name=d["view_configuration"], environment_blend_modes=[EnvironmentBlendModeEntry.from_json(b) for b in d["environment_blend_modes"]]) - - -@dataclass -class FormFactorEntry: - """ - An entry in the "form_factors" array for a runtime or layer. - - Corresponds to the schema reference `#/definitions/form_factor`. - """ - - name: str - """Form Factor name""" - - view_configurations: List[ViewConfigurationEntry] - """View configurations supported for the form factor""" - - @classmethod - def from_json(cls, d: Union[Dict, str]) -> "FormFactorEntry": - return FormFactorEntry(name=d["form_factor"], view_configurations=[ViewConfigurationEntry.from_json(v) for v in d["view_configurations"]]) - +from .inventory_data import ExtensionEntry, EnvironmentBlendModeEntry, ViewConfigurationEntry, FormFactorEntry @dataclass(order=True) class RuntimeData: diff --git a/openxr_inventory/templates/extension_support.jinja2.html b/openxr_inventory/templates/extension_support.jinja2.html index 6722132..c735f9b 100644 --- a/openxr_inventory/templates/extension_support.jinja2.html +++ b/openxr_inventory/templates/extension_support.jinja2.html @@ -16,7 +16,9 @@ {% block navbar_list_items %}
  • Extensions
  • Runtimes
  • -
  • Extension Support Matrix
  • +
  • Runtime Support Matrix
  • +
  • Clients
  • +
  • Client Support Matrix
  • {% endblock navbar_list_items %} {% block style %} @@ -64,16 +66,29 @@

    Extensions

    {% set support = extension_support[extension_name] %}
    -

    {{ extension_name }} ({{ support | length }} runtime{{ "s" if support | length != 1 }})

    +

    {{ extension_name }} ({{ support.runtime_count }} runtime{{ "s" if support.runtime_count != 1 }}, {{ support.client_count }} client{{ "s" if support.client_count != 1 }})

    Specification for {{ extension_name }}

    + Runtimes:
    + {% if support.client_count > 0 %} + Clients:
    + + {% endif %}
    {% endfor %} @@ -113,13 +128,14 @@

    {{ runtime.name }}

  • {{extension.name}} {% if extension.notes %} - {{ extension.notes }} {% endif %}
  • {% endfor %} + {% endfor %}
    -
    +

    Runtime support matrix

    @@ -229,4 +245,151 @@

    Runtime support matrix

    + +
    + +
    +

    Clients

    + + +
    + + {% for client in clients %} +
    +

    {{ client.name }}

    +
      +
    • Vendor: {{ client.vendor }}
    • + {% if client.notes %} +
    • Notes: {{ client.notes }}
    • + {% endif %} + {% for component in client.components %} +
    • Component: {{ component.name }}
      Extensions: +
        + {% for extension in component.extensions %} +
      • {{extension.name}} {% if extension.notes %} - {{ extension.notes }} {% endif %}
      • + {% endfor %} +
      +
    • + {% endfor %} +
    +
    + {% endfor %} +
    + +
    +
    +

    Client support matrix

    +
    + + + + + {% for client in clients | sort %} + {# pragmatic check if the client name fits in the layout or if it needs to be truncated and put the full name in tooltip #} + {% if client.name|length < 34 %} + + {% else %} + + {% endif %} + {% endfor %} + + + + + {% macro extension_matrix_row(extension_name) %} + + + {% for client in clients | sort %} + {% set component = client.get_component_for_extension(extension_name) %} + {% if component %} + + {% else %} + + {% endif %} + {% endfor %} + + {% endmacro %} + + {# Loop through all categories of extension (KHR, EXT, vendor, KHX, EXTX, Vendor-X) #} + {% for c in cat.all_categories() %} + {# Loop through all extensions in that category #} + {% for extension_name in extensions if categorize_ext(extension_name) == c %} + {% if loop.first %} + {# this header is inside the loop so it is skipped if we have no items in this category #} + {# it is inside this "if" so it only shows up once for each category. #} + + + + {% endif %} + {# Write out a row in the table for this extension #} + {{ extension_matrix_row(extension_name) }} + {% endfor %} + {% endfor %} + + + + + + {% for ff, view_configurations in known_form_factors.items() %} + + + + {% for client in clients | sort %} + + {% endfor %} + + + {% for vc, environment_blend_modes in view_configurations.items() %} + + + + {% for client in clients | sort %} + + {% endfor %} + + {% for ebm in environment_blend_modes %} + + + {% for client in clients | sort %} + {% if ff in form_factor_support[client.name] and vc in form_factor_support[client.name][ff] and ebm in form_factor_support[client.name][ff][vc] %} + + {% else %} + + {% endif %} + {% endfor %} + + {% endfor %} + + + {% endfor %} + + {% endfor %} + + +
    {{ client.name }}
    {{ client.name|truncate(34, True) }}
    + {{ extension_name }} + + + + {{ component.abbreviation }}Not supported or not applicable
    {{ cat_captions[c] }} Extensions
    Client Features
    + {{ ff }} + + + +
    + → {{ vc }} + + + +
    + → → {{ ebm }} + + + + SupportedNot supported or not applicable
    +
    {% endblock container_contents %} diff --git a/schema.json b/runtime_schema.json similarity index 100% rename from schema.json rename to runtime_schema.json diff --git a/schema.json.license b/runtime_schema.json.license similarity index 100% rename from schema.json.license rename to runtime_schema.json.license diff --git a/runtimes/bytedance_pico.json b/runtimes/bytedance_pico.json index f687da9..8b71bfe 100644 --- a/runtimes/bytedance_pico.json +++ b/runtimes/bytedance_pico.json @@ -1,5 +1,5 @@ { - "$schema": "../schema.json", + "$schema": "../runtime_schema.json", "name": "Bytedance PICO 4 / PICO Neo3", "conformance_submission": 25, "platform": "Android (All-in-one)", diff --git a/runtimes/bytedance_pico_4_ultra.json b/runtimes/bytedance_pico_4_ultra.json index 3ad4df7..8a60449 100644 --- a/runtimes/bytedance_pico_4_ultra.json +++ b/runtimes/bytedance_pico_4_ultra.json @@ -1,5 +1,5 @@ { - "$schema": "../schema.json", + "$schema": "../runtime_schema.json", "name": "Bytedance PICO 4 Ultra", "conformance_submission": 39, "platform": "Android (All-in-one)", diff --git a/runtimes/htc_vive_cosmos.json b/runtimes/htc_vive_cosmos.json index 9066a27..80428f6 100644 --- a/runtimes/htc_vive_cosmos.json +++ b/runtimes/htc_vive_cosmos.json @@ -1,5 +1,5 @@ { - "$schema": "../schema.json", + "$schema": "../runtime_schema.json", "name": "HTC Vive Cosmos", "conformance_submission": 13, "platform": "Windows (Desktop)", diff --git a/runtimes/htc_vive_focus.json b/runtimes/htc_vive_focus.json index 822163b..8fad368 100644 --- a/runtimes/htc_vive_focus.json +++ b/runtimes/htc_vive_focus.json @@ -1,5 +1,5 @@ { - "$schema": "../schema.json", + "$schema": "../runtime_schema.json", "name": "HTC Vive Focus 3", "conformance_submission": 21, "platform": "Android (All-in-one)", diff --git a/runtimes/magicleap_ml2.json b/runtimes/magicleap_ml2.json index 21e3fa9..807e00f 100644 --- a/runtimes/magicleap_ml2.json +++ b/runtimes/magicleap_ml2.json @@ -1,5 +1,5 @@ { - "$schema": "../schema.json", + "$schema": "../runtime_schema.json", "name": "Magic Leap ML2", "conformance_submission": 37, "form_factors": [ diff --git a/runtimes/magicleap_remote_rendering.json b/runtimes/magicleap_remote_rendering.json index d674f92..fd1b016 100644 --- a/runtimes/magicleap_remote_rendering.json +++ b/runtimes/magicleap_remote_rendering.json @@ -1,5 +1,5 @@ { - "$schema": "../schema.json", + "$schema": "../runtime_schema.json", "name": "Magic Leap Remote Rendering", "conformance_submission": 27, "form_factors": [ diff --git a/runtimes/meta_pc.json b/runtimes/meta_pc.json index e23b69a..984445e 100644 --- a/runtimes/meta_pc.json +++ b/runtimes/meta_pc.json @@ -1,6 +1,6 @@ { "notes": "Generated using list_json: 'Oculus' (1.104.0)", - "$schema": "../schema.json", + "$schema": "../runtime_schema.json", "name": "Meta (PC)", "conformance_submission": 3, "platform": "Windows (Desktop)", diff --git a/runtimes/meta_pc_dev_mode.json b/runtimes/meta_pc_dev_mode.json index 1904c1a..87722cd 100644 --- a/runtimes/meta_pc_dev_mode.json +++ b/runtimes/meta_pc_dev_mode.json @@ -1,6 +1,6 @@ { "notes": "Generated using list_json: 'Oculus' (1.104.0) with Developer Only features enabled", - "$schema": "../schema.json", + "$schema": "../runtime_schema.json", "name": "Meta (PC) Developer Mode", "conformance_submission": 3, "platform": "Windows (Desktop)", diff --git a/runtimes/meta_quest_1_mobile.json b/runtimes/meta_quest_1_mobile.json index 575cfef..d51e6aa 100644 --- a/runtimes/meta_quest_1_mobile.json +++ b/runtimes/meta_quest_1_mobile.json @@ -1,6 +1,6 @@ { "notes": "Generated using list_json: 'Oculus' (50.158.0) - using Quest 2", - "$schema": "../schema.json", + "$schema": "../runtime_schema.json", "name": "Meta Quest 1 (Mobile)", "conformance_submission": 2, "platform": "Android (All-in-one)", diff --git a/runtimes/meta_quest_2_mobile.json b/runtimes/meta_quest_2_mobile.json index 65d2a0c..4940627 100644 --- a/runtimes/meta_quest_2_mobile.json +++ b/runtimes/meta_quest_2_mobile.json @@ -1,6 +1,6 @@ { "notes": "Generated using list_json: 'Oculus' (72.945.0)", - "$schema": "../schema.json", + "$schema": "../runtime_schema.json", "name": "Meta Quest 2 (Mobile)", "conformance_submission": 4, "platform": "Android (All-in-one)", diff --git a/runtimes/meta_quest_3_mobile.json b/runtimes/meta_quest_3_mobile.json index 38a7ef9..285dd6e 100644 --- a/runtimes/meta_quest_3_mobile.json +++ b/runtimes/meta_quest_3_mobile.json @@ -1,6 +1,6 @@ { "notes": "Generated using list_json: 'Oculus' (72.946.0)", - "$schema": "../schema.json", + "$schema": "../runtime_schema.json", "name": "Meta Quest 3 (Mobile)", "conformance_submission": 32, "platform": "Android (All-in-one)", diff --git a/runtimes/meta_quest_3s_mobile.json b/runtimes/meta_quest_3s_mobile.json index 13032d1..b6b609c 100644 --- a/runtimes/meta_quest_3s_mobile.json +++ b/runtimes/meta_quest_3s_mobile.json @@ -1,6 +1,6 @@ { "notes": "Generated using list_json: 'Oculus' (71.0.0)", - "$schema": "../schema.json", + "$schema": "../runtime_schema.json", "name": "Meta Quest 3S (Mobile)", "platform": "Android (All-in-one)", "vendor": "Meta Platforms", diff --git a/runtimes/meta_quest_pro_mobile.json b/runtimes/meta_quest_pro_mobile.json index ab881ac..0c83280 100644 --- a/runtimes/meta_quest_pro_mobile.json +++ b/runtimes/meta_quest_pro_mobile.json @@ -1,6 +1,6 @@ { "notes": "Generated using list_json: 'Oculus' (72.946.0)", - "$schema": "../schema.json", + "$schema": "../runtime_schema.json", "name": "Meta Quest Pro (Mobile)", "conformance_submission": 33, "platform": "Android (All-in-one)", diff --git a/runtimes/meta_xr_simulator.json b/runtimes/meta_xr_simulator.json index d719974..efdc924 100644 --- a/runtimes/meta_xr_simulator.json +++ b/runtimes/meta_xr_simulator.json @@ -1,6 +1,6 @@ { "notes": "Generated using list_json: 'Meta' (1.59.0) - simulates Quest 3", - "$schema": "../schema.json", + "$schema": "../runtime_schema.json", "name": "Meta XR Simulator", "conformance_submission": 30, "platform": "Windows (Desktop)", diff --git a/runtimes/microsoft_hololens2.json b/runtimes/microsoft_hololens2.json index b851ac6..5ac8042 100644 --- a/runtimes/microsoft_hololens2.json +++ b/runtimes/microsoft_hololens2.json @@ -1,5 +1,5 @@ { - "$schema": "../schema.json", + "$schema": "../runtime_schema.json", "name": "Windows Mixed Reality - HoloLens 2", "conformance_submission": 1, "platform": "Windows (HoloLens 2)", diff --git a/runtimes/microsoft_pc.json b/runtimes/microsoft_pc.json index 4e48a27..18c7dce 100644 --- a/runtimes/microsoft_pc.json +++ b/runtimes/microsoft_pc.json @@ -1,5 +1,5 @@ { - "$schema": "../schema.json", + "$schema": "../runtime_schema.json", "name": "Windows Mixed Reality", "conformance_submission": 1, "platform": "Windows (Desktop)", diff --git a/runtimes/microsoft_remoting.json b/runtimes/microsoft_remoting.json index 252cdbd..73b78b7 100644 --- a/runtimes/microsoft_remoting.json +++ b/runtimes/microsoft_remoting.json @@ -1,5 +1,5 @@ { - "$schema": "../schema.json", + "$schema": "../runtime_schema.json", "name": "Windows Mixed Reality Remoting", "conformance_submission": 5, "platform": "Windows (Desktop)", diff --git a/runtimes/monado_android.json b/runtimes/monado_android.json index ff9ff78..d983a72 100644 --- a/runtimes/monado_android.json +++ b/runtimes/monado_android.json @@ -1,5 +1,5 @@ { - "$schema": "../schema.json", + "$schema": "../runtime_schema.json", "name": "Monado (Android)", "vendor": "Monado Community + Collabora, Ltd.", "platform": "Android (Phone/Installable)", diff --git a/runtimes/monado_linux.json b/runtimes/monado_linux.json index a39b05c..f7d9bfd 100644 --- a/runtimes/monado_linux.json +++ b/runtimes/monado_linux.json @@ -1,5 +1,5 @@ { - "$schema": "../schema.json", + "$schema": "../runtime_schema.json", "name": "Monado (Desktop / Embedded Linux)", "vendor": "Monado Community + Collabora, Ltd.", "platform": "Linux (Desktop/Embedded)", diff --git a/runtimes/monado_windows.json b/runtimes/monado_windows.json index f4ead8b..d203528 100644 --- a/runtimes/monado_windows.json +++ b/runtimes/monado_windows.json @@ -1,5 +1,5 @@ { - "$schema": "../schema.json", + "$schema": "../runtime_schema.json", "name": "Monado (Windows)", "vendor": "Monado Community + Collabora, Ltd.", "platform": "Windows (Desktop)", diff --git a/runtimes/qualcomm.json b/runtimes/qualcomm.json index 55f7143..ea0d9b9 100644 --- a/runtimes/qualcomm.json +++ b/runtimes/qualcomm.json @@ -1,5 +1,5 @@ { - "$schema": "../schema.json", + "$schema": "../runtime_schema.json", "name": "Monado 21.0.0 / Qualcomm Retina 1.0.0 (Snapdragon 840/865/888)", "conformance_submission": 16, "vendor": "QUALCOMM", diff --git a/runtimes/valve_steamvr.json b/runtimes/valve_steamvr.json index 38fb537..7e3a813 100644 --- a/runtimes/valve_steamvr.json +++ b/runtimes/valve_steamvr.json @@ -1,6 +1,6 @@ { "notes": "Generated using list_json: 'SteamVR/OpenXR' (0.1.0)", - "$schema": "../schema.json", + "$schema": "../runtime_schema.json", "name": "SteamVR", "conformance_submission": 11, "platform": "Windows (Desktop)", diff --git a/runtimes/varjo.json b/runtimes/varjo.json index 340ce97..07492ac 100644 --- a/runtimes/varjo.json +++ b/runtimes/varjo.json @@ -1,5 +1,5 @@ { - "$schema": "../schema.json", + "$schema": "../runtime_schema.json", "name": "Varjo", "conformance_submission": 15, "platform": "Windows (Desktop)", diff --git a/verify_schema.sh b/verify_schema.sh index 1008d81..8220554 100755 --- a/verify_schema.sh +++ b/verify_schema.sh @@ -5,6 +5,11 @@ set -e for fn in runtimes/*.json; do - echo "Checking $fn against the schema" - python3 -m jsonschema schema.json -i "$fn" + echo "Checking $fn against the runtime schema" + python3 -m jsonschema runtime_schema.json -i "$fn" +done + +for fn in clients/*.json; do + echo "Checking $fn against the client schema" + python3 -m jsonschema client_schema.json -i "$fn" done From 265dcec3f6e907a9f8288ddb04ba99cd8ac9782b Mon Sep 17 00:00:00 2001 From: Andy Biar Date: Tue, 15 Jul 2025 17:21:32 -0700 Subject: [PATCH 02/10] Add Unity support information. --- clients/unity.json | 115 +++++++++++++++++++++++++++++++++++++ clients/unity.json.license | 2 + 2 files changed, 117 insertions(+) create mode 100644 clients/unity.json create mode 100644 clients/unity.json.license diff --git a/clients/unity.json b/clients/unity.json new file mode 100644 index 0000000..c4bd3c6 --- /dev/null +++ b/clients/unity.json @@ -0,0 +1,115 @@ +{ + "$schema": "../client_schema.json", + "name": "Unity", + "vendor": "Unity Technologies", + "platforms": [ + "Windows (Desktop)", + "Windows Subsystem for Android (WSA)", + "Android (All-in-one)", + "macOS (Desktop)" + ], + "components": [ + { + "name": "OpenXR Plug-in", + "abbreviation": "OpenXR Plug-in", + "extensions": [ + "XR_KHR_android_create_instance", + "XR_KHR_android_surface_swapchain", + "XR_KHR_android_thread_settings" + "XR_KHR_composition_layer_depth", + "XR_KHR_D3D11_enable", + "XR_KHR_D3D12_enable", + "XR_KHR_loader_init", + "XR_KHR_opengl_es_enable", + "XR_KHR_visibility_mask", + "XR_KHR_vulkan_enable", + "XR_KHR_vulkan_enable2", + "XR_EXT_performance_settings", + "XR_EXT_user_presence", + "XR_FB_foveation", + "XR_FB_foveation_configuration", + "XR_FB_foveation_vulkan", + "XR_FB_space_warp", + "XR_FB_swapchain_update_state", + "XR_META_foveation_eye_tracked", + "XR_META_performance_metrics", + "XR_META_vulkan_swapchain_create_info", + "XR_MSFT_first_person_observer", + "XR_MSFT_holographic_window_attachment", + "XR_MSFT_secondary_view_configuration", + "XR_OCULUS_audio_device_guid", + "XR_KHR_maintenance1", + "XR_EXT_hp_mixed_reality_controller", + "XR_EXT_local_floor", + "XR_EXT_palm_pose", + "XR_VARJO_quad_views", + "XR_KHR_binding_modification", + "XR_EXT_conformance_automation", + "XR_EXT_dpad_binding", + "XR_EXT_eye_gaze_interaction", + "XR_EXT_hand_interaction", + "XR_FB_touch_controller_pro", + "XR_META_touch_controller_plus", + "XR_MSFT_unbounded_reference_space" + ] + }, + { + "name": "XR Composition Layers", + "abbreviation": "Comp Layers", + "extensions": [ + "XR_KHR_composition_layer_color_scale_bias", + "XR_KHR_composition_layer_cube", + "XR_KHR_composition_layer_cylinder", + "XR_KHR_composition_layer_equirect", + "XR_KHR_composition_layer_equirect2" + ] + }, + { + "name": "Unity OpenXR: Meta", + "abbreviation": "Meta", + "extensions": [ + "XR_FB_display_refresh_rate", + "XR_FB_passthrough", + "XR_FB_scene", + "XR_FB_scene_capture", + "XR_FB_spatial_entity", + "XR_FB_spatial_entity_container", + "XR_FB_spatial_entity_query", + "XR_META_colocation_discovery", + "XR_META_spatial_entity_group_sharing", + "XR_META_spatial_entity_mesh", + "XR_META_spatial_entity_sharing" + ] + }, + { + "name": "Unity OpenXR: Android XR", + "abbreviation": "Android XR", + "extensions": [ + "XR_FB_display_refresh_rate" + ] + }, + { + "name": "XR Hands", + "abbreviation": "Hands", + "extensions": [ + "XR_EXT_hand_tracking", + "XR_FB_hand_tracking_aim" + ] + } + ], + "form_factors": [ + { + "form_factor": "XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY", + "view_configurations": [ + { + "view_configuration": "XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO", + "environment_blend_modes": [ + "OPAQUE", + "ADDITIVE", + "ALPHA_BLEND" + ] + } + ] + } + ] +} diff --git a/clients/unity.json.license b/clients/unity.json.license new file mode 100644 index 0000000..bb89a0e --- /dev/null +++ b/clients/unity.json.license @@ -0,0 +1,2 @@ +Copyright 2025, The Khronos Group Inc. +SPDX-License-Identifier: CC-BY-4.0 From 665fc987a41bd9af43db1b1b11184b132b7f9f47 Mon Sep 17 00:00:00 2001 From: Andy Biar Date: Tue, 15 Jul 2025 17:26:32 -0700 Subject: [PATCH 03/10] fix Godot typo fix syntax and schema Removed color option as discussed and allowed multiple components to be listed --- client_schema.json | 23 ------------------- clients/godot.json | 4 +--- clients/unity.json | 2 +- openxr_inventory/client_inventory.py | 22 +++++++----------- openxr_inventory/extensions.py | 3 +-- .../templates/extension_support.jinja2.html | 13 ++++++++--- 6 files changed, 21 insertions(+), 46 deletions(-) diff --git a/client_schema.json b/client_schema.json index 7247793..43d1d87 100644 --- a/client_schema.json +++ b/client_schema.json @@ -103,28 +103,6 @@ "title": "Abbreviation shown in matrix", "type": "string" }, - "color": { - "title": "Background color used in matrix", - "type": "string", - "enum": [ - "black", - "white", - "gray", - "silver", - "maroon", - "red", - "purple", - "fushsia", - "green", - "lime", - "olive", - "yellow", - "navy", - "blue", - "teal", - "aqua" - ] - }, "extensions": { "title": "Supported extensions", "description": "A list of all supported extensions in this client, on this platform. Details or caveats may be included in 'notes'.", @@ -137,7 +115,6 @@ "required": [ "name", "abbreviation", - "color", "extensions" ] }, diff --git a/clients/godot.json b/clients/godot.json index 48cce4c..4aa9977 100644 --- a/clients/godot.json +++ b/clients/godot.json @@ -8,13 +8,12 @@ "Linux (Desktop/Embedded)", "Android (All-in-one)", "Android (Phone/Installable)", - "MacOS (Desktop)" + "macOS (Desktop)" ], "components": [ { "name": "Godot Core", "abbreviation": "Core", - "color": "green", "extensions": [ "XR_KHR_android_create_instance", "XR_KHR_android_surface_swapchain", @@ -57,7 +56,6 @@ { "name": "Godot Vendors plugin", "abbreviation": "Vendors", - "color": "yellow", "extensions": [ "XR_FB_body_tracking", "XR_FB_composition_layer_alpha_blend", diff --git a/clients/unity.json b/clients/unity.json index c4bd3c6..96678b1 100644 --- a/clients/unity.json +++ b/clients/unity.json @@ -15,7 +15,7 @@ "extensions": [ "XR_KHR_android_create_instance", "XR_KHR_android_surface_swapchain", - "XR_KHR_android_thread_settings" + "XR_KHR_android_thread_settings", "XR_KHR_composition_layer_depth", "XR_KHR_D3D11_enable", "XR_KHR_D3D12_enable", diff --git a/openxr_inventory/client_inventory.py b/openxr_inventory/client_inventory.py index 08e732c..f959aed 100644 --- a/openxr_inventory/client_inventory.py +++ b/openxr_inventory/client_inventory.py @@ -20,9 +20,6 @@ class ComponentEntry: abbreviation: str """The abbreviation to use in the client matrix""" - color: str - """The color to use in the client matrix""" - extensions: List[ExtensionEntry] """The supported extensions""" @@ -32,7 +29,6 @@ def from_json(self, d: Dict) -> "ComponentEntry": return ComponentEntry( name=d["name"], abbreviation=d["abbreviation"], - color=d["color"], extensions=exts ) @@ -59,29 +55,27 @@ class ClientData: form_factors: List[FormFactorEntry] """The supported form factors""" - def get_component_for_extension(self, ext_name: str) -> Optional[ComponentEntry]: + def get_component_for_extension(self, ext_name: str) -> List[ComponentEntry]: + component_entries = [] for component in self.components: for entry in component.extensions: if entry.name == ext_name: - return component + component_entries += [ component ] - return None + return component_entries - def get_extension_entry(self, ext_name: str) -> Optional[ExtensionEntry]: + def get_extension_entry(self, ext_name: str) -> List[ExtensionEntry]: """ - Get the entry for the named extension, if it exists. + Get the entries for the named extension, if they exists. - This can tell you if the runtime supports that extension, as well as any notes from the inventory. + This can tell you if the client supports that extension, as well as any notes from the inventory. """ extension_entries = [] for component in self.components: extension_entries += [ entry for entry in component.extensions if entry.name == ext_name ] - if not extension_entries: - return - assert len(extension_entries) == 1 - return extension_entries[0] + return extension_entries @property def conformance_submission_url(self) -> Optional[str]: diff --git a/openxr_inventory/extensions.py b/openxr_inventory/extensions.py index eb4997d..f9d4d02 100644 --- a/openxr_inventory/extensions.py +++ b/openxr_inventory/extensions.py @@ -201,8 +201,7 @@ def compute_extension_support( # Get all the client support for client in clients: - if client.get_extension_entry(extension_name): - client_count += 1 + client_count += len(client.get_extension_entry(extension_name)) # Filter out the empty ones extension_support[extension_name] = ExtensionSupport(runtime_count, client_count) diff --git a/openxr_inventory/templates/extension_support.jinja2.html b/openxr_inventory/templates/extension_support.jinja2.html index c735f9b..30f14c9 100644 --- a/openxr_inventory/templates/extension_support.jinja2.html +++ b/openxr_inventory/templates/extension_support.jinja2.html @@ -309,9 +309,16 @@

    Client support matrix

    {% for client in clients | sort %} - {% set component = client.get_component_for_extension(extension_name) %} - {% if component %} - {{ component.abbreviation }} + {% set components = client.get_component_for_extension(extension_name) %} + {% if components|length > 0 %} + + {% for component in components %} + {{ component.abbreviation }} + {% if not loop.last %} +
    + {% endif %} + {% endfor %} + {% else %} Not supported or not applicable {% endif %} From 954d42c8a5ad62f6459b6236ba0534088d1503e3 Mon Sep 17 00:00:00 2001 From: Bastiaan Olij Date: Wed, 16 Jul 2025 15:39:36 +1000 Subject: [PATCH 04/10] Added XR_KHR_loader_init_android to unity.json --- clients/unity.json | 1 + 1 file changed, 1 insertion(+) diff --git a/clients/unity.json b/clients/unity.json index 96678b1..3707765 100644 --- a/clients/unity.json +++ b/clients/unity.json @@ -20,6 +20,7 @@ "XR_KHR_D3D11_enable", "XR_KHR_D3D12_enable", "XR_KHR_loader_init", + "XR_KHR_loader_init_android", "XR_KHR_opengl_es_enable", "XR_KHR_visibility_mask", "XR_KHR_vulkan_enable", From 0b0036b5e0e9cce8fdaab3c9e8036d68a52e76f4 Mon Sep 17 00:00:00 2001 From: Bastiaan Olij Date: Wed, 16 Jul 2025 15:48:25 +1000 Subject: [PATCH 05/10] Added some missing core entries for Godot Fixed client count, we don't want to count components Add XR_MNDX_egl_enable support to Godot --- clients/godot.json | 9 +++++++-- openxr_inventory/extensions.py | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/clients/godot.json b/clients/godot.json index 4aa9977..eb42680 100644 --- a/clients/godot.json +++ b/clients/godot.json @@ -15,6 +15,7 @@ "name": "Godot Core", "abbreviation": "Core", "extensions": [ + "XR_KHR_D3D12_enable", "XR_KHR_android_create_instance", "XR_KHR_android_surface_swapchain", "XR_KHR_binding_modification", @@ -35,12 +36,15 @@ "XR_EXT_hand_tracking", "XR_EXT_hand_joints_motion_range", "XR_EXT_hand_tracking_data_source", + "XR_EXT_hp_mixed_reality_controller", + "XR_EXT_interaction_render_model", "XR_EXT_local_floor", "XR_EXT_view_configuration_depth_range", "XR_EXT_palm_pose", "XR_EXT_performance_settings", - "XR_EXT_hp_mixed_reality_controller", + "XR_EXT_render_model", "XR_EXT_samsung_odyssey_controller", + "XR_EXT_uuid", "XR_BD_controller_interaction", "XR_FB_touch_controller_proximity", "XR_FB_touch_controller_pro", @@ -50,7 +54,8 @@ "XR_HTC_hand_interaction", "XR_HTCX_vive_tracker_interaction", "XR_ML_ml2_controller_interaction", - "XR_MSFT_hand_interaction" + "XR_MSFT_hand_interaction", + "XR_MNDX_egl_enable" ] }, { diff --git a/openxr_inventory/extensions.py b/openxr_inventory/extensions.py index f9d4d02..d222ae1 100644 --- a/openxr_inventory/extensions.py +++ b/openxr_inventory/extensions.py @@ -201,7 +201,8 @@ def compute_extension_support( # Get all the client support for client in clients: - client_count += len(client.get_extension_entry(extension_name)) + if len(client.get_extension_entry(extension_name)) > 0: + client_count += 1 # Filter out the empty ones extension_support[extension_name] = ExtensionSupport(runtime_count, client_count) From 998000c761de1403f55290f477a02341bc27fa06 Mon Sep 17 00:00:00 2001 From: Bastiaan Olij Date: Wed, 16 Jul 2025 16:01:34 +1000 Subject: [PATCH 06/10] Remove XR_VARJO_quad_views from Unity --- clients/unity.json | 1 - 1 file changed, 1 deletion(-) diff --git a/clients/unity.json b/clients/unity.json index 3707765..a163f99 100644 --- a/clients/unity.json +++ b/clients/unity.json @@ -43,7 +43,6 @@ "XR_EXT_hp_mixed_reality_controller", "XR_EXT_local_floor", "XR_EXT_palm_pose", - "XR_VARJO_quad_views", "XR_KHR_binding_modification", "XR_EXT_conformance_automation", "XR_EXT_dpad_binding", From ad996b3d1623bf5019b7c041fc73dbfe63d598ae Mon Sep 17 00:00:00 2001 From: Bastiaan Olij Date: Wed, 16 Jul 2025 16:13:37 +1000 Subject: [PATCH 07/10] Added missing extensions for Godot Vendor Plugin --- clients/godot.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/clients/godot.json b/clients/godot.json index eb42680..eb29c42 100644 --- a/clients/godot.json +++ b/clients/godot.json @@ -55,7 +55,8 @@ "XR_HTCX_vive_tracker_interaction", "XR_ML_ml2_controller_interaction", "XR_MSFT_hand_interaction", - "XR_MNDX_egl_enable" + "XR_MNDX_egl_enable", + "XR_VALVE_analog_threshold" ] }, { @@ -63,7 +64,10 @@ "abbreviation": "Vendors", "extensions": [ "XR_FB_body_tracking", + "XR_FB_color_space", "XR_FB_composition_layer_alpha_blend", + "XR_FB_composition_layer_depth_test", + "XR_FB_composition_layer_image_layout", "XR_FB_composition_layer_secure_content", "XR_FB_composition_layer_settings", "XR_FB_display_refresh_rate", @@ -74,8 +78,11 @@ "XR_FB_hand_tracking_capsules", "XR_FB_hand_tracking_mesh", "XR_FB_passthrough", + "XR_FB_space_warp", + "XR_META_environment_depth", "XR_META_passthrough_preferences", "XR_META_passthrough_color_lut", + "XR_META_recommended_layer_resolution", "XR_META_spatial_entity_mesh", "XR_FB_render_model", "XR_FB_scene", From 8cdbb6ac126f8da8b152cc0f4bb7f4f3d8640033 Mon Sep 17 00:00:00 2001 From: Bastiaan Olij Date: Mon, 21 Jul 2025 11:06:11 +1000 Subject: [PATCH 08/10] Added files for Unreal as provided by Victor Brodin --- clients/unreal.json | 107 ++++++++++++++++++++++++++++++++++++ clients/unreal.json.license | 2 + 2 files changed, 109 insertions(+) create mode 100644 clients/unreal.json create mode 100644 clients/unreal.json.license diff --git a/clients/unreal.json b/clients/unreal.json new file mode 100644 index 0000000..25ddfa2 --- /dev/null +++ b/clients/unreal.json @@ -0,0 +1,107 @@ +{ + "$schema": "../client_schema.json", + "name": "Unreal Engine", + "vendor": "Epic Games", + "platforms": [ + "Windows (Desktop)", + "Android (All-in-one)" + ], + "components": [ + { + "name": "OpenXR", + "abbreviation": "OpenXR", + "extensions": [ + "XR_EPIC_view_configuration_fov", + "XR_EXT_active_action_set_priority", + "XR_EXT_composition_layer_inverted_alpha", + "XR_EXT_debug_utils", + "XR_EXT_dpad_binding", + "XR_EXT_local_floor", + "XR_EXT_palm_pose", + "XR_FB_composition_layer_alpha_blend", + "XR_FB_composition_layer_depth_test", + "XR_FB_foveation", + "XR_FB_foveation_configuration", + "XR_FB_foveation_vulkan", + "XR_FB_swapchain_update_state", + "XR_KHR_android_create_instance", + "XR_KHR_binding_modification", + "XR_KHR_composition_layer_color_scale_bias", + "XR_KHR_composition_layer_cylinder", + "XR_KHR_composition_layer_depth", + "XR_KHR_composition_layer_equirect", + "XR_KHR_composition_layer_equirect2", + "XR_KHR_D3D11_enable", + "XR_KHR_D3D12_enable", + "XR_KHR_opengl_enable", + "XR_KHR_opengl_es_enable", + "XR_KHR_visibility_mask", + "XR_KHR_vulkan_enable", + "XR_KHR_vulkan_swapchain_format_list", + "XR_KHR_win32_convert_performance_counter_time", + "XR_MND_headless", + "XR_OCULUS_audio_device_guid", + "XR_VARJO_quad_views" + ] + }, + { + "name": "OpenXRHandTracking", + "abbreviation": "Hand Tracking", + "extensions": [ + "XR_EXT_hand_tracking" + ] + }, + { + "name": "OpenXREyeTracker", + "abbreviation": "Eye Tracking", + "extensions": [ + "XR_EXT_eye_gaze_interaction" + ] + }, + { + "name": "OpenXRViveTracker", + "abbreviation": "Vive Tracker", + "extensions": [ + "XR_HTCX_vive_tracker_interaction" + ] + }, + { + "name": "HPMotionController", + "abbreviation": "HPMotionController", + "extensions": [ + "XR_EXT_hp_mixed_reality_controller" + ] + } + ], + "form_factors": [ + { + "form_factor": "XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY", + "view_configurations": [ + { + "view_configuration": "XR_VIEW_CONFIGURATION_TYPE_PRIMARY_MONO", + "environment_blend_modes": [ + "OPAQUE", + "ADDITIVE", + "ALPHA_BLEND" + ] + }, + { + "view_configuration": "XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO", + "environment_blend_modes": [ + "OPAQUE", + "ADDITIVE", + "ALPHA_BLEND" + ] + }, + { + "view_configuration": "XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO_WITH_FOVEATED_INSET", + "environment_blend_modes": [ + "OPAQUE", + "ADDITIVE", + "ALPHA_BLEND" + ] + } + ] + } + ] +} diff --git a/clients/unreal.json.license b/clients/unreal.json.license new file mode 100644 index 0000000..bb89a0e --- /dev/null +++ b/clients/unreal.json.license @@ -0,0 +1,2 @@ +Copyright 2025, The Khronos Group Inc. +SPDX-License-Identifier: CC-BY-4.0 From 5ccd4a89046ce8f1caa79cdbcedbff9ed33828ea Mon Sep 17 00:00:00 2001 From: Bastiaan Olij Date: Mon, 21 Jul 2025 18:25:50 +1000 Subject: [PATCH 09/10] Added link back to component from client matrix Added support for component notes Added XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO_WITH_FOVEATED_INSET to schemas Added some missing Godot entries Added notes and update support list Added spatial entities support to Godot --- client_schema.json | 7 +- clients/godot.json | 66 ++++++++++++------- clients/unity.json | 24 ++++++- openxr_inventory/client_inventory.py | 12 +++- .../templates/extension_support.jinja2.html | 22 +++++-- runtime_schema.json | 3 +- 6 files changed, 96 insertions(+), 38 deletions(-) diff --git a/client_schema.json b/client_schema.json index 43d1d87..d03e2e0 100644 --- a/client_schema.json +++ b/client_schema.json @@ -80,7 +80,8 @@ "XR_VIEW_CONFIGURATION_TYPE_PRIMARY_MONO", "XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO", "XR_VIEW_CONFIGURATION_TYPE_PRIMARY_QUAD_VARJO", - "XR_VIEW_CONFIGURATION_TYPE_SECONDARY_MONO_FIRST_PERSON_OBSERVER_MSFT" + "XR_VIEW_CONFIGURATION_TYPE_SECONDARY_MONO_FIRST_PERSON_OBSERVER_MSFT", + "XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO_WITH_FOVEATED_INSET" ] }, "environment_blend_mode": { @@ -103,6 +104,10 @@ "title": "Abbreviation shown in matrix", "type": "string" }, + "notes": { + "title": "Additional information about this component", + "type": "string" + }, "extensions": { "title": "Supported extensions", "description": "A list of all supported extensions in this client, on this platform. Details or caveats may be included in 'notes'.", diff --git a/clients/godot.json b/clients/godot.json index eb29c42..4e82215 100644 --- a/clients/godot.json +++ b/clients/godot.json @@ -15,17 +15,18 @@ "name": "Godot Core", "abbreviation": "Core", "extensions": [ - "XR_KHR_D3D12_enable", "XR_KHR_android_create_instance", "XR_KHR_android_surface_swapchain", "XR_KHR_binding_modification", "XR_KHR_composition_layer_cylinder", "XR_KHR_composition_layer_depth", "XR_KHR_composition_layer_equirect2", + "XR_KHR_D3D12_enable", "XR_KHR_loader_init", "XR_KHR_loader_init_android", - "XR_KHR_opengl_es_enable", + "XR_KHR_metal_enable", "XR_KHR_opengl_enable", + "XR_KHR_opengl_es_enable", "XR_KHR_visibility_mask", "XR_KHR_vulkan_enable2", "XR_EXT_debug_utils", @@ -33,26 +34,41 @@ "XR_EXT_eye_gaze_interaction", "XR_EXT_future", "XR_EXT_hand_interaction", - "XR_EXT_hand_tracking", "XR_EXT_hand_joints_motion_range", + "XR_EXT_hand_tracking", "XR_EXT_hand_tracking_data_source", - "XR_EXT_hp_mixed_reality_controller", "XR_EXT_interaction_render_model", - "XR_EXT_local_floor", - "XR_EXT_view_configuration_depth_range", - "XR_EXT_palm_pose", "XR_EXT_performance_settings", "XR_EXT_render_model", + "XR_EXT_hp_mixed_reality_controller", + "XR_EXT_local_floor", + "XR_EXT_palm_pose", "XR_EXT_samsung_odyssey_controller", "XR_EXT_uuid", "XR_BD_controller_interaction", + "XR_FB_display_refresh_rate", + "XR_FB_foveation", + "XR_FB_foveation_configuration", + "XR_FB_foveation_vulkan", + "XR_EXT_spatial_anchor", + "XR_EXT_spatial_entity", + "XR_EXT_spatial_marker_tracking", + "XR_EXT_spatial_persistence", + "XR_EXT_spatial_persistence_operations", + "XR_EXT_spatial_plane_tracking", + "XR_FB_swapchain_update_state", + "XR_FB_swapchain_update_state_android_surface", + "XR_FB_swapchain_update_state_opengl_es", + "XR_FB_swapchain_update_state_vulkan", "XR_FB_touch_controller_proximity", "XR_FB_touch_controller_pro", - "XR_META_touch_controller_plus", + "XR_HTC_hand_interaction", "XR_HTC_vive_cosmos_controller_interaction", "XR_HTC_vive_focus3_controller_interaction", - "XR_HTC_hand_interaction", "XR_HTCX_vive_tracker_interaction", + "XR_LOGITECH_mx_ink_stylus_interaction", + "XR_META_hand_tracking_microgestures", + "XR_META_touch_controller_plus", "XR_ML_ml2_controller_interaction", "XR_MSFT_hand_interaction", "XR_MNDX_egl_enable", @@ -62,7 +78,9 @@ { "name": "Godot Vendors plugin", "abbreviation": "Vendors", + "notes": "Optional plugin exposing vendor extensions and additional export configurations.", "extensions": [ + "XR_FB_android_surface_swapchain_create", "XR_FB_body_tracking", "XR_FB_color_space", "XR_FB_composition_layer_alpha_blend", @@ -70,37 +88,35 @@ "XR_FB_composition_layer_image_layout", "XR_FB_composition_layer_secure_content", "XR_FB_composition_layer_settings", - "XR_FB_display_refresh_rate", "XR_FB_face_tracking2", - "XR_FB_foveation", - "XR_FB_foveation_configuration", "XR_FB_hand_tracking_aim", "XR_FB_hand_tracking_capsules", "XR_FB_hand_tracking_mesh", "XR_FB_passthrough", - "XR_FB_space_warp", - "XR_META_environment_depth", - "XR_META_passthrough_preferences", - "XR_META_passthrough_color_lut", - "XR_META_recommended_layer_resolution", - "XR_META_spatial_entity_mesh", "XR_FB_render_model", "XR_FB_scene", "XR_FB_scene_capture", - "XR_FB_spatial_entity_container", + "XR_FB_space_warp", "XR_FB_spatial_entity", + "XR_FB_spatial_entity_container", "XR_FB_spatial_entity_query", "XR_FB_spatial_entity_sharing", - "XR_FB_spatial_entity_storage_batch", "XR_FB_spatial_entity_storage", + "XR_FB_spatial_entity_storage_batch", "XR_FB_spatial_entity_user", - "XR_FB_swapchain_update_state", - "XR_FB_swapchain_update_state_vulkan", - "XR_FB_swapchain_update_state_opengl_es", "XR_FB_triangle_mesh", - "XR_META_automatic_layer_filter", "XR_HTC_facial_tracking", - "XR_HTC_passthrough" + "XR_HTC_passthrough", + "XR_META_automatic_layer_filter", + "XR_META_body_tracking_full_body", + "XR_META_environment_depth", + "XR_META_headset_id", + "XR_META_passthrough_color_lut", + "XR_META_passthrough_preferences", + "XR_META_performance_metrics", + "XR_META_recommended_layer_resolution", + "XR_META_simultaneous_hands_and_controllers", + "XR_META_spatial_entity_mesh" ] } ], diff --git a/clients/unity.json b/clients/unity.json index a163f99..d523ecd 100644 --- a/clients/unity.json +++ b/clients/unity.json @@ -2,6 +2,7 @@ "$schema": "../client_schema.json", "name": "Unity", "vendor": "Unity Technologies", + "notes": "Refer to each package's documentation for more information.", "platforms": [ "Windows (Desktop)", "Windows Subsystem for Android (WSA)", @@ -12,6 +13,7 @@ { "name": "OpenXR Plug-in", "abbreviation": "OpenXR Plug-in", + "notes": "https://docs.unity3d.com/Packages/com.unity.xr.openxr@latest", "extensions": [ "XR_KHR_android_create_instance", "XR_KHR_android_surface_swapchain", @@ -50,12 +52,20 @@ "XR_EXT_hand_interaction", "XR_FB_touch_controller_pro", "XR_META_touch_controller_plus", - "XR_MSFT_unbounded_reference_space" + "XR_MSFT_unbounded_reference_space", + "XR_EXT_future", + "XR_EXT_spatial_anchor", + "XR_EXT_spatial_entity", + "XR_EXT_spatial_marker_tracking", + "XR_EXT_spatial_persistence", + "XR_EXT_spatial_persistence_operations", + "XR_EXT_spatial_plane_tracking" ] }, { "name": "XR Composition Layers", "abbreviation": "Comp Layers", + "notes": "https://docs.unity3d.com/Packages/com.unity.xr.compositionlayers@latest", "extensions": [ "XR_KHR_composition_layer_color_scale_bias", "XR_KHR_composition_layer_cube", @@ -67,6 +77,7 @@ { "name": "Unity OpenXR: Meta", "abbreviation": "Meta", + "notes": "https://docs.unity3d.com/Packages/com.unity.xr.meta-openxr@latest", "extensions": [ "XR_FB_display_refresh_rate", "XR_FB_passthrough", @@ -76,21 +87,30 @@ "XR_FB_spatial_entity_container", "XR_FB_spatial_entity_query", "XR_META_colocation_discovery", + "XR_META_spatial_entity_discovery", "XR_META_spatial_entity_group_sharing", "XR_META_spatial_entity_mesh", + "XR_META_spatial_entity_persistence", "XR_META_spatial_entity_sharing" ] }, { "name": "Unity OpenXR: Android XR", "abbreviation": "Android XR", + "notes": "https://docs.unity3d.com/Packages/com.unity.xr.androidxr-openxr@latest", "extensions": [ - "XR_FB_display_refresh_rate" + "XR_FB_display_refresh_rate", + "XR_ANDROID_device_anchor_persistence", + "XR_ANDROID_passthrough_camera_state", + "XR_ANDROID_raycast", + "XR_ANDROID_trackables", + "XR_ANDROID_trackables_object" ] }, { "name": "XR Hands", "abbreviation": "Hands", + "notes": "https://docs.unity3d.com/Packages/com.unity.xr.hands@latest", "extensions": [ "XR_EXT_hand_tracking", "XR_FB_hand_tracking_aim" diff --git a/openxr_inventory/client_inventory.py b/openxr_inventory/client_inventory.py index f959aed..02b8ce7 100644 --- a/openxr_inventory/client_inventory.py +++ b/openxr_inventory/client_inventory.py @@ -14,21 +14,29 @@ class ComponentEntry: """Data about component of a client""" + stub: str + """A short identifier suitable for use as an HTML anchor, file name stem, etc.""" + name: str """The name of the component""" abbreviation: str """The abbreviation to use in the client matrix""" + notes: str + """Optional additional information about this component""" + extensions: List[ExtensionEntry] """The supported extensions""" @classmethod - def from_json(self, d: Dict) -> "ComponentEntry": + def from_json(self, client_stub: str, d: Dict) -> "ComponentEntry": exts = [ExtensionEntry.from_json(entry) for entry in d["extensions"]] return ComponentEntry( + stub=client_stub + '_' + d["abbreviation"], name=d["name"], abbreviation=d["abbreviation"], + notes=d.get("notes"), extensions=exts ) @@ -91,7 +99,7 @@ def from_json(cls, stub: str, d: Dict) -> "ClientData": 'stub' should be the stem of the filename, typically. """ - comps = [ComponentEntry.from_json(entry) for entry in d["components"]] + comps = [ComponentEntry.from_json(stub, entry) for entry in d["components"]] form_factors = [FormFactorEntry.from_json(entry) for entry in (d.get("form_factors", []))] return ClientData( stub=stub, diff --git a/openxr_inventory/templates/extension_support.jinja2.html b/openxr_inventory/templates/extension_support.jinja2.html index 30f14c9..7fe215d 100644 --- a/openxr_inventory/templates/extension_support.jinja2.html +++ b/openxr_inventory/templates/extension_support.jinja2.html @@ -267,7 +267,11 @@

    {{ client.name }}

  • Notes: {{ client.notes }}
  • {% endif %} {% for component in client.components %} -
  • Component: {{ component.name }}
    Extensions: +
  • Component: {{ component.name }}
    + {% if component.notes %} + Notes: {{ component.notes }}
    + {% endif %} + Extensions:
      {% for extension in component.extensions %}
    • {{extension.name}} {% if extension.notes %} - {{ extension.notes }} {% endif %}
    • @@ -290,11 +294,15 @@

      Client support matrix

      {% for client in clients | sort %} {# pragmatic check if the client name fits in the layout or if it needs to be truncated and put the full name in tooltip #} - {% if client.name|length < 34 %} -
      {{ client.name }}
      - {% else %} -
      {{ client.name|truncate(34, True) }}
      - {% endif %} +
      + {% if client.name|length < 34 %} + {{ client.name }} + {% else %} + {{ client.name|truncate(34, True) }} + {% endif %} + + +
      {% endfor %} @@ -313,7 +321,7 @@

      Client support matrix

      {% if components|length > 0 %} {% for component in components %} - {{ component.abbreviation }} + {{ component.abbreviation }} {% if not loop.last %}
      {% endif %} diff --git a/runtime_schema.json b/runtime_schema.json index 9d641d8..91c7331 100644 --- a/runtime_schema.json +++ b/runtime_schema.json @@ -79,7 +79,8 @@ "XR_VIEW_CONFIGURATION_TYPE_PRIMARY_MONO", "XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO", "XR_VIEW_CONFIGURATION_TYPE_PRIMARY_QUAD_VARJO", - "XR_VIEW_CONFIGURATION_TYPE_SECONDARY_MONO_FIRST_PERSON_OBSERVER_MSFT" + "XR_VIEW_CONFIGURATION_TYPE_SECONDARY_MONO_FIRST_PERSON_OBSERVER_MSFT", + "XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO_WITH_FOVEATED_INSET" ] }, "environment_blend_mode": { From 8e6300829bf94c5cb7cddb41af1ba6647f891683 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Mon, 21 Jul 2025 12:21:47 -0700 Subject: [PATCH 10/10] Add StereoKit --- clients/stereokit.json | 94 ++++++++++++++++++++++++++++++++++ clients/stereokit.json.license | 2 + 2 files changed, 96 insertions(+) create mode 100644 clients/stereokit.json create mode 100644 clients/stereokit.json.license diff --git a/clients/stereokit.json b/clients/stereokit.json new file mode 100644 index 0000000..e22eb28 --- /dev/null +++ b/clients/stereokit.json @@ -0,0 +1,94 @@ +{ + "$schema": "../client_schema.json", + "name": "StereoKit", + "vendor": "StereoKit", + "platforms": [ + "Windows (Desktop)", + "Windows (HoloLens 2)", + "Android (All-in-one)", + "Linux (Desktop/Embedded)" + ], + "components": [ + { + "name": "StereoKit Core", + "abbreviation": "Core", + "extensions": [ + "XR_KHR_android_create_instance", + "XR_KHR_android_thread_settings", + "XR_KHR_composition_layer_depth", + "XR_KHR_convert_timespec_time", + "XR_KHR_D3D11_enable", + "XR_KHR_loader_init_android", + "XR_KHR_loader_init", + "XR_KHR_opengl_enable", + "XR_KHR_opengl_es_enable", + "XR_KHR_win32_convert_performance_counter_time", + "XR_EXT_debug_utils", + "XR_EXT_eye_gaze_interaction", + "XR_EXT_hand_interaction", + "XR_EXT_hand_tracking_data_source", + "XR_EXT_hand_tracking", + "XR_EXT_hp_mixed_reality_controller", + "XR_EXT_interaction_render_model", + "XR_EXT_local_floor", + "XR_EXT_palm_pose", + "XR_EXT_render_model", + "XR_EXT_uuid", + "XR_EXTX_overlay", + "XR_BD_controller_interaction", + "XR_FB_color_space", + "XR_MNDX_egl_enable", + "XR_MSFT_first_person_observer", + "XR_MSFT_hand_interaction", + "XR_MSFT_hand_tracking_mesh", + "XR_MSFT_perception_anchor_interop", + "XR_MSFT_scene_understanding", + "XR_MSFT_secondary_view_configuration", + "XR_MSFT_spatial_anchor_persistence", + "XR_MSFT_spatial_anchor", + "XR_MSFT_spatial_graph_bridge", + "XR_MSFT_unbounded_reference_space", + "XR_OCULUS_audio_device_guid" + ] + }, + { + "name": "StereoKit Tools", + "abbreviation": "Tools", + "extensions": [ + "XR_KHR_android_surface_swapchain", + "XR_FB_display_refresh_rate", + "XR_FB_passthrough" + ] + }, + { + "name": "StereoKit Holographic Remoting", + "abbreviation": "Remoting", + "extensions": [ + "XR_MSFT_holographic_remoting" + ] + } + ], + "form_factors": [ + { + "form_factor": "XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY", + "view_configurations": [ + { + "view_configuration": "XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO", + "environment_blend_modes": [ + "OPAQUE", + "ADDITIVE", + "ALPHA_BLEND" + ] + }, + { + "view_configuration": "XR_VIEW_CONFIGURATION_TYPE_SECONDARY_MONO_FIRST_PERSON_OBSERVER_MSFT", + "environment_blend_modes": [ + "OPAQUE", + "ADDITIVE", + "ALPHA_BLEND" + ] + } + ] + } + ] +} diff --git a/clients/stereokit.json.license b/clients/stereokit.json.license new file mode 100644 index 0000000..bb89a0e --- /dev/null +++ b/clients/stereokit.json.license @@ -0,0 +1,2 @@ +Copyright 2025, The Khronos Group Inc. +SPDX-License-Identifier: CC-BY-4.0