diff --git a/client_schema.json b/client_schema.json new file mode 100644 index 0000000..3c8f13b --- /dev/null +++ b/client_schema.json @@ -0,0 +1,189 @@ +{ + "$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" + ] + }, + "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" + }, + "extensions": { + "title": "Supported extensions", + "description": "A list of all supported extensions in this runtime, on this platform. Details or caveats may be included in 'notes'.", + "type": "array", + "items": { + "$ref": "#/definitions/extension" + } + }, + "form_factors": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/form_factor_data" + } + } + }, + "required": [ + "name", + "vendor", + "extensions" + ] +} diff --git a/schema.json.license b/client_schema.json.license similarity index 100% rename from schema.json.license rename to client_schema.json.license diff --git a/clients/godot.json b/clients/godot.json new file mode 100644 index 0000000..74c8a53 --- /dev/null +++ b/clients/godot.json @@ -0,0 +1,99 @@ +{ + "$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)" + ], + "extensions": [ + "XR_KHR_android_create_instance", + "XR_KHR_composition_layer_cylinder", + "XR_KHR_composition_layer_depth", + "XR_KHR_composition_layer_equirect2", + "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_eye_gaze_interaction", + "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_hp_mixed_reality_controller", + "XR_EXT_samsung_odyssey_controller", + "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_FB_touch_controller_proximity", + "XR_FB_touch_controller_pro", + "XR_META_touch_controller_plus", + "XR_META_automatic_layer_filter", + "XR_HTC_facial_tracking", + "XR_HTC_passthrough", + "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" + ], + "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" + ] + } + ] + } + ] +} diff --git a/clients/godot.json.license b/clients/godot.json.license new file mode 100644 index 0000000..6a0d854 --- /dev/null +++ b/clients/godot.json.license @@ -0,0 +1,2 @@ +Copyright 2024, 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..16aba93 --- /dev/null +++ b/openxr_inventory/client_inventory.py @@ -0,0 +1,103 @@ +#!/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(order=True) +class ClientData: + """Data about a single runtime on a single platform, 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 runtime (on this platform)""" + + notes: Optional[str] + """Free-form text with extra information""" + + vendor: str + """The vendor's name""" + + extensions: List[ExtensionEntry] + """The supported extensions""" + + form_factors: List[FormFactorEntry] + """The supported form factors""" + + 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 = [ + entry for entry in self.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) -> "RuntimeData": + """ + Create an object from the data loaded from a json file. + + 'stub' should be the stem of the filename, typically. + """ + exts = [ExtensionEntry.from_json(entry) for entry in d["extensions"]] + 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"], + extensions=exts, + 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 43c0196..17c0c6a 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,16 @@ 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: + known_extensions.update(ext.name for ext in client.extensions) + return list(sorted(known_extensions, key=ext_name_key)) @@ -100,8 +107,18 @@ def compute_runtime_support(runtimes: List[RuntimeData]) -> Dict[str, List[str]] 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 = [ext.name for ext in client.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 +126,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. + For each extension, counts the runtimes and clients that support it. - Returns a dict with extension names as the keys, and a list - of (RuntimeData, ExtensionEntry) tuples as the values. + Returns a dict with counts """ - 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 +213,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", ): @@ -172,12 +229,14 @@ def generate_report( env.globals["categorize_ext"] = categorize_ext_name template = env.get_template(template_filename) 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 ) if contents: @@ -189,6 +248,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..100f0f3 100644 --- a/openxr_inventory/runtime_inventory.py +++ b/openxr_inventory/runtime_inventory.py @@ -8,81 +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) @@ -104,6 +30,9 @@ class RuntimeData: devices_notes: Optional[str] """Free-form text about devices support""" + notes: Optional[str] + """Free-form text with extra information""" + vendor: str """The vendor's name""" @@ -150,6 +79,7 @@ def from_json(cls, stub: str, d: Dict) -> "RuntimeData": conformance_submission=d.get("conformance_submission"), conformance_notes=d.get("conformance_notes"), devices_notes=d.get("devices_notes"), + notes=d.get("notes"), vendor=d["vendor"], extensions=exts, form_factors=form_factors, diff --git a/openxr_inventory/templates/base.jinja2.html b/openxr_inventory/templates/base.jinja2.html index edfe9d4..d3c33ca 100644 --- a/openxr_inventory/templates/base.jinja2.html +++ b/openxr_inventory/templates/base.jinja2.html @@ -53,6 +53,11 @@ + diff --git a/openxr_inventory/templates/extension_support.jinja2.html b/openxr_inventory/templates/extension_support.jinja2.html index 1f6d105..1c0bf68 100644 --- a/openxr_inventory/templates/extension_support.jinja2.html +++ b/openxr_inventory/templates/extension_support.jinja2.html @@ -6,19 +6,13 @@ {% extends "base.jinja2.html" %} {% block title -%} - OpenXR Runtime Extension Support Report + OpenXR Extension Support Report {%- endblock title %} {% block navbar_brand_text -%} - OpenXR Runtime Extension Support Report + OpenXR Extension Support Report {%- endblock navbar_brand_text %} -{% block navbar_list_items %} -
  • Extensions
  • -
  • Runtimes
  • -
  • Extension Support Matrix
  • -{% endblock navbar_list_items %} - {% block style %} {{ super() }} @@ -41,86 +35,92 @@ padding: 5px; } +th.extname { + white-space: nowrap +} + {% endblock style %} {% block container_contents %} -
    - - -
    -

    Extensions

    - {% for col in extensions | slice(3) %} -
    - -
    - {% endfor %} -
    - {% for extension_name in extensions %} - {% set support = extension_support[extension_name] %} - -
    -

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

    -

    - Specification - for {{ extension_name }} -

    - -
    - {% endfor %} -
    - -
    -

    Runtimes

    - + {% for runtime in runtimes %} + + {% endfor %} - {% for runtime in runtimes %} -
    -

    {{ runtime.name }}

    -
      -
    • Vendor: {{ runtime.vendor }}
    • - {% if runtime.conformance_submission %} -
    • Most recent conformance submission: #{{runtime.conformance_submission}}
    • - {% endif %} - {% if not runtime.conformance_submission %} -
    • Not a conformant runtime
    • - {% endif %} - {% if runtime.conformance_notes %} -
    • Conformance notes: {{ runtime.conformance_notes }}
    • - {% endif %} - {% if runtime.devices_notes %} -
    • Device support: {{ runtime.devices_notes }}
    • - {% endif %} -
    • Supported extensions: -
        - {% for extension in runtime.extensions %} -
      • {{extension.name}} {% if extension.notes %} - {{ extension.notes }} {% endif %}
      • - {% endfor %} -
      -
    + {% for client in clients %} + {% endfor %}
    -

    Runtime support matrix

    +

    OpenXR adoption matrix

    @@ -128,20 +128,29 @@

    Runtime support matrix

    {% for runtime in runtimes | sort %} {# pragmatic check if the runtime name fits in the layout or if it needs to be truncated and put the full name in tooltip #} - {% if runtime.name|length < 34 %} - + {% if runtime.name|length <= 34 %} + {% else %} - + + {% endif %} + {% endfor %} + + {% 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) %} - + {% set support = extension_support[extension_name] %} + {% endif %} {% endfor %} + + {% for client in clients | sort %} + {% if extension_name in client_support[client.name] %} + + {% else %} + + {% endif %} + {% endfor %} {% endmacro %} @@ -164,7 +181,7 @@

    Runtime support matrix

    {# 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 #} @@ -173,7 +190,7 @@

    Runtime support matrix

    {% endfor %} - + {% for ff, view_configurations in known_form_factors.items() %} @@ -186,30 +203,42 @@

    Runtime support matrix

    {% for runtime in runtimes | sort %} - + + {% endfor %} + + {% for client in clients | sort %} + {% endfor %} {% for vc, environment_blend_modes in view_configurations.items() %} {% for runtime in runtimes | sort %} - + + {% endfor %} + + + + {% for client in clients | sort %} + {% endfor %} {% for ebm in environment_blend_modes %} {% for runtime in runtimes | sort %} {% if ff in form_factor_support[runtime.name] and vc in form_factor_support[runtime.name][ff] and ebm in form_factor_support[runtime.name][ff][vc] %} @@ -218,6 +247,16 @@

    Runtime support matrix

    {% endif %} {% endfor %} + + + + {% 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 %} diff --git a/schema.json b/runtime_schema.json similarity index 99% rename from schema.json rename to runtime_schema.json index 9d641d8..551b986 100644 --- a/schema.json +++ b/runtime_schema.json @@ -61,7 +61,7 @@ "Windows (HoloLens 2)", "Linux (Desktop/Embedded)", "Android (All-in-one)", - "Android (Phone/Installable)" + "Android (Phone/Installable)", ] }, "form_factor": { diff --git a/runtime_schema.json.license b/runtime_schema.json.license new file mode 100644 index 0000000..1eeb69b --- /dev/null +++ b/runtime_schema.json.license @@ -0,0 +1,2 @@ +Copyright 2022, The Khronos Group Inc. +SPDX-License-Identifier: CC-BY-4.0 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/meta_pc.json b/runtimes/meta_pc.json index dcb8224..91afd27 100644 --- a/runtimes/meta_pc.json +++ b/runtimes/meta_pc.json @@ -1,6 +1,6 @@ { "notes": "Generated using list_json: 'Oculus' (1.92.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 f92a144..81d8c8b 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.92.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 b383722..ca8450c 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": 4, "platform": "Android (All-in-one)", diff --git a/runtimes/meta_quest_pro_mobile.json b/runtimes/meta_quest_pro_mobile.json index 35cc4c6..358c4f0 100644 --- a/runtimes/meta_quest_pro_mobile.json +++ b/runtimes/meta_quest_pro_mobile.json @@ -1,6 +1,6 @@ { "notes": "Generated using from OS version v60 for Quest Pro", - "$schema": "../schema.json", + "$schema": "../runtime_schema.json", "name": "Meta Quest Pro (Mobile)", "conformance_submission": 4, "platform": "Android (All-in-one)", 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 52ad093..df8c671 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..d287ffd 100755 --- a/verify_schema.sh +++ b/verify_schema.sh @@ -6,5 +6,10 @@ set -e for fn in runtimes/*.json; do echo "Checking $fn against the schema" - python3 -m jsonschema schema.json -i "$fn" + python3 -m jsonschema runtime_schema.json -i "$fn" +done + +for fn in clients/*.json; do + echo "Checking $fn against the schema" + python3 -m jsonschema client_schema.json -i "$fn" done
    {{ runtime.name }}
    {{ runtime.name }}
    {{ runtime.name|truncate(34, True) }}
    {{ runtime.name|truncate(34, True) }}
    {{ client.name }}
    {{ client.name|truncate(34, True) }}
    - {{ extension_name }} + {{ extension_name }} @@ -153,6 +162,14 @@

    Runtime support matrix

    Not supported or not applicableSupportedNot supported or not applicable
    {{ cat_captions[c] }} Extensions{{ cat_captions[c] }} Extensions
    Runtime FeaturesRuntime Features
    - → {{ vc }} - - - + → {{ vc }} + + +
    - → → {{ ebm }} - - - + + → → {{ ebm }} + + + + Not supported or not applicableSupportedNot supported or not applicable