diff --git a/.gitignore b/.gitignore index 17bf9ab..d27e331 100644 --- a/.gitignore +++ b/.gitignore @@ -88,3 +88,4 @@ lint/tmp/ *.swp Makefile +tmp_* diff --git a/README.md b/README.md index a0afad3..becff7c 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ | Face tracking | Pose estimation | YOLO | |:-------------:|:---------------:|:----:| | ![Face tracking Demo](docs/Demo-UFO.gif) | ![Pose estimation demo](docs/Demo-Pose.gif) | ![YOLO demo](docs/Demo-YOLO.gif) | +| **Mnistwild** | **Rubic's cube** | **Whack-a-Mole** | +| ![Mnistwild demo](docs/mnistwild.gif) | ![Rubics cube demo](docs/rubics_cube.gif) | | ## Models Used in Samples @@ -13,6 +15,8 @@ | UFO (Face Detection) | MediaPipe Face Detection | [Qualcomm AI Hub - MediaPipe Face Detection](https://aihub.qualcomm.com/models/mediapipe_face?searchTerm=face) | | Pose (Pose Estimation) | MediaPipe Pose Landmarker | [Google AI Edge - Pose Landmark Detection](https://ai.google.dev/edge/mediapipe/solutions/vision/pose_landmarker#models) | | YOLO (Object Detection) | YOLOv11 Detection | [Qualcomm AI Hub - YOLOv11 Detection](https://aihub.qualcomm.com/models/yolov11_det?searchTerm=yolo) | +| MNIST (Digit Recognition) | Classic MNIST | [MNIST Database](https://en.wikipedia.org/wiki/MNIST_database) | +| Rubic's Cube (Solver) | No model used | N/A (Internal) | ## Project aims @@ -23,10 +27,15 @@ customized MR-based effects with deployment of open-sourced machine learning algorithms. Additionally, the project provides a set of utility classes, -located under [`./base/securemr_utils`](base/securemr_utils/README.md) +located under [`base/securemr_utils`](base/securemr_utils/README.md) to simplify your development of SecureMR-enabled applications. +The [`sample/mnist`](samples/mnistwild) also demonstrates how to defines the SecureMR +pipelines completely in JSON, using which allows your application +to dynamically load and update pipelines at run time, without hard +coding the algorithms into the app. + A docker file together with necessary resources are also contained under the `Docker/` directory, if you would like deploy your own algorithm packages. @@ -73,10 +82,31 @@ deploy your own algorithm packages. ├── samples │   │ Directory for all sample projects. │   │ -│   └── ufo -│   This is a sample showing a UFO "chasing" the human being -│   whoever it sees. The sample app uses an open-sourced -│   face detection model from MediaPipe. +│   ├── ufo +│   │ This is a sample showing a UFO "chasing" the human being +│   │ whoever it sees. The sample app uses an open-sourced +│   │ face detection model from MediaPipe. +│   │ +│   ├── mnistwild +│   │ Hand-written digit recognition using a self-trained MNIST-based +│   │ inference pipeline. +│   │ +│   ├── readback +│   │ A minimumal demo that shows the usage of the readback APIs, which +│   │ allows an app, if proper camera or spatial-data permission(s) +│   │ is granted, to read the tensor content back from the SecureMR server. +│   │ This demo does not deploy any algorithms, nor present any render +│   │ effects: it simply calls the readback methods to obtain the camera +│   │ image and save it to local storage. +│   │ +│   ├── rubics_cube +│   │ A Rubik's Cube scanner and solver with real-time +│   │ color classification, presenting the step-by-step instructions on +│   │ the screen. +│   │ +│   └── model_inspect +│   Diagnostic tool for validating serialized models +│   on device. | └── ... ``` @@ -85,7 +115,7 @@ deploy your own algorithm packages. #### (A) To run the demo, you will need -1. A PICO 4 Ultra device with the latest system update (OS version >= 5.14.0U) +1. A PICO 4 Ultra device with the latest system update (OS version >= 5.15.0) 1. Android Studio, with Android NDK installed, suggested NDK version = 25 1. Gradle and Android Gradle plugin (usually bundled with Android Studio install), suggested Gradle version = 8.7, Android Gradle Plugin version = 8.3.2 @@ -99,12 +129,16 @@ deploy your own algorithm packages. 1. Install and configure according to the [prerequisite](#prerequisite). 1. Open the repository root in Android Studio, as an Android project -1. After project sync, you will find there are four modules detected by the Android Studio, all under the `samples` folder: - 1. `pose` which contains a pose detection demo - 1. `ufo` which contains a face detection demo - 1. `yolo` which contains an object detection demo - 1. `ufo-origin`, the same demo as `ufo`, but written using direct calls to the OpenXR C-API, with no - simplification using SecureMR Utils classes. +1. After project sync, you will find several modules detected by the Android Studio, all under the `samples` folder: + 1. `pose` which contains a pose detection demo + 1. `ufo` which contains a face detection demo + 1. `yolo` which contains an object detection demo + 1. `mnistwild` which contains a hand-written digit recognition demo + 1. `readback` which contains a minimal demo showing the usage of the readback APIs + 1. `rubics_cube` which contains a Rubik's Cube solver demo + 1. `model_inspect` which is a utility for model validation + 1. `ufo-origin`, the same demo as `ufo`, but written using direct calls to the OpenXR C-API, with no + simplification using SecureMR Utils classes. 1. Connect to a PICO 4 Ultra device with the latest OS update installed 1. Select the module you want to run, and click the launch button. diff --git a/assets/UFO/facedetector_fp16_qnn229.json b/assets/UFO/facedetector_fp16_qnn229.json new file mode 100644 index 0000000..1c35120 --- /dev/null +++ b/assets/UFO/facedetector_fp16_qnn229.json @@ -0,0 +1,116 @@ +{ + "version": "QNN_SYSTEM_CONTEXT_BINARY_INFO_VERSION_3", + "info": { + "backendId": 6, + "buildId": "v2.29.0.241129103708_105762", + "coreApiVersion": "2.22.0", + "backendApiVersion": "5.29.0", + "socVersion": "", + "contextBlobVersion": "3.2.0", + "contextBlobSize": 1362248, + "numContextTensors": 0, + "contextTensors": [], + "numGraphs": 1, + "graphs": [ + { + "version": "QNN_SYSTEM_CONTEXT_GRAPH_INFO_VERSION_3", + "info": { + "graphName": "mediapipe_face_mediapipefacedetector_tflite", + "numGraphInputs": 1, + "graphInputs": [ + { + "version": "QNN_TENSOR_VERSION_1", + "info": { + "id": 1, + "name": "image", + "type": "QNN_TENSOR_TYPE_APP_WRITE", + "dataFormat": "QNN_TENSOR_DATA_FORMAT_FLAT_BUFFER", + "dataType": "QNN_DATATYPE_FLOAT_16", + "rank": 4, + "dimensions": [ + 1, + 256, + 256, + 3 + ], + "memType": "QNN_TENSORMEMTYPE_RAW", + "quantizeParams": { + "definition": "QNN_DEFINITION_UNDEFINED", + "quantizationEncoding": "QNN_QUANTIZATION_ENCODING_UNDEFINED" + } + } + } + ], + "numGraphOutputs": 2, + "graphOutputs": [ + { + "version": "QNN_TENSOR_VERSION_1", + "info": { + "id": 486, + "name": "box_coords", + "type": "QNN_TENSOR_TYPE_APP_READ", + "dataFormat": "QNN_TENSOR_DATA_FORMAT_FLAT_BUFFER", + "dataType": "QNN_DATATYPE_FLOAT_16", + "rank": 3, + "dimensions": [ + 1, + 896, + 16 + ], + "memType": "QNN_TENSORMEMTYPE_RAW", + "quantizeParams": { + "definition": "QNN_DEFINITION_UNDEFINED", + "quantizationEncoding": "QNN_QUANTIZATION_ENCODING_UNDEFINED" + } + } + }, + { + "version": "QNN_TENSOR_VERSION_1", + "info": { + "id": 501, + "name": "box_scores", + "type": "QNN_TENSOR_TYPE_APP_READ", + "dataFormat": "QNN_TENSOR_DATA_FORMAT_FLAT_BUFFER", + "dataType": "QNN_DATATYPE_FLOAT_16", + "rank": 3, + "dimensions": [ + 1, + 896, + 1 + ], + "memType": "QNN_TENSORMEMTYPE_RAW", + "quantizeParams": { + "definition": "QNN_DEFINITION_UNDEFINED", + "quantizationEncoding": "QNN_QUANTIZATION_ENCODING_UNDEFINED" + } + } + } + ], + "numUpdateableTensors": 0, + "updateableTensors": [], + "graphBlobInfoSize": 40, + "graphBlobInfo": [ + { + "version": "QNN_SYSTEM_CONTEXT_HTP_GRAPH_INFO_BLOB_VERSION_V1", + "info": { + "spillFillBufferSize": 0, + "optimizationLevel": 0, + "vtcmSize": 4, + "htpDlbc": 0, + "numHvxThreads": 0 + } + } + ] + } + } + ], + "contextMetadataSize": 8, + "contextMetadata": { + "version": "QNN_SYSTEM_CONTEXT_HTP_CONTEXT_INFO_BLOB_VERSION_V1", + "info": { + "dsp arch": 68 + } + }, + "soc model": 0 + } +} diff --git a/assets/mnistwild/mnist.serialized.bin b/assets/mnistwild/mnist.serialized.bin new file mode 100644 index 0000000..5ed305f Binary files /dev/null and b/assets/mnistwild/mnist.serialized.bin differ diff --git a/assets/mnistwild/mnist.serialized.json b/assets/mnistwild/mnist.serialized.json new file mode 100644 index 0000000..89c3a0c --- /dev/null +++ b/assets/mnistwild/mnist.serialized.json @@ -0,0 +1,112 @@ +{ + "version": "QNN_SYSTEM_CONTEXT_BINARY_INFO_VERSION_3", + "info": { + "backendId": 6, + "buildId": "v2.29.0.241129103708_105762", + "coreApiVersion": "2.22.0", + "backendApiVersion": "5.29.0", + "socVersion": "", + "contextBlobVersion": "3.2.0", + "contextBlobSize": 8650744, + "numContextTensors": 0, + "contextTensors": [], + "numGraphs": 1, + "graphs": [ + { + "version": "QNN_SYSTEM_CONTEXT_GRAPH_INFO_VERSION_3", + "info": { + "graphName": "mnist", + "numGraphInputs": 1, + "graphInputs": [ + { + "version": "QNN_TENSOR_VERSION_1", + "info": { + "id": 1, + "name": "input_1", + "type": "QNN_TENSOR_TYPE_APP_WRITE", + "dataFormat": "QNN_TENSOR_DATA_FORMAT_FLAT_BUFFER", + "dataType": "QNN_DATATYPE_FLOAT_16", + "rank": 4, + "dimensions": [ + 1, + 224, + 224, + 1 + ], + "memType": "QNN_TENSORMEMTYPE_RAW", + "quantizeParams": { + "definition": "QNN_DEFINITION_UNDEFINED", + "quantizationEncoding": "QNN_QUANTIZATION_ENCODING_UNDEFINED" + } + } + } + ], + "numGraphOutputs": 2, + "graphOutputs": [ + { + "version": "QNN_TENSOR_VERSION_1", + "info": { + "id": 370, + "name": "_538", + "type": "QNN_TENSOR_TYPE_APP_READ", + "dataFormat": "QNN_TENSOR_DATA_FORMAT_FLAT_BUFFER", + "dataType": "QNN_DATATYPE_FLOAT_16", + "rank": 1, + "dimensions": [ + 1 + ], + "memType": "QNN_TENSORMEMTYPE_RAW", + "quantizeParams": { + "definition": "QNN_DEFINITION_UNDEFINED", + "quantizationEncoding": "QNN_QUANTIZATION_ENCODING_UNDEFINED" + } + } + }, + { + "version": "QNN_TENSOR_VERSION_1", + "info": { + "id": 371, + "name": "_539", + "type": "QNN_TENSOR_TYPE_APP_READ", + "dataFormat": "QNN_TENSOR_DATA_FORMAT_FLAT_BUFFER", + "dataType": "QNN_DATATYPE_INT_32", + "rank": 1, + "dimensions": [ + 1 + ], + "memType": "QNN_TENSORMEMTYPE_RAW", + "quantizeParams": { + "definition": "QNN_DEFINITION_UNDEFINED", + "quantizationEncoding": "QNN_QUANTIZATION_ENCODING_UNDEFINED" + } + } + } + ], + "numUpdateableTensors": 0, + "updateableTensors": [], + "graphBlobInfoSize": 40, + "graphBlobInfo": [ + { + "version": "QNN_SYSTEM_CONTEXT_HTP_GRAPH_INFO_BLOB_VERSION_V1", + "info": { + "spillFillBufferSize": 0, + "optimizationLevel": 0, + "vtcmSize": 4, + "htpDlbc": 0, + "numHvxThreads": 0 + } + } + ] + } + } + ], + "contextMetadataSize": 8, + "contextMetadata": { + "version": "QNN_SYSTEM_CONTEXT_HTP_CONTEXT_INFO_BLOB_VERSION_V1", + "info": { + "dsp arch": 68 + } + }, + "soc model": 0 + } +} diff --git a/assets/mnistwild/mnist_app.png b/assets/mnistwild/mnist_app.png new file mode 100644 index 0000000..b2c8bc3 Binary files /dev/null and b/assets/mnistwild/mnist_app.png differ diff --git a/assets/mnistwild/mnist_pipeline.json b/assets/mnistwild/mnist_pipeline.json new file mode 100644 index 0000000..cd611aa --- /dev/null +++ b/assets/mnistwild/mnist_pipeline.json @@ -0,0 +1,269 @@ +{ + "metadata": { + "version": 1 + }, + "tensors": { + "src_points": { + "dimensions": [ + 3 + ], + "channels": 2, + "data_type": 6, + "is_placeholder": false, + "usage": 1, + "flag": 279042, + "value": [ + 1444.0, + 1332.0, + 2045.0, + 1332.0, + 2045.0, + 1933.0 + ] + }, + "dst_points": { + "dimensions": [ + 3 + ], + "channels": 2, + "data_type": 6, + "is_placeholder": false, + "usage": 1, + "flag": 279042, + "value": [ + 0.0, + 0.0, + 224.0, + 0.0, + 224.0, + 224.0 + ] + }, + "affine": { + "dimensions": [ + 2, + 3 + ], + "channels": 1, + "data_type": 6, + "is_placeholder": false, + "usage": 6, + "flag": 280577, + "value": null + }, + "crop_rgb_tensor": { + "dimensions": [ + 224, + 224 + ], + "channels": 3, + "data_type": 1, + "is_placeholder": false, + "usage": 6, + "flag": 71683, + "value": null + }, + "crop_gray_tensor": { + "dimensions": [ + 224, + 224 + ], + "channels": 1, + "data_type": 1, + "is_placeholder": false, + "usage": 6, + "flag": 71681, + "value": null + }, + "crop_float_tensor": { + "dimensions": [ + 224, + 224 + ], + "channels": 1, + "data_type": 6, + "is_placeholder": false, + "usage": 6, + "flag": 280577, + "value": null + }, + "normalized_input_tensor": { + "dimensions": [ + 224, + 224 + ], + "channels": 1, + "data_type": 6, + "is_placeholder": false, + "usage": 6, + "flag": 280577, + "value": null + }, + "cropped_image": { + "dimensions": [ + 224, + 224 + ], + "channels": 3, + "data_type": 8, + "is_placeholder": true, + "usage": 8, + "flag": 71683, + "value": null + }, + "right_rgb": { + "dimensions": [ + 2464, + 3248 + ], + "channels": 3, + "data_type": 1, + "is_placeholder": false, + "usage": 6 + }, + "left_rgb": { + "dimensions": [ + 2464, + 3248 + ], + "channels": 3, + "data_type": 1, + "is_placeholder": false, + "usage": 6 + }, + "timestamp_tensor": { + "dimensions": [ + 1 + ], + "channels": 4, + "data_type": 5, + "is_placeholder": false, + "usage": 5 + }, + "camera_matrix_tensor": { + "dimensions": [ + 3, + 3 + ], + "channels": 1, + "data_type": 6, + "is_placeholder": false, + "usage": 6 + }, + "predicted_score": { + "dimensions": [ + 1 + ], + "channels": 1, + "data_type": 6, + "is_placeholder": true, + "usage": 2 + }, + "predicted_class": { + "dimensions": [ + 1 + ], + "channels": 1, + "data_type": 5, + "is_placeholder": true, + "usage": 2 + } + }, + "operators": [ + { + "type": "XR_SECURE_MR_OPERATOR_TYPE_RECTIFIED_VST_ACCESS_PICO", + "inputs": [], + "outputs": [ + "right_rgb", + "left_rgb", + "timestamp_tensor", + "camera_matrix_tensor" + ] + }, + { + "type": "XR_SECURE_MR_OPERATOR_TYPE_GET_AFFINE_PICO", + "inputs": [ + "src_points", + "dst_points" + ], + "outputs": [ + "affine" + ] + }, + { + "type": "XR_SECURE_MR_OPERATOR_TYPE_APPLY_AFFINE_PICO", + "inputs": [ + "affine", + "left_rgb" + ], + "outputs": [ + "crop_rgb_tensor" + ] + }, + { + "type": "XR_SECURE_MR_OPERATOR_TYPE_CONVERT_COLOR_PICO", + "inputs": [ + "crop_rgb_tensor" + ], + "outputs": [ + "crop_gray_tensor" + ], + "flag": 6 + }, + { + "type": "XR_SECURE_MR_OPERATOR_TYPE_ASSIGNMENT_PICO", + "inputs": [ + "crop_gray_tensor" + ], + "outputs": [ + "crop_float_tensor" + ] + }, + { + "type": "XR_SECURE_MR_OPERATOR_TYPE_ARITHMETIC_COMPOSE_PICO", + "inputs": [ + "crop_float_tensor" + ], + "outputs": [ + "normalized_input_tensor" + ], + "expression": "{0} / 255.0" + }, + { + "type": "XR_SECURE_MR_OPERATOR_TYPE_ASSIGNMENT_PICO", + "inputs": [ + "crop_rgb_tensor" + ], + "outputs": [ + "cropped_image" + ] + }, + { + "type": "XR_SECURE_MR_OPERATOR_TYPE_RUN_MODEL_INFERENCE_PICO", + "inputs": [ + { + "name": "input_1", + "tensor": "normalized_input_tensor" + } + ], + "outputs": [ + { + "name": "_538", + "tensor": "predicted_score" + }, + { + "name": "_539", + "tensor": "predicted_class" + } + ], + "model_asset": "mnist.serialized.bin", + "model_name": "mnist" + } + ], + "inputs": [], + "outputs": [ + "cropped_image", + "predicted_score", + "predicted_class" + ] +} diff --git a/assets/mnistwild/number_5.png b/assets/mnistwild/number_5.png new file mode 100644 index 0000000..a1fb0c7 Binary files /dev/null and b/assets/mnistwild/number_5.png differ diff --git a/assets/mnistwild/tv.gltf b/assets/mnistwild/tv.gltf new file mode 100644 index 0000000..beed9a4 --- /dev/null +++ b/assets/mnistwild/tv.gltf @@ -0,0 +1,170 @@ +{ + "asset":{ + "generator":"Khronos glTF Blender I/O v3.6.27", + "version":"2.0" + }, + "extensionsUsed":[ + "KHR_materials_specular" + ], + "scene":0, + "scenes":[ + { + "name":"Scene", + "nodes":[ + 0 + ] + } + ], + "nodes":[ + { + "mesh":0, + "name":"Cube", + "rotation":[ + -0.5, + -0.5, + 0.5, + 0.5 + ], + "scale":[ + 0.009999999776482582, + 0.009999999776482582, + 0.009999999776482582 + ], + "translation":[ + 0.00012245179095771164, + -0.29584750533103943, + -1.0193099975585938 + ] + } + ], + "materials":[ + { + "doubleSided":true, + "extensions":{ + "KHR_materials_specular":{ + "specularColorFactor":[ + 0.32326281070709223, + 0.32326281070709223, + 0.32326281070709223 + ] + } + }, + "name":"Material.001", + "pbrMetallicRoughness":{ + "baseColorTexture":{ + "index":0 + }, + "metallicFactor":0, + "roughnessFactor":0.7987961173057556 + } + } + ], + "meshes":[ + { + "name":"Cube.001", + "primitives":[ + { + "attributes":{ + "POSITION":0, + "NORMAL":1, + "TEXCOORD_0":2 + }, + "indices":3, + "material":0 + } + ] + } + ], + "textures":[ + { + "sampler":0, + "source":0 + } + ], + "images":[ + { + "bufferView":4, + "mimeType":"image/jpeg", + "name":"Texture" + } + ], + "accessors":[ + { + "bufferView":0, + "componentType":5126, + "count":124, + "max":[ + 85.9017333984375, + 2.3966312408447266, + 87.56710815429688 + ], + "min":[ + -26.71697998046875, + -1.9309759140014648, + -88.04142761230469 + ], + "type":"VEC3" + }, + { + "bufferView":1, + "componentType":5126, + "count":124, + "type":"VEC3" + }, + { + "bufferView":2, + "componentType":5126, + "count":124, + "type":"VEC2" + }, + { + "bufferView":3, + "componentType":5123, + "count":228, + "type":"SCALAR" + } + ], + "bufferViews":[ + { + "buffer":0, + "byteLength":1488, + "byteOffset":0, + "target":34962 + }, + { + "buffer":0, + "byteLength":1488, + "byteOffset":1488, + "target":34962 + }, + { + "buffer":0, + "byteLength":992, + "byteOffset":2976, + "target":34962 + }, + { + "buffer":0, + "byteLength":456, + "byteOffset":3968, + "target":34963 + }, + { + "buffer":0, + "byteLength":55145, + "byteOffset":4424 + } + ], + "samplers":[ + { + "magFilter":9729, + "minFilter":9987 + } + ], + "buffers":[ + { + "byteLength":59572, + "uri":"data:application/octet-stream;base64,Z2eJwWhiGUBcIq9CZ2eJwWhiGUBcIq9CZ2eJwWhiGUBcIq9CsM2rQmhiGUBcIq9CsM2rQmhiGUBcIq9CsM2rQmhiGUBcIq9CsM2rQmhiGUA2FbDCsM2rQmhiGUA2FbDCsM2rQmhiGUA2FbDCZ2eJwWhiGUA2FbDCZ2eJwWhiGUA2FbDCZ2eJwWhiGUA2FbDCZ2eJwTgq9782FbDCZ2eJwTgq9782FbDCZ2eJwTgq9782FbDCsM2rQjgq9782FbDCsM2rQjgq9782FbDCsM2rQjgq9782FbDCsM2rQjgq979cIq9CsM2rQjgq979cIq9CsM2rQjgq979cIq9CZ2eJwTgq979cIq9CZ2eJwTgq979cIq9CZ2eJwTgq979cIq9C0rx0wTgq978EEa3C0rx0wTgq978EEa3C0rx0wTgq978EEa3C0rx0wTgq978qHqxC0rx0wTgq978qHqxC0rx0wTgq978qHqxCcQuoQjgq978qHqxCcQuoQjgq978qHqxCcQuoQjgq978qHqxCcQuoQjgq978EEa3CcQuoQjgq978EEa3CcQuoQjgq978EEa3C0rx0wQAKjD0EEa3C0rx0wQAKjD0EEa3C0rx0wQAKjD0EEa3C0rx0wQAKjD0qHqxC0rx0wQAKjD0qHqxC0rx0wQAKjD0qHqxCcQuoQgAKjD0qHqxCcQuoQgAKjD0qHqxCcQuoQgAKjD0qHqxCcQuoQgAKjD0EEa3CcQuoQgAKjD0EEa3CcQuoQgAKjD0EEa3CZ2eJwWhiGUDf/TBBZ2eJwWhiGUDf/TBBZ2eJwWhiGUDf/TBBZ2eJwWhiGUDf/TBBZ2eJwTgq97/f/TBBZ2eJwTgq97/f/TBBZ2eJwTgq97/f/TBBZ2eJwWhiGUCr7ivBZ2eJwWhiGUCr7ivBZ2eJwWhiGUCr7ivBZ2eJwWhiGUCr7ivBZ2eJwTgq97+r7ivBZ2eJwTgq97+r7ivBZ2eJwTgq97+r7ivBe+auwWhiGUBr4i1Be+auwWhiGUBr4i1Be+auwWhiGUBr4i1Be+auwTgq979r4i1Be+auwTgq979r4i1Be+auwTgq979r4i1Be+auwWhiGUA40yjBe+auwWhiGUA40yjBe+auwWhiGUA40yjBe+auwTgq97840yjBe+auwTgq97840yjBe+auwTgq97840yjBqfG4wWhiGUDHuD1BqfG4wWhiGUDHuD1BqfG4wWhiGUDHuD1BqfG4wTgq97/HuD1BqfG4wTgq97/HuD1BqfG4wTgq97/HuD1BqfG4wWhiGUCTqTjBqfG4wWhiGUCTqTjBqfG4wWhiGUCTqTjBqfG4wTgq97+TqTjBqfG4wTgq97+TqTjBqfG4wTgq97+TqTjB5fO6wWhiGUDM+U9C5fO6wWhiGUDM+U9C5fO6wWhiGUDM+U9C5fO6wTgq97/M+U9C5fO6wTgq97/M+U9C5fO6wTgq97/M+U9C5fO6wWhiGUABtk7C5fO6wWhiGUABtk7C5fO6wWhiGUABtk7C5fO6wTgq978Btk7C5fO6wTgq978Btk7C5fO6wTgq978Btk7C967JwWhiGUBWtU1C967JwWhiGUBWtU1C967JwWhiGUBWtU1C967JwWhiGUBWtU1C967JwTgq979WtU1C967JwTgq979WtU1C967JwTgq979WtU1C967JwWhiGUCLcUzC967JwWhiGUCLcUzC967JwWhiGUCLcUzC967JwWhiGUCLcUzC967JwTgq97+LcUzC967JwTgq97+LcUzC967JwTgq97+LcUzCYLzVwWhiGUDpbEpCYLzVwWhiGUDpbEpCYLzVwWhiGUDpbEpCYLzVwTgq97/pbEpCYLzVwTgq97/pbEpCYLzVwTgq97/pbEpCYLzVwWhiGUAeKUnCYLzVwWhiGUAeKUnCYLzVwWhiGUAeKUnCYLzVwTgq978eKUnCYLzVwTgq978eKUnCYLzVwTgq978eKUnCAACAvwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAACAvwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AAAAgAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAgAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAACAvwAAAAAAAACAAAAAgAAAgL8AAACAAAAAAAAAAAAAAIA/AAAAgAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAACAvwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAvwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAACAvwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAvwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACA5pQpvQAAAADPx38/AAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAACAvwAAAAAAAACA5pQpvQAAAADPx38/AAAAAAAAgL8AAACAAACAvwAAAAAAAACA5pQpvQAAAADPx3+/AAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAACAvwAAAAAAAACA5pQpvQAAAADPx3+/AAAAgAAAgL8AAACA5pQpvQAAAADPx38/AAAAAAAAgD8AAACAuX0ePwAAAADqCUk/5pQpvQAAAADPx38/AAAAAAAAgL8AAACAuX0ePwAAAADqCUk/5pQpvQAAAADPx3+/AAAAAAAAgD8AAACAuX0ePwAAAADqCUm/5pQpvQAAAADPx3+/AAAAAAAAgL8AAACAuX0ePwAAAADqCUm/AAAAAAAAgD8AAACAuX0ePwAAAADqCUk/tP5/PwAAAAAvb847AAAAAAAAgL8AAACAuX0ePwAAAADqCUk/tP5/PwAAAAAvb847AAAAAAAAgD8AAACAuX0ePwAAAADqCUm/tP5/PwAAAAAvb867AAAAAAAAgL8AAACAuX0ePwAAAADqCUm/tP5/PwAAAAAvb867AKOWvgAAAABiq3Q/AAAAgAAAgD8AAACAtP5/PwAAAAAvb847AKOWvgAAAABiq3Q/AAAAAAAAgL8AAACAtP5/PwAAAAAvb847AKOWvgAAAABiq3S/AAAAAAAAgD8AAACAtP5/PwAAAAAvb867AKOWvgAAAABiq3S/AAAAAAAAgL8AAACAtP5/PwAAAAAvb867KfP0vgAAAABJzWA/AKOWvgAAAABiq3Q/AAAAgAAAgD8AAACA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DSUAFFFJQAUUUlABRRRQAUlFJQAtJRRQAUUUhoAWkoooAKKKKACiiigAooooAKKKKQBRRRTAKKKKQBRRRTAKKKKACiiikAUUUUwCiiigAooopAFFFFMAooooAKKKKQBRRRQAUUUUwCiiikAUUUUwCiiikAUUUUAFFFFMAooooAKKKKACiiigAooooAKKKKACiiikAUUUUAFFFHemM1NU/wCQLof/AF7S/wDo+SqNh/yEbb/rsn8xV7VP+QLof/XtL/6PkqjYf8hG1/67J/MUhn078CP+RHn/AOvyT+deo15d8CP+RHn/AOvyT+deo0nuOOwVx/xQ/wCSda1/16yf+gGuwrj/AIof8k61r/r1k/8AQGoW4PY8+0Ljw9pn/XpF/wCgCo/EhH/CJ61/15Sfyp+if8i9pn/XpF/6AKj8R/8AIqaz/wBeUn8q26GC3MC0/wCPKD/rmv8AKpaitf8Ajzg/65r/ACqSmthPcM0hopDQIDSUGkoAKSlzSZoASkpabQAUlLSUwCm0ppKACkNGaSgApKDSGgAzSGikNACUlLSUAGaQmkpKQATSUGkzxQAhpM0GkNABSGg0lABSGikNABSE0GkNAxDSGlzTTQAUmaSkoADSGg0lABSUhNJSACaaTmgmkoAKQmikoAKSg0lAxM0lBpM0AFJmg0lAATTaKQ0gCmmlzSUAJRRSUAFJmg03NAwJpDQaSgApDRRmgAppNKTTaQCk000tNJoGFJRRQAlFFNzSAWkopDQAGg0ZpDQAGkNGaQ0DEopKKACkJooNACUUhooAKSikzQAtIaDSUAFJRmigAoopKBhSUUUCCiikpDCiiigBDRQaSgAooooAKSikNABmiiigAooooAKSiigAooooAKKKKQBRRRQAUUUUwCiiigAooooAKKKKQBRRRTAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooopAFFFFMAooopAFFFFABRRRTAKKKKQBRRRTAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACjvRRQM1NT/AOQLoX/XtL/6PkqlYf8AIRtf+uqfzFXdT/5Auhf9e0v/AKPkqlYf8hG1/wCuqfzFIZ9OfAj/AJEef/r8k/nXqNeXfAf/AJEef/r8k/nXqNJ7jjsFcf8AFH/knWtf9esn/oBrsK434p/8k51n/r2f/wBB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BpKAA02lpKBiHrRRSUALSUlGaACkzRmkoAKSiigApKKKAEooooGFJmlpKAEzRRRSAKSiigAoopKACkpaSgAoopKACiikoAKKKKACiikoAM0UUUAFFFFABRRRQAUUUUAFFFFIAooopgFFFFABRRRQAUUUUAFFFFIAooopgFFFFABRRRQAUUUUAFFFFABRRRQAUUUUgCiiimAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRSAKKKKACiiimAUUUUAa2p/8gPQ/+veX/wBHPVGx/wCQja/9dk/mKvan/wAgPQv+veX/ANHPVGx/5CNr/wBdk/mKBn038Cf+RIn/AOvyT+deo15d8Cf+RIn/AOvyT+deo1L3KWwVw/xc/wCSaav/ANcq7iuH+Lv/ACTTV/8ArnQtwex5XpZ/4k9j/wBe0f8A6AKXVj/xT2rj/pyl/lTNM/5BFj/17R/+gCjVD/xT+r/9eUv8q26GHUzrQ/6Db/8AXNf5VLUNp/x42/8A1zX+VS01sS9wPFJRRTAOlITSE0lAC03OKWmmgAJpKDSUAFJRSUABNIaM0hNIAptBpKAA0lGabmgApM0ZpKADNJS02gYUhozSGgApM0E00mgApKDTaAFpKDTaAA0lBpKQCGkopKACkNBpKAENJQaTNAwzSUUhoASkzRmkNABSGjNNJpAFJRmkNABSGjNITQAUlBpDQAGm0tJQMKaaUmkJpAFJmikoADSUhooGJSUtJQAUmaWm0gCkpaSgANJQTSUAFJQaSgYUhoooAKSg0lABSUZozQAhooNJQAUlFFACUUUUDCiikNABSUUUgCkoNFABRRRQAlJS0lABRRRQAhoopKACiiigAoopKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooopAFFFFMAooooAKKKKACiiigAooopAFFFFMAooooAKKKKQBRRRTAKKKKACiiigAooopAFFFFMAooooAKKKKACiiigAooooAKKKKACiiikAUUUUAFFFFABRRRTAKKKKANbUv+QHof8A17y/+jnqjYf8hG2/67J/MVe1L/kCaH/17y/+jnqjYf8AIQtv+uqfzFAz6b+BP/IkT/8AX5J/OvUa8u+BP/IkT/8AX5J/OvUal7lLYK4f4u/8k01f/rmK7iuH+Lv/ACTTV/8ArnQtwex5Npp/4lFj/wBe8f8A6CKXU/8AkAav/wBeUv8AKmab/wAgqyH/AE7x/wDoIp2pf8gDV/8Aryl/lW3Qw6mfaf8AHjb/APXNf5VNUFp/x5Qf9c1/lU2aa2E9wpKDSZxTEFJmikoADTc0pptAATSUtNNABSZopM0gENBopCaACmmgmkoASikNFABSGikzQMKQ0UhoAQ0maDSUAITSUUlAATSUGkoACaaaKQ0gDtTc0GkJoAM0lFNoAU000UhoGFJRSGgAJpKKQ0AIaTNBpKQATTaKKAEpKUmm0AL2pppTTSaACkopDQMKSikNIANJQaQmgAJpDSGigYUhopKACkpabSAKKKSgAzSUpptAAaKSkoGBNJRRQAUhopKACkoozQAUlFJQAUUUlAAaSg0UDCiiigBKSlpKQBSZpaSgAooozQAUlBpKACiiigApDRRmgBKKKKACiiigBKKKKACiiigAooooAKKKKACiiigAooopAFFFFMAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA1tT/AOQJof8A1wl/9HPVGw/5CFt/11T+Yq9qf/IE0P8A64S/+jnqhY/8hC2/66r/ADFIZ9OfAj/kSJ/+vuT+deo15d8CP+RHn/6/JP516jSe5S2CuH+Lv/JNNX/65V3FcP8AF3/kmmr/APXKhbg9jyLTj/xK7P8A694//QRTtQ50HV/+vKX+VR6ef+JXZ/8AXCP/ANBFO1A50LVv+vKX+VbdDDqUrQ/6FB/1zX+VSmobP/jyg/65r/Kpaa2EwpKDSUxBSUUlABSGikzQAGm0tNJpAGaQmikoATNJS5ppoAM0hNBpM0AFIaM0lABTTS5pM0DCkPSg02gAJpKDTTQAZpM0E0lAATTTS000gA0hozSGgBKSlptACmmmjNIaBhmkNIaSgANIaKSgAppoNJmkAUlFJQAUlFIaAA00mlNNNAATSUtJmgYlJS009aAFNNpaaaQATSGikoGFITRSUAGaKKSkAGkoooAQ0ZpKQ0ABNITRSUDA0maWkoAKQ0tNoAKKKTNACUUUlABRRRQAmaSiigYUUUUAJmkoNFIAoopKACiiigBKKDSUABooooAKSiigANJRRQAUUUlABmiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiikAUUUUAFFFFABRRRTAKKXFJQAUUUUAFFFFABRRRSAKKKKYBRRRQAUUUUAFFFFABRRRSAKKKKYBRRRQAUUUUAFFFFABRRRQBral/yA9D/wCuEv8A6OeqNl/yELb/AK6p/MVe1L/kB6H/ANcJf/Rz1Rsf+P8Atv8Arqv8xQM+m/gR/wAiNP8A9fkn869Rry74Ef8AIjz/APX5J/OvUal7lR2CuH+Lv/JNNX/6513FcN8Xv+Saav8A9c6FuD2PH7Bv+Jbaf9cI/wD0EU6/b/iR6sP+nGX+VRWJxptr/wBcE/8AQRS3hzomrf8AXjL/AOg1t0MOpWs/+PK3/wCua/yFS1DZ/wDHlB/1zX+VS01sJ7gTSUGkpiA0lFNoAWkopKQAaaTRmkNAAabmlpKAENJQTSZoAKQ0UhoAKQ0UhoGBpKDTaAAmkNBpKACkoptACmm5oNJSADTTSk02gAzTSaKSgApCaDSUAJRRSGgYZpDSE0lAAabSmm0gCkNKabQAUlBpKAA0lBpM0DA0hoptABSUUUAFJSUUgA02gmg0DEpKXNNzQAUZopDSADSUUmaAFpDRSZoASkpc0hoGJRRSUABpKWm0AFFFJQAZpKKKADtSUUUAFNzS5pKACiikoGFFFJSAKKKSgAooooAKSiigBKKKKACkpTSUAFJRRQAUUUUAFJS5pKACiiigAooooAKKKKQBRRRQAUUUUwCiiigAooooAKKKKACiiigAooopAFFFFMAooooAKKKKACiiigAooopAFFFFABRRRTAKKKKACiiikAUUUUwCiiigAooooAKKKKQBRRRTAUUUlLQAmaKKKQBRRRQAUUUUwCiiigAooooAKKKKACiiigAooooAKKKKQBRRRTAKKKKACiiigAooooAKKKKANbUv+QJoX/XCX/0c9UbH/kIWv/XVf5ir+pf8gLQ/+uEv/o56oWH/ACELX/rqn8xQM+m/gR/yI8//AF+Sfzr1GvLvgR/yI8//AF+Sfzr1Gpe5Udgrh/i7/wAkz1f/AK5V3FcP8Xf+Saav/wBc6FuD2PF7Nv8AiX2v/XFP/QRT7s/8SXVv+vGX/wBBNQ2h/wBAtv8Arin/AKCKfcn/AIk+rf8AXjL/AOg1t0MOpDZ/8eUH/XNf5VLUNp/x5Qf9c1/lUpprYT3AmkNFIaYgzSUGkoAKbmlpKQCZpDQaTNABmkNBptAAaSlpKAEpKKSgYUhozSE0ABNNJpabQAUhNGaSgBDSZpTTTSAM0hoNNNABTaXNJQAmaSikoAKQ0UhNAwpDQaTNACGkNBNIaQAaSg0lACGig02gApKU0maAEJpKDSUDA0lBNJmgApM0GkpABpKDSUDA0hNBNJQAdaSikpABNJQaSgBaSikNABSGjNJQMO1JRSUAFFFITzQAGkooNABTTS0lABSUGkoAKSlpKACiiigYlFFJQAUUUlIAzRRRQAUlFFACGiiigAozRSUABpKKKACiiigApKKKACiiigAooopAFFFFMAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKQBRRRTAKKKKACiiigAooooAKKKKACiiikAUUUUwCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiikAUUUUwCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA1tR/5AWh/wDXGX/0c9UrD/kI2v8A11T+Yq7qP/ID0P8A64S/+jnqlYf8hC1/66p/MUDPpr4Ef8iPP/1+Sfzr1GvLvgR/yI0//X5J/OvUal7lR2CuH+Lv/JM9X/6513FcP8Xf+SZ6v/1zoQPY8StT/oNt/wBcU/8AQRUlwc6Rqv8A14zf+g1Bat/oduP+mKf+gipJz/xKdVH/AE4zf+gmtuhj1I7Q/wChQf8AXNf5VLUNp/x5Qf8AXNf5VMTTWxL3CmmlppoEBptLSUAJSZpabQAGkopM0AIaM0hooAM0lFIaBiE0UlITQAHpTTSk000AGaTNBpM0ABpKDSGgAppoJppNIApM0UlABmkopDQAmaTNBNJmgYUlFJQAE000pNNNIApCaDSE0AFIaKbQAGkpabQAE0lBpKBhmkNFFACUlFFIBCaQ0Gm0DA0UUlABSZoooAKTNJRSAKSiigBKQ0GigYlJQaSgAoopCaADNJRSZoAWkopDQAUlFFACUUUlAC0lFFAwpKWkoAQ0UUUgCkoooAKSlpKACkoooAKKKSgAopKKACiiigAoopKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiikAUUUUwCiiikAUUUUwCiiigAooooAKKKKACiiigAooooAKKKKQBRRRTAKKKKACiiigAooopAFFFFMAooooAKKKKACiiigAooooAKKKKACiiikAUUUUwCiiigAooopAFFFFMAooooAKKKKACiiigAooooAKKKKACiiikAUUUUAa+o/wDID0P/AK4S/wDo56o2H/IQtf8Arsn8xV7Uf+QFof8A1wl/9HPVKwH/ABMbX/rsn8xTGfTPwI/5Eaf/AK/JP516jXl3wI/5Eef/AK/JP516jUsqOwVw/wAXf+Saav8A9c67iuH+Lv8AyTPV/wDrlQgex4ZbHFnb8/8ALJP/AEEVJI2dK1T/AK8Zv/QTVe3bFpAP+mSf+gipXOdM1T/rxm/9BNa9DHqFp/x5Qf8AXNf5VNmoLT/jyg/65r/Kps8VS2Je4Gmk0UhoEFJRTaAA0hopO1ABmkNFJmgApKKSgYGkNBNITQAmaQmim0ALTSaDSUABNJmg9KSgAppoNJSADSUGm0ABpKDSUABpDRmkzQMKaaDSE0AFJQTSGkAU2ikoADSUGkzQAGkoNJQAGm5paSgYhNJRRQAlFGabmkAZpM0ZpKAENFFJQMM0lFFIApDRSUAFJS0maACkJoNIaBhmmk0UUAFJRSUAGaSiigApKKSgApKWkoAKSikzQAtJRRQMKSikoAKKKKQBSUUUAFJRRQAUlGaKACiikoAKSlpKACiiigAoopKACiiigAooooAKKKKACiiikAUUUUwCiiigAooooAKKKKQBRRRTAKKKKACiiikAUUUUAFFFFABRRRTAKKKKQBRRRTAKKKKACiiikAUUUUwCiiigAooooAKKKKACiiikAUUUUwCiiikAUUUUwCiiigAooooAKKKKACiiigAooopAFFFFMAooooAKKKKACiiikAUUUUwCiiigAooooAKKKKQBRRRTAKKKKACiiikBr6j/AMgLQ/8ArjL/AOjnqlY/8hC1/wCuqfzFXdR/5AWh/wDXCX/0c9UrH/kI23/XZP5imM+mfgR/yI8//X5J/OvUa8t+BH/I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KKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooopAFFFFMAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKANnUP8AkAaJ/wBcpf8A0c9UrAf8TC2/66p/MVdv/wDkAaJ/1xl/9GvVOx/5CFt/12X+YoA+lvgR/wAiPcf9fkn869Rry74Ef8iPcf8AX5J/OvUallx2CuH+Lv8AyTLWP+uf9a7iuI+Ln/JM9Y/65UIHsfMmcKn+4v8AIVc08/udS/68J/8A0E1QJwF/3V/kKuaccxal/wBeE/8A6Ca0MjTtP+POD/rmv8qlNRWn/HnB/wBc1/lUhq0SwpKKQ0CEJpKU02gANJmg000ABNJRmkpABpDQaaTQAHrSGikzQAU2lJpKAEpDQaQ0DCkoNNoAWmmlpppAGaQ0ZpCaAEpM0UlABSGig0AJSZoNJQMM0lFJQAUmaWmmkAUlBptAwozRSUAFJmikoAKQ0ZopAFIaKKAEpM0UhoGFJRSUAFFFJQAZpKKQ0ALTaKMUAJRQaKACkopKACiikoGLSUGkoAKKKSkAUUUUAFJRRQAlFFFABRRSUABpKKKACiiigAopKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKQBRRRTAKKKKACiiigAooooAKKKKACiiigAooooAKKKKQBRRRTAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooopAFFFFMAooooAKKKKQBRRRTAKKKKACiiigAooooAKKKKANm/8A+QBon/XKX/0c1U7H/kIWv/XZf5irl9/yAND/AOuUv/o1qp2P/IQtv+uyfzFAH0r8B/8AkSLj/r8k/nXqVeW/Af8A5Ei4/wCv2T+depVLLjsFcR8XP+SZ6x/1yrt64j4uf8kz1j/rlQgex8vOfu/7q/yFXdMP7rUv+vCf/wBBqi55X/dX+Qq5ph/d6l/14T/+g1oZmta/8ekP/XNf5VIa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AKKKKACiiigAooooAKKKKACiiigAooopAFFFFMAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKQBRRRTAKKKKACiiigAooooAKKKKACiiikAUUUUwCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiikAUUUUwCiiigAooooAKKKKACiiigAooopAFFFFMDcu/8AkXtF/wCuc3/o1qq2X/IQtv8Arqv8xVq7/wCRe0X/AHJv/RhqrZf8hC2/66p/MUCPpH4Ef8iPcf8AX7J/OvUa8u+BH/IkXH/X5J/OvUal7mkdgri/ix/yTXWf+uDfyrtK4v4r/wDJNtZ/64N/KhA9j5Jm/wBYfoP5Vo6IedR/7B8//oNZs/8ArT9B/KtDQ/vaj/2D5/8A0GqINW2/49If+ua/yqSorb/jzh/65r/Kn1oZvcM0hoNJQAGkNBNITQAmaTNLTTSACaQ0UlACUlLSUAJSGikNABSUZpDQMSiikNABSGikpAFJmgmkJoGBpKKSgANJRRSASkpc0lACUUUhoAWmmikoAKSikoGLSUUhoADSdqKM0AJSUppKACkozRmgApKKSgAoopKBhRRSUAFFFJSAKKKKACkNLSUAJRRRQAUmaWkoAKSjNFABRRRQAUUmaKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKQBRRRTAKKKKQBRRRQAUUUUwCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKQBRRRTAKKKKACiiigAooooAKKKKACiiigAooooAKKKKQBRRRTAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigDcu/+Re0X/cm/wDRhqpY/wDH/bf9dU/mKt3X/Iu6L/uTf+jDVWx/5CFv/wBdU/mKBH0j8CP+RHn/AOv2T+deo15d8CP+RHn/AOv2T+deo1L3NI7BXF/Fj/km2s/9cG/lXaVxXxY/5JtrP/XA/wAjQgex8kT/AOtP0H8q0dCHOpf9g+f/ANBrOn/1p+g/lWlof/MS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KKACkpTSUgCikzRQAUUUlABRRSUAFFFFABSUUUAJRRRQAUUUmaACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiikAUUUUAFFFFABRRRTAKKKKACiiigAooooAKKKKACiiigAooooAKKKKQBRRRTAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKQBRRRQAUUUUwCiiigAooooAKKKKACiiigAooooAKKKKQBRRRTAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKQBRRRTAKKKKACiiikAUUUUwCiiikBu3H/Iu6N/uzf+jDVey/4/7b/rqv8AMVZuP+Rc0b/dm/8ARhqtZf8AIQt/+uq/zFMR9G/Af/kSLn/r9k/nXqVeW/Af/kSLn/r9k/nXqVS9zSOwVxPxa/5JrrH/AFxNdtXE/Fr/AJJprH/XE0IHsfI8/wDrfwH8hWloXXUv+wfP/wCg1mT/AOuP0H8hWloX3tS/7B8//oNUQXYP+PaL/cH8qfUcB/0aL/cH8qfmrRmwJppNKaQ0AGeKQ0GmmgYUhopKACkopKACkzRmkNIApKDSUDCkopKQBSUtIaACkoNJQAUUhpKAA0maU02gYUUlFABSE0UhoAKKKQ0ABpKKKAEzRRSGgANJRRQMKKKSgApM0UUAFJRRSAKKKKAEoopKACiiigApKKKAEooooAKKKSgAooopAFFFFABRRRQAUUUUAFFFFMAooooAKKKKACiiikAUUUUwCiiigAooooAKKKKACiiigAooooAKKKKACiiikAUUUUwCiiikAUUUUwCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiikAUUUUwCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooopAFFFFABRRRTAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAN64/5FzRv92b/wBGGq1l/wAhC2/66r/MVZuP+Rc0b/dm/wDRhqvY/wDIQtv+uq/zFAj6N+BH/IkXH/X7J/OvUa8u+BH/ACJFx/1+yfzr1Gpe5pHYK4j4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KACiiigAoopKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiikAUUUUwCiiigAooooAKKKKACiiikAUUUUwCiiigAooooAKKKKACiiigAooooAKKKKACiiikAUUUUwCiiikAUUUUwCiiigAooopAFFFFMAooooAKKKKACiiikAUUUUwCiiigAooooAKKKKACiiigAooooAKKKKQBRRRTAKKKKACiiigAooooAKKKKACiiigAooooAKKKKQBRRRTAKKKKACiij0oA6Cb/AJFzSP8Att/6HUFj/wAf9v8A9dV/mKnl/wCRb0j/ALbf+h1BZD/T7f8A66r/ADFMXU+ivgR/yJFx/wBfsn869Rry74Ef8iRcf9fsn869RqHuaLYK4f4vf8ky1j/rlXcVxHxbXf8ADTWBnH7rNCBnyLcf64/h/KtPQuupf9eE38qzJ/8AXN+H8q09B66l/wBeE38qoklh/wCPeP8A3R/KnUyH/j3j/wB0fyp2aozFNNNLmmmgApM0UGgYlJS0lABSUE0lIApM0ppKAAmm0ppKBiGkpaSgApKKTNABSUuaSgApKDSUAFFJRQAUlFFACUUUUDCkoooAKSiikAUlFFABSZpaSgApM0GigAoopKACkpaSgAooooAKKKSgAooooAKKKKACiiigAooooAKKKKACiiigAooopAFFFFABRRRTAKKKKACiiikAUUUUwCiiigAooooAKKKKACiiikAUUUUAFFFFMAooooAKKKKACiiigAooooAKKKKQBRRRTAKKKKACiiikAUUUUwCiiigAooooAKKKKQBRRRTAKKKKACiiikAUUUUwCiiigAooooAKKKKACiiikAUUUUAFFFFABRRRQAUUUUwCiiikAUUUUwCiiikAUUUUAFFFFMAooooAKKKKACiiigAooooAKKKKACiiigAooopAFFFFMAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAOgl/5FvSP+2//odQ2X/H/b/9dV/mKml/5FzSPrP/AOh1DZD/AE+2/wCuq/zFMR9E/Aj/AJEi4/6/ZP516jXl3wI/5Ei5/wCv2T+deo1D3NI7BXFfFn/kmusf9cTXa1xPxa4+Gmsf9cTQgex8iT/64/QfyrS0HrqX/YPm/lWbP/rj9B/KtLQeupf9g+b+VMkkh/494/8AdH8qeaZD/wAe8f8Auj+VOqyAzSGikJoASg0UlABSGijNIANJmikoAKSikNABSGikoGBpKDRQAlFJRQAUUU2gANFFJQAUUUhoAKSiigYUlFFABSGiikAUmaKKACikooAKDSUUAFFFIaAFpKKSgAooooAKKKSgAooooAKKKKACiiigAooooAKKKKACiiigAooopAFFFFMAooopAFFFFMAooooAKKKKACiiigAooooAKKKKACiiikAUUUUAFFFFMAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKQBRRRTAKKKKACiiikAUUUUwCiiigAooooAKKKKACiiikAUUUUAFFFFMAooooAKKKKQBRRRTAKKKKACiiigAooooAKKKKQBRRRTAKKKKACiiigAooooAKKKKQBRRRTAKKKKACiiigAooooA6F/+Rb0j/tv/wCh1DZf8f8Ab/8AXVf5ippP+Rb0n/tv/wCh1FZf8f8Abf8AXVf5imI+hvgR/wAiTc/9fsn869Sry34Ef8iTdf8AX7J/OvUqh7mkdgriPi3/AMk01j/rlXb1w/xd4+Gesf8AXKhA9j5GuP8AXN9B/KtLQeupf9eE38qzbj/XN+H8q0tB66l/14Tfypkjof8AUR/7o/lT6ZCf3Ef+6P5U41RAlFFJmgAoopuaAFzSUUmaADNITQaSgApKDSUDCiikoAM0maQnmigApKM0ZoASiikoADSZoooAKSiigYUhpaSgApKDRSAKSg0UAFFFJQAZozRSUAFFFFABSUUlABmiiigAoopKACiiigAooopAFFFFMAooooAKKKKBhRRRQIKKKKQBRRRTGFFFFAgooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooopAFFFFMAooooAKKKKQBRRRTAKKKKACiiigAooooAKKKKACiiikAUUUUAFFFFMAooooAKKKKACiiigAooopAFFFFMAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiikAU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jR9i1/8Auz/+O/4129FHtGHskcT9h1/+7N/47/jR9g17+7N/47/jXbUUe0YeyRxP2DXvSb/x3/Gl+wa96Tf+O12tFHtGHskcX9h13/pt/wCO0fYdd/6bf+O12lFHtGHskcX9h1z0m/JaX7Drn/Tb8lrs6KXOHskcb9h1z/pt+S0fYtb9JvyWuyoo5w9kjjfsWt+k35L/AI0fYtb9JvyX/Guyoo5w9kjjfsWt+k3/AHyv+NH2LXPSb8l/xrsqKOcPZI437HrnpN+Q/wAaT7HrnpN+Q/xrs6KOcPZI4z7HrnpN+Q/xo+x656TfkP8AGuzoo5w9mjjPseu56TfkP8aT7Hr3pN+Q/wAa7SijnD2aOL+ya/8A9NvyX/Gj7Jr/AP02/If412lFHOHskcX9k1//AKbf98r/AI0fY9f/AOmv/fK/412lFHOHskcZ9j17/pr/AN8r/jR9j171m/75H+NdnRRzh7NHGfY9e9ZvyH+NKLPXf+mv5D/Guyoo5w9mjjvseuf9NfyH+NJ9j13/AKa/kP8AGuyoo5w9kjjfsWuH/nr+Q/xo+xa5/wBNfyWuyoo5w9kjjvsWuesv/fK0fYtc/wCmv/fK12NFHOHskcd9i1z/AKa/ktL9j1z0l/Ja7CijnD2SOO+x656S/wDfK0v2PXPSX8lrsKKOcPZI4/7JrnpL+S0fZNc9JfyWuwoo5w9mjj/smuek36f40n2PXB0WXjpwP8a7GijnD2aON+x65j7sv5L/AI0hs9dxwsv5L/jXZ0U/aB7JHBXXhrUNcMFpqQb7IJklkDKMMFOccHvjFd2BhQAMD0p1FQ3cqMUhKKWikUJRS0UAJRS0UAJRS0UAJRS0UAJRS0UAJRS0UAJRS0UAJRS0UAJRS0UAJRS0UAJRS0UAJRS0UAJRS0UAJRS0UAJRS0UAJRS0UAJRS0UAAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//2QAAAA==" + } + ] +} diff --git a/assets/pose/detection.serialized.json b/assets/pose/detection.serialized.json new file mode 100644 index 0000000..cdeb1d4 --- /dev/null +++ b/assets/pose/detection.serialized.json @@ -0,0 +1,116 @@ +{ + "version": "QNN_SYSTEM_CONTEXT_BINARY_INFO_VERSION_3", + "info": { + "backendId": 6, + "buildId": "v2.29.0.241129103708_105762", + "coreApiVersion": "2.22.0", + "backendApiVersion": "5.29.0", + "socVersion": "", + "contextBlobVersion": "3.2.0", + "contextBlobSize": 7596008, + "numContextTensors": 0, + "contextTensors": [], + "numGraphs": 1, + "graphs": [ + { + "version": "QNN_SYSTEM_CONTEXT_GRAPH_INFO_VERSION_3", + "info": { + "graphName": "mediapipe_pose_mediapipeposedetector", + "numGraphInputs": 1, + "graphInputs": [ + { + "version": "QNN_TENSOR_VERSION_1", + "info": { + "id": 1, + "name": "image", + "type": "QNN_TENSOR_TYPE_APP_WRITE", + "dataFormat": "QNN_TENSOR_DATA_FORMAT_FLAT_BUFFER", + "dataType": "QNN_DATATYPE_FLOAT_16", + "rank": 4, + "dimensions": [ + 1, + 128, + 128, + 3 + ], + "memType": "QNN_TENSORMEMTYPE_RAW", + "quantizeParams": { + "definition": "QNN_DEFINITION_UNDEFINED", + "quantizationEncoding": "QNN_QUANTIZATION_ENCODING_UNDEFINED" + } + } + } + ], + "numGraphOutputs": 2, + "graphOutputs": [ + { + "version": "QNN_TENSOR_VERSION_1", + "info": { + "id": 469, + "name": "box_coords", + "type": "QNN_TENSOR_TYPE_APP_READ", + "dataFormat": "QNN_TENSOR_DATA_FORMAT_FLAT_BUFFER", + "dataType": "QNN_DATATYPE_FLOAT_16", + "rank": 3, + "dimensions": [ + 1, + 896, + 12 + ], + "memType": "QNN_TENSORMEMTYPE_RAW", + "quantizeParams": { + "definition": "QNN_DEFINITION_UNDEFINED", + "quantizationEncoding": "QNN_QUANTIZATION_ENCODING_UNDEFINED" + } + } + }, + { + "version": "QNN_TENSOR_VERSION_1", + "info": { + "id": 484, + "name": "box_scores", + "type": "QNN_TENSOR_TYPE_APP_READ", + "dataFormat": "QNN_TENSOR_DATA_FORMAT_FLAT_BUFFER", + "dataType": "QNN_DATATYPE_FLOAT_16", + "rank": 3, + "dimensions": [ + 1, + 896, + 1 + ], + "memType": "QNN_TENSORMEMTYPE_RAW", + "quantizeParams": { + "definition": "QNN_DEFINITION_UNDEFINED", + "quantizationEncoding": "QNN_QUANTIZATION_ENCODING_UNDEFINED" + } + } + } + ], + "numUpdateableTensors": 0, + "updateableTensors": [], + "graphBlobInfoSize": 40, + "graphBlobInfo": [ + { + "version": "QNN_SYSTEM_CONTEXT_HTP_GRAPH_INFO_BLOB_VERSION_V1", + "info": { + "spillFillBufferSize": 0, + "optimizationLevel": 0, + "vtcmSize": 4, + "htpDlbc": 0, + "numHvxThreads": 0 + } + } + ] + } + } + ], + "contextMetadataSize": 8, + "contextMetadata": { + "version": "QNN_SYSTEM_CONTEXT_HTP_CONTEXT_INFO_BLOB_VERSION_V1", + "info": { + "dsp arch": 68 + } + }, + "soc model": 0 + } +} diff --git a/assets/pose/landmark.serialized.json b/assets/pose/landmark.serialized.json new file mode 100644 index 0000000..b31c9b1 --- /dev/null +++ b/assets/pose/landmark.serialized.json @@ -0,0 +1,178 @@ +{ + "version": "QNN_SYSTEM_CONTEXT_BINARY_INFO_VERSION_3", + "info": { + "backendId": 6, + "buildId": "v2.29.0.241129103708_105762", + "coreApiVersion": "2.22.0", + "backendApiVersion": "5.29.0", + "socVersion": "", + "contextBlobVersion": "3.2.0", + "contextBlobSize": 20215272, + "numContextTensors": 0, + "contextTensors": [], + "numGraphs": 1, + "graphs": [ + { + "version": "QNN_SYSTEM_CONTEXT_GRAPH_INFO_VERSION_3", + "info": { + "graphName": "pose_landmarks_detector_full", + "numGraphInputs": 1, + "graphInputs": [ + { + "version": "QNN_TENSOR_VERSION_1", + "info": { + "id": 1, + "name": "input_1", + "type": "QNN_TENSOR_TYPE_APP_WRITE", + "dataFormat": "QNN_TENSOR_DATA_FORMAT_FLAT_BUFFER", + "dataType": "QNN_DATATYPE_FLOAT_16", + "rank": 4, + "dimensions": [ + 1, + 256, + 256, + 3 + ], + "memType": "QNN_TENSORMEMTYPE_RAW", + "quantizeParams": { + "definition": "QNN_DEFINITION_UNDEFINED", + "quantizationEncoding": "QNN_QUANTIZATION_ENCODING_UNDEFINED" + } + } + } + ], + "numGraphOutputs": 5, + "graphOutputs": [ + { + "version": "QNN_TENSOR_VERSION_1", + "info": { + "id": 528, + "name": "Identity", + "type": "QNN_TENSOR_TYPE_APP_READ", + "dataFormat": "QNN_TENSOR_DATA_FORMAT_FLAT_BUFFER", + "dataType": "QNN_DATATYPE_FLOAT_16", + "rank": 2, + "dimensions": [ + 1, + 195 + ], + "memType": "QNN_TENSORMEMTYPE_RAW", + "quantizeParams": { + "definition": "QNN_DEFINITION_UNDEFINED", + "quantizationEncoding": "QNN_QUANTIZATION_ENCODING_UNDEFINED" + } + } + }, + { + "version": "QNN_TENSOR_VERSION_1", + "info": { + "id": 536, + "name": "Identity_1", + "type": "QNN_TENSOR_TYPE_APP_READ", + "dataFormat": "QNN_TENSOR_DATA_FORMAT_FLAT_BUFFER", + "dataType": "QNN_DATATYPE_FLOAT_16", + "rank": 2, + "dimensions": [ + 1, + 1 + ], + "memType": "QNN_TENSORMEMTYPE_RAW", + "quantizeParams": { + "definition": "QNN_DEFINITION_UNDEFINED", + "quantizationEncoding": "QNN_QUANTIZATION_ENCODING_UNDEFINED" + } + } + }, + { + "version": "QNN_TENSOR_VERSION_1", + "info": { + "id": 695, + "name": "Identity_2", + "type": "QNN_TENSOR_TYPE_APP_READ", + "dataFormat": "QNN_TENSOR_DATA_FORMAT_FLAT_BUFFER", + "dataType": "QNN_DATATYPE_FLOAT_16", + "rank": 4, + "dimensions": [ + 1, + 256, + 256, + 1 + ], + "memType": "QNN_TENSORMEMTYPE_RAW", + "quantizeParams": { + "definition": "QNN_DEFINITION_UNDEFINED", + "quantizationEncoding": "QNN_QUANTIZATION_ENCODING_UNDEFINED" + } + } + }, + { + "version": "QNN_TENSOR_VERSION_1", + "info": { + "id": 701, + "name": "Identity_3", + "type": "QNN_TENSOR_TYPE_APP_READ", + "dataFormat": "QNN_TENSOR_DATA_FORMAT_FLAT_BUFFER", + "dataType": "QNN_DATATYPE_FLOAT_16", + "rank": 4, + "dimensions": [ + 1, + 64, + 64, + 39 + ], + "memType": "QNN_TENSORMEMTYPE_RAW", + "quantizeParams": { + "definition": "QNN_DEFINITION_UNDEFINED", + "quantizationEncoding": "QNN_QUANTIZATION_ENCODING_UNDEFINED" + } + } + }, + { + "version": "QNN_TENSOR_VERSION_1", + "info": { + "id": 708, + "name": "Identity_4", + "type": "QNN_TENSOR_TYPE_APP_READ", + "dataFormat": "QNN_TENSOR_DATA_FORMAT_FLAT_BUFFER", + "dataType": "QNN_DATATYPE_FLOAT_16", + "rank": 2, + "dimensions": [ + 1, + 117 + ], + "memType": "QNN_TENSORMEMTYPE_RAW", + "quantizeParams": { + "definition": "QNN_DEFINITION_UNDEFINED", + "quantizationEncoding": "QNN_QUANTIZATION_ENCODING_UNDEFINED" + } + } + } + ], + "numUpdateableTensors": 0, + "updateableTensors": [], + "graphBlobInfoSize": 40, + "graphBlobInfo": [ + { + "version": "QNN_SYSTEM_CONTEXT_HTP_GRAPH_INFO_BLOB_VERSION_V1", + "info": { + "spillFillBufferSize": 0, + "optimizationLevel": 0, + "vtcmSize": 4, + "htpDlbc": 0, + "numHvxThreads": 0 + } + } + ] + } + } + ], + "contextMetadataSize": 8, + "contextMetadata": { + "version": "QNN_SYSTEM_CONTEXT_HTP_CONTEXT_INFO_BLOB_VERSION_V1", + "info": { + "dsp arch": 68 + } + }, + "soc model": 0 + } +} diff --git a/assets/whackamole/Duck.gltf b/assets/whackamole/Duck.gltf new file mode 100644 index 0000000..eb159de --- /dev/null +++ b/assets/whackamole/Duck.gltf @@ -0,0 +1,271 @@ +{ + "asset": { + "generator": "gltfeditor.com", + "version": "2.0" + }, + "buffers": [ + { + "uri": "data:application/octet-stream;base64,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WwBcAFQAUwBdAF0AUwBeAF8AYABhAGEAYABiAGAAUwBiAGIAUwBVAFMAYABeAF4AYABjAGAAXwBjAGMAXwBkAGUAZgBkAGQAZgBjAGYAZwBjAGMAZwBeAGcAZgBoAGgAZgBpAGYAZQBpAGkAZQBqAGsAbABtAG0AbABuAG4AbABvAG8AbABwAGwAZQBwAHAAZQBkAGwAawBlAGUAawBqAHEAGAAjACMAGAAUABgAcQAZABkAcQByAHEAcwByAHIAcwB0AHMAcQAlACUAcQAjAHUAdgB3AHcAdgB4AHYAeQB4AHgAeQB6ACQAewAlACUAewB8AHsAfQB8AHwAfQB+AH0AewB/AH8AewCAAHsAJACAAIAAJAAmAHMAgQB0AHQAgQCCAIEAgwCCAIIAgwCEAIMAgQB+AH4AgQB8AIEAcwB8AHwAcwAlAIUAhgCHAIcAhgCIAHkAhQB6AHoAhQCHAIkAigCLAIsAigCMAI0AjgBNAE0AjgBMAI4AjwBMAEwAjwBBAJAAkQCSAJIAkQCTAJEAlACTAJMAlACVAJYAlwCYAJgAlwCZAJcAOwCZAJkAOwA9AJoAmwBJAEkAmwBHAJsAnABHAEcAnABIAJwAmwCWAJYAmwCXAJsAmgCXAJcAmgA7AEAAnQA9AD0AnQCZAJ0AngCZAJkAngCYAJ8AUgBGAEYAUgBFAEYASACfAJ8ASACgAKEAogBcAFwAogBaAKIAowBaAFoAowBZAKMAogCVAJUAogCTAKIAoQCTAJMAoQCSAKQApQCmAKYApQCnAKUAqACnAKcAqACpAKgApQBvAG8ApQBuAKUApABuAG4ApABtAKoAqwChAKEAqwCSAKwArQBoAGgArQBnAK0AXQBnAGcAXQBeAFsAqgBcAFwAqgChAHAArgBvAG8ArgCoAKgArgCpAKkArgCvAK4AXwCvAK8AXwBhAF8ArgBkAGQArgBwAJ4AsACYAJgAsACxALAAsgCxALEAsgCzALQAtQC2ALYAtQCgALUAtAC3ALcAtAC4ALQAuQC4ALgAuQC6ALkAtACWAJYAtAC2ALoAuQCzALMAuQCxALEAuQCYAJgAuQCWALUAuwCgAKAAuwCfALsAvACfAJ8AvABSALwAuwC9AL0AuwC+ALsAtQC+AL4AtQC3AL8AwAAPAA8AwAAOAMEAwgDDAMMAwgDEAMIAvwDEAMQAvwAPAMUAxgADAAMAxgACAMAAxwAOAA4AxwAbAMgAyQDEAMQAyQDDAMkAyADKAMoAyADLAMgACgDLAMsACgAMAAoAyAAPAA8AyADEAMYAzAACAAIAzAARAMwAzQARABEAzQAVAM4AzwDQANAAzwDRAM8A0gDRANEA0gDTANQA1QDWANYA1QDXANUA1ADYANgA1ADZANIA2gDTANMA2gDbANsA2gDcANwA2gDdAN4A3wDgAOAA3wDhAN8A1gDhAOEA1gDXAOIA4wDdAN0A4wDcAOMA4gDkAOQA4gDlAOYA5wDoAOgA5wDpAOcA3gDpAOkA3gDgAOoAmgDrAOsAmgBJAJoA6gA7ADsA6gA8AOoA7AA8ADwA7AA1AJwAtgBIAEgAtgCgALYAnACWAOwA7QA1ADUA7QA2AO0A7gA2ADYA7gAvAO4A7wAvAC8A7wAwADAA7wArACsA7wDwAPEAKgDwAPAAKgArACoA8QAfAB8A8QDyAPMA9ADLAMsA9ADKAPMAHQDyAPIAHQAfAB0A8wAMAAwA8wDLAM0A9QAVABUA9QAiAPUA9gAiACIA9gAmAPYA9wAmACYA9wCAAPcA+ACAAIAA+AB/APkA+gD7APsA+gD8APgA+QB/AH8A+QD7AP0A/gD6APoA/gD8AP4A/QD/AP8A/QCJAAABAQGGAIYAAQGIAAIBAwEAAQABAwEBAYsAjwCJAIkAjwD/AAQBQgCLAEIAQQCLAIsAQQCPAAUBBgH3APcABgH4AAcBCAEJAQkBCAEKAQsBBwEMAQwBBwEJAQ0BBQH2APYABQH3AA4BDwH5APkADwH6ABABEQESARIBEQETAQgBEAEKAQoBEAESAQYBDgH4APgADgH5APMAFAH0APQAFAEVARYBFwEYARgBFwEZARoBGwEcARwBGwEdARQB8wAeAR4B8wDyAB8BDQH1APUADQH2ACABCwEhASEBCwEMAfEAIgHyAPIAIgEeARwBHQEjASMBHQEkASMBJAElASUBJAEmASIB8QAnAScB8QDwAO8AKAHwAPAAKAEnASUBJgEpASkBJgEqASsBKQEsASwBKQEqAS0BKAHuAO4AKAHvAC4BLwHqAOoALwHsADABMQEyATIBMQEzATQBNQE2ATYBNQE3ATgBLgHrAOsALgHqADkBLQHtAO0ALQHuADoBKwE7ATsBKwEsATEBOgEzATMBOgE7AS8BOQHsAOwAOQHtAP0APAGJAIkAPAGKAD0BPgE/AT8BPgFAAT8BQAERAREBQAETATwB/QAPAQ8B/QD6AEEBQgE9AT0BQgE+AUMBRAFBAUEBRAFCAUUBRgF6AHoARgF4AHcAeABHAUcBeABGAUgBSQGIAIgASQGHAHoAhwBFAUUBhwBJAUoBzgBLAUsBzgDQANkATAHYANgATAFNAUcBTgF3AHcATgF1ANgATQHVANUATQFPAdUATwHXANcATwFQAdcAUAHhAOEAUAFRAeAA4QBSAVIB4QBRAVMBVAHoAOgAVAHmAOQA5QBVAVUB5QBWAVIBVwHgAOAAVwHpAOgA6QBTAVMB6QBXAVgBAQFZAVkBAQEDAUgBiABYAVgBiAABAVkBAwFaAVoBAwECAVoBAgFbAVsBAgFcAVwB5ABbAVsB5ABVAQABXQECAQIBXQFcAeMAXQHcANwAXQFeAV8BXgGFAIUAXgGGAF8BYAHbANsAYAHTAGEBYAF2AHYAYAF5AGEBYgHRANEAYgHQAGIBdQBjAWMBdQBOAWIBYwHQANAAYwFLAWQBIAFlAWUBIAEhAWYBZwEVARUBZwH0AGcBaAH0APQAaAHKAGkBaAHMAMwAaAHNAMkAaQHDAMMAaQFqAWsBagHFAMUAagHGABABCAFJAUkBCAFFAREBEAFIAUgBEAFJAQsBIAFHAUcBIAFOAQgBBwFFAUUBBwFGAQcBCwFGAUYBCwFHAVgBPwFIAUgBPwERAT8BWAE9AT0BWAFZASABZAFOAU4BZAFjARcBFgFlAWUBFgFkAU8BIwFQAVABIwElASMBTwEcARwBTwFNARgBSgEWARYBSgFLAUwBGgFNAU0BGgEcASkBUQElASUBUQFQASkBKwFRAVEBKwFSASsBOgFSAVIBOgFXAToBMQFXAVcBMQFTAUMBNAFEAUQBNAE2ATQBQwFVAVUBQwFbATEBMAFTAVMBMAFUATUBNAFWAVYBNAFVAVoBQQFZAVkBQQE9ARoAbAEcABwAbAFtAWwBGgBuAW4BGgAbACcAbwEoACgAbwFwAW8BJwBtAW0BJwAcAC0AcQEuAC4AcQFyAXEBLQBwAXABLQAoAHMBdAEzADMAdAE0AHIBcwEuAC4AcwEzAHUBdgE5ADkAdgE6AHQBdQE0ADQAdQE5AHcBeAE/AD8AeAFAAHYBdwE6ADoAdwE/AHkBegGdAJ0AegGeAJ0AQAB5AXkBQAB4AXsBfAGwALAAfAGyALAAngB7AXsBngB6AccAfQEbABsAfQFuAX4BUAC8ALwAUABSAFAAfgFRAFEAfgF/AX4BgAF/AX8BgAGBAX4BvACAAYABvAC9AIIBgwG9AL0AgwGAAYMBhAGAAYABhAGBAYQBgwGFAYUBgwGGAYMBggGGAYYBggGHAYgBiQG3ALcAiQG+AIkBggG+AL4AggG9AIIBiQGHAYcBiQGKAYkBiAGKAYoBiAGLAYwBiAG4ALgAiAG3AIgBjAGLAYsBjAGNAY0BjAGOAY4BjAGPAYwBuACPAY8BuAC6AI4BjwGQAZABjwGRAY8BugCRAZEBugCzALIAkgGzALMAkgGRAZEBkgGQAZABkgGTAXwBlAGyALIAlAGSAZQBlQGSAZIBlQGTAYsAjAAEAQQBjACWAQQB6wBCAEIA6wBJAJYBOAEEAQQBOAHrAGoBawHDAMMAawHBAGoBaQHGAMYAaQHMAMoAaAHJAMkAaAFpAWgBZwHNAM0AZwH1APUAZwEfAR8BZwFmAWMBZAFLAUsBZAEWAWIBYQF1AHUAYQF2AGABYQHTANMAYQHRAGABXwF5AHkAXwGFANwAXgHbANsAXgFfAYYAXgEAAQABXgFdAeQAXAHjAOMAXAFdAVsBQwFaAVoBQwFBAQgACQCXAZcBCQCYAQkAFwCYAZgBFwCZARcAGQCZAZkBGQCaAWwBmwFtAW0BmwGcAW4BnQFsAWwBnQGbAW8BngFwAXABngGfAW8BbQGeAZ4BbQGcAXEBoAFyAXIBoAGhAXABnwFxAXEBnwGgAXMBogF0AXQBogGjAXIBoQFzAXMBoQGiAXUBpAF2AXYBpAGlAXQBowF1AXUBowGkAXgBdwGmAaYBdwGnAXYBpQF3AXcBpQGnAagBqQGqAaoBqQGrAawBrQGoAagBrQGpAa4BrwGsAawBrwGtAVUAVgCwAbABVgCxAbIBswG0AbQBswG1AWEAYgC2AbYBYgC3AWIAVQC3AbcBVQCwARkAcgCaAZoBcgC4AbkBugG7AbsBugG8Ab0BvgG/Ab8BvgHAAaoBqwHBAcEBqwHCAXkBwwF6AXoBwwHEAXgBpgF5AXkBpgHDAcUBxgG1AbUBxgG0Ab8BwAHFAcUBwAHGAaYApwDHAccBpwDIAacAqQDIAcgBqQDJAakArwDJAckBrwDKAa8AYQDKAcoBYQC2AXsBywF8AXwBywHMAXoBxAF7AXsBxAHLAc0BzgGuAa4BzgGvAc8B0AHNAc0B0AHOAX0B0QFuAW4B0QGdAdIB0wHPAc8B0wHQAdQB1QHSAdIB1QHTAZUBlAHWAdYBlAHXAZQBfAHXAdcBfAHMAdgB2QHaAdoB2QHbAdwB2AHdAd0B2AHaAd4B3AHfAd8B3AHdAeAB3gHhAeEB3gHfAeIB4wHkAeQB4wHlAeMB5gHlAeUB5gHnAdkB2AHoAegB2AHpAdgB3AHpAekB3AHqAesB7AHqAeoB7AHpAewB7QHpAekB7QHoAdwB3gHqAeoB3gHuAd4B4AHuAe4B4AHvAfAB8QHvAe8B8QHuAfEB6wHuAe4B6wHqAeYB4wHyAfIB4wHzAeMB4gHzAfMB4gH0AfUB9gH3AfcB9gH4AfYB4gH4AfgB4gHkAe0B7AH5AfkB7AH6AewB6wH6AfoB6wH7AfwB/QH7AfsB/QH6Af0B/gH6AfoB/gH5AeIB9gH0AfQB9gH/AfYB9QH/Af8B9QEAAgEC9QECAgIC9QH3AQMCAQIEAgQCAQICAvUBAQIAAgACAQIFAgECAwIFAgUCAwIGAgcCCAIJAgkCCAIKAgoCAwIJAgkCAwIEAgMCCgIGAgYCCgILAggCDAIKAgoCDAILAg0CDgIPAg8CDgIQAggCBwIQAhACBwIPAgwCCAIRAhECCAIQAg4CEgIQAhACEgIRAhMCFAIVAhUCFAIWAg4CDQIWAhYCDQIVAhICDgIXAhcCDgIWAhQCGAIWAhYCGAIXAhkCGgIbAhsCGgIcAhoCHQIcAhwCHQIeAh8CIAIeAh4CIAIcAiACIQIcAhwCIQIbAiICIwIkAiQCIwIlAiMCJgIlAiUCJgInAh0CGgInAicCGgIlAhoCGQIlAiUCGQIkAicCJgIoAigCJgIpAicCKAIdAh0CKAIqAisCLAItAi0CLAIuAi8CMAIxAjECMAIyAjMCNAIyAjICNAIxAjUCKwI2AjYCKwItAjcCOAI5AjkCOAI6AiwCKwI6AjoCKwI5AisCNQI5AjkCNQI7AjwCNwI7AjsCNwI5Aj0CPAI+Aj4CPAI7AjUCPwI7AjsCPwI+Aj8CQAI+Aj4CQAJBAkICPQJBAkECPQI+AkMCRAJFAkUCRAJGAkYCRwJFAkUCRwJIAjwCPQJIAkgCPQJFAkICQwI9Aj0CQwJFAusB8QH7AfsB8QFJAvEB8AFJAkkC8AFKAksCTAJKAkoCTAJJAkwC/AFJAkkC/AH7AU0CTgJPAk8CTgJQAlECUgJQAlACUgJPAv0B/AFTAlMC/AFUAlUCVgJUAlQCVgJTAlYCVwJTAlMCVwJYAv4B/QFYAlgC/QFTAkwCSwJZAlkCSwJaAlsCXAJaAloCXAJZAlwCVQJZAlkCVQJUAvwBTAJUAlQCTAJZAl0CXgJfAl8CXgJgAlICUQJgAmACUQJfAmECYgJjAmMCYgJkAmUCIgJmAmYCIgIkAhkCZwIkAiQCZwJmAmgCaQJqAmoCaQJrAmwCbQJrAmsCbQJqAm4CbwJwAnACbwJxAhQCEwJxAnECEwJwAnICIQJzAnMCIQIgAh8CdAIgAiACdAJzAnQCbgJzAnMCbgJwAhMCcgJwAnACcgJzAhgCFAJ1AnUCFAJxAm8CdgJxAnECdgJ1AncCHgIqAioCHgIdAngCHwJ3AncCHwIeAnkCMwJ6AnoCMwIyAjACewIyAjICewJ6AnsCbAJ6AnoCbAJrAmkCeQJrAmsCeQJ6AnwCfQJ+An4CfQJ/AoACgQJ/An8CgQJ+AoECRwJ+An4CRwJGAkQCfAJGAkYCfAJ+AnkCaQKCAoICaQKDAkACPwKEAoQCPwKFAjUCNgI/Aj8CNgKFAjQCMwKCAoICMwJ5AkcCgQJIAkgCgQKGAoECgAKGAoYCgAKHAjgCNwKHAocCNwKGAjcCPAKGAoYCPAJIAnYCbwKIAogCbwKJAooCiwKJAokCiwKIAngCjAKNAo0CjAKOAowCjwKOAo4CjwKQApACkQKOAo4CkQKSApICbgKOAo4CbgKNApECigKSApICigKJAm8CbgKJAokCbgKSAowCeAKTApMCeAJ3AioClAJ3AncClAKTApQClQKTApMClQKWAo8CjAKWApYCjAKTAuYBlwLnAecBlwKYApkCmgKbApsCmgKcAucBmAKcApwCmAKbAtkBnQLbAdsBnQKeApcC5gGfAp8C5gHyAZoCoAKcApwCoAKhAqACogKhAqECogKjAuQB5QGjAqMC5QGhAuUB5wGhAqEC5wGcAp0C2QGkAqQC2QHoAe0BpQLoAegBpQKkAqYCpwKoAqgCpwKpAqoCqwKpAqkCqwKoAqwCrQKuAq4CrQKvAq0CsAKvAq8CsAKxAqsCqgKyArICqgKzArQCtQKzArMCtQKyArYCtwK4ArgCtwK5ArkCrAK4ArgCrAKuArUCtAK6AroCtAK7ArsCvAK6AroCvAK9Ar4CvwLAAsACvwLBArcCtgLBAsECtgLAAiECcgLCAsICcgLDAnICEwLDAsMCEwIVAg0CxAIVAhUCxALDAh8CeAJ0AnQCeAKNAo0CbgJ0AsQCDQLFAsUCDQIPAgcCxgIPAg8CxgLFAsYCBwLHAscCBwIJAgkCBALHAscCBALIAgQCAgLIAsgCAgLJAgIC9wHJAskC9wHKAqICywKjAqMCywLMAvcB+AHKAsoC+AHMAvgB5AHMAswC5AGjAqUC7QHNAs0C7QH5Af4BzgL5AfkBzgLNAs4C/gHPAs8C/gFYAlcC0AJYAlgC0ALPAtEC0gLTAtMC0gLUAtACVwLUAtQCVwLTAtEC1QLSAtIC1QLWAtUC1wLWAtYC1wJkAl0C2AJeAl4C2ALZAtoC2QLbAtsC2QLYAtcCZwJkAmQCZwJjAtwCYwIbAmMCZwIbAmcCGQIbAtAC3QLPAs8C3QLeAt8C4ALhAuEC4ALiAuMC5ALfAt8C5ALgAuUCzgLeAt4CzgLPAtIC5gLUAtQC5gLnAugC6QLqAuoC6QLrAuEC4gLoAugC4gLpAt0C0ALnAucC0ALUAswCywLsAuwCywLtAu4C7wLwAvAC7wLxAvIC8wL0AvQC8wL1AsoCzAL2AvYCzALsAs4C5QLNAs0C5QL3AvgC+QLjAuMC+QLkAskCygL6AvoCygL2AvMC+wL1AvUC+wL8AvsC/QL8AvwC/QL+AsgCyQL/Av8CyQL6AscCyAIAAwADyAL/Av0CAQP+Av4CAQMCAwMDBAMBAwEDBAMCAwUDxgIAAwADxgLHAsQCBgPDAsMCBgMHAwgDCQMKAwoDCQMLAwwDDQMOAw4DDQMPAxADwgIHAwcDwgLDAsYCBQPFAsUCBQMRAxIDEwMDAwMDEwMEAwoDCwMSAxIDCwMTAwYDxAIRAxEDxALFAtYCZAIUAxQDZAJiAhUDFgMXAxcDFgMYAxYD6gIYAxgD6gLrAtIC1gLmAuYC1gIUAxkDFQMaAxoDFQMXAxsDGQMcAxwDGQMaAx0DUQIeAx4DUQJQAh4DUAIfAx8DUAJOAiADXQIhAyEDXQJfAiEDXwIdAx0DXwJRAiIDIwOmAqYCIwOnArECsAIkAyQDsAIlAx8DTgImAyYDTgJNAicDJQOtAq0CJQOwAq0CrAInAycDrAIoAykDKAO5ArkCKAOsAikDuQIqAyoDuQK3AisDvwIsAywDvwK+Ai0DvQIuAy4DvQK8AioDtwIvAy8DtwLBAi8DwQIrAysDwQK/AjADMQPYAtgCMQPbAtgCXQIwAzADXQIgAzEDMgPbAtsCMgPaAjIDMwPaAtoCMwM0Ay4DvAIzAzMDvAI0A9kC2gI1AzUD2gI0A7QCNgO7ArsCNgM1A14CNgNgAmACNgM3A6oCOAOzArMCOAM3A1ICOANPAk8COAM5A6cCOgOpAqkCOgM5AyYDTQI7AzsDTQI6AyMDOwOnAqcCOwM6A/kC+AI8AzwD+AI9A8sCPgPtAu0CPgM/A6ICQAPLAssCQAM+A6UCQAOkAqQCQANBA6ACmgJBA0EDmgJCA50CQgOeAp4CQgNDAx0D4QIhAyED4QLoAuoCIAPoAugCIAMhA+MCHwP4AvgCHwMmA+ECHQPfAt8CHQMeAx8D4wIeAx4D4wLfAjADIAMWAxYDIAPqAjEDMAMVAxUDMAMWA/gCJgM9Az0DJgM7Az0D7gI8AzwD7gLwAicDKAP7AvsCKAP9AiUDJwPzAvMCJwP7AiMDIgPuAu4CIgPvAiQDJQPyAvICJQPzAigDKQP9Av0CKQMBAyoDAwMpAykDAwMBAwMDKgMSAxIDKgMvAysDCgMvAy8DCgMSAxsDHAMMAwwDHAMNAwwDLgMbAxsDLgMzAwoDKwMIAwgDKwMsAy4DDAMtAy0DDAMOAzIDMQMZAxkDMQMVA0QD4AFFA0UD4AHhAeABRAPvAe8BRANGA0cD8AFGA0YD8AHvAfMB9AGbAZsB9AGcAfIB8wGdAZ0B8wGbAf8BAAKeAZ4BAAKfAfQB/wGcAZwB/wGeAQYCoQEFAgUCoQGgAQACBQKfAZ8BBQKgAQwCowELAgsCowGiAaEBBgKiAaIBBgILAhICpQERAhECpQGkAaMBDAKkAaQBDAIRAhgCpgEXAhcCpgGnAaUBEgKnAacBEgIXAiMCIgKpAakBIgKrASYCIwKtAa0BIwKpASYCrQEpAikCrQGvASwCSAMuAi4CSANJA7QBMAKyAbIBMAIvAjgCSgM6AjoCSgNLA0gDLAJLA0sDLAI6AvABRwNKAkoCRwNMA7kBSwJMA0wDSwJKAksCuQFaAloCuQG7AU0DWwK7AbsBWwJaAmwCwAFtAm0CwAG+AasBIgLCAcIBIgJlAnYCxAF1AnUCxAHDAXUCwwEYAhgCwwGmAXsCMALGAcYBMAK0AWwCewLAAcABewLGAX0CTgN/An8CTgNPA1ADgAJPA08DgAJ/AoACUAOHAocCUANRA0oDOAJRA1EDOAKHAosCzAGIAogCzAHLAYgCywF2AnYCywHEASkCrwFSA1IDrwHOAdABUwPOAc4BUwNSA58C8gHRAdEB8gGdASoCKAKUApQCKAJUAygCKQJUA1QDKQJSA1MDVQNSA1IDVQNUA1UDlQJUA1QDlQKUAlMD0AFWA1YD0AHTAVcDVgPVAdUBVgPTAZUCVQNYA1gDVQNZA1UDUwNZA1kDUwNWA1cDWgNWA1YDWgNZA1sDWANaA1oDWANZA1wDjwJdA10DjwKWApYClQJdA10DlQJYA1sDXgNYA1gDXgNdA14DXwNdA10DXwNcA5ACjwJgA2ADjwJcA18DYQNcA1wDYQNgA2EDYgNgA2ADYgNjA5ECkAJjA2MDkAJgA2IDZANjA2MDZANlA4oCkQJlA2UDkQJjA4sCigJmA2YDigJlA2QDZwNlA2UDZwNmA8wBiwLXAdcBiwJmA2cD1gFmA2YD1gHXAWgDaQNqA2oDaQNrA2wDbQNoA2gDbQNpA24DbwNsA2wDbwNtA3ADcQNuA24DcQNvA3IDcwNwA3ADcwNxA3QDdQNyA3IDdQNzA3YDdwN0A3QDdwN1A3gDeQN2A3YDeQN3A3gDegN5A3kDegN7A3oDfAN7A3sDfAN9A34DfwN8A3wDfwN9A4ADgQN+A34DgQN/A4IDgwOEA4QDgwOFA2kDhgNrA2sDhgOHA20DiANpA2kDiAOGA28DiQNtA20DiQOIA3EDigNvA28DigOJA3MDiwNxA3EDiwOKA3UDjANzA3MDjAOLA3cDjQN1A3UDjQOMA3kDjgN3A3cDjgONA3kDewOOA44DewOPA3sDfQOPA48DfQOQA38DkQN9A30DkQOQA5IDgQOTA5MDgQOFA4YDlAOHA4cDlAOVA4gDlgOGA4YDlgOUA4kDlwOIA4gDlwOWA4oDmAOJA4kDmAOXA4sDmQOKA4oDmQOYA4wDmgOLA4sDmgOZA40DmwOMA4wDmwOaA44DnAONA40DnAObA44DjwOcA5wDjwOdA5ADngOPA48DngOdA5EDnwOQA5ADnwOeA6ADkgOhA6EDkgOTA5UDlAOiA6IDlAOjA5YDpAOUA5QDpAOjA5cDpQOWA5YDpQOkA5gDpgOXA5cDpgOlA5kDpwOYA5gDpwOmA5oDqAOZA5kDqAOnA5sDqQOaA5oDqQOoA5wDqgObA5sDqgOpA5wDnQOqA6oDnQOrA54DrAOdA50DrAOrA58DrQOeA54DrQOsA64DoAOvA68DoAOhA6IDowOwA7ADowOxA6QDsgOjA6MDsgOxA6UDswOkA6QDswOyA6YDtAOlA6UDtAOzA6cDtQOmA6YDtQO0A6gDtgOnA6cDtgO1A6kDtwOoA6gDtwO2A6oDuAOpA6kDuAO3A6oDqwO4A7gDqwO5A6wDugOrA6sDugO5A60DuwOsA6wDuwO6A7wDrgO9A70DrgOvA7ADsQO+A74DsQO/A7IDwAOxA7EDwAO/A7MDwQOyA7IDwQPAA7QDwgOzA7MDwgPBA7UDwwO0A7QDwwPCA7YDxAO1A7UDxAPDA7cDxQO2A7YDxQPEA7gDxgO3A7cDxgPFA7gDuQPGA8YDuQPHA7kDugPHA8cDugPIA7sDyQO6A7oDyQPIA4cBvAOGAYYBvAO9A74DvwPKA8oDvwPLA8ADzAO/A78DzAPLA8EDzQPAA8ADzQPMA8IDzgPBA8EDzgPNA8MDzwPCA8IDzwPOA8QD0APDA8MD0APPA8UD0QPEA8QD0QPQA8YD0gPFA8UD0gPRA8YDxwPSA9IDxwPTA8cDyAPTA9MDyAPUA8kD1QPIA8gD1QPUA8oDywPWA9YDywPXA8wD2APLA8sD2APXA80D2QPMA8wD2QPYA84D2gPNA80D2gPZA88D2wPOA84D2wPaA9AD3APPA88D3APbA9ED3QPQA9AD3QPcA9ID3gPRA9ED3gPdA9ID0wPeA94D0wPfA9MD1APfA98D1APgA9UD4QPUA9QD4QPgA9YD1wPiA+ID1wPjA9gD5APXA9cD5APjA9kD5QPYA9gD5QPkA9oD5gPZA9kD5gPlA9sD5wPaA9oD5wPmA9wD6APbA9sD6APnA90D6QPcA9wD6QPoA94D6gPdA90D6gPpA94D3wPqA+oD3wPrA98D4APrA+sD4APsA+ED7QPgA+AD7QPsA+ID4wPuA+4D4wPvA+QD8APjA+MD8APvA+UD8QPkA+QD8QPwA+YD8gPlA+UD8gPxA+cD8wPmA+YD8wPyA+gD9APnA+cD9APzA+kD9QPoA+gD9QP0A+kD6gP1A/UD6gP2A+oD6wP2A/YD6wP3A+sD7AP3A/cD7AP4A+wD7QP4A/gD7QP5A+4D7wP6A/oD7wP7A/AD/APvA+8D/AP7A/ED/QPwA/AD/QP8A/ID/gPxA/ED/gP9A/MD/wPyA/ID/wP+A/QDAATzA/MDAAT/A/UDAQT0A/QDAQQABPUD9gMBBAEE9gMCBPYD9wMCBAIE9wMDBPgDBAT3A/cDBAQDBPgD+QMEBAQE+QMFBPoD+wMGBAYE+wMHBPsD/AMHBAcE/AMIBPwD/QMIBAgE/QMJBP0D/gMJBAkE/gMKBP8DCwT+A/4DCwQKBAAEDAT/A/8DDAQLBAEEDQQABAAEDQQMBAEEAgQNBA0EAgQOBAMEDwQCBAIEDwQOBAQEEAQDBAMEEAQPBAUEEQQEBAQEEQQQBAYEBwQSBBIEBwQTBAcECAQTBBMECAQUBAgECQQUBBQECQQVBAwEDQQWBA4EFwQNBA0EFwQWBA8EGAQOBA4EGAQXBBAEGQQPBA8EGQQYBBEEGgQQBBAEGgQZBBIEEwQbBBsEEwQcBBMEFAQcBBwEFAQdBBsEHAQeBB4EHAQfBB0EIAQcBBwEIAQfBCEEIgQYBBgEIgQXBBkEIwQYBBgEIwQhBBoEJAQZBBkEJAQjBB4EHwQlBCUEHwQmBCAEJwQfBB8EJwQmBCgEIgQpBCkEIgQhBCEEIwQpBCkEIwQqBCQEKwQjBCMEKwQqBCUEJgQsBCwEJgQtBCYEJwQtBC0EJwQuBC8EMAQpBCkEMAQoBCkEKgQvBC8EKgQxBCsEMgQqBCoEMgQxBCwELQQzBDMELQQ0BC0ELgQ0BDQELgQ1BDAELwQ2BDYELwQ3BC8EMQQ3BDcEMQQ4BDEEMgQ4BDgEMgQ5BDIEOgQ5BDkEOgQ7BDwEOgQ9BD0EOgQ+BDMENAQ/BD8ENARABDQENQRABEAENQRBBEIEQwREBEQEQwRFBGgDagNGBGwDaANGBG4DbANGBHADbgNGBHIDcANGBHQDcgNGBHYDdANGBHgDdgNGBHoDeANGBHwDegNGBH4DfANGBH4DRgSAA4IDhANGBEcESARJBEkESARKBEkESwRHBEcESwRMBE0ETgRJBEkETgRLBEkESgRNBE0ESgRPBFAEUQRSBFIEUQRTBFIEVARQBFAEVARVBEcETARSBFIETARUBFIEUwRHBEcEUwRIBFYEVwRYBFgEVwRZBFgEWgRWBFYEWgRbBFAEVQRYBFgEVQRaBFgEWQRQBFAEWQRRBFwEXQReBF4EXQRfBF8EYAReBF4EYARhBFYEWwRfBF8EWwRgBF8EXQRWBFYEXQRXBGIEYwRkBGQEYwRlBGEEYgReBF4EYgRkBGQEZQRmBGYEZQRnBGgEaQRqBGoEaQRrBGwEbQRrBGsEbQRqBGMEbARlBGUEbARrBGkEZwRrBGsEZwRlBG4EbwRwBHAEbwRxBHIEcwRxBHEEcwRwBG0EcgRqBGoEcgRxBG8EaARxBHEEaARqBHQEdQR2BHYEdQR3BHgEeQR3BHcEeQR2BHMEeARwBHAEeAR3BHUEbgR3BHcEbgRwBHoEewR8BHwEewR9BH4EfwR9BH0EfwR8BHkEfgR2BHYEfgR9BHsEdAR9BH0EdAR2BE0ETwSABIAETwSBBIAEggRNBE0EggROBHwEfwSABIAEfwSCBIAEgQR8BHwEgQR6BHUEdARKBEoEdARPBIMEhASFBIUEhASGBIcEiASJBIkEiASGBIoEiwSMBIwEiwSNBI4EjwSQBJAEjwSNBJEEkgSQBJAEkgSTBJQElQSFBIUElQSTBJYElwSYBJgElwSJBJkEmgSbBJsEmgSXBJwEnQSbBJsEnQSeBJ8EngSgBKAEngShBKIEowSkBKQEowSlBKYEowSnBKcEowShBKgEqQSqBKoEqQSlBKsEqQSsBKwEqQStBK4ErwSwBLAErwStBLEErwSyBLIErwSzBLQEtQS2BLYEtQSzBLcEtQS4BLgEtQS5BLoEuwS8BLwEuwS5BL0EuwS+BL4EuwSMBFoEvwRbBFsEvwTABMEEWgTCBMIEWgRVBEsEwwRMBEwEwwTEBMUESwTGBMYESwROBFQExwRVBFUExwTIBMkEVATKBMoEVARMBGAEywRhBGEEywTMBM0EYATOBM4EYARbBIIEzwROBE4EzwTQBNEEggTSBNIEggR/BGMEYgTTBNMEYgTUBGIEYQTVBNUEYQTWBH8EfgTXBNcEfgTYBH4EeQTZBNkEeQTaBG0EbATbBNsEbATcBGwEYwTdBN0EYwTeBHkEeATfBN8EeATgBHgEcwThBOEEcwTiBHMEcgTjBOMEcgTkBHIEbQTlBOUEbQTmBOcEmgToBOgEmgSZBJoE6QSHBIcE6QTqBOsElQTsBOwElQSUBI4ElQTtBO0ElQTuBIcE7wSIBIgE7wTwBPEElATyBPIElASIBPMEnwT0BPQEnwSgBPUEmQT2BPYEmQSfBPcE+ASOBI4E+ASPBL4EjwT5BPkEjwT6BPsEogT8BPwEogSkBP0EoAT+BP4EoASiBP8EAAW+BL4EAAW9BLgEvQQBBQEFvQQCBQMFqwQEBQQFqwSsBAUFpAQGBQYFpASrBAcFCAW4BLgECAW3BLIEtwQJBQkFtwQKBbIECwWxBLEECwUMBQ0FrAQOBQ4FrASxBKYEDwWqBKoEDwUQBaYEpwQPBQ8FpwQRBagEEgWwBLAEEgUTBRIFqAQQBRAFqASqBJgEhAQUBRQFhAQVBYQEgwQVBRUFgwQWBbQEFwW8BLwEFwUYBRcFtAQZBRkFtAS2BJEEiwQaBRoFiwQbBRsFiwQcBRwFiwSKBIMEkgQWBRYFkgQdBZIEkQQdBR0FkQQaBZwElgQeBR4FlgQfBZYEmAQfBR8FmAQUBacEnQQRBREFnQQgBZ0EnAQgBSAFnAQeBa4EIQW2BLYEIQUZBSEFrgQTBRMFrgSwBLoEIgWKBIoEIgUcBSIFugQYBRgFugS8BCMFJAUPBQ8FJAUQBSUFIwURBREFIwUPBRIFJgUTBRMFJgUnBSYFEgUkBSQFEgUQBSgFKQUVBRUFKQUUBSoFKAUWBRYFKAUVBRcFKwUYBRgFKwUsBSsFFwUtBS0FFwUZBRsFLgUaBRoFLgUvBS4FGwUwBTAFGwUcBTEFKgUdBR0FKgUWBS8FMQUaBRoFMQUdBTIFMwUfBR8FMwUeBSkFMgUUBRQFMgUfBTQFJQUgBSAFJQURBTMFNAUeBR4FNAUgBSEFNQUZBRkFNQUtBTUFIQUnBScFIQUTBSIFNgUcBRwFNgUwBTYFIgUsBSwFIgUYBXsEegR0BHQEegRPBHoEgQRPBEgEUwRuBG4EUwRvBEoESAR1BHUESARuBFMEUQRvBG8EUQRoBF0EXARXBGcEVwRmBFcEXARmBFkEaQRRBFEEaQRoBGYEXARkBGQEXAReBFcEZwRZBFkEZwRpBCQFIwU3BTcFIwU4BSMFJQU4BTgFJQU2BCYFOQUnBScFOQU6BSgFFgQpBSkFFgQXBCgFKgUWBBYEKgUMBCsFLgQsBSwFLgQnBC0FNQQrBSsFNQQuBC8FLgUKBAoELgUVBC4FMAUVBBUEMAUdBDEFCwQqBSoFCwQMBDEFLwULBAsELwUKBDMFMgUoBCgEMgUiBDIFKQUiBCIEKQUXBDQFMwUwBDAEMwUoBC0FNQU1BDUENQVBBCcFOgU1BTUFOgVBBDAFNgUdBB0ENgUgBDYFLAUgBCAELAUnBCQFNwUmBSYFNwU5BR0EFAQVBBUECQQKBDQFMAQlBSUFMAQ2BDsFPAU9BTwFPgU/BT8FPgU9BUAFQQVCBUIFQQVDBUEFQAVEBUQFQAVFBUYFRwVABUAFRwVFBUgFSQVKBUoFSQVGBUYFQAVKBUoFQAVCBUsFTAVNBU0FTAVOBU8FTgVQBVAFTgVMBVEFUgVTBVMFUgVUBVUFVAVWBVYFVAVXBUwFWAVZBVkFWAVaBVgFWwVcBVwFWwVdBVsFWAVeBV4FWAVfBVgFTAVfBV8FTAVLBWAFYQViBWIFYQVjBU0FYwVLBUsFYwVhBWQFZQVmBWYFZQVnBWUFaAVnBWcFaAVpBWgFZQVqBWoFZQVrBWsFZQVkBWwFbQVTBVMFbQVuBW0FbAVvBW8FbAVwBWwFcQVwBXAFcQVyBXEFcwVyBXIFcwV0BXMFcQVVBVUFcQVUBXEFbAVUBVQFbAVTBXUFdgV3BXcFdgV4BXgFdgVgBWAFdgVhBXYFXwVhBWEFXwVLBXYFdQVfBV8FdQVeBWkFaAV5BXkFaAV6BXoFaAV7BXwFfQV+BX4FfQV/BX0FfAWABYAFfAWBBYIFgwWEBYQFgwWFBYUFgwWGBYYFgwWHBYMFfQWHBYcFfQWABYMFggV9BX0FggV/BYgFiQWKBYoFiQWLBYkFjAWLBYsFjAWNBY4FjwWQBZAFjwWRBY4FPAWPBY8FPAU7BZIFkwWQBZAFkwWOBY4FlAU8BTwFlAU+BZQFjgWVBZUFjgWTBZYFlwWYBZgFlwWZBZcFQQWZBZkFQQVEBUEFlwVDBUMFlwWaBZcFlgWaBZoFlgWbBWIFnAVgBWAFnAWdBZ4FnQWfBZ8FnQWcBaAFoQWiBaIFoQWjBaEFpAWjBaMFpAWlBaQFoQWRBZEFoQWQBaYFoAWnBacFoAWiBZMFkgWoBakFqAWSBakFpgV5BXkFpgWqBaYFpwWqBaoFpwWrBawFpAWtBa0FpAWRBaQFrAWlBaUFrAWuBawFrwWuBa4FrwWwBa8FrAWxBbEFrAWtBbIFswW0BbQFswW1BbYFtQW3BbcFtQWzBbgFuQW6BboFuQW7BbkFvAW7BbsFvAW9BbwFuQW+Bb4FuQW/BbkFuAW/Bb8FuAXABcEFwgXDBcMFwgXEBcIFxQXEBcQFxQWNBcIFuAXFBcUFuAW6BbgFwgXABcAFwgXBBcYFxwXIBcgFxwXJBccFygXJBckFygXLBcoFxwW2BbYFxwW1BccFxgW1BbUFxgW0BcwFvAXNBc0FvAW+BbwFzAW9Bb0FzAXOBcwFzwXOBc4FzwXQBc8FzAXRBdEFzAXNBbcF0gW2BbYF0gXKBdMFywXSBdIFywXKBdQF1QXWBdYF1QXXBdUF1AXQBdAF1AXOBc4F1AW9Bb0F1AW7BdQF1gW7BbsF1gW6BdgF2QVwBXAF2QVvBdkF2AXaBdoF2AXbBdwF3QXeBd4F3QXfBZMF4AWVBZUF4AXhBeIF4wXkBeQF4wXhBdgF5QXbBdsF5QXmBeUF5wXmBeYF5wXoBecF5QV0BXQF5QVyBeUF2AVyBXIF2AVwBekFlgXjBeMFlgWYBZYF6QWbBZsF6QXqBekF6wXqBeoF6wXsBesF6QXiBeIF6QXjBe0F7gXbBdsF7gXaBd0F7wXfBd8F7wXwBfEF4gXyBfIF4gXkBfMF9AXoBegF9AXmBfQF7QXmBeYF7QXbBesF9QXsBewF9QX2BfUF6wXxBfEF6wXiBe8FfAXwBfAFfAV+BYYFhwXzBfMFhwX0BYcFgAX0BfQFgAXtBfcF+AX5BfkF+AX6BfgF9wWeBZ4F9wWdBfcFeAWdBZ0FeAVgBXgF9wV3BXcF9wX5BfsF/AWwBbAF/AWuBfwF/QWuBa4F/QWlBf0F/AW+Bb4F/AXNBfwF+wXNBc0F+wXRBf4F/wXDBcMF/wUABv8FAQYABgAGAQarBZ8FAgaeBZ4FAgYDBrQFAwayBbIFAwYCBgQGBQbABcAFBQa/BQUG/QW/Bb8F/QW+Bf0FBQalBaUFBQajBQUGBAajBaMFBAaiBdMF0gWIBYgF0gWJBdIFtwWJBYkFtwWMBbcFswWMBYwFswUGBrMFsgUGBgYGsgX+BZ8FnAUBBgEGnAUHBpwFYgUHBgcGYgVpBWIFYwVpBWkFYwVnBWMFTQVnBWcFTQVmBQgGZAUJBgkGZAVmBQoGagVrBdwF2QXdBd0F2QXaBe4F7wXaBdoF7wXdBYEFfAXuBe4FfAXvBQsGWwUMBgwGWwUNBg0GWwUOBg4GWwVeBXUFDwZeBV4FDwYOBg8GdQUQBhAGdQV3BfkFEQZ3BXcFEQYQBvkF+gURBhEG+gUSBvoFEwYSBhIGEwYUBhMGyAUUBhQGyAUVBsgFyQUVBhUGyQUWBskFywUWBhYGywUXBssF0wUXBhcG0wUYBtMFiAUYBhgGiAUZBhoGGwaKBYoFGwYcBhsGGgYdBh0GGgbXBR4G1QUfBh8G1QXQBdUFHgbXBdcFHgYdBs8FIAbQBdAFIAYfBiAGzwUhBiEGzwXRBSIG+wUjBiMG+wWwBfsFIgbRBdEFIgYhBq8FJAawBbAFJAYjBiQGrwUlBiUGrwWxBUkFSAWxBbEFSAUlBoIFJgZ/BX8FJgYnBiYG9QUnBicG9QXxBfUFJgb2BfYFJgYoBiYGggUoBigGggWEBRkGiAUcBhwGiAWKBSkGqAXeBd4FqAUqBqgFKQaTBZMFKQbgBSkGKwbgBeAFKwYsBisGKQbfBd8FKQbeBQEGBwarBasFBwaqBaoFBwZ5BXkFBwZpBS0GKwbwBfAFKwbfBSsGLQYsBiwGLQYuBgQGLwaiBaIFLwanBacFLwarBasFLwYABi8GwQUABgAGwQXDBS8GBAbBBcEFBAbABTAGLQZ+BX4FLQbwBS0GMAYuBi4GMAbyBYwFBgaNBY0FBgbEBQYG/gXEBcQF/gXDBScGMAZ/BX8FMAZ+BRoGMQbXBdcFMQbWBTEGxQXWBdYFxQW6BcUFMQaNBY0FMQaLBTEGGgaLBYsFGgaKBYAFgQXtBe0FgQXuBU0FTgVmBWYFTgUyBnkFegWpBXoFKgapBagFqQUqBjMGSQWtBa0FSQWxBUkFMwZGBUYFMwZHBY8FMwaRBZEFMwatBfgFNAb6BfoFNAYTBjQGxgUTBhMGxgXIBcYFNAa0BbQFNAYDBjQG+AUDBgMG+AWeBbIFAgb+Bf4FAgb/BQIGnwX/Bf8FnwUBBpIFoAWpBakFoAWmBaEFoAWQBZAFoAWSBSwGLgbkBeQFLgbyBeAFLAbhBeEFLAbkBfIFMAbxBfEFMAYnBuMFmAXhBeEFmAWVBZkFlAWYBZgFlAWVBZQFmQU+BT4FmQVEBUQFRQU+BT4FRQU9BUcFOwVFBUUFOwU9BTMGjwVHBUcFjwU7BTUGNgY3BjcGNgY4BjkGNgY5BTkFNgZFBDkFRQQ6BToFRQRDBEAEQQRDBEMEQQQ6BT8EQARCBEIEQARDBDoGOwY8BjwGOwY9Bj4GOwY2BDYEOwY4BT8GQAZBBkEGQAZCBkMGRAZFBkUGRAY7BDoGOQY3BTcFOQY5BT8GPgY3BDcEPgY2BEYGRwZkBWQFRwZrBVMFbgVIBkgGbgVJBkoGQAY5BDkEQAY4BG4FawVLBksGawVMBjYGNQZFBEUENQZEBEQGQwZNBk0GQwZOBk8GNgZQBlAGNgY5BlEGOQZSBlIGOQY6BjgFOwY3BTcFOwY6BjsGPgZTBlMGPgZUBj4GPwZVBlUGPwZWBkAGPwY4BDgEPwY3BEAGSgZXBlcGSgZYBkoGRAZZBlkGRAZaBlsG3AUqBioG3AXeBdkF3AVvBW8F3AVbBm0FbwVcBlwGbwVbBlsGKgZ7BXsFKgZ6BVwGWwYKBgoGWwZ7BVwGCgZuBW4FCgZrBW4FbQVcBgoGewVqBWoFewVoBTkEOwRKBkoGOwREBoEDkgN/A38DkgORA5IDoAORA5EDoAOfA6ADrgOfA58DrgOtA64DvAOtA60DvAO7A7sDvAPJA8kDvAOHAYcBigHJA8kDigHVAysEPgQyBDIEPgQ6BGsDXQZqA2oDXQZeBl0GXwZeBl4GXwZgBl8GYQZgBmAGYQZiBmEGYwZiBmIGYwZkBmMGZQZkBmQGZQZmBmUGZwZmBmYGZwZoBmcGaQZoBmgGaQZqBmkGawZqBmoGawZsBmwGawZtBm0GawZuBm0GbgZvBm8GbgZwBnAGcQZvBm8GcQZyBnEGcwZyBnIGcwZ0BnMGdQZ0BnQGdQZ2BocDdwZrA2sDdwZdBncGeAZdBl0GeAZfBngGeQZfBl8GeQZhBnkGegZhBmEGegZjBnoGewZjBmMGewZlBnsGfAZlBmUGfAZnBnwGfQZnBmcGfQZpBn0GfgZpBmkGfgZrBmsGfgZuBm4GfgZ/Bm4GfwZwBnAGfwaABoAGgQZwBnAGgQZxBoIGgwZzBnMGgwZ1BpUDhAaHA4cDhAZ3BoQGhQZ3BncGhQZ4BoUGhgZ4BngGhgZ5BoYGhwZ5BnkGhwZ6BocGiAZ6BnoGiAZ7BogGiQZ7BnsGiQZ8BokGigZ8BnwGigZ9BooGiwZ9Bn0GiwZ+Bn4GiwZ/Bn8GiwaMBn8GjAaABoAGjAaNBoAGjQaBBoEGjQaOBo8GkAaCBoIGkAaDBpUDogOEBoQGogORBpEGkgaEBoQGkgaFBpIGkwaFBoUGkwaGBpMGlAaGBoYGlAaHBpQGlQaHBocGlQaIBpUGlgaIBogGlgaJBpYGlwaJBokGlwaKBpcGmAaKBooGmAaLBosGmAaMBowGmAaZBpkGmgaMBowGmgaNBo0GmgaOBo4GmgabBpwGnQaPBo8GnQaQBqIDsAORBpEGsAOeBp4GnwaRBpEGnwaSBp8GoAaSBpIGoAaTBqAGoQaTBpMGoQaUBqEGogaUBpQGogaVBqIGowaVBpUGowaWBqMGpAaWBpYGpAaXBqQGpQaXBpcGpQaYBpgGpQaZBpkGpQamBqYGpwaZBpkGpwaaBqcGqAaaBpoGqAabBqkGqgacBpwGqgadBrADvgOeBp4GvgOrBqsGrAaeBp4GrAafBqwGrQafBp8GrQagBq0GrgagBqAGrgahBq4GrwahBqEGrwaiBq8GsAaiBqIGsAajBrAGsQajBqMGsQakBrEGsgakBqQGsgalBqUGsgamBqYGsgazBrMGtAamBqYGtAanBrQGtQanBqcGtQaoBrYGqgbUAdQBqgbVAb4DygOrBqsGygO3BrcGuAarBqsGuAasBrgGuQasBqwGuQatBrkGugatBq0GugauBroGuwauBq4GuwavBrsGvAavBq8GvAawBrwGvQawBrAGvQaxBr0GvgaxBrEGvgayBrIGvgazBrMGvga/BrMGvwa0BrQGvwbABsAGwQa0BrQGwQa1BsoD1gO3BrcG1gPCBsIGwwa3BrcGwwa4BsMGxAa4BrgGxAa5BsQGxQa5BrkGxQa6BsUGxga6BroGxga7BsYGxwa7BrsGxwa8BscGyAa8BrwGyAa9BsgGyQa9Br0GyQa+Br4GyQa/Br8GyQbKBsoGywa/Br8GywbABssGzAbABsAGzAbBBtYD4gPCBsIG4gPNBs0GzgbCBsIGzgbDBs4GzwbDBsMGzwbEBs8G0AbEBsQG0AbFBtAG0QbFBsUG0QbGBtEG0gbGBsYG0gbHBtIG0wbHBscG0wbIBtMG1AbIBsgG1AbJBskG1AbKBsoG1AbVBtUG1gbKBsoG1gbLBtYG1wbLBssG1wbMBuID7gPNBs0G7gPYBtgG2QbNBs0G2QbOBtkG2gbOBs4G2gbPBtoG2wbPBs8G2wbQBtsG3AbQBtAG3AbRBtwG3QbRBtEG3QbSBt0G3gbSBtIG3gbTBt4G3wbTBtMG3wbUBtQG3wbVBtUG3wbgBtUG4AbWBtYG4AbhBtYG4QbXBtcG4QbiBu4D+gPYBtgG+gPjBuMG5AbYBtgG5AbZBuQG5QbZBtkG5QbaBuUG5gbaBtoG5gbbBuYG5wbbBtsG5wbcBucG6AbcBtwG6AbdBugG6QbdBt0G6QbeBt4G6QbfBt8G6QbqBt8G6gbgBuAG6gbrBuAG6wbhBuEG6wbsBuEG7AbiBuIG7AbtBvoDBgTjBuMGBgTuBuMG7gbkBuQG7gbvBuQG7wblBuUG7wbwBuUG8AbmBuYG8AbxBvEG8gbmBuYG8gbnBvIG8wbnBucG8wboBvMG9AboBugG9AbpBukG9AbqBuoG9Ab1BuoG9QbrBusG9Qb2BusG9gbsBuwG9gb3BuwG9wbtBu0G9wb4BgYEEgTuBu4GEgT5Bu4G+QbvBu8G+Qb6Bu8G+gbwBvAG+gb7BvMG/Ab0BvwG/Qb0BvQG/Qb1Bv0G/gb1BvUG/gb2Bv4G/wb2BvYG/wb3BvcG/wb4BvgG/wYABxIEGwT5BvkGGwQBB/kGAQf6BvoGAQcCBxsEHgQBBwEHHgQDBwMHBAcBBwEHBAcCBwUH/gYGBwYH/gb9BgUHBwf+Bv4GBwf/Bv8GBwcABwAHBwcIBx4EJQQDBwMHJQQJBwkHCgcDBwMHCgcEBwUHBgcLBwsHBgcMBwUHCwcHBwcHCwcNBwcHDQcIBwgHDQcOByUELAQJBwkHLAQPBwkHDwcKBwoHDwcQBxEHCwcSBxIHCwcMBwsHEQcNBw0HEQcTBw0HEwcOBw4HEwcUBywEMwQPBw8HMwQVBw8HFQcQBxAHFQcWBxcHEQcYBxgHEQcSBxEHFwcTBxMHFwcZBxMHGQcUBxQHGQcaBxQHGgcbBxsHGgccBxsHPAQdBx0HPAQ9BDMEPwQVBxUHPwQeBxUHHgcWBxYHHgcfB0IERAQgByAHRAQhB14GRgRqA2AGRgReBmIGRgRgBmQGRgRiBmYGRgRkBmgGRgRmBmoGRgRoBmwGRgRqBm0GRgRsBm8GRgRtBnIGRgRvBnIGdAZGBHYGRgR0BiIHIwckByQHIwclByMHIgcmByYHIgcnBygHIwcpBykHIwcmByMHKAclByUHKAcqBysHLActBy0HLAcuBywHKwcvBy8HKwcwByIHLAcnBycHLAcvBywHIgcuBy4HIgckBzEHMgczBzMHMgc0BzIHMQc1BzUHMQc2BysHMgcwBzAHMgc1BzIHKwc0BzQHKwctBzcHOAc5BzkHOAc6BzcHOQc7BzsHOQc8BzEHNwc2BzYHNwc7BzcHMQc4BzgHMQczBz0HPgc/Bz8HPgdABz8HQAc5BzkHQAc8Bz8HQQc9Bz0HQQdCB0MHRAdFB0UHRAdGB0UHRwdDB0MHRwdIB0MHSAc9Bz0HSAc+Bz0HQgdDB0MHQgdEB0kHSgdLB0sHSgdMB0sHTQdJB0kHTQdOB0kHTgdFB0UHTgdHB0UHRgdJB0kHRgdKB08HUAdRB1EHUAdSB1EHUwdPB08HUwdUB08HVAdLB0sHVAdNB0sHTAdPB08HTAdQB1UHVgdXB1cHVgdYB1cHWQdVB1UHWQdaB1UHWgdRB1EHWgdTB1EHUgdVB1UHUgdWBygHWwcqByoHWwdcB1sHKAddB10HKAcpB1cHWwdZB1kHWwddB1sHVwdcB1wHVwdYByoHUgclByUHUgdQB14HXwdgB2AHXwdhB2IHYwdkB2QHYwdhB2UHZgdnB2cHZgdoB2kHagdrB2sHagdoB2wHagdtB20HagduB28HXwdwB3AHXwduB3EHcgdzB3MHcgdjB3QHdQd2B3YHdQdzB3cHdQd4B3gHdQd5B3oHewd5B3kHewd8B30Hfgd/B38HfgeAB4EHggd/B38Hggd8B4MHhAeFB4UHhAeAB4YHhweFB4UHhweIB4kHigeLB4sHigeIB4wHjQeLB4sHjQeOB48HkAeRB5EHkAeOB5IHkweRB5EHkweUB5UHlgeXB5cHlgeUB5gHmQeXB5cHmQdmB5oHmwc2BzYHmwc1B5wHnQc1BzUHnQcwB54HnwcnBycHnwcmB6AHoQcmByYHoQcpB6IHowcwBzAHowcvB6QHpQcvBy8HpQcnB6YHpwc8BzwHpwc7B6gHqQc7BzsHqQc2B6oHqwcpBykHqwddB6wHrQddB10HrQdZBz4HrgdAB0AHrgevB7AHPAexB7EHPAdAB1kHsgdaB1oHsgezB7QHUwe1B7UHUwdaB0cHtgdIB0gHtge3B7gHPge5B7kHPgdIB1MHugdUB1QHuge7B7wHTQe9B70HTQdUB00HvgdOB04Hvge/B8AHRwfBB8EHRwdOB8IHwwd2B3YHwwd0B3YHYgfEB8QHYgfFB8YHxwdwB3AHxwdvB8gHcAfJB8kHcAdpB8oHywdkB2QHywdiB2QHbwfMB8wHbwfNB84Hzwd6B3oHzwd7B3oHdAfQB9AHdAfRB9IHaQfTB9MHaQdrB9QHawfVB9UHaweZB9YH1wd9B30H1wd+B30HewfYB9gHewfZB9oHmQfbB9sHmQeYB9wHmAfdB90HmAeTB94H3weGB4YH3weHB4YHfgfgB+AHfgfhB+IHkwfjB+MHkweSB+QHkgflB+UHkgeNB+YH5weMB4wH5weNB4wHhwfoB+gHhwfpB+oH6weEB4QH6weBB+wHggfrB+sHggeBB+0H7geKB4oH7geDB+4H6geDB4MH6geEB3IH7wdgB2AH7wfwB/EHXgfwB/AHXgdgB/IH8weWB5YH8wePB/MH9AePB48H9AeQB2wH9QdnB2cH9Qf2B/YH9wdnB2cH9wdlB14H8QdtB20H8Qf4B/UHbAf4B/gHbAdtB3cH+QdxB3EH+Qf6B+8Hcgf6B/oHcgdxB4IH7Ad4B3gH7Af7B/kHdwf7B/sHdwd4B/QH/AeQB5AH/AeJB/wH7QeJB4kH7QeKB/cH/QdlB2UH/QeVB/0H8geVB5UH8geWB+oH/gfrB+sH/gf/BwAI7Af/B/8H7AfrB+4H7QcBCAEI7QcCCOoH7gf+B/4H7gcBCO8HAwjwB/AHAwgECAUI8QcECAQI8QfwB/MH8gcGCAYI8gcHCPQH8wcICAgI8wcGCPYH9QcJCAkI9QcKCPcH9gcLCAsI9gcJCPEHBQj4B/gHBQgMCAoI9QcMCAwI9Qf4B/kHDQj6B/oHDQgOCAMI7wcOCA4I7wf6B+wHAAj7B/sHAAgPCA0I+QcPCA8I+Qf7B/wH9AcQCBAI9AcICO0H/AcCCAII/AcQCP0H9wcRCBEI9wcLCPIH/QcHCAcI/QcRCFYHUgdYB1IHKgdYByoHXAdYByQHTAcuBy4HTAdKByUHUAckByQHUAdMBy4HSgctBy0HSgdGBzgHMwc6B0IHQQczBzMHQQc6B0YHRActBy0HRAc0BzkHOgc/Bz8HOgdBBzMHNAdCB0IHNAdEB/4HEgj/B/8HEggTCP8HEwgACAAIEwgYBxQIFQgCCAIIFQgBCP0G/AYDCAMI/AYECAQI/AYFCAUI/AbzBgoHEAcHCAcIEAcGCBAHFgcGCAYIFgcICAoI8QYJCAkI8Qb7BgkI+wYLCAsI+wYCB/MG8gYFCAUI8gYMCAwI8gYKCAoI8gbxBg0IDAcOCA4IDAcGBw4IBgcDCAMIBgf9Bg8IEgcNCA0IEgcMBwgIFgcQCBAIFgcfBx8HFAgQCBAIFAgCCAsIAgcRCBEIAgcEBxEIBAcHCAcIBAcKBxUIEggBCAEIEgj+BwIH+wb6BvsG8QbwBg8IAAgSBxIHAAgYBxYIFwgYCBcIGQgYCBgIGQgWCBoIQgUbCBsIQgVDBRwIGggdCB0IGggbCBoIHAgeCB4IHAgfCEoFHghIBUgFHgggCEIFGghKBUoFGggeCCEIIggjCCMIIggkCCIIIQglCCUIIQgmCCcIKAgpCCkIKAgqCFUFKwgnCCcIKwgsCCIILQguCC4ILQgvCDAIMQgyCDIIMQguCDEIMwguCC4IMwg0CCQIIgg0CDQIIgguCDUINgg3CDcINgg4CCMIJAg1CDUIJAg2CDkIOgg7CDsIOgg8CD0IPgg8CDwIPgg7CD4IPwg7CD8IQAg7CDsIQAg5CEEIKQhCCEIIKQhDCEQIQQhFCEUIQQhCCEEIRAhGCEYIRAhHCHQFcwVHCEcIcwVGCHMFVQVGCEYIVQUnCCkIQQgnCCcIQQhGCEgISQhKCEoISQhLCEsIOAhKCEoIOAg2CCQINAg2CDYINAhKCDMISAg0CDQISAhKCD0ITAg+CD4ITAhNCE0ITgg+CE8IUAhRCFEIUAhSCFAIUwhSCFIIUwhUCFUIhAVWCFYIhAWFBYUFhgVWCFYIhgVXCFMIUAhXCFcIUAhWCE8IVQhQCFAIVQhWCFgIWQhaCFoIWQhbCFwIXQhbCFsIXQhaCF4IXwhgCGAIXwhhCGIIFghfCF8IFghhCGEIYwhgCGAIYwhkCBkIZQgWCBYIZQhhCGUIZghhCGEIZghjCGcIaAhpCGkIaAhqCGgIHQhqCGoIHQgbCBsIQwVqCGoIQwWaBZsFaQiaBZoFaQhqCDcIOAhrCGsIOAhsCGsIbAhtCG0IbAhuCG8IcAhxCHEIcAhyCHMIdAhyCHIIdAhxCHQIXghxCHEIXghgCHAIbwh1CHUIbwh2CGMIdwhkCHgIZAh3CHgITAh2CHYITAh5CHoIdQh5CHkIdQh2CF4IdAh7CHsIdAh8CHQIcwh8CHwIcwh9CH4Ifwh9CH0Ifwh8CH8IgAh8CHwIgAh7CIEIggiDCIMIggiECIIIgQiFCIUIgQiGCIcIiAiJCIkIiAiKCIsIjAiKCIoIjAiJCIwIjQiJCIkIjQiOCI8IhwiOCI4IhwiJCJAIkQiSCJIIkQiTCFwIlAiTCJMIlAiSCIgIhwiUCJQIhwiSCIcIjwiSCJIIjwiQCJUIlgiXCJcIlgiYCJkImgiYCJgImgiXCJoIhgiXCJcIhgiBCIMIlQiBCIEIlQiXCI0IjAibCJsIjAicCIwIiwicCJwIiwidCJ4InwidCJ0InwicCJ8IoAicCJwIoAibCIUIhgihCKEIhgiaCKIIoQiZCJkIoQiaCKMIpAilCKUIpAimCKQIngimCKYIngidCJ0IiwimCKYIiwiKCIgIpQiKCIoIpQimCKcIRAioCKgIRAhFCKkIpwiqCKoIpwioCKsIrAitCK0IrAiuCGMIZgivCK8IZgiwCLAIsQiyCLIIsQizCKcIqQi0CLQIqQi1COgF5wW1CLUI5wW0COcFdAW0CLQIdAVHCEQIpwhHCEcIpwi0CGcIaQixCLEIaQi2CGkImwW2CLYImwXqBewFtwjqBeoFtwi2CLcIswi2CLYIswixCLgIqQi5CLkIqQiqCKwIqwi6CLoIqwi7CLIIswi8CLwIswi9CPMF6AW+CL4I6AW1CKkIuAi1CLUIuAi+CLcI7AW/CL8I7AX2BbMItwi9CL0Itwi/CLoIuwhSCFIIuwhRCIYF8wVXCFcI8wW+CLgIUwi+CL4IUwhXCMAIwQjCCMIIwQjDCMEIbgjDCMMIbghsCDgISwhsCGwISwjDCEsISQjDCMMISQjCCMQIfgjFCMUIfgh9CHMIxgh9CH0IxgjFCMYIjQjFCMUIjQibCKAIxAibCJsIxAjFCMcIkQjICMgIkQjJCHoIygjJCMkIygjICG0IbgjLCMsIbgjMCIMIhAjMCMwIhAjLCM0IjwjOCM4IjwiOCI0IxgiOCI4IxgjOCMYIcwjOCM4IcwhyCHAIzQhyCHIIzQjOCKIIWAihCKEIWAhaCF0IhQhaCFoIhQihCIUIXQiCCIIIXQjPCMcIhAjPCM8IhAiCCG0IyghrCGsIygjQCD0INwjQCNAINwhrCDcIPQg1CDUIPQg8CDoIIwg8CDwIIwg1CDoIOQjRCNEIOQjSCNMIQAg/CK4IrAioCKgIrAiqCKwIugiqCKoIugi5CLoIUgi5CLkIUghUCNQIDQbVCNUIDQYxCA0GDgYxCDEIDgYzCA4GDwYzCDMIDwZICA8GEAZICEgIEAZJCBAGEQZJCEkIEQbCCBIGwAgRBhEGwAjCCMAIEgbWCNYIEgYUBhUGlggUBhQGlgjWCJYIFQaYCJgIFQYWBhcGmQgWBhYGmQiYCBgGoggXBhcGogiZCBkGWAgYBhgGWAiiCBwGGwZZCFkIGwbXCBsGHQbXCNcIHQajCB4GHwakCKQIHwaeCB0GHgajCKMIHgakCB8GIAaeCJ4IIAafCCAGIQafCJ8IIQagCCIGIwbECMQIIwZ+CCEGIgagCKAIIgbECCMGJAZ+CH4IJAZ/CCQGJQZ/CH8IJQaACCUGSAWACIAISAUgCFUITwjYCNgITwjZCL0IvwjZCNkIvwjYCL8I9gXYCNgI9gUoBoQFVQgoBigGVQjYCBkGHAZYCFgIHAZZCNoIdwitCK0IdwjbCHcIYwjbCNsIYwivCNwI3QivCK8I3QjbCN0IqwjbCNsIqwitCMoIegjQCNAIegh5CD0I0AhMCEwI0Ah5CKsI3Qi7CLsI3QjeCN0I3AjeCN4I3AjfCM0IcAjgCOAIcAh1CHUIegjgCOAIegjJCJEIkAjJCMkIkAjgCI8IzQiQCJAIzQjgCLsI3ghRCFEI3gjhCN8IvAjeCN4IvAjhCF0IXAjPCM8IXAiTCJEIxwiTCJMIxwjPCNkITwjhCOEITwhRCNcIowjiCOIIowilCIgIlAilCKUIlAjiCJQIXAjiCOIIXAhbCFkI1whbCFsI1wjiCFMIuAhUCFQIuAi5CCMIOgghCCEIOgjjCEwIeAhNCE0IeAjaCHcI2gh4CIAIIAh7CHsIIAjkCCAIHgjkCB4IHwjkCF8IXgjkCOQIXgh7CMEIwAjlCOUIwAjWCJYIlQjWCNYIlQjlCJUIgwjlCOUIgwjMCG4IwQjMCMwIwQjlCIQIxwjLCMsIxwjICMoIbQjICMgIbQjLCGQIeAhvCG8IeAh2CGQIbwhgCGAIbwhxCNwIsgjfCN8Isgi8CLII3AiwCLAI3AivCLwIvQjhCOEIvQjZCGYIZwiwCLAIZwixCGgIZwhlCGUIZwhmCGUIGQhoCGgIGQgdCBcIHAgZCBkIHAgdCB8IHAhiCGIIHAgXCOQIHwhfCF8IHwhiCOYI5wjoCOgI5wg1BiEH5wgVCBUI5wjpCBUIFAghByEHFAggBxQIHwcgByAHHwceBz8EQgQeBx4HQgQgB+oI6wjsCOwI6wjtCBgHEwjuCO4IEwjtCO8I8AjxCPEI8AjyCBwH8whFBkUG8whDBhUI6QgSCBII6QjsCBgH7ggXBxcH7gjyCDkIQAj0CPQIQAj1CCkI9ghDCEMI9gj3CBkH8AgaBxoH8Aj4CPkIQAj6CPoIQAhDCEQENQYhByEHNQbnCPsIQwb8CPwIQwbzCP0I/gjnCOcI/gjpCP8IAAnpCOkIAAnsCBMIEgjtCO0IEgjsCAEJ7ggCCQIJ7gjtCAMJ8ggECQQJ8gjuCBcH8ggZBxkH8gjwCAUJ+AgGCQYJ+AjwCAcJ8wgICQgJ8wj4CAkJ2giuCK4I2gitCAkJrghFCEUIrgioCAkJRQgKCQoJRQhCCE0I2ghOCE4I2ggJCU4ICQnTCNMICQkKCUAI0whDCEMI0wgKCUMICglCCD4ITgg/CD8ITgjTCBoH+AgcBxwH+AjzCIEGggZxBnEGggZzBo4GjwaBBoEGjwaCBpsGnAaOBo4GnAaPBqgGqQabBpsGqQacBqgGtQapBqkGtQZXA8EGWgO1BrUGWgNXAwgHHQdnAw4HFAcdBx0HFAcbB5IEgwSTBJMEgwSFBIQEmASGBIYEmASJBIgElASGBIYElASFBJUEjgSTBJMEjgSQBJoEhwSXBJcEhwSJBIsEkQSNBI0EkQSQBJYEnASXBJcEnASbBI8EvgSNBI0EvgSMBJ8EmQSeBJ4EmQSbBIoEjAS6BLoEjAS7BKcEoQSdBJ0EoQSeBLgEuQS9BL0EuQS7BKAEoQSiBKIEoQSjBLwEuQS0BLQEuQS1BKoEpQSmBKYEpQSjBLIEswS3BLcEswS1BKQEpQSrBKsEpQSpBLYEswSuBK4EswSvBLAErQSoBKgErQSpBKwErQSxBLEErQSvBGAHYQdyB3IHYQdjB20HbgdeB14HbgdfB2QHYQdvB28HYQdfB3YHcwdiB2IHcwdjB3AHbgdpB2kHbgdqB3EHcwd3B3cHcwd1B2cHaAdsB2wHaAdqB3oHeQd0B3QHeQd1B2sHaAeZB5kHaAdmB4IHeAd8B3wHeAd5B2UHlQdmB2YHlQeXB3sHfQd8B3wHfQd/B5MHmAeUB5QHmAeXB4QHgQeAB4AHgQd/B5YHjweUB5QHjweRB34HhgeAB4AHhgeFB40HkgeOB44HkgeRB4oHgweIB4gHgweFB5AHiQeOB44HiQeLB4cHjAeIB4gHjAeLB2MC3AJhAmEC3AILCSECwgIbAhsCwgLcAgsJ3AIQAxAD3ALCApoCmQJCA0IDmQJDA6QCQQOdAp0CQQNCA6ICoAJAA0ADoAJBA80CPgOlAqUCPgNAA80C9wI+Az4D9wI/A+4CPQMjAyMDPQM7A08COQNNAk0COQM6A6kCOQOqAqoCOQM4A2ACNwNSAlICNwM4A7MCNwO0ArQCNwM2A14C2QI2AzYD2QI1A7wCuwI0AzQDuwI1AxkDGwMyAzIDGwMzA+0G+AZhA2ED+AZiA+IG7QZfA18D7QZhA9cG4gZeA14D4gZfA8wG1wZbA1sD1wZeA2QDYgMABwAHYgP4BmcDZAMIBwgHZAMAB2cDHQfWAdYBHQc9BNYBPQSVAZUBPQQ+BFoDwQZbA1sDwQbMBgwJdgYNCQ0JdgZ1BlcD1QGpBqkG1QGqBg4JDQmDBoMGDQl1Bg8JDgmQBpAGDgmDBg8JkAYQCRAJkAadBrYGEAmqBqoGEAmdBrsBvAFNA00DvAERCXIAdAC4AbgBdAC6AREEkAEaBBoEkAGTAREEBQSQAZABBQSOAYEDgAOFA4UDgAOEA4UBhgESCRIJhgG9AxMJkwODA4MDkwOFAxQJoQMTCRMJoQOTAxUJrwMUCRQJrwOhAxIJvQMVCRUJvQOvA+ED1QOLAYsB1QOKAYsBjQHhA+EDjQHtA+0DjQH5A/kDjQGOAQUE+QOOAZUBPgQWCRYJkwGVARwHRQYbBxsHRQY8BDoEPAQ7BDsEPARFBgEAFwkDAAMAFwkYCQQAGQkBAAEAGQkXCQcAGgkEAAQAGgkZCQgAGwkHAAcAGwkaCcAAvwAcCRwJvwAdCcIAwQAeCR4JwQAfCb8AwgAdCR0JwgAeCQMAGAnFAMUAGAkgCccAwAAhCSEJwAAcCcUAIAlrAWsBIAkiCX0BxwAjCSMJxwAhCWsBIgnBAMEAIgkfCRsJCAAkCSQJCACXAX0BIwnRAdEBIwklCdoB2wEmCSYJ2wEnCd0B2gEoCSgJ2gEmCd8B3QEpCSkJ3QEoCd8BKQnhAeEBKQkqCZcCKwmYApgCKwksCZsCLQmZApkCLQkuCZgCLAmbApsCLAktCdsBngInCScJngIvCZ8CMAmXApcCMAkrCZ4CQwMvCS8JQwMxCeEBKglFA0UDKgkyCdEBJQmfAp8CJQkwCUMDmQIxCTEJmQIuCRgJFwkzCRcJGQkzCRkJGgkzCRoJGwkzCRwJHQkzCR4JHwkzCR0JHgkzCSAJGAkzCSEJHAkzCSIJIAkzCSMJIQkzCR8JIgkzCRsJJAkzCSQJMgkzCSUJIwkzCSYJJwkzCSgJJgkzCSkJKAkzCSoJKQkzCSwJKwkzCS4JLQkzCS0JLAkzCScJLwkzCSsJMAkzCS8JMQkzCTIJKgkzCTAJJQkzCTEJLgkzCT4EKwQWCRYJKwQkBBoEkwEkBCQEkwEWCQgHDgcdBzIJJAlFA0UDJAmXAVkANAlXAFcANAk1CUYDRAOZAZkBRAOYAUQDRQOYAZgBRQOXAUcDRgOaAZoBRgOZAUcDmgFMA0wDmgG4AUwDuAG5AbkBuAG6AXQAggC6AboBggC8AYIAhAC8AbwBhAARCU4DNglPA08DNgk3CU8DNwlQA1ADNwk4CVADOAlRA1EDOAk5CVEDOQlKA0oDOQk6CUsDSgM7CTsJSgM6CUgDSwM8CTwJSwM7CUgDPAlJA0kDPAk9CTQJWQA+CT4JWQCjAJUAPwmjAKMAPwk+CZQAQAmVAJUAQAk/CY0ATQDBAcEBTQCqAUsAqAFNAE0AqAGqAU4ArAFLAEsArAGoAU4AUQCsAawBUQCuAVEAfwGuAa4BfwHNAX8BgQHNAc0BgQHPAYQB0gGBAYEB0gHPAYUB1AGEAYQB1AHSARIJtgaFAYUBtgbUARUJEAkSCRIJEAm2BhUJFAkQCRAJFAkPCRQJEwkPCQ8JEwkOCRMJgwMOCQ4JgwMNCYMDggMNCQ0JggMMCUYEDAmCA3YGDAlGBEYEhAOAAz4BQQlAAUABQQlCCUQBQwlCAUIBQwlECUIBRAk+AT4BRAlBCQwBCQFFCUUJCQFGCQoBEgFHCUcJEgFICQkBCgFGCUYJCgFHCSEBDAFJCUkJDAFFCUABQgkTARMBQglKCRIBEwFICUgJEwFKCRsBFAEdAR0BFAEeAWUBSwkXARcBSwlMCRcBTAkZARkBTAlNCR0BHgEkASQBHgEiAWUBIQFLCUsJIQFJCSQBIgEmASYBIgEnASYBJwEqASoBJwEoASwBKgEtAS0BKgEoATsBLAE5ATkBLAEtATYBNwFOCU4JNwFPCTMBOwEvAS8BOwE5ATIBMwEuAS4BMwEvAUQBNgFDCUMJNgFOCRwDGgNQCVAJGgNRCRcDGANSCVIJGANTCRoDFwNRCVEJFwNSCeICVAnpAukCVAlVCeQCVgngAuACVglXCeACVwniAuICVwlUCfkCWAnkAuQCWAlWCRgD6wJTCVMJ6wJZCekCVQnrAusCVQlZCTwD8AJaCVoJ8AJbCfQC9QLsAuwC9QL2AvAC8QJbCVsJ8QJcCfUC/AL2AvYC/AL6AjwDWgn5AvkCWglYCfwC/gL6AvoC/gL/Av4CAgP/Av8CAgMAAwQDBQMCAwIDBQMAAxMDEQMEAwQDEQMFAwsDBgMTAxMDBgMRAw0DXQkPAw8DXQleCQkDBwMLAwsDBwMGAxwDUAkNAw0DUAldCWIIFwgWCD0FPAU/BQ==", + "byteLength": 102040 + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteOffset": 0, + "byteLength": 57576, + "byteStride": 24, + "target": 34962 + }, + { + "buffer": 0, + "byteOffset": 57576, + "byteLength": 19192, + "byteStride": 8, + "target": 34962 + }, + { + "buffer": 0, + "byteOffset": 76768, + "byteLength": 25272, + "target": 34963 + } + ], + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5126, + "normalized": false, + "count": 2399, + "min": [ + -0.9990839958190918, + -1, + -0.9998319745063782 + ], + "max": [ + 0.9995989799499512, + 0.999580979347229, + 0.9984359741210938 + ], + "type": "VEC3" + }, + { + "bufferView": 0, + "byteOffset": 12, + "componentType": 5126, + "normalized": false, + "count": 2399, + "min": [ + -69.29850006103516, + 9.929369926452637, + -61.32819747924805 + ], + "max": [ + 96.17990112304688, + 163.97000122070312, + 53.92519760131836 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5126, + "normalized": false, + "count": 2399, + "min": [ + 0.026409000158309937, + 0.01996302604675293 + ], + "max": [ + 0.9833459854125977, + 0.9800369739532471 + ], + "type": "VEC2" + }, + { + "bufferView": 2, + "byteOffset": 0, + "componentType": 5123, + "normalized": false, + "count": 12636, + "min": [ + 0 + ], + "max": [ + 2398 + ], + "type": "SCALAR" + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [ + 3 + ] + } + ], + "nodes": [ + { + "name": "LOD3spShape", + "extensions": { + "KHR_node_visibility": { + "visible": true + } + }, + "mesh": 0 + }, + { + "name": "Camera", + "matrix": [ + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 100, + 1 + ], + "extensions": { + "KHR_node_visibility": { + "visible": true + } + } + }, + { + "children": [ + 1 + ], + "matrix": [ + -0.7289687991142273, + -2.967253820429505e-8, + -0.6845471262931824, + 0, + -0.42520490288734436, + 0.7836934328079224, + 0.4527972936630249, + 0, + 0.5364750623703003, + 0.621147871017456, + -0.5712881088256836, + 0, + 400.1130065917969, + 463.2640075683594, + -431.0780334472656, + 1 + ], + "extensions": { + "KHR_node_visibility": { + "visible": true + } + } + }, + { + "children": [ + 0, + 2 + ], + "matrix": [ + 3.422854014112353e-11, + 0, + 0.0009999999310821295, + 0, + 0, + 0.0010000000474974513, + 0, + 0, + -0.0009999999310821295, + 0, + 3.422854014112353e-11, + 0, + 0, + 0, + 0, + 1 + ], + "extensions": { + "KHR_node_visibility": { + "visible": true + } + } + } + ], + "meshes": [ + { + "primitives": [ + { + "material": 0, + "mode": 4, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TEXCOORD_0": 2 + }, + "indices": 3 + } + ] + } + ], + "materials": [ + { + "name": "blinn3-fx", + "pbrMetallicRoughness": { + "baseColorFactor": [ + 1, + 1, + 1, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1, + "baseColorTexture": { + "index": 0, + "texCoord": 0 + } + }, + "emissiveFactor": [ + 0, + 0, + 0 + ], + "alphaMode": "OPAQUE", + "doubleSided": false + } + ], + "textures": [ + { + "sampler": 0, + "name": "base.jpg", + "source": 0 + } + ], + "samplers": [ + { + "magFilter": 9729, + "minFilter": 9986, + "wrapS": 10497, + "wrapT": 10497 + } + ], + "images": [ + { + "uri": "data:image/jpeg;base64,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", + "mimeType": "image/jpeg", + "name": "base.jpg" + } + ], + "extensionsUsed": [ + "KHR_node_visibility" + ] +} \ No newline at end of file diff --git a/assets/whackamole/anchors_1.mat b/assets/whackamole/anchors_1.mat new file mode 100644 index 0000000..68c1c19 Binary files /dev/null and b/assets/whackamole/anchors_1.mat differ diff --git a/assets/whackamole/detection.serialized.bin b/assets/whackamole/detection.serialized.bin new file mode 100644 index 0000000..364fb85 Binary files /dev/null and b/assets/whackamole/detection.serialized.bin differ diff --git a/assets/whackamole/jointsTransformer.js b/assets/whackamole/jointsTransformer.js new file mode 100644 index 0000000..82f5c71 --- /dev/null +++ b/assets/whackamole/jointsTransformer.js @@ -0,0 +1,144 @@ +var positionArray; +var matrixArray; +var moleVisibleArray; +var moleCollisions; +var realtimesincestartup; +var moleOnTimings; +var moleOffTimings; +var moleDeadlines; +var score; + +let indexArray = [26, 25, 28, 27, 12, 11, 14, 13, 16, 15, 0, 24, 23]; +let whackerIndices = [8, 9]; +let MAX_MOLES = 5; +let hitThreshold = 0.15; + +/** + * Multiplies two 4x4 matrices (A and B) and stores the result in a new Float32Array. + * + * @param {Float32Array} matA The first 4x4 matrix (16 elements). + * @param {Float32Array} matB The second 4x4 matrix (16 elements). + * @returns {Float32Array} The resulting 4x4 matrix. + */ +function multiply4x4Matrices(matA, matB) { + // Both matrices must be 4x4, represented as 16-element Float32Arrays. + if (matA.length !== 16 || matB.length !== 16) { + throw new Error("Both matrices must be 4x4 (16 elements)."); + } + + const result = new Float32Array(16); + + for (let i = 0; i < 4; i++) { // Row index for the result matrix + for (let j = 0; j < 4; j++) { // Column index for the result matrix + let sum = 0; + for (let k = 0; k < 4; k++) { + // The formula for a row-major matrix element at (row, column) is index = row * 4 + column. + // The standard matrix multiplication formula for an element C[i, j] is + // sum(A[i, k] * B[k, j]) for all k from 0 to 3. + sum += matA[i * 4 + k] * matB[k * 4 + j]; + } + // Store the result element C[i, j] at the row-major index i * 4 + j + result[i * 4 + j] = sum; + } + } + + return result; +} + + +function createTransformationMatrix(position, scale) { + +// 1. Scaling Matrix + let scaleMatrix = new Float32Array([ + scale[0], 0, 0, 0, + 0, scale[1], 0, 0, + 0, 0, scale[2], 0, + 0, 0, 0, 1 + ]); + + // 2. Translation Matrix + let translationMatrix = new Float32Array([ + 1, 0, 0, position[0], // Translation X in the last column of the first row + 0, 1, 0, position[1], // Translation Y in the last column of the second row + 0, 0, 1, position[2], // Translation Z in the last column of the third row + 0, 0, 0, 1 // The bottom row consists of [0, 0, 0, 1] + ]); + // Combine (Scale -> Rotate -> Translate) + // Order of multiplication for row-major format: M = S * R * T + let matrix = multiply4x4Matrices(scaleMatrix, translationMatrix); + + return matrix; +} + +function copyMatrixToArray(matrix, array, arrayIndex) +{ + + for (var j = 0; j < matrix.length; j++) + { + array[arrayIndex+j] = matrix[j]; + } + +} + + +let scale = new Float32Array([1.0, -1.0, -1.0]); + + +for (var i = 0; i < indexArray.length; i++) { + var index = indexArray[i]; + arrayIndex = index*3; + let position = new Float32Array([positionArray[arrayIndex], positionArray[arrayIndex+1], positionArray[arrayIndex+2]]); + let matrix = createTransformationMatrix(position, scale); + copyMatrixToArray(matrix, matrixArray, i*16); +} + +// moleVisibleArray and moleDeadlines are provided as persistent globals from C++ + +for (let m = 0; m < MAX_MOLES; m++) { + if (moleVisibleArray[m] === 0) { + if (realtimesincestartup[0] >= moleDeadlines[m]) { + moleVisibleArray[m] = 1; + moleDeadlines[m] = realtimesincestartup[0] + moleOffTimings[m]; + } + } else { + if (realtimesincestartup[0] >= moleDeadlines[m]) { + moleVisibleArray[m] = 0; + moleDeadlines[m] = realtimesincestartup[0] + moleOnTimings[m]; + } + } +} + +let whackerPositions = new Float32Array(whackerIndices.length * 3); +for (let w = 0; w < whackerIndices.length; w++) { + let wi = whackerIndices[w] * 3; + whackerPositions[w * 3] = positionArray[wi]; + whackerPositions[w * 3 + 1] = positionArray[wi + 1]; + whackerPositions[w * 3 + 2] = positionArray[wi + 2]; +} + +for (let m = 0; m < MAX_MOLES; m++) { + if (moleVisibleArray[m] !== 1) continue; + let base = m * 16; + let mx = moleCollisions[base + 3]; + let my = moleCollisions[base + 7]; + let mz = moleCollisions[base + 11]; + let hit = false; + for (let w = 0; w < whackerIndices.length && !hit; w++) { + let wbase = whackerIndices[w] * 16; + let wx = matrixArray[wbase + 3]; + let wy = matrixArray[wbase + 7]; + let wz = matrixArray[wbase + 11]; + let dx = wx - mx; + let dy = wy - my; + let dz = 0; + let d2 = dx * dx + dy * dy + dz * dz; + if (d2 < hitThreshold * hitThreshold) { + hit = true; + } + } + if (hit) { + score[0] = score[0] + 1; + moleVisibleArray[m] = 0; + moleDeadlines[m] = realtimesincestartup[0] + moleOnTimings[m]; + } +} diff --git a/assets/whackamole/landmark.serialized.bin b/assets/whackamole/landmark.serialized.bin new file mode 100644 index 0000000..567186b Binary files /dev/null and b/assets/whackamole/landmark.serialized.bin differ diff --git a/assets/whackamole/pose_marker.gltf b/assets/whackamole/pose_marker.gltf new file mode 100644 index 0000000..6772333 --- /dev/null +++ b/assets/whackamole/pose_marker.gltf @@ -0,0 +1,511 @@ +{ + "asset":{ + "generator":"Khronos glTF Blender I/O v4.4.55", + "version":"2.0" + }, + "scene":0, + "scenes":[ + { + "name":"Scene", + "nodes":[ + 15 + ] + } + ], + "nodes":[ + { + "name":"26", + "rotation":[ + 5.7042779388893905e-09, + 0, + -0.0001400127075612545, + 1 + ], + "scale":[ + 0.9999999403953552, + 0.9999999403953552, + 1 + ], + "translation":[ + -0.12116039544343948, + -0.8410383462905884, + -6.725223755665866e-08 + ] + }, + { + "name":"25", + "rotation":[ + 5.7042779388893905e-09, + 0, + -0.0001400127075612545, + 1 + ], + "scale":[ + 0.9999999403953552, + 0.9999999403953552, + 1 + ], + "translation":[ + 0.11814472824335098, + -0.8430860638618469, + -6.725223755665866e-08 + ] + }, + { + "name":"28", + "rotation":[ + -1.5304914668945457e-08, + 4.336809206929901e-19, + 0.0003756656078621745, + 0.9999999403953552 + ], + "scale":[ + 0.9999999403953552, + 0.9999999403953552, + 1 + ], + "translation":[ + -0.12043662369251251, + -1.66641366481781, + -3.776512969011492e-08 + ] + }, + { + "name":"27", + "rotation":[ + -1.5304914668945457e-08, + 4.336809206929901e-19, + 0.00037566557875834405, + 0.9999999403953552 + ], + "scale":[ + 0.9999999403953552, + 0.9999999403953552, + 1 + ], + "translation":[ + 0.11886849999427795, + -1.6684614419937134, + -3.776512969011492e-08 + ] + }, + { + "name":"12", + "scale":[ + 1, + 0.9999999403953552, + 1 + ], + "translation":[ + -0.287443608045578, + 1.0556492805480957, + 1.2935036375338882e-10 + ] + }, + { + "name":"11", + "scale":[ + 1, + 0.9999999403953552, + 1 + ], + "translation":[ + 0.28873172402381897, + 1.056704044342041, + 7.761021686425451e-11 + ] + }, + { + "name":"14", + "scale":[ + 1, + 0.9999999403953552, + 1 + ], + "translation":[ + -0.8656536936759949, + 1.0573620796203613, + 1.663076065749891e-10 + ] + }, + { + "name":"13", + "scale":[ + 1, + 0.9999999403953552, + 1 + ], + "translation":[ + 0.8649399280548096, + 1.0573620796203613, + 9.978456949610859e-11 + ] + }, + { + "name":"16", + "translation":[ + -1.443036437034607, + 1.0560864210128784, + 3.1044086745701804e-10 + ] + }, + { + "name":"15", + "translation":[ + 1.4417040348052979, + 1.0560864210128784, + 3.1044086745701804e-10 + ] + }, + { + "name":"0", + "translation":[ + 0, + 1.3125332593917847, + -0.005190655589103699 + ] + }, + { + "name":"24", + "translation":[ + -0.15115943551063538, + 0.017970770597457886, + 1.0348028683937471e-10 + ] + }, + { + "name":"23", + "translation":[ + 0.15138792991638184, + 0.017970770597457886, + 1.0348028683937471e-10 + ] + }, + { + "mesh":0, + "name":"Marker", + "skin":0 + }, + { + "mesh":1, + "name":"Stick Man", + "skin":0 + }, + { + "children":[ + 13, + 14, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12 + ], + "name":"Armature" + } + ], + "materials":[ + { + "doubleSided":true, + "name":"\u6750\u8d28", + "pbrMetallicRoughness":{ + "baseColorFactor":[ + 0.004775917157530785, + 0.0069934166967868805, + 0.8000937700271606, + 1 + ], + "metallicFactor":0.09000000357627869, + "roughnessFactor":0.012000000104308128 + } + }, + { + "doubleSided":true, + "name":"default", + "pbrMetallicRoughness":{ + "baseColorFactor":[ + 0.9181969165802002, + 0.9169202446937561, + 1, + 1 + ], + "metallicFactor":0.00906344410032034, + "roughnessFactor":0.012084592133760452 + } + } + ], + "meshes":[ + { + "name":"\u68f1\u89d2\u7403", + "primitives":[ + { + "attributes":{ + "POSITION":0, + "NORMAL":1, + "TEXCOORD_0":2, + "JOINTS_0":3, + "WEIGHTS_0":4 + }, + "indices":5, + "material":0 + } + ] + }, + { + "name":"connection", + "primitives":[ + { + "attributes":{ + "POSITION":7, + "NORMAL":8, + "TEXCOORD_0":9, + "JOINTS_0":12, + "WEIGHTS_0":13 + }, + "indices":14, + "material":1 + } + ] + } + ], + "skins":[ + { + "inverseBindMatrices":6, + "joints":[ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12 + ], + "name":"Armature" + } + ], + "accessors":[ + { + "bufferView":0, + "componentType":5126, + "count":960, + "max":[ + 1.4791815280914307, + 1.3911685943603516, + 0.07344230264425278 + ], + "min":[ + -1.4799505472183228, + -1.7053508758544922, + -0.08382783085107803 + ], + "type":"VEC3" + }, + { + "bufferView":1, + "componentType":5126, + "count":960, + "type":"VEC3" + }, + { + "bufferView":2, + "componentType":5126, + "count":960, + "type":"VEC2" + }, + { + "bufferView":3, + "componentType":5121, + "count":960, + "type":"VEC4" + }, + { + "bufferView":4, + "componentType":5126, + "count":960, + "type":"VEC4" + }, + { + "bufferView":5, + "componentType":5123, + "count":960, + "type":"SCALAR" + }, + { + "bufferView":6, + "componentType":5126, + "count":13, + "type":"MAT4" + }, + { + "bufferView":7, + "componentType":5126, + "count":928, + "max":[ + 1.4113847017288208, + 1.0757596492767334, + 0.02158801443874836 + ], + "min":[ + -1.410385251045227, + -1.6699929237365723, + -0.021588053554296494 + ], + "type":"VEC3" + }, + { + "bufferView":8, + "componentType":5126, + "count":928, + "type":"VEC3" + }, + { + "bufferView":9, + "componentType":5126, + "count":928, + "type":"VEC2" + }, + { + "bufferView":10, + "componentType":5121, + "count":928, + "normalized":true, + "type":"VEC4" + }, + { + "bufferView":11, + "componentType":5123, + "count":928, + "normalized":true, + "type":"VEC4" + }, + { + "bufferView":12, + "componentType":5121, + "count":928, + "type":"VEC4" + }, + { + "bufferView":13, + "componentType":5126, + "count":928, + "type":"VEC4" + }, + { + "bufferView":14, + "componentType":5123, + "count":1512, + "type":"SCALAR" + } + ], + "bufferViews":[ + { + "buffer":0, + "byteLength":11520, + "byteOffset":0, + "target":34962 + }, + { + "buffer":0, + "byteLength":11520, + "byteOffset":11520, + "target":34962 + }, + { + "buffer":0, + "byteLength":7680, + "byteOffset":23040, + "target":34962 + }, + { + "buffer":0, + "byteLength":3840, + "byteOffset":30720, + "target":34962 + }, + { + "buffer":0, + "byteLength":15360, + "byteOffset":34560, + "target":34962 + }, + { + "buffer":0, + "byteLength":1920, + "byteOffset":49920, + "target":34963 + }, + { + "buffer":0, + "byteLength":832, + "byteOffset":51840 + }, + { + "buffer":0, + "byteLength":11136, + "byteOffset":52672, + "target":34962 + }, + { + "buffer":0, + "byteLength":11136, + "byteOffset":63808, + "target":34962 + }, + { + "buffer":0, + "byteLength":7424, + "byteOffset":74944, + "target":34962 + }, + { + "buffer":0, + "byteLength":3712, + "byteOffset":82368, + "target":34962 + }, + { + "buffer":0, + "byteLength":7424, + "byteOffset":86080, + "target":34962 + }, + { + "buffer":0, + "byteLength":3712, + "byteOffset":93504, + "target":34962 + }, + { + "buffer":0, + "byteLength":14848, + "byteOffset":97216, + "target":34962 + }, + { + "buffer":0, + "byteLength":3024, + "byteOffset":112064, + "target":34963 + } + ], + "buffers":[ + { + "byteLength":115088, + "uri":"data:application/octet-stream;base64,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xlE9vYDcnz8gKKq7xlE9vYDcnz8gKKq7xlE9vYDcnz8gKKq7xlE9vYDcnz8gKKq7xlE9vYDcnz8gKKq7xlE9vYDcnz8gKKq7xlE9vbQlsD8gKKq7xlE9vbQlsD8gKKq7xlE9vbQlsD8gKKq7xlE9vbQlsD8gKKq7xlE9vbQlsD8gKKq7xlE9vbQlsD8gKKq7qymZvVYdqz8gKKq7qymZvVYdqz8gKKq7qymZvVYdqz8gKKq7qymZvVYdqz8gKKq7qymZvVYdqz8gKKq7qymZvVYdqz8gKKq7ulE9PbQlsD8gKKq7ulE9PbQlsD8gKKq7ulE9PbQlsD8gKKq7ulE9PbQlsD8gKKq7ulE9PbQlsD8gKKq7ulE9PbQlsD8gKKq7AAC6sdARsj8gKKq7AAC6sdARsj8gKKq7AAC6sdARsj8gKKq7AAC6sdARsj8gKKq7AAC6sdARsj8gKKq7AAC6sdARsj8gKKq7iqhdPcD4oj92EBQ9iqhdPcD4oj92EBQ9iqhdPcD4oj92EBQ9iqhdPcD4oj92EBQ9iqhdPcD4oj92EBQ9iqhdPcD4oj92EBQ9nVWpvILcnz+eEBQ9nVWpvILcnz+eEBQ9nVWpvILcnz+eEBQ9nVWpvILcnz+eEBQ9nVWpvILcnz+eEBQ9nVWpvILcnz+eEBQ9A/6IvRoBqD92EBQ9A/6IvRoBqD92EBQ9A/6IvRoBqD92EBQ9A/6IvRoBqD92EBQ9A/6IvRoBqD92EBQ9A/6IvRoBqD92EBQ9nVWpvLIlsD+eEBQ9nVWpvLIlsD+eEBQ9nVWpvLIlsD+eEBQ9nVWpvLIlsD+eEBQ9nVWpvLIlsD+eEBQ9nVWpvLIlsD+eEBQ9iqhdPXQJrT92EBQ9iqhdPXQJrT92EBQ9iqhdPXQJrT92EBQ9iqhdPXQJrT92EBQ9iqhdPXQJrT92EBQ9iqhdPXQJrT92EBQ99kxRPML4oj+Wt3w99kxRPML4oj+Wt3w99kxRPML4oj+Wt3w99kxRPML4oj+Wt3w99kxRPML4oj+Wt3w99kxRPML4oj+Wt3w98lQpPRoBqD9Wt3w98lQpPRoBqD9Wt3w98lQpPRoBqD9Wt3w98lQpPRoBqD9Wt3w98lQpPRoBqD9Wt3w98lQpPRoBqD9Wt3w97v0Ivd7kpD+Wt3w97v0Ivd7kpD+Wt3w97v0Ivd7kpD+Wt3w9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8KILP1ZhLz/wogs/VmEvP/CiCz9WYS8/8KILP1ZhLz/wogs/VmEvP6Euuj5WYS8/oS66PlZhLz+hLro+VmEvP6Euuj5WYS8/oS66PlZhLz8Bo4s++REHP0G66D75EQc/wOgiP/kRBz8GL7o9+REHP2B0UT/5EQc/CgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEBgsABAYLAAQGCwAEBgsABAYLAAQLBgAECwYABAsGAAQLBgAECwYABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQGAAAEBgAABAYAAAQGAAAEBgAABAsGAAQLBgAECwYABAsGAAQLBgAECwAABAsAAAQLAAAECwAABAsAAAQLAAAECwAABAsAAAQLAAAECwAABAAAAAQAAAAEAAAABAAAAAQAAAAECwYABAsGAAQLBgAECwYABAsGAAQLBgAECwYABAsGAAQLBgAECwYABggAAAYIAAAGCAAABggAAAYIAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABggAAAYIAAAGCAAABggAAAYIAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAsAAAwLAAAMCwAADAsAAAwLAAAMCwAADAsAAAwLAAAMCwAADAsAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMCwAADAsAAAwLAAAMCwAADAsAAAwLAAAMCwAADAsAAAwLAAAMCwAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAABqMXs/OLZPPC7exzsAAAAAajF7Pzi2Tzwu3sc7AAAAAGoxez84tk88Lt7HOwAAAABqMXs/OLZPPC7exzsAAAAAajF7Pzi2Tzwu3sc7AAAAAHQpez9Eoow82PPiOgAAAAB0KXs/RKKMPNjz4joAAAAAdCl7P0SijDzY8+I6AAAAAHQpez9Eoow82PPiOgAAAAB0KXs/RKKMPNjz4joAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAD3pfz+fIbY5AAAAAAAAAAA96X8/nyG2OQAAAAAAAAAAPel/P58htjkAAAAAAAAAAD3pfz+fIbY5AAAAAAAAAAA96X8/nyG2OQAAAAAAAAAATm9yP5QcRj2VdJc7AAAAAE5vcj+UHEY9lXSXOwAAAABOb3I/lBxGPZV0lzsAAAAATm9yP5QcRj2VdJc7AAAAAE5vcj+UHEY9lXSXOwAAAABv9Xs/HlKBPAAAAAAAAAAAb/V7Px5SgTwAAAAAAAAAAG/1ez8eUoE8AAAAAAAAAABv9Xs/HlKBPAAAAAAAAAAAb/V7Px5SgTwAAAAAAAAAACDFfz/egWs6AAAAAAAAAAAgxX8/3oFrOgAAAAAAAAAAIMV/P96BazoAAAAAAAAAACDFfz/egWs6AAAAAAAAAAAgxX8/3oFrOgAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAC/mXg/pPqCPMiaUzwAAAAAv5l4P6T6gjzImlM8AAAAAL+ZeD+k+oI8yJpTPAAAAAC/mXg/pPqCPMiaUzwAAAAAv5l4P6T6gjzImlM8AAAAAB8SfT/XuzQ80JHXOQAAAAAfEn0/17s0PNCR1zkAAAAAHxJ9P9e7NDzQkdc5AAAAAB8SfT/XuzQ80JHXOQAAAAAfEn0/17s0PNCR1zkAAAAASux/P6asnTkAAAAAAAAAAErsfz+mrJ05AAAAAAAAAABK7H8/pqydOQAAAAAAAAAASux/P6asnTkAAAAAAAAAAErsfz+mrJ05AAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAqvh/P0Wz6jgAAAAAAAAAAKr4fz9Fs+o4AAAAAAAAAACq+H8/RbPqOAAAAAAAAAAAqvh/P0Wz6jgAAAAAAAAAAKr4fz9Fs+o4AAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/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AAAAAFbNfz+yqUo6AAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAQBCADwABwBHAFAABAA/AFsAAwBaAGkAAABmAFQACQBSAHsACgBIAIQAEgBlAJIAGABuAJ8AGgB2AKoACAB5AI8ADACJAJcAEwCVAKcAFgCdALMAHACtAH8AIAC0ANQAIwC+AOEAKgDBAOkALQDGAOoAMgDPANwA2ADrADcA2gDNAO0AzAAvAOwA7gDkADkA7wDIAOUAygAoAOcA5gDgADsA6ADAAOIAwgAlAOMA3gDWADoA3wC8ANcAugAeANIA0wDZADgA1QC1ANsAtwAzAN0AgADRADQAfgCsANAAqwAwAM4AsQDHADEAsgCcAMkAngApAMsApQDDACwApgCUAMUAkwAnAMQAmAC/ACQAlgCIAL0AhwAiALsAjQC2ACEAjgB4ALgAegA1ALkAqQCwAC4AqAB0AK8AcgAUAK4AoQCjACsAoABwAKIAcQARAKQAkQCbACYAkABkAJoAYgAOAJkAhgCLAB8AhQBKAIoATAAGAIwAfQCDADYAfABTAIIAUQAdAIEAVwB1ABkAVQBnAHMAaAAVAHcAagBsABcAawBcAG0AXgAPAG8AXwBjABAAXQBBAGEAQAANAGAATwBZABsATgBGAFgARAACAFYAPgBNAAsAPQBDAEkARQAFAEsA8QD1APoA9wDyAAsB8wD9AP8A9AAAAQcB8AAEAQkB+QANASYB+wD2AA4BAgH+ABYBCAEBARsBCgEFAR0B+AAkARAB/AASARQBAwEXARwBBgEZASEBDAEfASUBDwEiAScBEwERASoBGgEVASsBIAEYASkBIwEeASgBLQExATYBMwEuAUcBMAE5ATwBLwE7AUMBLAFAAUUBNQFJAWIBNwEyAUoBPgE6AVIBRAE9AVcBRgFBAVkBNAFgAUwBOAFOAVABPwFTAVgBQgFVAV0BSAFbAWEBSwFfAWQBTwFNAWYBVgFRAWcBXAFUAWUBXgFaAWMBaAFtAXIBbwFqAYMBbAF1AXgBawF3AX8BaQF8AYEBcQGFAZ4BcwFuAYYBeQF2AY4BgAF6AZMBggF9AZUBcAGdAYgBdAGKAYwBewGPAZQBfgGRAZkBhAGXAZwBhwGbAaABiwGJAaIBkgGNAaMBmAGQAaEBmgGWAZ8BpQGpAa4BqwGmAb8BpwGxAbMBqAG0AbsBpAG4Ab0BrQHBAdoBrwGqAcIBtQGyAcoBvAG2Ac8BvgG5AdEBrAHYAcQBsAHGAcgBtwHLAdABugHNAdUBwAHTAdkBwwHWAdsBxwHFAd4BzgHJAd8B1AHMAd0B1wHSAdwB4AHlAeoB5wHiAfsB5AHtAfAB4wHvAfcB4QH0AfkB6QH9ARYC6wHmAf4B8QHuAQYC+AHyAQsC+gH1AQ0C6AEUAgAC7AECAgQC8wEHAgwC9gEJAhEC/AEPAhUC/wESAhcCAwIBAhoCCgIFAhsCEAIIAhkCEwIOAhgCHQIhAiYCIwIeAjcCIAIpAiwCHwIrAjMCHAIwAjUCJQI5AlICJwIiAjoCLQIqAkICNAIuAkcCNgIxAkkCJAJQAjwCKAI+AkACLwJDAkgCMgJFAk0COAJLAlECOwJOAlMCPwI9AlUCRgJBAlcCTAJEAlYCTwJKAlQCWQJdAmICXwJaAnMCXAJlAmgCWwJnAm8CWAJsAnECYQJ1Ao4CYwJeAnYCagJmAn4CcAJpAoMCcgJtAoUCYAKNAngCZAJ6AnwCawJ/AoQCbgKBAokCdAKHAowCdwKLApACewJ5ApICggJ9ApMCiAKAApECigKGAo8ClQKZAp4CmwKWAq8ClwKhAqMCmAKkAqsClAKoAq0CnQKxAsoCnwKaArICpQKiAroCrAKmAr8CrgKpAsEC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B9unrralgr74jok/B9unrralgr74jok/B9unrralgr74jok/B9unrv/0hj6x5Yg/qFVMPP/0hj6x5Yg/qFVMPP/0hj6x5Yg/qFVMPLalgr6x5Yg/p1VMPLalgr6x5Yg/p1VMPLalgr6x5Yg/p1VMPP/0hj4GTYc/gHyQPP/0hj4GTYc/gHyQPP/0hj4GTYc/gHyQPLalgr4GTYc/gHyQPLalgr4GTYc/gHyQPLalgr4GTYc/gHyQPP/0hj5btIU/qFVMPP/0hj5btIU/qFVMPP/0hj5btIU/qFVMPLalgr5btIU/p1VMPLalgr5btIU/p1VMPLalgr5btIU/p1VMPP/0hj4UC4U/B9unrv/0hj4UC4U/B9unrv/0hj4UC4U/B9unrralgr4UC4U/B9unrralgr4UC4U/B9unrralgr4UC4U/B9unrv/0hj5btIU/p1VMvP/0hj5btIU/p1VMvP/0hj5btIU/p1VMvLalgr5btIU/qFVMvLalgr5btIU/qFVMvLalgr5btIU/qFVMvBxItz2o9cQ8gHyQvBxItz2o9cQ8gHyQvBxItz2o9cQ8gHyQvHpst72o9cQ8gHyQvHpst72o9cQ8gHyQvHpst72o9cQ8gHyQvBxItz00kBU9p1VMvBxItz00kBU9p1VMvBxItz00kBU9p1VMvHpst700kBU9qFVMvHpst700kBU9qFVMvHpst700kBU9qFVMvBxItz0UuSo9B9unrhxItz0UuSo9B9unrhxItz0UuSo9B9unrnpst70UuSo9B9unrnpst70UuSo9B9unrnpst70UuSo9B9unrhxItz00kBU9qFVMPBxItz00kBU9qFVMPBxItz00kBU9qFVMPHpst700kBU9p1VMPHpst700kBU9p1VMPHpst700kBU9p1VMPBxItz2o9cQ8gHyQPBxItz2o9cQ8gHyQPBxItz2o9cQ8gHyQPHpst72o9cQ8gHyQPHpst72o9cQ8gHyQPHpst72o9cQ8gHyQPBxItz3QlT08qFVMPBxItz3QlT08qFVMPBxItz3QlT08qFVMPHpst73QlT08p1VMPHpst73QlT08p1VMPHpst73QlT08p1VMPBxItz2g5NE7B9unrhxItz2g5NE7B9unrhxItz2g5NE7B9unrnpst72g5NE7B9unrnpst72g5NE7B9unrnpst72g5NE7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p1VMvEtpZr9374g/p1VMvEtpZr9374g/p1VMvIGHtL9374g/qFVMvIGHtL9374g/qFVMvIGHtL9374g/qFVMvEtpZr++mIk/FhI9r0tpZr++mIk/FhI9r0tpZr++mIk/FhI9r4GHtL++mIk/FhI9r4GHtL++mIk/FhI9r4GHtL++mIk/FhI9r0tpZr9374g/qFVMPEtpZr9374g/qFVMPEtpZr9374g/qFVMPIGHtL9374g/p1VMPIGHtL9374g/p1VMPIGHtL9374g/p1VMPEtpZr/MVoc/gHyQPEtpZr/MVoc/gHyQPEtpZr/MVoc/gHyQPIGHtL/MVoc/gHyQPIGHtL/MVoc/gHyQPIGHtL/MVoc/gHyQPEtpZr8hvoU/qFVMPEtpZr8hvoU/qFVMPEtpZr8hvoU/qFVMPIGHtL8hvoU/p1VMPIGHtL8hvoU/p1VMPIGHtL8hvoU/p1VMPEtpZr/aFIU/FhI9r0tpZr/aFIU/FhI9r0tpZr/aFIU/FhI9r4GHtL/aFIU/FhI9r4GHtL/aFIU/FhI9r4GHtL/aFIU/FhI9r0tpZr8hvoU/p1VMvEtpZr8hvoU/p1VMvEtpZr8hvoU/p1VMvIGHtL8hvoU/qFVMvIGHtL8hvoU/qFVMvIGHtL8hvoU/qFVMvEGotD/MVoc/gHyQvEGotD/MVoc/gHyQvEGotD/MVoc/gHyQvMyqZj/MVoc/gHyQvMyqZj/MVoc/gHyQvMyqZj/MVoc/gHyQvEGotD9374g/p1VMvEGotD9374g/p1VMvEGotD9374g/p1VMvMyqZj9374g/qFVMvMyqZj9374g/qFVMvMyqZj9374g/qFVMvEGotD++mIk/FhI9r0GotD++mIk/FhI9r0GotD++mIk/FhI9r8yqZj++mIk/FhI9r8yqZj++mIk/FhI9r8yqZj++mIk/FhI9r0GotD9374g/qFVMPEGotD9374g/qFVMPEGotD9374g/qFVMPMyqZj9374g/p1VMPMyqZj9374g/p1VMPMyqZj9374g/p1VMPEGotD/MVoc/gHyQPEGotD/MVoc/gHyQPEGotD/MVoc/gHyQPMyqZj/MVoc/gHyQPMyqZj/MVoc/gHyQPMyqZj/MVoc/gHyQPEGotD8hvoU/qFVMPEGotD8hvoU/qFVMPEGotD8hvoU/qFVMPMyqZj8hvoU/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veTDPgAAAAD978M+L4NsvwAAAAD978O+L4NsvwAAgD8AAAAAAAAAAAAAAAD978M+L4NsvwAAAAD978O+L4NsvwAAgL8AAAAAAAAAAAAAAAD978M+L4NsvwAAAAAvg2w//e/DvgAAgD8AAAAAAAAAAAAAAAD978M+L4NsvwAAAAAvg2w//e/DvgAAgL8AAAAAAAAAAAAAAAAvg2w//e/DPgAAAAAvg2w//e/DvgAAgD8AAAAAAAAAAAAAAAAvg2w//e/DPgAAAAAvg2w//e/DvgAAgL8AAAAAAAAAAAAAAAD978M+L4NsPwAAAAAvg2w//e/DPgAAgD8AAAAAAAAAAAAAAAD978M+L4NsPwAAAAAvg2w//e/DPgAAgL8AAAAAAAAAAAAAAAD978M+L4NsPwAAAAD978O+L4NsPwAAgD8AAAAAAAAAAAAAAAD978M+L4NsPwAAAAD978O+L4NsPwAAgL8AAAAAAAAAAAAAAAD978O+L4NsPwAAAAAvg2y//e/DPgAAgD8AAAAAAAAAAAAAAAD978O+L4NsPwAAAAAvg2y//e/DPgAAgL8AAAAAAAAAAAAAAAAvg2y//e/DPgAAAAAvg2y//e/DvgAAgD8AAAAAAAAAAAAAAAAvg2y//e/DPgAAAAAvg2y//e/DvgAAgL8AAAAAAAAAAAAAAAD978O+L4NsvwAAAAAvg2y//e/DvgAAgD8AAAAAAAAAAAAAAAD978O+L4NsvwAAAAAvg2y//e/DvgAAgL8AAAAAAAAAAAAAAAD978M+L4NsvwAAAAD978O+L4NsvwAAgD8AAAAAAAAAAAAAAAD978M+L4NsvwAAAAD978O+L4NsvwAAgL8AAAAAAAAAAAAAAAD978M+L4NsvwAAAAAvg2w//e/DvgAAgD8AAAAAAAAAAAAAAAD978M+L4NsvwAAAAAvg2w//e/DvgAAgL8AAAAAAAAAAAAAAAAvg2w//e/DPgAAAAAvg2w//e/DvgAAgD8AAAAAAAAAAAAAAAAvg2w//e/DPgAAAAAvg2w//e/DvgAAgL8AAAAAAAAAAAAAAAD978M+L4NsPwAAAAAvg2w//e/DPgAAgD8AAAAAAAAAAAAAAAD978M+L4NsPwAAAAAvg2w//e/DPgAAgL8AAAAAAAAAAAAAAAD978M+L4NsPwAAAAD978O+L4NsPwAAgD8AAAAAAAAAAAAAAAD978M+L4NsPwAAAAD978O+L4NsPwAAgL8AAAAAAAAAAAAAAAD978O+L4NsPwAAAAAvg2y//e/DPgAAgD8AAAAAAAAAAAAAAAD978O+L4NsPwAAAAAvg2y//e/DPgAAgL8AAAAAAAAAAAAAAAAvg2y//e/DPgAAAAAvg2y//e/DvgAAgD8AAAAAAAAAAAAAAAAvg2y//e/DPgAAAAAvg2y//e/DvgAAgL8AAAAAAAAAAAAAAAD978O+L4NsvwAAAAAvg2y//e/DvgAAgD8AAAAAAAAAAAAAAAD978O+L4NsvwAAAAAvg2y//e/DvgAAgL8AAAAAAAAAAAAAAAD978M+L4NsvwAAAAD978O+L4NsvwAAgD8AAAAAAAAAAAAAAAD978M+L4NsvwAAAAD978O+L4NsvwAAgL8AAAAAAAAAAAAAAAD978M+L4NsvwAAAAAvg2w//e/DvgAAgD8AAAAAAAAAAAAAAAD978M+L4NsvwAAAAAvg2w//e/DvgAAgL8AAAAAAAAAAAAAAAAvg2w//e/DPgAAAAAvg2w//e/DvgAAgD8AAAAAAAAAAAAAAAAvg2w//e/DPgAAAAAvg2w//e/DvgAAgL8AAAAAAAAAAAAAAAD978M+L4NsPwAAAAAvg2w//e/DPgAAgD8AAAAAAAAAAAAAAAD978M+L4NsPwAAAAAvg2w//e/DPgAAgL8AAAAAAAAAAAAAAAD978M+L4NsPwAAAAD978O+L4NsPwAAgD8AAAAAAAAAAAAAAAD978M+L4NsPwAAAAD978O+L4NsPwAAgL8AAAAAAAAAAAAAAAD978O+L4NsPwAAAAAvg2y//e/DPgAAgD8AAAAAAAAAAAAAAAD978O+L4NsPwAAAAAvg2y//e/DPgAAgL8AAAAAAAAAAAAAAAAvg2y//e/DPgAAAAAvg2y//e/DvgAAgD8AAAAAAAAAAAAAAAAvg2y//e/DPgAAAAAvg2y//e/DvgAAgL8AAAAAAAAAAAAAAAD978O+L4NsvwAAAAAvg2y//e/DvgAAgD8AAAAAAAAAAAAAAAD978O+L4NsvwAAAAAvg2y//e/DvgAAgL8AAAAAAAAAAAAAAAD978M+L4NsvwAAAAD978O+L4NsvwAAgD8AAAAAAAAAAAAAAAD978M+L4NsvwAAAAD978O+L4NsvwAAgL8AAAAA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AAAAPwAAgD4AAAA/AACAPgAAAD8AAIA+AAAAPwAAAAAAAAA/AACAPwAAAD8AAAAAAAAAPwAAwD4AAAA/AADAPgAAAD8AAMA+AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAID8AAAA/AAAgPwAAAD8AACA/AAAAPwAAQD8AAAA/AABAPwAAAD8AAEA/AAAAPwAAYD8AAAA/AABgPwAAAD8AAGA/AAAAPwAAgD8AAAA/AACAPwAAAD8AAAAAAAAAPwAAAAAAAAA/AABgPwAAAD8AAGA/AAAAPwAAYD8AAAA/AABgPwAAAD8AAEA/AAAAPwAAQD8AAAA/AABAPwAAAD8AAEA/AAAAPwAAID8AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAMA+AAAAPwAAwD4AAAA/AADAPgAAAD8AAMA+AAAAPwAAgD4AAAA/AACAPgAAAD8AAIA+AAAAPwAAgD4AAAA/AAAAPgAAAD8AAAA+AAAAPwAAAD4AAAA/AAAAPgAAAD8AAIA/AAAAPwAAgD8AAAA/AAAAAAAAAD8AAAAAAAAAPwAAYD8AAAA/AABgPwAAAD8AAGA/AAAAPwAAYD8AAAA/AABAPwAAAD8AAEA/AAAAPwAAQD8AAAA/AABAPwAAAD8AACA/AAAAPwAAID8AAAA/AAAgPwAAAD8AACA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AADAPgAAAD8AAIA+AAAAPwAAgD4AAAA/AACAPgAAAD8AAIA+AAAAPwAAAD4AAAA/AAAAPgAAAD8AAAA+AAAAPwAAAD4AAAA/AACAPwAAAD8AAIA/AAAAPwAAAAAAAAA/AAAAAAAAAD8AAGA/AAAAPwAAYD8AAAA/AABgPwAAAD8AAGA/AAAAPwAAQD8AAAA/AABAPwAAAD8AAEA/AAAAPwAAQD8AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AAAgPwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAwD4AAAA/AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AACAPgAAAD8AAIA+AAAAPwAAgD4AAAA/AACAPgAAAD8AAAA+AAAAPwAAAD4AAAA/AAAAPgAAAD8AAAA+AAAAPwAAgD8AAAA/AACAPwAAAD8AAAAAAAAAPwAAAAAAAAA/AABgPwAAAD8AAGA/AAAAPwAAYD8AAAA/AABgPwAAAD8AAEA/AAAAPwAAQD8AAAA/AABAPwAAAD8AAEA/AAAAPwAAID8AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAMA+AAAAPwAAwD4AAAA/AADAPgAAAD8AAMA+AAAAPwAAgD4AAAA/AACAPgAAAD8AAIA+AAAAPwAAgD4AAAA/AAAAPgAAAD8AAAA+AAAAPwAAAD4AAAA/AAAAPgAAAD8AAIA/AAAAPwAAgD8AAAA/AAAAAAAAAD8AAAAAAAAAPwAAYD8AAAA/AABgPwAAAD8AAGA/AAAAPwAAYD8AAAA/AABAPwAAAD8AAEA/AAAAPwAAQD8AAAA/AABAPwAAAD8AACA/AAAAPwAAID8AAAA/AAAgPwAAAD8AACA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AADAPgAAAD8AAIA+AAAAPwAAgD4AAAA/AACAPgAAAD8AAIA+AAAAPwAAAD4AAAA/AAAAPgAAAD8AAAA+AAAAPwAAAD4AAAA/AACAPwAAAD8AAEA/XI8CPwAAAAAAAAA/AABgPwAAAD8AAGA/AAAAP9Rxaz8sjhQ/AABAPwAAAD8AAEA/AAAAP6RwfT8AAEA/AAAgPwAAAD8AACA/AAAAP9Rxaz/UcWs/AAAAPwAAAD8AAEA/pHB9PwAAAD8AAAA/LI4UP9Rxaz8AAMA+AAAAPwAAwD4AAAA/XI8CPwAAQD8AAIA+AAAAPwAAgD4AAAA/LI4UPyyOFD8AAAA+AAAAPwAAAD4AAAA/AAAAPgAAAD8AAAA+AAAAPwAAAD4AAAA/AACAPgAAAD8AAIA+AAAAPwAAgD4AAAA/AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AABAPwAAAD8AAEA/AAAAPwAAQD8AAAA/AABgPwAAAD8AAGA/AAAAPwAAYD8AAAA/AAAAAAAAAD8AAIA/AAAAPwAAAAAAAAA/AACAPwAAAD8AAIA/AAAAPwAAAAAAAAA/AAAAAAAAAD8AAGA/AAAAPwAAYD8AAAA/AABgPwAAAD8AAGA/AAAAPwAAQD8AAAA/AABAPwAAAD8AAEA/AAAAPwAAQD8AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AAAgPwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAwD4AAAA/AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AACAPgAAAD8AAIA+AAAAPwAAgD4AAAA/AACAPgAAAD8AAAA+AAAAPwAAAD4AAAA/AAAAPgAAAD8AAAA+AAAAPwAAgD8AAAA/AACAPwAAAD8AAAAAAAAAPwAAAAAAAAA/AABgPwAAAD8AAGA/AAAAPwAAYD8AAAA/AABgPwAAAD8AAEA/AAAAPwAAQD8AAAA/AABAPwAAAD8AAEA/AAAAPwAAID8AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAMA+AAAAPwAAwD4AAAA/AADAPgAAAD8AAMA+AAAAPwAAgD4AAAA/AACAPgAAAD8AAIA+AAAAPwAAgD4AAAA/AAAAPgAAAD8AAAA+AAAAPwAAAD4AAAA/AAAAPgAAAD8AAIA/AAAAPwAAgD8AAAA/AAAAAAAAAD8AAAAAAAAAPwAAYD8AAAA/AABgPwAAAD8AAGA/AAAAPwAAYD8AAAA/AABAPwAAAD8AAEA/AAAAPwAAQD8AAAA/AABAPwAAAD8AACA/AAAAPwAAID8AAAA/AAAgPwAAAD8AACA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AADAPgAAAD8AAIA+AAAAPwAAgD4AAAA/AACAPgAAAD8AAIA+AAAAPwAAAD4AAAA/AAAAPgAAAD8AAAA+AAAAPwAAAD4AAAA/AACAPwAAAD8AAIA/AAAAPwAAAAAAAAA/AAAAAAAAAD8AAGA/AAAAPwAAYD8AAAA/AABgPwAAAD8AAGA/AAAAPwAAQD8AAAA/AABAPwAAAD8AAEA/AAAAPwAAQD8AAAA/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GHBoPgAAYD8YcGg+AABgPxhwaD4AAEA/GHBoPgAAQD8YcGg+AABAPxhwaD4AAEA/GHBoPgAAID8ccGg+AAAgPxxwaD4AACA/HHBoPgAAID8ccGg+AAAAPxhwaD4AAAA/GHBoPgAAAD8YcGg+AAAAPxhwaD4BAMA+FHBoPgEAwD4UcGg+AQDAPhRwaD4BAMA+FHBoPgAAgD4YcGg+AACAPhhwaD4AAIA+GHBoPgAAgD4YcGg+/v//PRhwaD7+//89GHBoPv7//z0YcGg+/v//PRhwaD4AAGA/WnaPPgAAYD9ado8+AABgP1p2jz4AAGA/WnaPPgAAQD9ado8+AABAP1p2jz4AAEA/WnaPPgAAQD9ado8+//8fP1x2jz7//x8/XHaPPv//Hz9cdo8+//8fP1x2jz4AAAA/WnaPPgAAAD9ado8+AAAAP1p2jz4AAAA/WnaPPgAAwD5Ydo8+AADAPlh2jz4AAMA+WHaPPgAAwD5Ydo8+AACAPlp2jz4AAIA+WnaPPgAAgD5ado8+AACAPlp2jz4AAAA+WHaPPgAAAD5Ydo8+AAAAPlh2jz4AAAA+WHaPPgAAgD9ado8+AAAAAFp2jz4AAIA/WnaPPgAAAABado8+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////BAAAAAQAAAAEAAAACwAKAAsACgALAAoABAAAAAQAAAAEAAAACwAAAAsAAAALAAAABAAKAAQACgAEAAoACwAAAAsAAAALAAAABAoAAAQKAAAECgAACwAAAAsAAAALAAAABAoAAAQKAAAECgAACwAAAAsAAAALAAAABAoAAAQKAAAECgAACwAAAAsAAAALAAAABAAKAAQACgAEAAoACwAKAAsACgALAAoABAAKAAQACgAEAAoACwAKAAsACgALAAoABQEKAAUBCgAFAQoADAEKAAwBCgAMAQoABQEAAAUBAAAFAQAADAEKAAwBCgAMAQoABQEAAAUBAAAFAQAADAEKAAwBCgAMAQoABQEKAAUBCgAFAQoADAEKAAwBCgAMAQoABQEKAAUBCgAFAQoADAEKAAwBCgAMAQoABQEKAAUBCgAFAQoADAEKAAwBCgAMAQoABQEKAAUBCgAFAQoADAEKAAwBCgAMAQoABQEKAAUBCgAFAQoADAEKAAwBCgAMAQoABQAAAAUAAAAFAAAABAAAAAQAAAAEAAAABQAAAAUAAAAFAAAABAAAAAQAAAAEAAAABQAAAAUAAAAFAAAABAAAAAQAAAAEAAAABQAAAAUAAAAFAAAABAAAAAQAAAAEAAAABQAAAAUAAAAFAAAABAAAAAQAAAAEAAAABQAAAAUAAAAFAAAABAAAAAQAAAAEAAAABQAAAAUAAAAFAAAABAAAAAQAAAAEAAAABQAAAAUAAAAFAAAABAAAAAQAAAAEAAAADAAAAAwAAAAMAAAACwEAAAsBAAALAQAADAAAAAwAAAAMAAAACwEAAAsBAAALAQAADAAAAAwAAAAMAAAACwEAAAsBAAALAQAADAAAAAwAAAAMAAAACwEAAAsBAAALAQAADAAAAAwAAAAMAAAACwEAAAsBAAALAQAADAAAAAwAAAAMAAAACwEAAAsBAAALAQAADAAAAAwAAAAMAAAACwEAAAsBAAALAQAADAAAAAwAAAAMAAAACwEAAAsBAAALAQAADAEAAAwBAAAMAQAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAEAAAwBAAAMAQAADAMAAAwDAAAMAwAADAEDAAwBAwAMAQMADAEAAAwBAAAMAQAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAgAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAsCAAALAgAACwIAAAsCAAALAgAACwIAAAsCAAALAgAACwIAAAsCAAALAgAACwIAAAsCAAALAgAACwIAAAsAAgALAAIACwACAAsCAAALAgAACwIAAAsCAAALAgAACwIAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAABAwAAAQMAAAEDAAADAQAAAwEAAAMBAAADAQAAAwEAAAMBAAADAQAAAwEAAAMBAAADAQAAAwEAAAMBAAADAQAAAwEAAAMBAAADAQAAAwEAAAMBAAADAQAAAwEAAAMBAAADAQAAAwEAAAMBAAAECgAABAoAAAQKAAAGCgAABgoAAAYKAAAECgAABAoAAAQKAAAGCgAABgoAAAYKAAAECgAABAoAAAQKAAAGCgAABgoAAAYKAAAECgAABAoAAAQKAAAGCgAABgoAAAYKAAAECgAABAoAAAQKAAAGCgAABgoAAAYKAAAEBQoABAUKAAQFCgAGAAAABgAAAAYAAAAEBQoABAUKAAQFCgAGAAAABgAAAAYAAAAECgAABAoAAAQKAAAGBQoABgUKAAYFCgAHCgAABwoAAAcKAAAFBAoABQQKAAUECgAHCgAABwoAAAcKAAAFBAoABQQKAAUECgAHCgAABwoAAAcKAAAFCgAABQoAAAUKAAAHCgAABwoAAAcKAAAFCgAABQoAAAUKAAAHCgAABwoAAAcKAAAFAAAABQAAAAUAAAAHCgAABwoAAAcKAAAFAAAABQAAAAUAAAAHCgAABwoAAAcKAAAFAAAABQAAAAUAAAAHCgAABwoAAAcKAAAFBAoABQQKAAUECgAGCgAABgoAAAYKAAAICgAACAoAAAgKAAAGCgAABgoAAAYKAAAICgUACAoFAAgKBQAGCgAABgoAAAYKAAAICgUACAoFAAgKBQAGCgAABgoAAAYKAAAICgAACAoAAAgKAAAGCgAABgoAAAYKAAAICgAACAoAAAgKAAAGCgAABgoAAAYKAAAICgAACAoAAAgKAAAGCgAABgoAAAYKAAAICgAACAoAAAgKAAAGCgAABgoAAAYKAAAIAAAACAAAAAgAAAAJCgAACQoAAAkKAAAHCgAABwoAAAcKAAAJCgAACQoAAAkKAAAHCgAABwoAAAcKAAAJCgAACQoAAAkKAAAHCgAABwoAAAcKAAAJCgAACQoAAAkKAAAHCgAABwoAAAcKAAAJCgAACQoAAAkKAAAHCgAABwoAAAcKAAAJCgAACQoAAAkKAAAHCgAABwoAAAcKAAAJCgAACQoAAAkKAAAHCgAABwoAAAcKAAAJAAAACQAAAAkAAAAHCgAABwoAAAcKAAAEBQoABAUKAAQFCgAEBQoABAUKAAQFCgAEBQoABAUKAAQFCgAEBQoABAUKAAQFCgAEBQoABAUKAAQFCgAEBQoABAUAAAQFAAAEBQAABAUAAAQFCgAEBQoABAUKAAQFCgAEBQoABAUKAAQFCgAEBQoABAUKAAQFCgAEBQoABAUKAAUECgAFBAoABQQKAAUECgAFBAoABQQKAAUECgAFBAoABQQKAAUECgAFBAoABQQKAAUEAAAFBAAABQQAAAUEAAAFBAoABQQKAAUECgAFBAoABQQKAAUECgAFBAoABQQKAAUECgAFBAoABQQKAAUECgAFBAoABQQKAAUECgAFBAoABAUKAAQFCgAEBQoABAUKAAQFCgAEBQoABAUKAAQFCgAEBQoABAUKAAQFCgAEBQoABAUKAAQFCgAEBQoABAUKAAQFCgAEBQoABAUKAAQFCgAEBQoABAUKAAQFCgAEBQoABAUKAAQFCgAEBQoABAUKAAQFCgAEBQoABAUKAAQFCgAFBAoABQQKAAUECgAFBAoABAUKAAQFCgAEBQoABAUKAAUECgAFBAoABQQKAAUECgAFBAoABQQKAAUECgAFBAoABAUKAAQFCgAEBQoABAUKAAUECgAFBAoABQQKAAUECgAFBAoABQQKAAUECgAFBAoA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iQCJAH0AcQA5A4gAjgA5A44APQM8A40AYwA8A2MAIAOMAGEAZgBmAGwAcgByAHkAgACAAIYAjACMAGYAcgByAIAAjACQAJMAmQCQAJkAlgCXAJoAoACXAKAAnQCcAJ8ApgCcAKYAowCiAKUAqwCiAKsAqACpAKwAsQCpALEArgCvALIAtwCvALcAtAChAJsAlQCVAL8AuQC5ALMArQCtAKcAoQChAJUAuQC5AK0AoQC1ALgAvgC1AL4AuwC6AL0AlAC6AJQAkQC8AJIAmACYAJ4ApACkAKoAsACwALYAvAC8AJgApACkALAAvADsAOoAmwHsAJsBlwHzAPcAFwHzABcBEwEOARABMgEOATIBLwHrAO0ADQHrAA0BCwHiAOcABgHiAAYBAAHZAN0A/ADZAPwA+AD1ANsA+wD1APsAFQHvAPIAEgHvABIBDwHlAOkACAHlAAgBBAHfAOMAAQHfAAEB/gAYARwBPAEYATwBOAEFAQkBKQEFASkBJQH/AAMBIwH/ACMBHwERARYBNwERATcBMwEKAQwBLAEKASwBKgECAQcBJwECAScBIgH5AP0AHQH5AB0BGQEUAfoAGgEUARoBNAFTAVcBdwFTAXcBcwE1ARsBOgE1AToBVAEuATABUAEuAVABTgEkASgBSAEkAUgBRAEeASEBQQEeAUEBPgExATYBVgExAVYBUQErAS0BTAErAUwBSgEgASYBRgEgAUYBQAFeAWEBfwFeAX8BfAFLAU0BbQFLAW0BawFCAUcBZwFCAWcBYgE5AT0BXQE5AV0BWQFVATsBWwFVAVsBdQFPAVIBcgFPAXIBbwFFAUkBaQFFAWkBZQE/AUMBYwE/AWMBXwGNAXkBfQF9AYABgwGDAYUBhwGHAYoBjQGNAX0BgwGDAYcBjQFxAXYBjwFxAY8BjAFqAWwBiAFqAYgBhgFgAWYBggFgAYIBfgFYAVwBewFYAXsBeAF0AVoBegF0AXoBjgFuAXABiwFuAYsBiQFkAWgBhAFkAYQBgQGiAaUBkAGQAZMBlgGWAZkBnAGcAZ8BogGiAZABlgGWAZwBogHCAccB5wHCAecB4gH2APEAlQH2AJUBkgHeAeEBAwLeAQMC/wG7Ab0B3QG7Ad0B2wGzAbcB1gGzAdYB0QGpAa0BzAGpAcwByAHFAasBywHFAcsB5QG/AcMB4wG/AeMB3wG1AbkB2AG1AdgB1AGvAbIB0AGvAdABzgHoAewBDALoAQwCCALVAdkB+QHVAfkB9QHPAdIB8gHPAfIB7wHgAeYBBwLgAQcCAgLaAdwB/AHaAfwB+gHTAdcB9wHTAfcB8wHJAc0B7QHJAe0B6QHkAcoB6gHkAeoBBAIiAicCRwIiAkcCQgIFAusBCgIFAgoCJAL+AQECIQL+ASECHgL0AfgBGAL0ARgCFALuAfABEALuARACDgIAAgYCJgIAAiYCIAL7Af0BHAL7ARwCGgLxAfYBFgLxARYCEQIuAjACTgIuAk4CTAIbAh0CPQIbAj0COwITAhcCNwITAjcCMwIJAg0CLQIJAi0CKQIlAgsCKwIlAisCRQIfAiMCQwIfAkMCPwIVAhkCOQIVAjkCNQIPAhICMgIPAjICLwJdAkkCTQJNAlACUwJTAlUCVwJXAloCXQJdAk0CUwJTAlcCXQJAAkYCXwJAAl8CWwI6AjwCWAI6AlgCVgIxAjYCUgIxAlICTwIoAiwCSwIoAksCSAJEAioCSgJEAkoCXgI+AkECXAI+AlwCWQI0AjgCVAI0AlQCUQLmAOAAoAHmAKABngHcANgApgHcAKYBowHaAPQAkQHaAJEBpwHwAO4AmAHwAJgBlAHoAOQAnQHoAJ0BmgHhAN4ApAHhAKQBoQG2AbEB1AC2AdQA0QDVAMYAwADAAMMAyQDJAMwAzwDPANIA1QDVAMAAyQDJAM8A1QCsAagBxwCsAccA1gCqAcQBwQCqAcEAyADBAb4BywDBAcsAxQC4AbQB0AC4AdAAzQCwAa4B1wCwAdcA0wDGAcABxADGAcQAwgC8AboBzgC8Ac4AygBgAmMCaQJgAmkCZgJnAmoCcAJnAnACbQJsAm8CdgJsAnYCcwJyAnUCewJyAnsCeAJ5AnwCgQJ5AoECfgJ/AoIChwJ/AocChAJxAmsCZQJlAo8CiQKJAoMCfQJ9AncCcQJxAmUCiQKJAn0CcQKFAogCjgKFAo4CiwKKAo0CZAKKAmQCYQKMAmICaAJoAm4CdAJ0AnoCgAKAAoYCjAKMAmgCdAJ0AoACjAKQApMCmQKQApkClgKXApoCoAKXAqACnQKcAp8CpgKcAqYCowKiAqUCqwKiAqsCqAKpAqwCsQKpArECrgKvArICtwKvArcCtAKhApsClQKVAr8CuQK5ArMCrQKtAqcCoQKhApUCuQK5Aq0CoQK1ArgCvgK1Ar4CuwK6Ar0ClAK6ApQCkQK8ApICmAKYAp4CpAKkAqoCsAKwArYCvAK8ApgCpAKkArACvALAAsMCyQLAAskCxgLHAsoC0ALHAtACzQLMAs8C1gLMAtYC0wLSAtUC2wLSAtsC2ALZAtwC4QLZAuEC3gLfAuIC5wLfAucC5ALRAssCxQLFAu8C6QLpAuMC3QLdAtcC0QLRAsUC6QLpAt0C0QLlAugC7gLlAu4C6wLqAu0CxALqAsQCwQLsAsICyALIAs4C1ALUAtoC4ALgAuYC7ALsAsgC1ALUAuAC7ALwAvMC+QLwAvkC9gL3AvoCAAP3AgAD/QL8Av8CBgP8AgYDAwMCAwUDCwMCAwsDCAMJAwwDEQMJAxEDDgMPAxIDFwMPAxcDFAMBA/sC9QL1Ah8DGQMZAxMDDQMNAwcDAQMBA/UCGQMZAw0DAQMVAxgDHgMVAx4DGwMaAx0D9AIaA/QC8QIcA/IC+AL4Av4CBAMEAwoDEAMQAxYDHAMcA/gCBAMEAxADHAN+Az4DIwN+AyMDYwN7AzsDPwN7Az8DfwN3AzcDOgN3AzoDegNzAzMDNgNzAzYDdgNuAywDMQNuAzEDcgNqAygDLQNqAy0DbwNnAyUDKQNnAykDawNiAyEDJANiAyQDZgNiAF4DQgNiAEIDZwBoAEMDRwNoAEcDbgBtAEYDSwNtAEsDdABzAEoDTgNzAE4DegB4AEwDUAN4AFADfgB/AFEDVAN/AFQDhACFAFUDWQOFAFkDiwCKAFgDXAOKAFwDYACcA2ADZAOcA2QDgAOBA2UDaQOBA2kDhQOEA2gDbQOEA20DiQOIA2wDcAOIA3ADjAONA3EDdAONA3QDkAORA3UDeAORA3gDlAOVA3kDfQOVA30DmQOYA3wDYQOYA2EDnQNaA5oDnwNaA58DXwNXA5cDmwNXA5sDWwNTA5MDlgNTA5YDVgNPA48DkgNPA5IDUgNIA4oDjgNIA44DTQNEA4YDiwNEA4sDSQNBA4MDhwNBA4cDRQNdA54DggNdA4IDQAM=" + } + ] +} diff --git a/assets/whackamole/redbox.gltf b/assets/whackamole/redbox.gltf new file mode 100644 index 0000000..6d89c9a --- /dev/null +++ b/assets/whackamole/redbox.gltf @@ -0,0 +1,164 @@ +{ + "asset": { + "version": "2.0", + "generator": "gltfeditor.com" + }, + "buffers": [ + { + "uri": "data:application/octet-stream;base64,zcxMvc3MTL3NzEw9AAAAAAAAAAAAAKBBAAAAAAAAgD/NzEw9zcxMvc3MTD0AAAAAAAAAAAAAoEEAAIA/AACAP83MTL3NzEw9zcxMPQAAAAAAAAAAAACgQQAAAAAAAAAAzcxMPc3MTD3NzEw9AAAAAAAAAAAAAKBBAACAPwAAAADMzEw9zcxMvczMTD0BAKBBAAAAAFvDNzUAAAAAAACAP83MTD3NzEy9zMxMvQEAoEEAAAAAW8M3NQAAgD8AAIA/zMxMPc3MTD3MzEw9AQCgQQAAAABbwzc1AAAAAAAAAADNzEw9zcxMPczMTL0BAKBBAAAAAFvDNzUAAIA/AAAAAM3MTD3NzEy9zcxMvX59MCcAAAAAAACgwQAAAAAAAIA/zcxMvc3MTL3NzEy9fn0wJwAAAAAAAKDBAACAPwAAgD/NzEw9zcxMPc3MTL1+fTAnAAAAAAAAoMEAAAAAAAAAAM3MTL3NzEw9zcxMvX59MCcAAAAAAACgwQAAgD8AAAAAzcxMvc3MTL3MzEy9AQCgwQAAAABbwzc1AAAAAAAAgD/MzEy9zcxMvczMTD0BAKDBAAAAAFvDNzUAAIA/AACAP83MTL3NzEw9zMxMvQEAoMEAAAAAW8M3NQAAAAAAAAAAzMxMvc3MTD3MzEw9AQCgwQAAAABbwzc1AACAPwAAAADNzEy9zMxMPczMTD0AAAAAAQCgQVvDNzUAAAAAAACAP83MTD3MzEw9zMxMPQAAAAABAKBBW8M3NQAAgD8AAIA/zcxMvc3MTD3MzEy9AAAAAAEAoEFbwzc1AAAAAAAAAADNzEw9zcxMPczMTL0AAAAAAQCgQVvDNzUAAIA/AAAAAM3MTL3NzEy9zMxMvQAAAAABAKDBW8M3NQAAAAAAAIA/zcxMPc3MTL3MzEy9AAAAAAEAoMFbwzc1AACAPwAAgD/NzEy9zMxMvczMTD0AAAAAAQCgwVvDNzUAAAAAAAAAAM3MTD3MzEy9zMxMPQAAAAABAKDBW8M3NQAAgD8AAAAAAAABAAMAAAADAAIABAAFAAcABAAHAAYACAAJAAsACAALAAoADAANAA8ADAAPAA4AEAARABMAEAATABIAFAAVABcAFAAXABYA", + "byteLength": 840 + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteOffset": 0, + "byteLength": 768, + "byteStride": 32, + "target": 34962 + }, + { + "buffer": 0, + "byteOffset": 768, + "byteLength": 72, + "target": 34963 + } + ], + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5126, + "normalized": false, + "count": 24, + "min": [ + -0.05000000074505806, + -0.05000000074505806, + -0.05000000074505806 + ], + "max": [ + 0.05000000074505806, + 0.05000000074505806, + 0.05000000074505806 + ], + "type": "VEC3" + }, + { + "bufferView": 0, + "byteOffset": 12, + "componentType": 5126, + "normalized": false, + "count": 24, + "min": [ + -20.000001907348633, + -20.000001907348633, + -20 + ], + "max": [ + 20.000001907348633, + 20.000001907348633, + 20 + ], + "type": "VEC3" + }, + { + "bufferView": 0, + "byteOffset": 24, + "componentType": 5126, + "normalized": false, + "count": 24, + "min": [ + 0, + 0 + ], + "max": [ + 1, + 1 + ], + "type": "VEC2" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5123, + "normalized": false, + "count": 36, + "min": [ + 0 + ], + "max": [ + 23 + ], + "type": "SCALAR" + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [ + 0, + 1 + ] + } + ], + "nodes": [ + { + "name": "Sphere", + "extensions": { + "KHR_node_visibility": { + "visible": true + } + } + }, + { + "name": "Cube", + "extensions": { + "KHR_node_visibility": { + "visible": true + } + }, + "mesh": 0 + } + ], + "meshes": [ + { + "primitives": [ + { + "material": 0, + "mode": 4, + "attributes": { + "POSITION": 0, + "NORMAL": 1, + "TEXCOORD_0": 2 + }, + "indices": 3 + } + ] + } + ], + "materials": [ + { + "name": "", + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.9176470588235294, + 0.043137254901960784, + 0.043137254901960784, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 0.5 + }, + "emissiveFactor": [ + 0, + 0, + 0 + ], + "alphaMode": "OPAQUE", + "doubleSided": false + } + ], + "extensionsUsed": [ + "KHR_node_visibility" + ] +} \ No newline at end of file diff --git a/assets/whackamole/scoreboard_panel.gltf b/assets/whackamole/scoreboard_panel.gltf new file mode 100644 index 0000000..215e3a3 --- /dev/null +++ b/assets/whackamole/scoreboard_panel.gltf @@ -0,0 +1,148 @@ +{ + "asset":{ + "generator":"Khronos glTF Blender I/O v4.4.55", + "version":"2.0" + }, + "scene":0, + "scenes":[ + { + "name":"Scene", + "nodes":[ + 0 + ] + } + ], + "nodes":[ + { + "mesh":0, + "name":"Panel", + "rotation":[ + 0.70710688829422, + 0, + 0, + 0.7071066498756409 + ] + } + ], + "materials":[ + { + "doubleSided":true, + "name":"PanelColorPlaceHolder", + "pbrMetallicRoughness":{ + "baseColorTexture":{ + "index":0 + }, + "metallicFactor":0, + "roughnessFactor":0.5 + } + } + ], + "meshes":[ + { + "name":"PlaneMeshForPanel", + "primitives":[ + { + "attributes":{ + "POSITION":0, + "NORMAL":1, + "TEXCOORD_0":2 + }, + "indices":3, + "material":0 + } + ] + } + ], + "textures":[ + { + "sampler":0, + "source":0 + } + ], + "images":[ + { + "bufferView":4, + "mimeType":"image/png", + "name":"Holder" + } + ], + "accessors":[ + { + "bufferView":0, + "componentType":5126, + "count":4, + "max":[ + 0.25, + 0, + 0.25 + ], + "min":[ + -0.25, + 0, + -0.25 + ], + "type":"VEC3" + }, + { + "bufferView":1, + "componentType":5126, + "count":4, + "type":"VEC3" + }, + { + "bufferView":2, + "componentType":5126, + "count":4, + "type":"VEC2" + }, + { + "bufferView":3, + "componentType":5123, + "count":6, + "type":"SCALAR" + } + ], + "bufferViews":[ + { + "buffer":0, + "byteLength":48, + "byteOffset":0, + "target":34962 + }, + { + "buffer":0, + "byteLength":48, + "byteOffset":48, + "target":34962 + }, + { + "buffer":0, + "byteLength":32, + "byteOffset":96, + "target":34962 + }, + { + "buffer":0, + "byteLength":12, + "byteOffset":128, + "target":34963 + }, + { + "buffer":0, + "byteLength":196136, + "byteOffset":140 + } + ], + "samplers":[ + { + "magFilter":9729, + "minFilter":9987 + } + ], + "buffers":[ + { + "byteLength":196276, + "uri":"data:application/octet-stream;base64,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" + } + ] +} diff --git a/assets/yolo_det/yolo.serialized.json b/assets/yolo_det/yolo.serialized.json new file mode 100644 index 0000000..1bb5665 --- /dev/null +++ b/assets/yolo_det/yolo.serialized.json @@ -0,0 +1,95 @@ +{ + "version": "QNN_SYSTEM_CONTEXT_BINARY_INFO_VERSION_3", + "info": { + "backendId": 6, + "buildId": "v2.29.0.241129103708_105762", + "coreApiVersion": "2.22.0", + "backendApiVersion": "5.29.0", + "socVersion": "", + "contextBlobVersion": "3.2.0", + "contextBlobSize": 6955304, + "numContextTensors": 0, + "contextTensors": [], + "numGraphs": 1, + "graphs": [ + { + "version": "QNN_SYSTEM_CONTEXT_GRAPH_INFO_VERSION_3", + "info": { + "graphName": "yolo11n", + "numGraphInputs": 1, + "graphInputs": [ + { + "version": "QNN_TENSOR_VERSION_1", + "info": { + "id": 1, + "name": "images", + "type": "QNN_TENSOR_TYPE_APP_WRITE", + "dataFormat": "QNN_TENSOR_DATA_FORMAT_FLAT_BUFFER", + "dataType": "QNN_DATATYPE_FLOAT_16", + "rank": 4, + "dimensions": [ + 1, + 640, + 640, + 3 + ], + "memType": "QNN_TENSORMEMTYPE_RAW", + "quantizeParams": { + "definition": "QNN_DEFINITION_UNDEFINED", + "quantizationEncoding": "QNN_QUANTIZATION_ENCODING_UNDEFINED" + } + } + } + ], + "numGraphOutputs": 1, + "graphOutputs": [ + { + "version": "QNN_TENSOR_VERSION_1", + "info": { + "id": 819, + "name": "output0", + "type": "QNN_TENSOR_TYPE_APP_READ", + "dataFormat": "QNN_TENSOR_DATA_FORMAT_FLAT_BUFFER", + "dataType": "QNN_DATATYPE_FLOAT_16", + "rank": 3, + "dimensions": [ + 1, + 8400, + 84 + ], + "memType": "QNN_TENSORMEMTYPE_RAW", + "quantizeParams": { + "definition": "QNN_DEFINITION_UNDEFINED", + "quantizationEncoding": "QNN_QUANTIZATION_ENCODING_UNDEFINED" + } + } + } + ], + "numUpdateableTensors": 0, + "updateableTensors": [], + "graphBlobInfoSize": 40, + "graphBlobInfo": [ + { + "version": "QNN_SYSTEM_CONTEXT_HTP_GRAPH_INFO_BLOB_VERSION_V1", + "info": { + "spillFillBufferSize": 0, + "optimizationLevel": 0, + "vtcmSize": 4, + "htpDlbc": 0, + "numHvxThreads": 0 + } + } + ] + } + } + ], + "contextMetadataSize": 8, + "contextMetadata": { + "version": "QNN_SYSTEM_CONTEXT_HTP_CONTEXT_INFO_BLOB_VERSION_V1", + "info": { + "dsp arch": 68 + } + }, + "soc model": 0 + } +} diff --git a/assets/yolo_det/yolom.serialized.json b/assets/yolo_det/yolom.serialized.json new file mode 100644 index 0000000..123204c --- /dev/null +++ b/assets/yolo_det/yolom.serialized.json @@ -0,0 +1,95 @@ +{ + "version": "QNN_SYSTEM_CONTEXT_BINARY_INFO_VERSION_3", + "info": { + "backendId": 6, + "buildId": "v2.29.0.241129103708_105762", + "coreApiVersion": "2.22.0", + "backendApiVersion": "5.29.0", + "socVersion": "", + "contextBlobVersion": "3.2.0", + "contextBlobSize": 58962592, + "numContextTensors": 0, + "contextTensors": [], + "numGraphs": 1, + "graphs": [ + { + "version": "QNN_SYSTEM_CONTEXT_GRAPH_INFO_VERSION_3", + "info": { + "graphName": "yolo11m", + "numGraphInputs": 1, + "graphInputs": [ + { + "version": "QNN_TENSOR_VERSION_1", + "info": { + "id": 1, + "name": "images", + "type": "QNN_TENSOR_TYPE_APP_WRITE", + "dataFormat": "QNN_TENSOR_DATA_FORMAT_FLAT_BUFFER", + "dataType": "QNN_DATATYPE_FLOAT_16", + "rank": 4, + "dimensions": [ + 1, + 640, + 640, + 3 + ], + "memType": "QNN_TENSORMEMTYPE_RAW", + "quantizeParams": { + "definition": "QNN_DEFINITION_UNDEFINED", + "quantizationEncoding": "QNN_QUANTIZATION_ENCODING_UNDEFINED" + } + } + } + ], + "numGraphOutputs": 1, + "graphOutputs": [ + { + "version": "QNN_TENSOR_VERSION_1", + "info": { + "id": 1029, + "name": "output0", + "type": "QNN_TENSOR_TYPE_APP_READ", + "dataFormat": "QNN_TENSOR_DATA_FORMAT_FLAT_BUFFER", + "dataType": "QNN_DATATYPE_FLOAT_16", + "rank": 3, + "dimensions": [ + 1, + 8400, + 84 + ], + "memType": "QNN_TENSORMEMTYPE_RAW", + "quantizeParams": { + "definition": "QNN_DEFINITION_UNDEFINED", + "quantizationEncoding": "QNN_QUANTIZATION_ENCODING_UNDEFINED" + } + } + } + ], + "numUpdateableTensors": 0, + "updateableTensors": [], + "graphBlobInfoSize": 40, + "graphBlobInfo": [ + { + "version": "QNN_SYSTEM_CONTEXT_HTP_GRAPH_INFO_BLOB_VERSION_V1", + "info": { + "spillFillBufferSize": 0, + "optimizationLevel": 0, + "vtcmSize": 4, + "htpDlbc": 0, + "numHvxThreads": 0 + } + } + ] + } + } + ], + "contextMetadataSize": 8, + "contextMetadata": { + "version": "QNN_SYSTEM_CONTEXT_HTP_CONTEXT_INFO_BLOB_VERSION_V1", + "info": { + "dsp arch": 68 + } + }, + "soc model": 0 + } +} diff --git a/base/base.cmake b/base/base.cmake index c8be22c..1dfa816 100644 --- a/base/base.cmake +++ b/base/base.cmake @@ -1,4 +1,39 @@ # common dependencies for all demo projects + +# Dependency: OpenXR +include("${CMAKE_CURRENT_LIST_DIR}/../external/openxr/openxr.cmake") + +if (ANDROID AND NOT Vulkan_LIBRARY) + find_library(Vulkan_LIBRARY NAMES vulkan) +endif() +#message(FATAL_ERROR "READBACK_USE_GPU ${READBACK_USE_GPU}") + +if (READBACK_USE_OPENGL) + if (NOT ANDROID) + message(FATAL_ERROR "The OpenGL ES readback path now only supports Android builds.") + endif() + + set(XR_USE_GRAPHICS_API_OPENGL_ES TRUE) + add_definitions(-DXR_USE_GRAPHICS_API_OPENGL_ES) + message(STATUS "Enabling Android OpenGL ES support") + + # Only bring in the OpenGL wrapper now located under oxr_utils + set(COMMON_LIB_SRCS ${CMAKE_CURRENT_LIST_DIR}/oxr_utils/gfxwrapper_opengl.c) + add_library(common_lib ${COMMON_LIB_SRCS}) + target_include_directories(common_lib PUBLIC ${CMAKE_CURRENT_LIST_DIR}/oxr_utils) + target_link_libraries(common_lib PUBLIC EGL GLESv3) +else() + find_package(Vulkan) + if(Vulkan_FOUND) + set(XR_USE_GRAPHICS_API_VULKAN TRUE) + add_definitions(-DXR_USE_GRAPHICS_API_VULKAN) + message(STATUS "Enabling Vulkan support") + elseif(BUILD_ALL_EXTENSIONS) + message(FATAL_ERROR "Vulkan headers not found") + endif() +endif() + + set(BASE_SRCS ${CMAKE_CURRENT_LIST_DIR}/oxr_utils/d3d_common.cpp ${CMAKE_CURRENT_LIST_DIR}/oxr_utils/graphicsplugin_d3d11.cpp @@ -27,39 +62,51 @@ if (USE_SECURE_MR_UTILS) list(APPEND SECUREMR_UTILS_SRCS ${CMAKE_CURRENT_LIST_DIR}/securemr_utils/pipeline.cpp ${CMAKE_CURRENT_LIST_DIR}/securemr_utils/rendercommand.cpp + ${CMAKE_CURRENT_LIST_DIR}/securemr_utils/serialization.cpp ${CMAKE_CURRENT_LIST_DIR}/securemr_utils/session.cpp ${CMAKE_CURRENT_LIST_DIR}/securemr_utils/tensor.cpp + ${CMAKE_CURRENT_LIST_DIR}/securemr_utils/readback_async.cpp + ${CMAKE_CURRENT_LIST_DIR}/securemr_utils/readback.cpp + ${CMAKE_CURRENT_LIST_DIR}/securemr_utils/utils.cpp ) endif() +set(BASE_THIRD_PARTY_DIRS ${CMAKE_CURRENT_LIST_DIR}/third_party) -# Dependency: OpenXR -include("${CMAKE_CURRENT_LIST_DIR}/../external/openxr/openxr.cmake") -# Dependency: Vulkan -find_package(Vulkan) -if(Vulkan_FOUND) - set(XR_USE_GRAPHICS_API_VULKAN TRUE) - add_definitions(-DXR_USE_GRAPHICS_API_VULKAN) - message(STATUS "Enabling Vulkan support") -elseif(BUILD_ALL_EXTENSIONS) - message(FATAL_ERROR "Vulkan headers not found") +# Dependency: nlohmann_json +include(FetchContent) +FetchContent_Declare( + nlohmann_json + URL https://github.com/nlohmann/json/archive/refs/tags/v3.11.3.tar.gz +) +FetchContent_GetProperties(nlohmann_json) +if(NOT nlohmann_json_POPULATED) + FetchContent_Populate(nlohmann_json) endif() add_library( ${PROJECT_NAME} MODULE ${BASE_SRCS} ${SAMPLE_SRCS} - ${VULKAN_SHADERS} ${SECUREMR_UTILS_SRCS} + ${VULKAN_SHADERS} ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c ) +target_link_libraries( + ${PROJECT_NAME} PRIVATE + android + log + OpenXR::openxr_loader + ${Vulkan_LIBRARY} +) + +if (READBACK_USE_OPENGL) target_link_libraries( - ${PROJECT_NAME} PRIVATE - android - log - OpenXR::openxr_loader - ${Vulkan_LIBRARY} + ${PROJECT_NAME} PRIVATE + common_lib ) +endif() + target_include_directories(${PROJECT_NAME} PRIVATE "${ANDROID_NDK}/sources/android/native_app_glue" ${CMAKE_CURRENT_LIST_DIR} @@ -68,6 +115,9 @@ target_include_directories(${PROJECT_NAME} PRIVATE ${SAMPLE_DIR} ${CMAKE_SOURCE_DIR} ${Vulkan_INCLUDE_DIRS} + ${nlohmann_json_SOURCE_DIR}/single_include + ${BASE_THIRD_PARTY_DIRS} + ${THIRD_PARTY_DIRS} ) # Shader compilation for **client** @@ -78,8 +128,23 @@ if(GLSLANG_VALIDATOR AND NOT GLSLC_COMMAND) target_compile_definitions(${PROJECT_NAME} PRIVATE USE_GLSLANGVALIDATOR) endif() add_dependencies(${PROJECT_NAME} run_glsl_compiles) +if (NOT READBACK_USE_GPU) + target_compile_definitions(${PROJECT_NAME} PRIVATE + DEFAULT_GRAPHICS_PLUGIN_VULKAN + XR_USE_PLATFORM_ANDROID + XR_READBACK_USE_CPU + XR_USE_GRAPHICS_API_VULKAN) +endif () +if (READBACK_USE_VULKAN) target_compile_definitions(${PROJECT_NAME} PRIVATE DEFAULT_GRAPHICS_PLUGIN_VULKAN XR_USE_PLATFORM_ANDROID XR_USE_GRAPHICS_API_VULKAN) +endif () +if (READBACK_USE_OPENGL) +target_compile_definitions(${PROJECT_NAME} PRIVATE + DEFAULT_GRAPHICS_PLUGIN_OPENGLES + XR_USE_PLATFORM_ANDROID + XR_USE_GRAPHICS_API_OPENGL_ES) +endif () diff --git a/base/main.cpp b/base/main.cpp index ce1fd1c..b76bb81 100644 --- a/base/main.cpp +++ b/base/main.cpp @@ -14,6 +14,7 @@ #include "openxr_program.h" AAssetManager* g_assetManager; +std::string g_internalDataPath; namespace { @@ -181,6 +182,7 @@ static void app_handle_cmd(struct android_app* app, int32_t cmd) { } } +android_app* IOpenXrProgram::gapp = nullptr; void android_main(struct android_app* app) { Log::Write(Log::Level::Error, "=========== main ==========="); try { @@ -189,6 +191,12 @@ void android_main(struct android_app* app) { AAssetManager* assetManager = app->activity->assetManager; g_assetManager = assetManager; + IOpenXrProgram::gapp = app; + if (app->activity->internalDataPath != nullptr) { + g_internalDataPath = app->activity->internalDataPath; + } else { + g_internalDataPath.clear(); + } AndroidAppState appState = {}; @@ -241,6 +249,7 @@ void android_main(struct android_app* app) { while (app->destroyRequested == 0) { // Read all pending events. + for (;;) { int events; struct android_poll_source* source; @@ -258,6 +267,7 @@ void android_main(struct android_app* app) { } } + program->TickSecureMr(); program->PollEvents(&exitRenderLoop, &requestRestart); if (exitRenderLoop) { ANativeActivity_finish(app->activity); diff --git a/base/openxr_program.cpp b/base/openxr_program.cpp index bd391ec..bb5f18f 100644 --- a/base/openxr_program.cpp +++ b/base/openxr_program.cpp @@ -5,12 +5,13 @@ // This file may have been modified by Bytedance Ltd. and/or its affiliates ("Bytedance's Modifications"). All // Bytedance's Modifications are Copyright (2025) Bytedance Ltd. and/or its affiliates. -#include "openxr/openxr.h" #include "pch.h" +#include "openxr/openxr.h" #include "common.h" #include "options.h" #include "platformplugin.h" #include "graphicsplugin.h" +#include "oxr_utils/geometry.h" #include "openxr_program.h" #include "xr_linear.h" #include @@ -193,6 +194,13 @@ struct OpenXrProgram : IOpenXrProgram { [](const std::string& ext) { return ext.c_str(); }); extensions.push_back(XR_PICO_SECURE_MIXED_REALITY_EXTENSION_NAME); + extensions.push_back(XR_PICO_READBACK_TENSOR_EXTENSION_NAME); +#ifdef XR_USE_GRAPHICS_API_VULKAN + extensions.push_back(XR_PICO_READBACK_TENSOR_VULKAN_EXTENSION_NAME); +#endif +#ifdef XR_USE_GRAPHICS_API_OPENGL_ES + extensions.push_back(XR_PICO_READBACK_TENSOR_OPENGLES_EXTENSION_NAME); +#endif extensions.push_back(XR_FB_DISPLAY_REFRESH_RATE_EXTENSION_NAME); XrInstanceCreateInfo createInfo{XR_TYPE_INSTANCE_CREATE_INFO}; @@ -573,6 +581,13 @@ struct OpenXrProgram : IOpenXrProgram { LogReferenceSpaces(); InitializeActions(); CreateVisualizedSpaces(); + // Create a pure view space for overlays + { + XrReferenceSpaceCreateInfo referenceSpaceCreateInfo = GetXrReferenceSpaceCreateInfo("View"); + XrSpace viewSpace{XR_NULL_HANDLE}; + CHECK_XRCMD(xrCreateReferenceSpace(m_session, &referenceSpaceCreateInfo, &viewSpace)); + m_viewSpace = viewSpace; + } { XrReferenceSpaceCreateInfo referenceSpaceCreateInfo = GetXrReferenceSpaceCreateInfo(m_options->AppSpace); @@ -680,6 +695,8 @@ struct OpenXrProgram : IOpenXrProgram { m_swapchainImages.insert(std::make_pair(swapchain.handle, std::move(swapchainImages))); } + // Create overlay quad swapchain after main swapchains are ready + CreateOverlaySwapchain(); } } @@ -706,7 +723,6 @@ struct OpenXrProgram : IOpenXrProgram { void PollEvents(bool* exitRenderLoop, bool* requestRestart) override { *exitRenderLoop = *requestRestart = false; - // Process all pending messages. while (const XrEventDataBaseHeader* event = TryReadNextEvent()) { switch (event->type) { @@ -896,6 +912,59 @@ struct OpenXrProgram : IOpenXrProgram { if (RenderLayer(frameState.predictedDisplayTime, projectionLayerViews, layer)) { layers.push_back(reinterpret_cast(&layer)); } + if (m_overlayInitialized && m_secureMrProgram && m_secureMrProgram->WantsScanOverlay()) { + // Ensure overlay swapchain has a recently released image for the runtime + XrSwapchainImageAcquireInfo acquireInfo{XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO}; + uint32_t overlayImageIndex = 0; + CHECK_XRCMD(xrAcquireSwapchainImage(m_overlaySwapchain.handle, &acquireInfo, &overlayImageIndex)); + XrSwapchainImageWaitInfo waitInfo{XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO}; + waitInfo.timeout = XR_INFINITE_DURATION; + CHECK_XRCMD(xrWaitSwapchainImage(m_overlaySwapchain.handle, &waitInfo)); + + // Allow the app to update overlay RGBA content dynamically. + const int W = m_overlaySwapchain.width; + const int H = m_overlaySwapchain.height; + if (m_secureMrProgram->UpdateOverlayRgba(W, H, m_overlayRgba)) { + if (m_overlayRgba.size() != static_cast(W) * H * 4) { + // Ensure buffer size correctness if the implementation changed it. + m_overlayRgba.resize(static_cast(W) * H * 4, 0); + } + m_graphicsPlugin->UploadRgbaToSwapchainImages(m_overlayImages, W, H, m_overlayRgba); + Log::Write(Log::Level::Debug, + Fmt("OpenXR|Overlay: Uploaded dynamic RGBA (%dx%d, bytes=%zu)", W, H, m_overlayRgba.size())); + } + + XrSwapchainImageReleaseInfo releaseInfo{XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO}; + CHECK_XRCMD(xrReleaseSwapchainImage(m_overlaySwapchain.handle, &releaseInfo)); + + // Submit two quads with different poses for left/right eyes + XrCompositionLayerQuad quadL{XR_TYPE_COMPOSITION_LAYER_QUAD}; + quadL.space = m_viewSpace == XR_NULL_HANDLE ? m_appSpace : m_viewSpace; + quadL.eyeVisibility = XR_EYE_VISIBILITY_LEFT; + quadL.layerFlags = + m_options->Parsed.EnvironmentBlendMode == XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND + ? XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT | XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT + : 0; + quadL.subImage.swapchain = m_overlaySwapchain.handle; + quadL.subImage.imageArrayIndex = 0; + quadL.subImage.imageRect.offset = {0, 0}; + quadL.subImage.imageRect.extent = {m_overlaySwapchain.width, m_overlaySwapchain.height}; + quadL.pose = Math::Pose::Translation({0.0f, 0.f, -0.35f}); + quadL.size = {0.3f, 0.3f}; + layers.push_back(reinterpret_cast(&quadL)); + + XrCompositionLayerQuad quadR{XR_TYPE_COMPOSITION_LAYER_QUAD}; + quadR.space = m_viewSpace == XR_NULL_HANDLE ? m_appSpace : m_viewSpace; + quadR.eyeVisibility = XR_EYE_VISIBILITY_RIGHT; + quadR.layerFlags = quadL.layerFlags; + quadR.subImage.swapchain = m_overlaySwapchain.handle; + quadR.subImage.imageArrayIndex = 0; + quadR.subImage.imageRect.offset = {0, 0}; + quadR.subImage.imageRect.extent = {m_overlaySwapchain.width, m_overlaySwapchain.height}; + quadR.pose = Math::Pose::Translation({0.0f, 0.f, -0.35f}); + quadR.size = quadL.size; + layers.push_back(reinterpret_cast(&quadR)); + } } // End the frame without standard layers @@ -907,6 +976,69 @@ struct OpenXrProgram : IOpenXrProgram { CHECK_XRCMD(xrEndFrame(m_session, &frameEndInfo)); } + void CreateOverlaySwapchain() { + if (m_overlayInitialized) return; + // Choose overlay size and format + m_overlaySwapchain.width = 1024; + m_overlaySwapchain.height = 1024; + + XrSwapchainCreateInfo swapchainCreateInfo{XR_TYPE_SWAPCHAIN_CREATE_INFO}; + swapchainCreateInfo.arraySize = 1; + swapchainCreateInfo.format = m_colorSwapchainFormat; + swapchainCreateInfo.width = m_overlaySwapchain.width; + swapchainCreateInfo.height = m_overlaySwapchain.height; + swapchainCreateInfo.mipCount = 1; + swapchainCreateInfo.faceCount = 1; + swapchainCreateInfo.sampleCount = 1; + swapchainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_TRANSFER_DST_BIT | XR_SWAPCHAIN_USAGE_SAMPLED_BIT; + CHECK_XRCMD(xrCreateSwapchain(m_session, &swapchainCreateInfo, &m_overlaySwapchain.handle)); + + uint32_t imageCount = 0; + CHECK_XRCMD(xrEnumerateSwapchainImages(m_overlaySwapchain.handle, 0, &imageCount, nullptr)); + m_overlayImages = m_graphicsPlugin->AllocateSwapchainImageStructs(imageCount, swapchainCreateInfo); + CHECK_XRCMD(xrEnumerateSwapchainImages(m_overlaySwapchain.handle, imageCount, &imageCount, m_overlayImages[0])); + + // Generate an RGBA image with transparent background and a 500x500 red border + const int W = m_overlaySwapchain.width; + const int H = m_overlaySwapchain.height; + m_overlayRgba.assign(static_cast(W) * H * 4, 0); + const int rectW = 500; + const int rectH = 500; + const int thickness = 12; // increased line width for red border + const int left = (W - rectW) / 2; + const int top = (H - rectH) / 2 - 100; + const int right = left + rectW - 1; + const int bottom = top + rectH - 1; + auto setPixel = [&](int x, int y) { + if (x < 0 || x >= W || y < 0 || y >= H) return; + size_t idx = (static_cast(y) * W + x) * 4; + m_overlayRgba[idx + 0] = 255; + m_overlayRgba[idx + 1] = 0; + m_overlayRgba[idx + 2] = 0; + m_overlayRgba[idx + 3] = 255; + }; + for (int t = 0; t < thickness; ++t) { + int yTop = top + t; + int yBot = bottom - t; + for (int x = left; x <= right; ++x) { + setPixel(x, yTop); + setPixel(x, yBot); + } + } + for (int t = 0; t < thickness; ++t) { + int xL = left + t; + int xR = right - t; + for (int y = top; y <= bottom; ++y) { + setPixel(xL, y); + setPixel(xR, y); + } + } + + m_graphicsPlugin->UploadRgbaToSwapchainImages(m_overlayImages, W, H, m_overlayRgba); + m_overlayInitialized = true; + Log::Write(Log::Level::Info, Fmt("OpenXR|Overlay: Created overlay swapchain %dx%d and uploaded initial content", W, H)); + } + bool RenderLayer(XrTime predictedDisplayTime, std::vector& projectionLayerViews, XrCompositionLayerProjection& layer) { XrResult res; @@ -935,6 +1067,8 @@ struct OpenXrProgram : IOpenXrProgram { // For each locatable space that we want to visualize, render a 25cm cube. std::vector cubes; + const bool renderControllerCubes = + (m_secureMrProgram == nullptr) || m_secureMrProgram->WantsControllerVisualization(); static auto firstLoadingTime = std::chrono::steady_clock::now(); if (!m_secureMrProgram->LoadingFinished()) { @@ -960,7 +1094,7 @@ struct OpenXrProgram : IOpenXrProgram { } } - // Render a 10cm cube scaled by grabAction for each hand. Note renderHand will only be + // Optionally render a 10cm cube scaled by grabAction for each hand. Note renderHand will only be // true when the application has focus. std::array handDeltas{}; for (auto hand : {Side::LEFT, Side::RIGHT}) { @@ -971,7 +1105,9 @@ struct OpenXrProgram : IOpenXrProgram { if ((spaceLocation.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0 && (spaceLocation.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) != 0) { float scale = 0.1f * m_input.handScale[hand]; - cubes.push_back(Cube{spaceLocation.pose, {scale, scale, scale}}); + if (renderControllerCubes) { + cubes.push_back(Cube{spaceLocation.pose, {scale, scale, scale}}); + } if (m_input.handToggle[hand] == InputState::PRESS_DOWN) { if (!m_input.toggleStartPosition[hand]) { @@ -1002,6 +1138,31 @@ struct OpenXrProgram : IOpenXrProgram { } if (m_secureMrProgram) { m_secureMrProgram->UpdateHandPose(handDeltas[0], handDeltas[1]); + // Head motion: compare current view[0] pose with previous + static bool hasPrevHead = false; + static XrPosef prevHeadPose{}; + float linDelta = 0.f; + float angDelta = 0.f; + if (!m_views.empty()) { + const XrPosef& cur = m_views[0].pose; + m_secureMrProgram->UpdateHeadPose(cur); + if (hasPrevHead) { + const float dx = cur.position.x - prevHeadPose.position.x; + const float dy = cur.position.y - prevHeadPose.position.y; + const float dz = cur.position.z - prevHeadPose.position.z; + linDelta = std::sqrt(dx * dx + dy * dy + dz * dz); + // Quaternion delta angle = 2*acos(|dot(q1,q2)|) when normalized + const float dot = std::abs(cur.orientation.x * prevHeadPose.orientation.x + + cur.orientation.y * prevHeadPose.orientation.y + + cur.orientation.z * prevHeadPose.orientation.z + + cur.orientation.w * prevHeadPose.orientation.w); + float clamped = std::min(1.0f, std::max(0.0f, dot)); + angDelta = 2.0f * std::acos(clamped); + } + prevHeadPose = cur; + hasPrevHead = true; + } + m_secureMrProgram->UpdateHeadMotion(linDelta, angDelta); } for (const auto& handPtr : handDeltas) { delete handPtr; @@ -1032,6 +1193,19 @@ struct OpenXrProgram : IOpenXrProgram { m_swapchainImages[viewSwapchain.handle][swapchainImageIndex]; m_graphicsPlugin->RenderView(projectionLayerViews[i], swapchainImage, m_colorSwapchainFormat, cubes); + // Optional: render user-provided procedural mesh (e.g., native Rubik's cube) + if (m_secureMrProgram && m_secureMrProgram->WantsUserMesh()) { + std::vector verts; + std::vector idx; + XrPosef worldPose{}; + worldPose.orientation.w = 1.0f; + if (m_secureMrProgram->UpdateUserMesh(verts, idx, worldPose) && !verts.empty() && !idx.empty()) { + m_graphicsPlugin->RenderUserMesh(projectionLayerViews[i], swapchainImage, m_colorSwapchainFormat, + verts.data(), static_cast(verts.size()), idx.data(), + static_cast(idx.size()), worldPose); + } + } + XrSwapchainImageReleaseInfo releaseInfo{XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO}; CHECK_XRCMD(xrReleaseSwapchainImage(viewSwapchain.handle, &releaseInfo)); } @@ -1048,7 +1222,12 @@ struct OpenXrProgram : IOpenXrProgram { void InitializeSecureMrProgram() override { m_secureMrProgram = SecureMR::CreateSecureMrProgram(m_instance, m_session); + // Provide overlay size before framework/pipeline creation so app can align image sizes. + if (m_secureMrProgram) { + m_secureMrProgram->SetOverlaySize(m_overlaySwapchain.width, m_overlaySwapchain.height); + } m_secureMrProgram->CreateFramework(); + m_secureMrProgram->RequestPermission(IOpenXrProgram::gapp); m_secureMrProgram->CreatePipelines(); } @@ -1060,6 +1239,14 @@ struct OpenXrProgram : IOpenXrProgram { m_secureMrProgram->RunPipelines(); } + void TickSecureMr() override { + if (m_secureMrProgram == nullptr) { + Log::Write(Log::Level::Error, "m_secureMrProgram is nullptr, skip RunSecureMrVSTImagePipeline"); + return; + } + m_secureMrProgram->Tick(); + } + void DestroySecureMr() override { if (m_secureMrProgram != nullptr) { m_secureMrProgram = nullptr; @@ -1093,6 +1280,16 @@ struct OpenXrProgram : IOpenXrProgram { const std::set m_acceptableBlendModes; std::shared_ptr m_secureMrProgram = nullptr; + // Overlay quad swapchain and space + struct OverlaySwapchain { + XrSwapchain handle{XR_NULL_HANDLE}; + int width{0}; + int height{0}; + } m_overlaySwapchain; + bool m_overlayInitialized{false}; + std::vector m_overlayImages; // Overlay swapchain images + std::vector m_overlayRgba; // Staging RGBA buffer for overlay updates + XrSpace m_viewSpace{XR_NULL_HANDLE}; }; } // namespace diff --git a/base/openxr_program.h b/base/openxr_program.h index 43a8973..39c1ca8 100644 --- a/base/openxr_program.h +++ b/base/openxr_program.h @@ -10,6 +10,7 @@ int add(int a, int b); struct IOpenXrProgram { + static android_app* gapp; virtual ~IOpenXrProgram() = default; // Create an Instance and other basic instance-level initialization. @@ -47,12 +48,18 @@ struct IOpenXrProgram { virtual void RunSecureMr() = 0; + virtual void TickSecureMr() = 0; + virtual void DestroySecureMr() = 0; // Get preferred blend mode based on the view configuration specified in the Options virtual XrEnvironmentBlendMode GetPreferredBlendMode() const = 0; }; +struct Options; +class IPlatformPlugin; +class IGraphicsPlugin; + struct Swapchain { XrSwapchain handle; int32_t width; diff --git a/base/oxr_utils/d3d_common.cpp b/base/oxr_utils/d3d_common.cpp index 8e65bb3..1e344b9 100644 --- a/base/oxr_utils/d3d_common.cpp +++ b/base/oxr_utils/d3d_common.cpp @@ -7,7 +7,7 @@ #if defined(XR_USE_GRAPHICS_API_D3D11) || defined(XR_USE_GRAPHICS_API_D3D12) -#include +#include "xr_linear.h" #include #include diff --git a/base/oxr_utils/gfxwrapper_opengl.c b/base/oxr_utils/gfxwrapper_opengl.c new file mode 100644 index 0000000..0f6fe5a --- /dev/null +++ b/base/oxr_utils/gfxwrapper_opengl.c @@ -0,0 +1,2840 @@ +/* +Copyright (c) 2017-2025 The Khronos Group Inc. +Copyright (c) 2016 Oculus VR, LLC. +Portions of macOS, iOS, functionality copyright (c) 2016 The Brenwill Workshop Ltd. + +SPDX-License-Identifier: Apache-2.0 +*/ + +#include "gfxwrapper_opengl.h" + +#if !defined(OS_ANDROID) +#error "This OpenGL wrapper now only supports Android builds." +#endif + +#include +#include +#include + +/* +================================================================================================================================ + +System level functionality + +================================================================================================================================ +*/ + +static void Error(const char *format, ...) { +#if defined(OS_WINDOWS) + char buffer[4096]; + va_list args; + va_start(args, format); + vsnprintf_s(buffer, 4096, _TRUNCATE, format, args); + va_end(args); + + OutputDebugStringA(buffer); + + // MessageBoxA(NULL, buffer, "ERROR", MB_OK | MB_ICONINFORMATION); +#elif defined(OS_LINUX) + va_list args; + va_start(args, format); + vprintf(format, args); + va_end(args); + printf("\n"); + fflush(stdout); +#elif defined(OS_APPLE_MACOS) + char buffer[4096]; + va_list args; + va_start(args, format); + int length = vsnprintf(buffer, 4096, format, args); + va_end(args); + + NSLog(@"%s\n", buffer); + + if ([NSThread isMainThread]) { + NSString *string = [[NSString alloc] initWithBytes:buffer length:length encoding:NSASCIIStringEncoding]; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + NSAlert *alert = [[NSAlert alloc] init]; + [alert addButtonWithTitle:@"OK"]; + [alert setMessageText:@"Error"]; + [alert setInformativeText:string]; + [alert setAlertStyle:NSWarningAlertStyle]; + [alert runModal]; +#pragma GCC diagnostic pop + } +#elif defined(OS_APPLE_IOS) + char buffer[4096]; + va_list args; + va_start(args, format); + int length = vsnprintf(buffer, 4096, format, args); + va_end(args); + + NSLog(@"%s\n", buffer); + + if ([NSThread isMainThread]) { + NSString *string = [[NSString alloc] initWithBytes:buffer length:length encoding:NSASCIIStringEncoding]; + UIAlertController *alert = + [UIAlertController alertControllerWithTitle:@"Error" message:string preferredStyle:UIAlertControllerStyleAlert]; + [alert addAction:[UIAlertAction actionWithTitle:@"OK" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *action){ + }]]; + [UIApplication.sharedApplication.keyWindow.rootViewController presentViewController:alert animated:YES completion:nil]; + } +#elif defined(OS_ANDROID) + char buffer[4096]; + va_list args; + va_start(args, format); + vsnprintf(buffer, 4096, format, args); + va_end(args); + + __android_log_print(ANDROID_LOG_ERROR, "atw", "%s", buffer); +#endif + // Without exiting, the application will likely crash. + if (format != NULL) { + exit(1); + } +} + +/* +================================================================================================================================ + +EGL error checking. + +================================================================================================================================ +*/ + +#if defined(OS_ANDROID) || defined(OS_LINUX_WAYLAND) + +#define EGL(func) \ + do { \ + if (func == EGL_FALSE) { \ + Error(#func " failed: %s", EglErrorString(eglGetError())); \ + } \ + } while (0) + +static const char *EglErrorString(const EGLint error) { + switch (error) { + case EGL_SUCCESS: + return "EGL_SUCCESS"; + case EGL_NOT_INITIALIZED: + return "EGL_NOT_INITIALIZED"; + case EGL_BAD_ACCESS: + return "EGL_BAD_ACCESS"; + case EGL_BAD_ALLOC: + return "EGL_BAD_ALLOC"; + case EGL_BAD_ATTRIBUTE: + return "EGL_BAD_ATTRIBUTE"; + case EGL_BAD_CONTEXT: + return "EGL_BAD_CONTEXT"; + case EGL_BAD_CONFIG: + return "EGL_BAD_CONFIG"; + case EGL_BAD_CURRENT_SURFACE: + return "EGL_BAD_CURRENT_SURFACE"; + case EGL_BAD_DISPLAY: + return "EGL_BAD_DISPLAY"; + case EGL_BAD_SURFACE: + return "EGL_BAD_SURFACE"; + case EGL_BAD_MATCH: + return "EGL_BAD_MATCH"; + case EGL_BAD_PARAMETER: + return "EGL_BAD_PARAMETER"; + case EGL_BAD_NATIVE_PIXMAP: + return "EGL_BAD_NATIVE_PIXMAP"; + case EGL_BAD_NATIVE_WINDOW: + return "EGL_BAD_NATIVE_WINDOW"; + case EGL_CONTEXT_LOST: + return "EGL_CONTEXT_LOST"; + default: + return "unknown"; + } +} + +#endif + +/* +================================================================================================================================ + +OpenGL extensions. + +================================================================================================================================ +*/ + +#if defined(OS_ANDROID) + +// GL_EXT_disjoint_timer_query without _EXT +#if !defined(GL_TIMESTAMP) +#define GL_QUERY_COUNTER_BITS GL_QUERY_COUNTER_BITS_EXT +#define GL_TIME_ELAPSED GL_TIME_ELAPSED_EXT +#define GL_TIMESTAMP GL_TIMESTAMP_EXT +#define GL_GPU_DISJOINT GL_GPU_DISJOINT_EXT +#endif + +// GL_EXT_buffer_storage without _EXT +#if !defined(GL_BUFFER_STORAGE_FLAGS) +#define GL_MAP_READ_BIT 0x0001 // GL_MAP_READ_BIT_EXT +#define GL_MAP_WRITE_BIT 0x0002 // GL_MAP_WRITE_BIT_EXT +#define GL_MAP_PERSISTENT_BIT 0x0040 // GL_MAP_PERSISTENT_BIT_EXT +#define GL_MAP_COHERENT_BIT 0x0080 // GL_MAP_COHERENT_BIT_EXT +#define GL_DYNAMIC_STORAGE_BIT 0x0100 // GL_DYNAMIC_STORAGE_BIT_EXT +#define GL_CLIENT_STORAGE_BIT 0x0200 // GL_CLIENT_STORAGE_BIT_EXT +#define GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT 0x00004000 // GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT_EXT +#define GL_BUFFER_IMMUTABLE_STORAGE 0x821F // GL_BUFFER_IMMUTABLE_STORAGE_EXT +#define GL_BUFFER_STORAGE_FLAGS 0x8220 // GL_BUFFER_STORAGE_FLAGS_EXT +#endif + +#if !defined(EGL_OPENGL_ES3_BIT) +#define EGL_OPENGL_ES3_BIT 0x0040 +#endif + +// GL_EXT_texture_cube_map_array +#if !defined(GL_TEXTURE_CUBE_MAP_ARRAY) +#define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009 +#endif + +// GL_EXT_texture_filter_anisotropic +#if !defined(GL_TEXTURE_MAX_ANISOTROPY_EXT) +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF +#endif + +// GL_EXT_texture_border_clamp or GL_OES_texture_border_clamp +#if !defined(GL_CLAMP_TO_BORDER) +#define GL_CLAMP_TO_BORDER 0x812D +#endif + +// No 1D textures in OpenGL ES. +#if !defined(GL_TEXTURE_1D) +#define GL_TEXTURE_1D 0x0DE0 +#endif + +// No 1D texture arrays in OpenGL ES. +#if !defined(GL_TEXTURE_1D_ARRAY) +#define GL_TEXTURE_1D_ARRAY 0x8C18 +#endif + +// No multi-sampled texture arrays in OpenGL ES. +#if !defined(GL_TEXTURE_2D_MULTISAMPLE_ARRAY) +#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102 +#endif + +#endif + +/* +================================================================================================================================ + +Driver Instance. + +================================================================================================================================ +*/ + +bool ksDriverInstance_Create(ksDriverInstance *instance) { + memset(instance, 0, sizeof(ksDriverInstance)); + return true; +} + +void ksDriverInstance_Destroy(ksDriverInstance *instance) { memset(instance, 0, sizeof(ksDriverInstance)); } + +/* +================================================================================================================================ + +GPU Device. + +================================================================================================================================ +*/ + +bool ksGpuDevice_Create(ksGpuDevice *device, ksDriverInstance *instance, const ksGpuQueueInfo *queueInfo) { + /* + Use an extensions to select the appropriate device: + https://www.opengl.org/registry/specs/NV/gpu_affinity.txt + https://www.opengl.org/registry/specs/AMD/wgl_gpu_association.txt + https://www.opengl.org/registry/specs/AMD/glx_gpu_association.txt + + On Linux configure each GPU to use a separate X screen and then select + the X screen to render to. + */ + + memset(device, 0, sizeof(ksGpuDevice)); + + device->instance = instance; + device->queueInfo = *queueInfo; + + return true; +} + +void ksGpuDevice_Destroy(ksGpuDevice *device) { memset(device, 0, sizeof(ksGpuDevice)); } + +/* +================================================================================================================================ + +GPU Context. + +================================================================================================================================ +*/ + +ksGpuSurfaceBits ksGpuContext_BitsForSurfaceFormat(const ksGpuSurfaceColorFormat colorFormat, + const ksGpuSurfaceDepthFormat depthFormat) { + ksGpuSurfaceBits bits; + bits.redBits = ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_R8G8B8A8) + ? 8 + : ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_B8G8R8A8) + ? 8 + : ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_R5G6B5) + ? 5 + : ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_B5G6R5) ? 5 : 8)))); + bits.greenBits = ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_R8G8B8A8) + ? 8 + : ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_B8G8R8A8) + ? 8 + : ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_R5G6B5) + ? 6 + : ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_B5G6R5) ? 6 : 8)))); + bits.blueBits = ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_R8G8B8A8) + ? 8 + : ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_B8G8R8A8) + ? 8 + : ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_R5G6B5) + ? 5 + : ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_B5G6R5) ? 5 : 8)))); + bits.alphaBits = ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_R8G8B8A8) + ? 8 + : ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_B8G8R8A8) + ? 8 + : ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_R5G6B5) + ? 0 + : ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_B5G6R5) ? 0 : 8)))); + bits.colorBits = bits.redBits + bits.greenBits + bits.blueBits + bits.alphaBits; + bits.depthBits = + ((depthFormat == KS_GPU_SURFACE_DEPTH_FORMAT_D16) ? 16 : ((depthFormat == KS_GPU_SURFACE_DEPTH_FORMAT_D24) ? 24 : 0)); + return bits; +} + +GLenum ksGpuContext_InternalSurfaceColorFormat(const ksGpuSurfaceColorFormat colorFormat) { + return ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_R8G8B8A8) + ? GL_RGBA8 + : ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_B8G8R8A8) + ? GL_RGBA8 + : ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_R5G6B5) + ? GL_RGB565 + : ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_B5G6R5) ? GL_RGB565 : GL_RGBA8)))); +} + +GLenum ksGpuContext_InternalSurfaceDepthFormat(const ksGpuSurfaceDepthFormat depthFormat) { + return ((depthFormat == KS_GPU_SURFACE_DEPTH_FORMAT_D16) + ? GL_DEPTH_COMPONENT16 + : ((depthFormat == KS_GPU_SURFACE_DEPTH_FORMAT_D24) ? GL_DEPTH_COMPONENT24 : GL_DEPTH_COMPONENT24)); +} + +#if defined(OS_WINDOWS) + +static bool ksGpuContext_CreateForSurface(ksGpuContext *context, const ksGpuDevice *device, const int queueIndex, + const ksGpuSurfaceColorFormat colorFormat, const ksGpuSurfaceDepthFormat depthFormat, + const ksGpuSampleCount sampleCount, HINSTANCE hInstance, HDC hDC) { + UNUSED_PARM(queueIndex); + + context->device = device; + + const ksGpuSurfaceBits bits = ksGpuContext_BitsForSurfaceFormat(colorFormat, depthFormat); + + PIXELFORMATDESCRIPTOR pfd = { + sizeof(PIXELFORMATDESCRIPTOR), + 1, // version + PFD_DRAW_TO_WINDOW | // must support windowed + PFD_SUPPORT_OPENGL | // must support OpenGL + PFD_DOUBLEBUFFER, // must support double buffering + PFD_TYPE_RGBA, // iPixelType + bits.colorBits, // cColorBits + 0, + 0, // cRedBits, cRedShift + 0, + 0, // cGreenBits, cGreenShift + 0, + 0, // cBlueBits, cBlueShift + 0, + 0, // cAlphaBits, cAlphaShift + 0, // cAccumBits + 0, // cAccumRedBits + 0, // cAccumGreenBits + 0, // cAccumBlueBits + 0, // cAccumAlphaBits + bits.depthBits, // cDepthBits + 0, // cStencilBits + 0, // cAuxBuffers + PFD_MAIN_PLANE, // iLayerType + 0, // bReserved + 0, // dwLayerMask + 0, // dwVisibleMask + 0 // dwDamageMask + }; + + HWND localWnd = NULL; + HDC localDC = hDC; + + if (sampleCount > KS_GPU_SAMPLE_COUNT_1) { + // A valid OpenGL context is needed to get OpenGL extensions including wglChoosePixelFormatARB + // and wglCreateContextAttribsARB. A device context with a valid pixel format is needed to create + // an OpenGL context. However, once a pixel format is set on a device context it is final. + // Therefore a pixel format is set on the device context of a temporary window to create a context + // to get the extensions for multi-sampling. + localWnd = CreateWindowA(APPLICATION_NAME, "temp", 0, 0, 0, 0, 0, NULL, NULL, hInstance, NULL); + localDC = GetDC(localWnd); + } + + int pixelFormat = ChoosePixelFormat(localDC, &pfd); + if (pixelFormat == 0) { + Error("Failed to find a suitable pixel format."); + return false; + } + + if (!SetPixelFormat(localDC, pixelFormat, &pfd)) { + Error("Failed to set the pixel format."); + return false; + } + + // Now that the pixel format is set, create a temporary context to get the extensions. + { + HGLRC hGLRC = wglCreateContext(localDC); + wglMakeCurrent(localDC, hGLRC); + + wglMakeCurrent(NULL, NULL); + wglDeleteContext(hGLRC); + } + + if (sampleCount > KS_GPU_SAMPLE_COUNT_1) { + // Release the device context and destroy the window that were created to get extensions. + ReleaseDC(localWnd, localDC); + DestroyWindow(localWnd); + + int pixelFormatAttribs[] = {WGL_DRAW_TO_WINDOW_ARB, + GL_TRUE, + WGL_SUPPORT_OPENGL_ARB, + GL_TRUE, + WGL_DOUBLE_BUFFER_ARB, + GL_TRUE, + WGL_PIXEL_TYPE_ARB, + WGL_TYPE_RGBA_ARB, + WGL_COLOR_BITS_ARB, + bits.colorBits, + WGL_DEPTH_BITS_ARB, + bits.depthBits, + WGL_SAMPLE_BUFFERS_ARB, + 1, + WGL_SAMPLES_ARB, + sampleCount, + 0}; + + unsigned int numPixelFormats = 0; + + if (!wglChoosePixelFormatARB(hDC, pixelFormatAttribs, NULL, 1, &pixelFormat, &numPixelFormats) || numPixelFormats == 0) { + Error("Failed to find MSAA pixel format."); + return false; + } + + memset(&pfd, 0, sizeof(pfd)); + + if (!DescribePixelFormat(hDC, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd)) { + Error("Failed to describe the pixel format."); + return false; + } + + if (!SetPixelFormat(hDC, pixelFormat, &pfd)) { + Error("Failed to set the pixel format."); + return false; + } + } + + int contextAttribs[] = {WGL_CONTEXT_MAJOR_VERSION_ARB, + OPENGL_VERSION_MAJOR, + WGL_CONTEXT_MINOR_VERSION_ARB, + OPENGL_VERSION_MINOR, + WGL_CONTEXT_PROFILE_MASK_ARB, + WGL_CONTEXT_CORE_PROFILE_BIT_ARB, + WGL_CONTEXT_FLAGS_ARB, + WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB | WGL_CONTEXT_DEBUG_BIT_ARB, + 0}; + + context->hDC = hDC; + context->hGLRC = wglCreateContextAttribsARB(hDC, NULL, contextAttribs); + if (!context->hGLRC) { + Error("Failed to create GL context."); + return false; + } + + wglMakeCurrent(hDC, context->hGLRC); + + return true; +} + +#elif defined(OS_LINUX_XLIB) || defined(OS_LINUX_XCB_GLX) + +static int glxGetFBConfigAttrib2(Display *dpy, GLXFBConfig config, int attribute) { + int value; + glXGetFBConfigAttrib(dpy, config, attribute, &value); + return value; +} + +static bool ksGpuContext_CreateForSurface(ksGpuContext *context, const ksGpuDevice *device, const int queueIndex, + const ksGpuSurfaceColorFormat colorFormat, const ksGpuSurfaceDepthFormat depthFormat, + const ksGpuSampleCount sampleCount, Display *xDisplay, int xScreen) { + UNUSED_PARM(queueIndex); + + context->device = device; + + int glxErrorBase; + int glxEventBase; + if (!glXQueryExtension(xDisplay, &glxErrorBase, &glxEventBase)) { + Error("X display does not support the GLX extension."); + return false; + } + + int glxVersionMajor; + int glxVersionMinor; + if (!glXQueryVersion(xDisplay, &glxVersionMajor, &glxVersionMinor)) { + Error("Unable to retrieve GLX version."); + return false; + } + + int fbConfigCount = 0; + GLXFBConfig *fbConfigs = glXGetFBConfigs(xDisplay, xScreen, &fbConfigCount); + if (fbConfigCount == 0) { + Error("No valid framebuffer configurations found."); + return false; + } + + const ksGpuSurfaceBits bits = ksGpuContext_BitsForSurfaceFormat(colorFormat, depthFormat); + + bool foundFbConfig = false; + for (int i = 0; i < fbConfigCount; i++) { + if (glxGetFBConfigAttrib2(xDisplay, fbConfigs[i], GLX_FBCONFIG_ID) == 0) { + continue; + } + if (glxGetFBConfigAttrib2(xDisplay, fbConfigs[i], GLX_VISUAL_ID) == 0) { + continue; + } + if (glxGetFBConfigAttrib2(xDisplay, fbConfigs[i], GLX_DOUBLEBUFFER) == 0) { + continue; + } + if ((glxGetFBConfigAttrib2(xDisplay, fbConfigs[i], GLX_RENDER_TYPE) & GLX_RGBA_BIT) == 0) { + continue; + } + if ((glxGetFBConfigAttrib2(xDisplay, fbConfigs[i], GLX_DRAWABLE_TYPE) & GLX_WINDOW_BIT) == 0) { + continue; + } + if (glxGetFBConfigAttrib2(xDisplay, fbConfigs[i], GLX_RED_SIZE) != bits.redBits) { + continue; + } + if (glxGetFBConfigAttrib2(xDisplay, fbConfigs[i], GLX_GREEN_SIZE) != bits.greenBits) { + continue; + } + if (glxGetFBConfigAttrib2(xDisplay, fbConfigs[i], GLX_BLUE_SIZE) != bits.blueBits) { + continue; + } + if (glxGetFBConfigAttrib2(xDisplay, fbConfigs[i], GLX_ALPHA_SIZE) != bits.alphaBits) { + continue; + } + if (glxGetFBConfigAttrib2(xDisplay, fbConfigs[i], GLX_DEPTH_SIZE) != bits.depthBits) { + continue; + } + if (sampleCount > KS_GPU_SAMPLE_COUNT_1) { + if (glxGetFBConfigAttrib2(xDisplay, fbConfigs[i], GLX_SAMPLE_BUFFERS) != 1) { + continue; + } + if (glxGetFBConfigAttrib2(xDisplay, fbConfigs[i], GLX_SAMPLES) != (int)sampleCount) { + continue; + } + } + + context->visualid = glxGetFBConfigAttrib2(xDisplay, fbConfigs[i], GLX_VISUAL_ID); + context->glxFBConfig = fbConfigs[i]; + foundFbConfig = true; + break; + } + + XFree(fbConfigs); + + if (!foundFbConfig) { + Error("Failed to to find desired framebuffer configuration."); + return false; + } + + context->xDisplay = xDisplay; + + int attribs[] = {GLX_CONTEXT_MAJOR_VERSION_ARB, + OPENGL_VERSION_MAJOR, + GLX_CONTEXT_MINOR_VERSION_ARB, + OPENGL_VERSION_MINOR, + GL_CONTEXT_PROFILE_MASK, + GL_CONTEXT_CORE_PROFILE_BIT, + GLX_CONTEXT_FLAGS_ARB, + GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, + 0}; + + context->glxContext = glXCreateContextAttribsARB(xDisplay, // Display * dpy + context->glxFBConfig, // GLXFBConfig config + NULL, // GLXContext share_context + True, // Bool direct + attribs); // const int * attrib_list + + if (context->glxContext == NULL) { + Error("Unable to create GLX context."); + return false; + } + + if (!glXIsDirect(xDisplay, context->glxContext)) { + Error("Unable to create direct rendering context."); + return false; + } + + return true; +} + +#elif defined(OS_LINUX_XCB) + +static uint32_t xcb_glx_get_property(const uint32_t *properties, const uint32_t numProperties, uint32_t propertyName) { + for (uint32_t i = 0; i < numProperties; i++) { + if (properties[i * 2 + 0] == propertyName) { + return properties[i * 2 + 1]; + } + } + return 0; +} + +static bool ksGpuContext_CreateForSurface(ksGpuContext *context, const ksGpuDevice *device, const int queueIndex, + const ksGpuSurfaceColorFormat colorFormat, const ksGpuSurfaceDepthFormat depthFormat, + const ksGpuSampleCount sampleCount, xcb_connection_t *connection, int screen_number) { + UNUSED_PARM(queueIndex); + + context->device = device; + + xcb_glx_query_version_cookie_t glx_query_version_cookie = + xcb_glx_query_version(connection, OPENGL_VERSION_MAJOR, OPENGL_VERSION_MINOR); + xcb_glx_query_version_reply_t *glx_query_version_reply = + xcb_glx_query_version_reply(connection, glx_query_version_cookie, NULL); + if (glx_query_version_reply == NULL) { + Error("Unable to retrieve GLX version."); + return false; + } + free(glx_query_version_reply); + + xcb_glx_get_fb_configs_cookie_t get_fb_configs_cookie = xcb_glx_get_fb_configs(connection, screen_number); + xcb_glx_get_fb_configs_reply_t *get_fb_configs_reply = xcb_glx_get_fb_configs_reply(connection, get_fb_configs_cookie, NULL); + + if (get_fb_configs_reply == NULL || get_fb_configs_reply->num_FB_configs == 0) { + Error("No valid framebuffer configurations found."); + return false; + } + + const ksGpuSurfaceBits bits = ksGpuContext_BitsForSurfaceFormat(colorFormat, depthFormat); + + const uint32_t *fb_configs_properties = xcb_glx_get_fb_configs_property_list(get_fb_configs_reply); + const uint32_t fb_configs_num_properties = get_fb_configs_reply->num_properties; + + bool foundFbConfig = false; + for (uint32_t i = 0; i < get_fb_configs_reply->num_FB_configs; i++) { + const uint32_t *fb_config = fb_configs_properties + i * fb_configs_num_properties * 2; + + if (xcb_glx_get_property(fb_config, fb_configs_num_properties, GLX_FBCONFIG_ID) == 0) { + continue; + } + if (xcb_glx_get_property(fb_config, fb_configs_num_properties, GLX_VISUAL_ID) == 0) { + continue; + } + if (xcb_glx_get_property(fb_config, fb_configs_num_properties, GLX_DOUBLEBUFFER) == 0) { + continue; + } + if ((xcb_glx_get_property(fb_config, fb_configs_num_properties, GLX_RENDER_TYPE) & GLX_RGBA_BIT) == 0) { + continue; + } + if ((xcb_glx_get_property(fb_config, fb_configs_num_properties, GLX_DRAWABLE_TYPE) & GLX_WINDOW_BIT) == 0) { + continue; + } + if (xcb_glx_get_property(fb_config, fb_configs_num_properties, GLX_RED_SIZE) != bits.redBits) { + continue; + } + if (xcb_glx_get_property(fb_config, fb_configs_num_properties, GLX_GREEN_SIZE) != bits.greenBits) { + continue; + } + if (xcb_glx_get_property(fb_config, fb_configs_num_properties, GLX_BLUE_SIZE) != bits.blueBits) { + continue; + } + if (xcb_glx_get_property(fb_config, fb_configs_num_properties, GLX_ALPHA_SIZE) != bits.alphaBits) { + continue; + } + if (xcb_glx_get_property(fb_config, fb_configs_num_properties, GLX_DEPTH_SIZE) != bits.depthBits) { + continue; + } + if (sampleCount > KS_GPU_SAMPLE_COUNT_1) { + if (xcb_glx_get_property(fb_config, fb_configs_num_properties, GLX_SAMPLE_BUFFERS) != 1) { + continue; + } + if (xcb_glx_get_property(fb_config, fb_configs_num_properties, GLX_SAMPLES) != sampleCount) { + continue; + } + } + + context->fbconfigid = xcb_glx_get_property(fb_config, fb_configs_num_properties, GLX_FBCONFIG_ID); + context->visualid = xcb_glx_get_property(fb_config, fb_configs_num_properties, GLX_VISUAL_ID); + foundFbConfig = true; + break; + } + + free(get_fb_configs_reply); + + if (!foundFbConfig) { + Error("Failed to to find desired framebuffer configuration."); + return false; + } + + context->connection = connection; + context->screen_number = screen_number; + + // Create the context. + uint32_t attribs[] = {GLX_CONTEXT_MAJOR_VERSION_ARB, + OPENGL_VERSION_MAJOR, + GLX_CONTEXT_MINOR_VERSION_ARB, + OPENGL_VERSION_MINOR, + GLX_CONTEXT_PROFILE_MASK_ARB, + GLX_CONTEXT_CORE_PROFILE_BIT_ARB, + GLX_CONTEXT_FLAGS_ARB, + GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, + 0}; + + context->glxContext = xcb_generate_id(connection); + xcb_glx_create_context_attribs_arb(connection, // xcb_connection_t * connection + context->glxContext, // xcb_glx_context_t context + context->fbconfigid, // xcb_glx_fbconfig_t fbconfig + screen_number, // uint32_t screen + 0, // xcb_glx_context_t share_list + 1, // uint8_t is_direct + 4, // uint32_t num_attribs + attribs); // const uint32_t * attribs + + // Make sure the context is direct. + xcb_generic_error_t *error; + xcb_glx_is_direct_cookie_t glx_is_direct_cookie = xcb_glx_is_direct_unchecked(connection, context->glxContext); + xcb_glx_is_direct_reply_t *glx_is_direct_reply = xcb_glx_is_direct_reply(connection, glx_is_direct_cookie, &error); + const bool is_direct = (glx_is_direct_reply != NULL && glx_is_direct_reply->is_direct); + free(glx_is_direct_reply); + + if (!is_direct) { + Error("Unable to create direct rendering context."); + return false; + } + + return true; +} + +#elif defined(OS_LINUX_WAYLAND) + +static bool ksGpuContext_CreateForSurface(ksGpuContext *context, const ksGpuDevice *device, struct wl_display *native_display) { + context->device = device; + + EGLint numConfigs; + EGLint majorVersion; + EGLint minorVersion; + + // clang-format off + EGLint fbAttribs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_NONE, + }; + + EGLint contextAttribs[] = { + EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, + EGL_CONTEXT_CLIENT_VERSION, OPENGL_VERSION_MAJOR, + EGL_CONTEXT_MINOR_VERSION, OPENGL_VERSION_MINOR, + EGL_NONE, + }; + // clang-format on + + context->display = eglGetDisplay(native_display); + if (context->display == EGL_NO_DISPLAY) { + Error("Could not create EGL Display."); + return false; + } + + if (!eglInitialize(context->display, &majorVersion, &minorVersion)) { + Error("eglInitialize failed."); + return false; + } + + printf("Initialized EGL context version %d.%d\n", majorVersion, minorVersion); + + EGLBoolean ret = eglGetConfigs(context->display, NULL, 0, &numConfigs); + if (ret != EGL_TRUE || numConfigs == 0) { + Error("eglGetConfigs failed."); + return false; + } + + ret = eglChooseConfig(context->display, fbAttribs, &context->config, 1, &numConfigs); + if (ret != EGL_TRUE || numConfigs != 1) { + Error("eglChooseConfig failed."); + return false; + } + + context->mainSurface = eglCreateWindowSurface(context->display, context->config, context->native_window, NULL); + if (context->mainSurface == EGL_NO_SURFACE) { + Error("eglCreateWindowSurface failed"); + return false; + } + + eglBindAPI(EGL_OPENGL_API); + + context->context = eglCreateContext(context->display, context->config, EGL_NO_CONTEXT, contextAttribs); + if (context->context == EGL_NO_CONTEXT) { + Error("Could not create OpenGL context."); + return false; + } + + if (!eglMakeCurrent(context->display, context->mainSurface, context->mainSurface, context->context)) { + Error("Could not make the current context current."); + return false; + } + + return true; +} + +#elif defined(OS_APPLE_MACOS) + +static bool ksGpuContext_CreateForSurface(ksGpuContext *context, const ksGpuDevice *device, const int queueIndex, + const ksGpuSurfaceColorFormat colorFormat, const ksGpuSurfaceDepthFormat depthFormat, + const ksGpuSampleCount sampleCount, CGDirectDisplayID display) { + UNUSED_PARM(queueIndex); + + context->device = device; + + const ksGpuSurfaceBits bits = ksGpuContext_BitsForSurfaceFormat(colorFormat, depthFormat); + + NSOpenGLPixelFormatAttribute pixelFormatAttributes[] = {NSOpenGLPFAMinimumPolicy, + 1, + NSOpenGLPFAScreenMask, + CGDisplayIDToOpenGLDisplayMask(display), + NSOpenGLPFAAccelerated, + NSOpenGLPFAOpenGLProfile, + NSOpenGLProfileVersion3_2Core, + NSOpenGLPFADoubleBuffer, + NSOpenGLPFAColorSize, + bits.colorBits, + NSOpenGLPFADepthSize, + bits.depthBits, + NSOpenGLPFASampleBuffers, + (sampleCount > KS_GPU_SAMPLE_COUNT_1), + NSOpenGLPFASamples, + sampleCount, + 0}; + + NSOpenGLPixelFormat *pixelFormat = [[[NSOpenGLPixelFormat alloc] initWithAttributes:pixelFormatAttributes] autorelease]; + if (pixelFormat == nil) { + Error("Failed : NSOpenGLPixelFormat."); + return false; + } + context->nsContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil]; + if (context->nsContext == nil) { + Error("Failed : NSOpenGLContext."); + return false; + } + + context->cglContext = [context->nsContext CGLContextObj]; + + return true; +} + +#elif defined(OS_ANDROID) + +static bool ksGpuContext_CreateForSurface(ksGpuContext *context, const ksGpuDevice *device, const int queueIndex, + const ksGpuSurfaceColorFormat colorFormat, const ksGpuSurfaceDepthFormat depthFormat, + const ksGpuSampleCount sampleCount, EGLDisplay display) { + context->device = device; + + context->display = display; + + // Do NOT use eglChooseConfig, because the Android EGL code pushes in multisample + // flags in eglChooseConfig when the user has selected the "force 4x MSAA" option in + // settings, and that is completely wasted on the time warped frontbuffer. + enum { MAX_CONFIGS = 1024 }; + EGLConfig configs[MAX_CONFIGS]; + EGLint numConfigs = 0; + EGL(eglGetConfigs(display, configs, MAX_CONFIGS, &numConfigs)); + + const ksGpuSurfaceBits bits = ksGpuContext_BitsForSurfaceFormat(colorFormat, depthFormat); + + // clang-format off + const EGLint configAttribs[] = { + EGL_RED_SIZE, bits.redBits, + EGL_GREEN_SIZE, bits.greenBits, + EGL_BLUE_SIZE, bits.blueBits, + EGL_ALPHA_SIZE, bits.alphaBits, + EGL_DEPTH_SIZE, bits.depthBits, + // EGL_STENCIL_SIZE, 0, + EGL_SAMPLE_BUFFERS, (sampleCount > KS_GPU_SAMPLE_COUNT_1), + EGL_SAMPLES, (sampleCount > KS_GPU_SAMPLE_COUNT_1) ? sampleCount : 0, + EGL_NONE, + }; + // clang-format on + + context->config = 0; + for (int i = 0; i < numConfigs; i++) { + EGLint value = 0; + + eglGetConfigAttrib(display, configs[i], EGL_RENDERABLE_TYPE, &value); + if ((value & EGL_OPENGL_ES3_BIT) != EGL_OPENGL_ES3_BIT) { + continue; + } + + // Without EGL_KHR_surfaceless_context, the config needs to support both pbuffers and window surfaces. + eglGetConfigAttrib(display, configs[i], EGL_SURFACE_TYPE, &value); + if ((value & (EGL_WINDOW_BIT | EGL_PBUFFER_BIT)) != (EGL_WINDOW_BIT | EGL_PBUFFER_BIT)) { + continue; + } + + int j = 0; + for (; configAttribs[j] != EGL_NONE; j += 2) { + eglGetConfigAttrib(display, configs[i], configAttribs[j], &value); + if (value != configAttribs[j + 1]) { + break; + } + } + if (configAttribs[j] == EGL_NONE) { + context->config = configs[i]; + break; + } + } + if (context->config == 0) { + Error("Failed to find EGLConfig"); + return false; + } + + EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, OPENGL_VERSION_MAJOR, EGL_NONE, EGL_NONE, EGL_NONE}; + // Use the default priority if KS_GPU_QUEUE_PRIORITY_MEDIUM is selected. + const ksGpuQueuePriority priority = device->queueInfo.queuePriorities[queueIndex]; + if (priority != KS_GPU_QUEUE_PRIORITY_MEDIUM) { + contextAttribs[2] = EGL_CONTEXT_PRIORITY_LEVEL_IMG; + contextAttribs[3] = (priority == KS_GPU_QUEUE_PRIORITY_LOW) ? EGL_CONTEXT_PRIORITY_LOW_IMG : EGL_CONTEXT_PRIORITY_HIGH_IMG; + } + context->context = eglCreateContext(display, context->config, EGL_NO_CONTEXT, contextAttribs); + if (context->context == EGL_NO_CONTEXT) { + Error("eglCreateContext() failed: %s", EglErrorString(eglGetError())); + return false; + } + + const EGLint surfaceAttribs[] = {EGL_WIDTH, 16, EGL_HEIGHT, 16, EGL_NONE}; + context->tinySurface = eglCreatePbufferSurface(display, context->config, surfaceAttribs); + if (context->tinySurface == EGL_NO_SURFACE) { + Error("eglCreatePbufferSurface() failed: %s", EglErrorString(eglGetError())); + eglDestroyContext(display, context->context); + context->context = EGL_NO_CONTEXT; + return false; + } + context->mainSurface = context->tinySurface; + + return true; +} + +#endif + +bool ksGpuContext_CreateShared(ksGpuContext *context, const ksGpuContext *other, int queueIndex) { + UNUSED_PARM(queueIndex); + + memset(context, 0, sizeof(ksGpuContext)); + + context->device = other->device; + +#if defined(OS_WINDOWS) + context->hDC = other->hDC; + context->hGLRC = wglCreateContext(other->hDC); + if (!wglShareLists(other->hGLRC, context->hGLRC)) { + return false; + } +#elif defined(OS_LINUX_XLIB) || defined(OS_LINUX_XCB_GLX) + context->xDisplay = other->xDisplay; + context->visualid = other->visualid; + context->glxFBConfig = other->glxFBConfig; + context->glxDrawable = other->glxDrawable; + context->glxContext = glXCreateNewContext(other->xDisplay, other->glxFBConfig, GLX_RGBA_TYPE, other->glxContext, True); + if (context->glxContext == NULL) { + return false; + } +#elif defined(OS_LINUX_XCB) + context->connection = other->connection; + context->screen_number = other->screen_number; + context->fbconfigid = other->fbconfigid; + context->visualid = other->visualid; + context->glxDrawable = other->glxDrawable; + context->glxContext = xcb_generate_id(other->connection); + xcb_glx_create_context(other->connection, context->glxContext, other->visualid, other->screen_number, other->glxContext, 1); + context->glxContextTag = 0; +#elif defined(OS_APPLE_MACOS) + context->nsContext = NULL; + CGLPixelFormatObj pf = CGLGetPixelFormat(other->cglContext); + if (CGLCreateContext(pf, other->cglContext, &context->cglContext) != kCGLNoError) { + Error("Failed : CGLCreateContext."); + return false; + } + CGSConnectionID cid; + CGSWindowID wid; + CGSSurfaceID sid; + if (CGLGetSurface(other->cglContext, &cid, &wid, &sid) != kCGLNoError) { + Error("Failed : CGLGetSurface."); + return false; + } + if (CGLSetSurface(context->cglContext, cid, wid, sid) != kCGLNoError) { + Error("Failed : CGLSetSurface."); + return false; + } +#elif defined(OS_ANDROID) || defined(OS_LINUX_WAYLAND) + context->display = other->display; + EGLint configID; + if (!eglQueryContext(context->display, other->context, EGL_CONFIG_ID, &configID)) { + Error("eglQueryContext EGL_CONFIG_ID failed: %s", EglErrorString(eglGetError())); + return false; + } + enum { MAX_CONFIGS = 1024 }; + EGLConfig configs[MAX_CONFIGS]; + EGLint numConfigs = 0; + EGL(eglGetConfigs(context->display, configs, MAX_CONFIGS, &numConfigs)); + context->config = 0; + for (int i = 0; i < numConfigs; i++) { + EGLint value = 0; + eglGetConfigAttrib(context->display, configs[i], EGL_CONFIG_ID, &value); + if (value == configID) { + context->config = configs[i]; + break; + } + } + if (context->config == 0) { + Error("Failed to find share context config."); + return false; + } + EGLint surfaceType = 0; + eglGetConfigAttrib(context->display, context->config, EGL_SURFACE_TYPE, &surfaceType); + +#if defined(OS_ANDROID) + if ((surfaceType & EGL_PBUFFER_BIT) == 0) { + Error("Share context config does not have EGL_PBUFFER_BIT."); + return false; + } +#endif + EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, OPENGL_VERSION_MAJOR, EGL_NONE}; + context->context = eglCreateContext(context->display, context->config, other->context, contextAttribs); + if (context->context == EGL_NO_CONTEXT) { + Error("eglCreateContext() failed: %s", EglErrorString(eglGetError())); + return false; + } +#if defined(OS_ANDROID) + const EGLint surfaceAttribs[] = {EGL_WIDTH, 16, EGL_HEIGHT, 16, EGL_NONE}; + context->tinySurface = eglCreatePbufferSurface(context->display, context->config, surfaceAttribs); + if (context->tinySurface == EGL_NO_SURFACE) { + Error("eglCreatePbufferSurface() failed: %s", EglErrorString(eglGetError())); + eglDestroyContext(context->display, context->context); + context->context = EGL_NO_CONTEXT; + return false; + } + context->mainSurface = context->tinySurface; +#endif +#endif + return true; +} + +void ksGpuContext_Destroy(ksGpuContext *context) { +#if defined(OS_WINDOWS) + if (context->hGLRC) { + if (!wglMakeCurrent(NULL, NULL)) { + DWORD error = GetLastError(); + Error("Failed to release context error code (%d).", error); + } + + if (!wglDeleteContext(context->hGLRC)) { + DWORD error = GetLastError(); + Error("Failed to delete context error code (%d).", error); + } + context->hGLRC = NULL; + } + context->hDC = NULL; +#elif defined(OS_LINUX_XLIB) || defined(OS_LINUX_XCB_GLX) + glXDestroyContext(context->xDisplay, context->glxContext); + context->xDisplay = NULL; + context->visualid = 0; + context->glxFBConfig = NULL; + context->glxDrawable = 0; + context->glxContext = NULL; +#elif defined(OS_LINUX_XCB) + xcb_glx_destroy_context(context->connection, context->glxContext); + context->connection = NULL; + context->screen_number = 0; + context->fbconfigid = 0; + context->visualid = 0; + context->glxDrawable = 0; + context->glxContext = 0; + context->glxContextTag = 0; +#elif defined(OS_APPLE_MACOS) + CGLSetCurrentContext(NULL); + if (context->nsContext != NULL) { + [context->nsContext clearDrawable]; + [context->nsContext release]; + context->nsContext = nil; + } else { + CGLDestroyContext(context->cglContext); + } + context->cglContext = nil; +#elif defined(OS_ANDROID) || defined(OS_LINUX_WAYLAND) + if (context->display != 0) { + EGL(eglMakeCurrent(context->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); + } + if (context->context != EGL_NO_CONTEXT) { + EGL(eglDestroyContext(context->display, context->context)); + } + +#if defined(OS_ANDROID) + if (context->mainSurface != context->tinySurface) { + EGL(eglDestroySurface(context->display, context->mainSurface)); + } + if (context->tinySurface != EGL_NO_SURFACE) { + EGL(eglDestroySurface(context->display, context->tinySurface)); + } + context->tinySurface = EGL_NO_SURFACE; +#elif defined(OS_LINUX_WAYLAND) + if (context->mainSurface != EGL_NO_SURFACE) { + EGL(eglDestroySurface(context->display, context->mainSurface)); + } +#endif + context->display = 0; + context->config = 0; + context->mainSurface = EGL_NO_SURFACE; + context->context = EGL_NO_CONTEXT; +#endif +} + +void ksGpuContext_SetCurrent(ksGpuContext *context) { +#if defined(OS_WINDOWS) + wglMakeCurrent(context->hDC, context->hGLRC); +#elif defined(OS_LINUX_XLIB) || defined(OS_LINUX_XCB_GLX) + glXMakeCurrent(context->xDisplay, context->glxDrawable, context->glxContext); +#elif defined(OS_LINUX_XCB) + xcb_glx_make_current_cookie_t glx_make_current_cookie = + xcb_glx_make_current(context->connection, context->glxDrawable, context->glxContext, 0); + xcb_glx_make_current_reply_t *glx_make_current_reply = + xcb_glx_make_current_reply(context->connection, glx_make_current_cookie, NULL); + context->glxContextTag = glx_make_current_reply->context_tag; + free(glx_make_current_reply); +#elif defined(OS_APPLE_MACOS) + CGLSetCurrentContext(context->cglContext); +#elif defined(OS_ANDROID) || defined(OS_LINUX_WAYLAND) + EGL(eglMakeCurrent(context->display, context->mainSurface, context->mainSurface, context->context)); +#endif +} + +void ksGpuContext_UnsetCurrent(ksGpuContext *context) { +#if defined(OS_WINDOWS) + wglMakeCurrent(context->hDC, NULL); +#elif defined(OS_LINUX_XLIB) || defined(OS_LINUX_XCB_GLX) + glXMakeCurrent(context->xDisplay, /* None */ 0L, NULL); +#elif defined(OS_LINUX_XCB) + xcb_glx_make_current(context->connection, 0, 0, 0); +#elif defined(OS_APPLE_MACOS) + (void)context; + CGLSetCurrentContext(NULL); +#elif defined(OS_ANDROID) || defined(OS_LINUX_WAYLAND) + EGL(eglMakeCurrent(context->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); +#endif +} + +bool ksGpuContext_CheckCurrent(ksGpuContext *context) { +#if defined(OS_WINDOWS) + return (wglGetCurrentContext() == context->hGLRC); +#elif defined(OS_LINUX_XLIB) || defined(OS_LINUX_XCB_GLX) + return (glXGetCurrentContext() == context->glxContext); +#elif defined(OS_LINUX_XCB) + return true; +#elif defined(OS_APPLE_MACOS) + return (CGLGetCurrentContext() == context->cglContext); +#elif defined(OS_APPLE_IOS) + return (false); // TODO: pick current context off the UIView +#elif defined(OS_ANDROID) || defined(OS_LINUX_WAYLAND) + return (eglGetCurrentContext() == context->context); +#endif +} + +/* +================================================================================================================================ + +GPU Window. + +================================================================================================================================ +*/ + +#if defined(OS_WINDOWS) + +typedef enum { + KEY_A = 0x41, + KEY_B = 0x42, + KEY_C = 0x43, + KEY_D = 0x44, + KEY_E = 0x45, + KEY_F = 0x46, + KEY_G = 0x47, + KEY_H = 0x48, + KEY_I = 0x49, + KEY_J = 0x4A, + KEY_K = 0x4B, + KEY_L = 0x4C, + KEY_M = 0x4D, + KEY_N = 0x4E, + KEY_O = 0x4F, + KEY_P = 0x50, + KEY_Q = 0x51, + KEY_R = 0x52, + KEY_S = 0x53, + KEY_T = 0x54, + KEY_U = 0x55, + KEY_V = 0x56, + KEY_W = 0x57, + KEY_X = 0x58, + KEY_Y = 0x59, + KEY_Z = 0x5A, + KEY_RETURN = VK_RETURN, + KEY_TAB = VK_TAB, + KEY_ESCAPE = VK_ESCAPE, + KEY_SHIFT_LEFT = VK_LSHIFT, + KEY_CTRL_LEFT = VK_LCONTROL, + KEY_ALT_LEFT = VK_LMENU, + KEY_CURSOR_UP = VK_UP, + KEY_CURSOR_DOWN = VK_DOWN, + KEY_CURSOR_LEFT = VK_LEFT, + KEY_CURSOR_RIGHT = VK_RIGHT +} ksKeyboardKey; + +typedef enum { MOUSE_LEFT = 0, MOUSE_RIGHT = 1 } ksMouseButton; + +LRESULT APIENTRY WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { + ksGpuWindow *window = (ksGpuWindow *)GetWindowLongPtrA(hWnd, GWLP_USERDATA); + + switch (message) { + case WM_SIZE: { + if (window != NULL) { + window->windowWidth = (int)LOWORD(lParam); + window->windowHeight = (int)HIWORD(lParam); + } + return 0; + } + case WM_ACTIVATE: { + if (window != NULL) { + window->windowActiveState = !HIWORD(wParam); + } + return 0; + } + case WM_ERASEBKGND: { + return 0; + } + case WM_CLOSE: { + PostQuitMessage(0); + return 0; + } + case WM_KEYDOWN: { + if (window != NULL) { + if ((int)wParam >= 0 && (int)wParam < 256) { + if ((int)wParam != KEY_SHIFT_LEFT && (int)wParam != KEY_CTRL_LEFT && (int)wParam != KEY_ALT_LEFT && + (int)wParam != KEY_CURSOR_UP && (int)wParam != KEY_CURSOR_DOWN && (int)wParam != KEY_CURSOR_LEFT && + (int)wParam != KEY_CURSOR_RIGHT) { + window->input.keyInput[(int)wParam] = true; + } + } + } + break; + } + case WM_LBUTTONDOWN: { + window->input.mouseInput[MOUSE_LEFT] = true; + window->input.mouseInputX[MOUSE_LEFT] = LOWORD(lParam); + window->input.mouseInputY[MOUSE_LEFT] = window->windowHeight - HIWORD(lParam); + break; + } + case WM_RBUTTONDOWN: { + window->input.mouseInput[MOUSE_RIGHT] = true; + window->input.mouseInputX[MOUSE_RIGHT] = LOWORD(lParam); + window->input.mouseInputY[MOUSE_RIGHT] = window->windowHeight - HIWORD(lParam); + break; + } + } + return DefWindowProcA(hWnd, message, wParam, lParam); +} + +void ksGpuWindow_Destroy(ksGpuWindow *window) { + ksGpuContext_Destroy(&window->context); + ksGpuDevice_Destroy(&window->device); + + if (window->windowFullscreen) { + ChangeDisplaySettingsA(NULL, 0); + ShowCursor(TRUE); + } + + if (window->hDC) { + if (!ReleaseDC(window->hWnd, window->hDC)) { + Error("Failed to release device context."); + } + window->hDC = NULL; + } + + if (window->hWnd) { + if (!DestroyWindow(window->hWnd)) { + Error("Failed to destroy the window."); + } + window->hWnd = NULL; + } + + if (window->hInstance) { + if (!UnregisterClassA(APPLICATION_NAME, window->hInstance)) { + Error("Failed to unregister window class."); + } + window->hInstance = NULL; + } +} + +bool ksGpuWindow_Create(ksGpuWindow *window, ksDriverInstance *instance, const ksGpuQueueInfo *queueInfo, int queueIndex, + ksGpuSurfaceColorFormat colorFormat, ksGpuSurfaceDepthFormat depthFormat, ksGpuSampleCount sampleCount, + int width, int height, bool fullscreen) { + memset(window, 0, sizeof(ksGpuWindow)); + + window->colorFormat = colorFormat; + window->depthFormat = depthFormat; + window->sampleCount = sampleCount; + window->windowWidth = width; + window->windowHeight = height; + window->windowSwapInterval = 1; + window->windowRefreshRate = 60.0f; + window->windowFullscreen = fullscreen; + window->windowActive = false; + window->windowExit = false; + window->windowActiveState = false; + + const LPCSTR displayDevice = NULL; + + if (window->windowFullscreen) { + DEVMODEA dmScreenSettings; + memset(&dmScreenSettings, 0, sizeof(dmScreenSettings)); + dmScreenSettings.dmSize = sizeof(dmScreenSettings); + dmScreenSettings.dmPelsWidth = width; + dmScreenSettings.dmPelsHeight = height; + dmScreenSettings.dmBitsPerPel = 32; + dmScreenSettings.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL; + + if (ChangeDisplaySettingsExA(displayDevice, &dmScreenSettings, NULL, CDS_FULLSCREEN, NULL) != DISP_CHANGE_SUCCESSFUL) { + Error("The requested fullscreen mode is not supported."); + return false; + } + } + + DEVMODEA lpDevMode; + memset(&lpDevMode, 0, sizeof(DEVMODEA)); + lpDevMode.dmSize = sizeof(DEVMODEA); + lpDevMode.dmDriverExtra = 0; + + if (EnumDisplaySettingsA(displayDevice, ENUM_CURRENT_SETTINGS, &lpDevMode) != FALSE) { + window->windowRefreshRate = (float)lpDevMode.dmDisplayFrequency; + } + + window->hInstance = GetModuleHandleA(NULL); + + WNDCLASSA wc; + wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + wc.lpfnWndProc = (WNDPROC)WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = window->hInstance; + wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = NULL; + wc.lpszMenuName = NULL; + wc.lpszClassName = APPLICATION_NAME; + + if (!RegisterClassA(&wc)) { + Error("Failed to register window class."); + return false; + } + + DWORD dwExStyle = 0; + DWORD dwStyle = 0; + if (window->windowFullscreen) { + dwExStyle = WS_EX_APPWINDOW; + dwStyle = WS_POPUP; + ShowCursor(FALSE); + } else { + // Fixed size window. + dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; + dwStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; + } + + RECT windowRect; + windowRect.left = (long)0; + windowRect.right = (long)width; + windowRect.top = (long)0; + windowRect.bottom = (long)height; + + AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle); + + if (!window->windowFullscreen) { + RECT desktopRect; + GetWindowRect(GetDesktopWindow(), &desktopRect); + + const int offsetX = (desktopRect.right - (windowRect.right - windowRect.left)) / 2; + const int offsetY = (desktopRect.bottom - (windowRect.bottom - windowRect.top)) / 2; + + windowRect.left += offsetX; + windowRect.right += offsetX; + windowRect.top += offsetY; + windowRect.bottom += offsetY; + } + + window->hWnd = CreateWindowExA(dwExStyle, // Extended style for the window + APPLICATION_NAME, // Class name + WINDOW_TITLE, // Window title + dwStyle | // Defined window style + WS_CLIPSIBLINGS | // Required window style + WS_CLIPCHILDREN, // Required window style + windowRect.left, // Window X position + windowRect.top, // Window Y position + windowRect.right - windowRect.left, // Window width + windowRect.bottom - windowRect.top, // Window height + NULL, // No parent window + NULL, // No menu + window->hInstance, // Instance + NULL); // No WM_CREATE parameter + if (!window->hWnd) { + ksGpuWindow_Destroy(window); + Error("Failed to create window."); + return false; + } + + SetWindowLongPtrA(window->hWnd, GWLP_USERDATA, (LONG_PTR)window); + + window->hDC = GetDC(window->hWnd); + if (!window->hDC) { + ksGpuWindow_Destroy(window); + Error("Failed to acquire device context."); + return false; + } + + ksGpuDevice_Create(&window->device, instance, queueInfo); + ksGpuContext_CreateForSurface(&window->context, &window->device, queueIndex, colorFormat, depthFormat, sampleCount, + window->hInstance, window->hDC); + ksGpuContext_SetCurrent(&window->context); + + + ShowWindow(window->hWnd, SW_SHOW); + SetForegroundWindow(window->hWnd); + SetFocus(window->hWnd); + + return true; +} + +#elif defined(OS_LINUX_XLIB) + +typedef enum // keysym.h +{ KEY_A = XK_a, + KEY_B = XK_b, + KEY_C = XK_c, + KEY_D = XK_d, + KEY_E = XK_e, + KEY_F = XK_f, + KEY_G = XK_g, + KEY_H = XK_h, + KEY_I = XK_i, + KEY_J = XK_j, + KEY_K = XK_k, + KEY_L = XK_l, + KEY_M = XK_m, + KEY_N = XK_n, + KEY_O = XK_o, + KEY_P = XK_p, + KEY_Q = XK_q, + KEY_R = XK_r, + KEY_S = XK_s, + KEY_T = XK_t, + KEY_U = XK_u, + KEY_V = XK_v, + KEY_W = XK_w, + KEY_X = XK_x, + KEY_Y = XK_y, + KEY_Z = XK_z, + KEY_RETURN = (XK_Return & 0xFF), + KEY_TAB = (XK_Tab & 0xFF), + KEY_ESCAPE = (XK_Escape & 0xFF), + KEY_SHIFT_LEFT = (XK_Shift_L & 0xFF), + KEY_CTRL_LEFT = (XK_Control_L & 0xFF), + KEY_ALT_LEFT = (XK_Alt_L & 0xFF), + KEY_CURSOR_UP = (XK_Up & 0xFF), + KEY_CURSOR_DOWN = (XK_Down & 0xFF), + KEY_CURSOR_LEFT = (XK_Left & 0xFF), + KEY_CURSOR_RIGHT = (XK_Right & 0xFF) } ksKeyboardKey; + +typedef enum { MOUSE_LEFT = Button1, MOUSE_RIGHT = Button2 } ksMouseButton; + +/* + Change video mode using the XFree86-VidMode X extension. + + While the XFree86-VidMode X extension should be superseded by the XRandR X extension, + this still appears to be the most reliable way to change video modes for a single + monitor configuration. +*/ +static bool ChangeVideoMode_XF86VidMode(Display *xDisplay, int xScreen, int *currentWidth, int *currentHeight, + float *currentRefreshRate, int *desiredWidth, int *desiredHeight, + float *desiredRefreshRate) { + int videoModeCount; + XF86VidModeModeInfo **videoModeInfos; + + XF86VidModeGetAllModeLines(xDisplay, xScreen, &videoModeCount, &videoModeInfos); + + if (currentWidth != NULL && currentHeight != NULL && currentRefreshRate != NULL) { + XF86VidModeModeInfo *mode = videoModeInfos[0]; + *currentWidth = mode->hdisplay; + *currentHeight = mode->vdisplay; + *currentRefreshRate = (mode->dotclock * 1000.0f) / (mode->htotal * mode->vtotal); + } + + if (desiredWidth != NULL && desiredHeight != NULL && desiredRefreshRate != NULL) { + XF86VidModeModeInfo *bestMode = NULL; + int bestModeWidth = 0; + int bestModeHeight = 0; + float bestModeRefreshRate = 0.0f; + int bestSizeError = 0x7FFFFFFF; + float bestRefreshRateError = 1e6f; + for (int j = 0; j < videoModeCount; j++) { + XF86VidModeModeInfo *mode = videoModeInfos[j]; + const int modeWidth = mode->hdisplay; + const int modeHeight = mode->vdisplay; + const float modeRefreshRate = (mode->dotclock * 1000.0f) / (mode->htotal * mode->vtotal); + + const int dw = modeWidth - *desiredWidth; + const int dh = modeHeight - *desiredHeight; + const int sizeError = dw * dw + dh * dh; + const float refreshRateError = fabsf(modeRefreshRate - *desiredRefreshRate); + if (sizeError < bestSizeError || (sizeError == bestSizeError && refreshRateError < bestRefreshRateError)) { + bestSizeError = sizeError; + bestRefreshRateError = refreshRateError; + bestMode = mode; + bestModeWidth = modeWidth; + bestModeHeight = modeHeight; + bestModeRefreshRate = modeRefreshRate; + } + } + + XF86VidModeSwitchToMode(xDisplay, xScreen, bestMode); + XF86VidModeSetViewPort(xDisplay, xScreen, 0, 0); + + *desiredWidth = bestModeWidth; + *desiredHeight = bestModeHeight; + *desiredRefreshRate = bestModeRefreshRate; + } + + for (int i = 0; i < videoModeCount; i++) { + if (videoModeInfos[i]->privsize > 0) { + XFree(videoModeInfos[i]->private); + } + } + XFree(videoModeInfos); + + return true; +} + +void ksGpuWindow_Destroy(ksGpuWindow *window) { + ksGpuContext_Destroy(&window->context); + ksGpuDevice_Destroy(&window->device); + + if (window->windowFullscreen) { + ChangeVideoMode_XF86VidMode(window->xDisplay, window->xScreen, NULL, NULL, NULL, &window->desktopWidth, + &window->desktopHeight, &window->desktopRefreshRate); + + XUngrabPointer(window->xDisplay, CurrentTime); + XUngrabKeyboard(window->xDisplay, CurrentTime); + } + + if (window->xWindow) { + XUnmapWindow(window->xDisplay, window->xWindow); + XDestroyWindow(window->xDisplay, window->xWindow); + window->xWindow = 0; + } + + if (window->xColormap) { + XFreeColormap(window->xDisplay, window->xColormap); + window->xColormap = 0; + } + + if (window->xVisual) { + XFree(window->xVisual); + window->xVisual = NULL; + } + + XFlush(window->xDisplay); + XCloseDisplay(window->xDisplay); + window->xDisplay = NULL; +} + +bool ksGpuWindow_Create(ksGpuWindow *window, ksDriverInstance *instance, const ksGpuQueueInfo *queueInfo, const int queueIndex, + const ksGpuSurfaceColorFormat colorFormat, const ksGpuSurfaceDepthFormat depthFormat, + const ksGpuSampleCount sampleCount, const int width, const int height, const bool fullscreen) { + memset(window, 0, sizeof(ksGpuWindow)); + + window->colorFormat = colorFormat; + window->depthFormat = depthFormat; + window->sampleCount = sampleCount; + window->windowWidth = width; + window->windowHeight = height; + window->windowSwapInterval = 1; + window->windowRefreshRate = 60.0f; + window->windowFullscreen = fullscreen; + window->windowActive = false; + window->windowExit = false; + + const char *displayName = NULL; + window->xDisplay = XOpenDisplay(displayName); + if (!window->xDisplay) { + Error("Unable to open X Display."); + return false; + } + + window->xScreen = XDefaultScreen(window->xDisplay); + window->xRoot = XRootWindow(window->xDisplay, window->xScreen); + + if (window->windowFullscreen) { + ChangeVideoMode_XF86VidMode(window->xDisplay, window->xScreen, &window->desktopWidth, &window->desktopHeight, + &window->desktopRefreshRate, &window->windowWidth, &window->windowHeight, + &window->windowRefreshRate); + } else { + ChangeVideoMode_XF86VidMode(window->xDisplay, window->xScreen, &window->desktopWidth, &window->desktopHeight, + &window->desktopRefreshRate, NULL, NULL, NULL); + window->windowRefreshRate = window->desktopRefreshRate; + } + + ksGpuDevice_Create(&window->device, instance, queueInfo); + ksGpuContext_CreateForSurface(&window->context, &window->device, queueIndex, colorFormat, depthFormat, sampleCount, + window->xDisplay, window->xScreen); + + window->xVisual = glXGetVisualFromFBConfig(window->xDisplay, window->context.glxFBConfig); + if (window->xVisual == NULL) { + Error("Failed to retrieve visual for framebuffer config."); + ksGpuWindow_Destroy(window); + return false; + } + + window->xColormap = XCreateColormap(window->xDisplay, window->xRoot, window->xVisual->visual, AllocNone); + + const unsigned long wamask = CWColormap | CWEventMask | (window->windowFullscreen ? 0 : CWBorderPixel); + + XSetWindowAttributes wa; + memset(&wa, 0, sizeof(wa)); + wa.colormap = window->xColormap; + wa.border_pixel = 0; + wa.event_mask = StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask | KeyPressMask | KeyReleaseMask | + ButtonPressMask | ButtonReleaseMask | FocusChangeMask | ExposureMask | VisibilityChangeMask | EnterWindowMask | + LeaveWindowMask; + + window->xWindow = XCreateWindow(window->xDisplay, // Display * display + window->xRoot, // Window parent + 0, // int x + 0, // int y + window->windowWidth, // unsigned int width + window->windowHeight, // unsigned int height + 0, // unsigned int border_width + window->xVisual->depth, // int depth + InputOutput, // unsigned int class + window->xVisual->visual, // Visual * visual + wamask, // unsigned long valuemask + &wa); // XSetWindowAttributes * attributes + + if (!window->xWindow) { + Error("Failed to create window."); + ksGpuWindow_Destroy(window); + return false; + } + + // Change the window title. + Atom _NET_WM_NAME = XInternAtom(window->xDisplay, "_NET_WM_NAME", False); + XChangeProperty(window->xDisplay, window->xWindow, _NET_WM_NAME, XA_STRING, 8, PropModeReplace, + (const unsigned char *)WINDOW_TITLE, strlen(WINDOW_TITLE)); + + if (window->windowFullscreen) { + // Bypass the compositor in fullscreen mode. + const unsigned long bypass = 1; + Atom _NET_WM_BYPASS_COMPOSITOR = XInternAtom(window->xDisplay, "_NET_WM_BYPASS_COMPOSITOR", False); + XChangeProperty(window->xDisplay, window->xWindow, _NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32, PropModeReplace, + (const unsigned char *)&bypass, 1); + + // Completely disassociate window from window manager. + XSetWindowAttributes attributes; + attributes.override_redirect = True; + XChangeWindowAttributes(window->xDisplay, window->xWindow, CWOverrideRedirect, &attributes); + + // Make the window visible. + XMapRaised(window->xDisplay, window->xWindow); + XMoveResizeWindow(window->xDisplay, window->xWindow, 0, 0, window->windowWidth, window->windowHeight); + XFlush(window->xDisplay); + + // Grab mouse and keyboard input now that the window is disassociated from the window manager. + XGrabPointer(window->xDisplay, window->xWindow, True, 0, GrabModeAsync, GrabModeAsync, window->xWindow, 0L, CurrentTime); + XGrabKeyboard(window->xDisplay, window->xWindow, True, GrabModeAsync, GrabModeAsync, CurrentTime); + } else { + // Make the window fixed size. + XSizeHints *hints = XAllocSizeHints(); + hints->flags = (PMinSize | PMaxSize); + hints->min_width = window->windowWidth; + hints->max_width = window->windowWidth; + hints->min_height = window->windowHeight; + hints->max_height = window->windowHeight; + XSetWMNormalHints(window->xDisplay, window->xWindow, hints); + XFree(hints); + + // First map the window and then center the window on the screen. + XMapRaised(window->xDisplay, window->xWindow); + const int x = (window->desktopWidth - window->windowWidth) / 2; + const int y = (window->desktopHeight - window->windowHeight) / 2; + XMoveResizeWindow(window->xDisplay, window->xWindow, x, y, window->windowWidth, window->windowHeight); + XFlush(window->xDisplay); + } + + window->context.glxDrawable = window->xWindow; + + ksGpuContext_SetCurrent(&window->context); + + + return true; +} + +#elif defined(OS_LINUX_XCB) || defined(OS_LINUX_XCB_GLX) + +typedef enum // keysym.h +{ KEY_A = XK_a, + KEY_B = XK_b, + KEY_C = XK_c, + KEY_D = XK_d, + KEY_E = XK_e, + KEY_F = XK_f, + KEY_G = XK_g, + KEY_H = XK_h, + KEY_I = XK_i, + KEY_J = XK_j, + KEY_K = XK_k, + KEY_L = XK_l, + KEY_M = XK_m, + KEY_N = XK_n, + KEY_O = XK_o, + KEY_P = XK_p, + KEY_Q = XK_q, + KEY_R = XK_r, + KEY_S = XK_s, + KEY_T = XK_t, + KEY_U = XK_u, + KEY_V = XK_v, + KEY_W = XK_w, + KEY_X = XK_x, + KEY_Y = XK_y, + KEY_Z = XK_z, + KEY_RETURN = (XK_Return & 0xFF), + KEY_TAB = (XK_Tab & 0xFF), + KEY_ESCAPE = (XK_Escape & 0xFF), + KEY_SHIFT_LEFT = (XK_Shift_L & 0xFF), + KEY_CTRL_LEFT = (XK_Control_L & 0xFF), + KEY_ALT_LEFT = (XK_Alt_L & 0xFF), + KEY_CURSOR_UP = (XK_Up & 0xFF), + KEY_CURSOR_DOWN = (XK_Down & 0xFF), + KEY_CURSOR_LEFT = (XK_Left & 0xFF), + KEY_CURSOR_RIGHT = (XK_Right & 0xFF) } ksKeyboardKey; + +typedef enum { MOUSE_LEFT = 0, MOUSE_RIGHT = 1 } ksMouseButton; + +typedef enum { + XCB_SIZE_HINT_US_POSITION = 1 << 0, + XCB_SIZE_HINT_US_SIZE = 1 << 1, + XCB_SIZE_HINT_P_POSITION = 1 << 2, + XCB_SIZE_HINT_P_SIZE = 1 << 3, + XCB_SIZE_HINT_P_MIN_SIZE = 1 << 4, + XCB_SIZE_HINT_P_MAX_SIZE = 1 << 5, + XCB_SIZE_HINT_P_RESIZE_INC = 1 << 6, + XCB_SIZE_HINT_P_ASPECT = 1 << 7, + XCB_SIZE_HINT_BASE_SIZE = 1 << 8, + XCB_SIZE_HINT_P_WIN_GRAVITY = 1 << 9 +} xcb_size_hints_flags_t; + +static const int _NET_WM_STATE_ADD = 1; // add/set property + +/* + Change video mode using the RandR X extension version 1.4 + + The following code does not necessarily work out of the box, because on + some configurations the modes list returned by XRRGetScreenResources() + is populated with nothing other than the maximum display resolution, + even though XF86VidModeGetAllModeLines() and XRRConfigSizes() *will* + list all resolutions for the same display. + + The user can manually add new modes from the command-line using the + xrandr utility: + + xrandr --newmode + + Where is generated with a utility that implements either + the General Timing Formula (GTF) or the Coordinated Video Timing (CVT) + standard put forth by the Video Electronics Standards Association (VESA): + + gft // http://gtf.sourceforge.net/ + cvt // http://www.uruk.org/~erich/projects/cvt/ + + Alternatively, new modes can be added in code using XRRCreateMode(). + However, this requires calculating all the timing information in code + because there is no standard library that implements the GTF or CVT. +*/ +static bool ChangeVideoMode_XcbRandR_1_4(xcb_connection_t *connection, xcb_screen_t *screen, int *currentWidth, int *currentHeight, + float *currentRefreshRate, int *desiredWidth, int *desiredHeight, + float *desiredRefreshRate) { + /* + Screen - virtual screenspace which may be covered by multiple CRTCs + CRTC - display controller + Output - display/monitor connected to a CRTC + Clones - outputs that are simultaneously connected to the same CRTC + */ + + xcb_randr_get_screen_resources_cookie_t screen_resources_cookie = xcb_randr_get_screen_resources(connection, screen->root); + xcb_randr_get_screen_resources_reply_t *screen_resources_reply = + xcb_randr_get_screen_resources_reply(connection, screen_resources_cookie, 0); + if (screen_resources_reply == NULL) { + return false; + } + + xcb_randr_mode_info_t *mode_info = xcb_randr_get_screen_resources_modes(screen_resources_reply); + const int modes_length = xcb_randr_get_screen_resources_modes_length(screen_resources_reply); + assert(modes_length > 0); + + xcb_randr_crtc_t *crtcs = xcb_randr_get_screen_resources_crtcs(screen_resources_reply); + const int crtcs_length = xcb_randr_get_screen_resources_crtcs_length(screen_resources_reply); + assert(crtcs_length > 0); + UNUSED_PARM(crtcs_length); + + const int PRIMARY_CRTC_INDEX = 0; + const int PRIMARY_OUTPUT_INDEX = 0; + + xcb_randr_get_crtc_info_cookie_t primary_crtc_info_cookie = xcb_randr_get_crtc_info(connection, crtcs[PRIMARY_CRTC_INDEX], 0); + xcb_randr_get_crtc_info_reply_t *primary_crtc_info_reply = + xcb_randr_get_crtc_info_reply(connection, primary_crtc_info_cookie, NULL); + + xcb_randr_output_t *crtc_outputs = xcb_randr_get_crtc_info_outputs(primary_crtc_info_reply); + + xcb_randr_get_output_info_cookie_t primary_output_info_cookie = + xcb_randr_get_output_info(connection, crtc_outputs[PRIMARY_OUTPUT_INDEX], 0); + xcb_randr_get_output_info_reply_t *primary_output_info_reply = + xcb_randr_get_output_info_reply(connection, primary_output_info_cookie, NULL); + + if (currentWidth != NULL && currentHeight != NULL && currentRefreshRate != NULL) { + for (int i = 0; i < modes_length; i++) { + if (mode_info[i].id == primary_crtc_info_reply->mode) { + *currentWidth = mode_info[i].width; + *currentHeight = mode_info[i].height; + *currentRefreshRate = mode_info[i].dot_clock / ((float)mode_info[i].htotal * (float)mode_info[i].vtotal); + break; + } + } + } + + if (desiredWidth != NULL && desiredHeight != NULL && desiredRefreshRate != NULL) { + xcb_randr_mode_t bestMode = 0; + int bestModeWidth = 0; + int bestModeHeight = 0; + float bestModeRefreshRate = 0.0f; + int bestSizeError = 0x7FFFFFFF; + float bestRefreshRateError = 1e6f; + for (int i = 0; i < modes_length; i++) { + if (mode_info[i].mode_flags & XCB_RANDR_MODE_FLAG_INTERLACE) { + continue; + } + + xcb_randr_mode_t *primary_output_info_modes = xcb_randr_get_output_info_modes(primary_output_info_reply); + int primary_output_info_modes_length = xcb_randr_get_output_info_modes_length(primary_output_info_reply); + + bool validOutputMode = false; + for (int j = 0; j < primary_output_info_modes_length; j++) { + if (mode_info[i].id == primary_output_info_modes[j]) { + validOutputMode = true; + break; + } + } + if (!validOutputMode) { + continue; + } + + const int modeWidth = mode_info[i].width; + const int modeHeight = mode_info[i].height; + const float modeRefreshRate = mode_info[i].dot_clock / ((float)mode_info[i].htotal * (float)mode_info[i].vtotal); + + const int dw = modeWidth - *desiredWidth; + const int dh = modeHeight - *desiredHeight; + const int sizeError = dw * dw + dh * dh; + const float refreshRateError = fabs(modeRefreshRate - *desiredRefreshRate); + if (sizeError < bestSizeError || (sizeError == bestSizeError && refreshRateError < bestRefreshRateError)) { + bestSizeError = sizeError; + bestRefreshRateError = refreshRateError; + bestMode = mode_info[i].id; + bestModeWidth = modeWidth; + bestModeHeight = modeHeight; + bestModeRefreshRate = modeRefreshRate; + } + } + + xcb_randr_output_t *primary_crtc_info_outputs = xcb_randr_get_crtc_info_outputs(primary_crtc_info_reply); + int primary_crtc_info_outputs_length = xcb_randr_get_crtc_info_outputs_length(primary_crtc_info_reply); + + xcb_randr_set_crtc_config(connection, primary_output_info_reply->crtc, XCB_TIME_CURRENT_TIME, XCB_TIME_CURRENT_TIME, + primary_crtc_info_reply->x, primary_crtc_info_reply->y, bestMode, + primary_crtc_info_reply->rotation, primary_crtc_info_outputs_length, primary_crtc_info_outputs); + + *desiredWidth = bestModeWidth; + *desiredHeight = bestModeHeight; + *desiredRefreshRate = bestModeRefreshRate; + } + + free(primary_output_info_reply); + free(primary_crtc_info_reply); + free(screen_resources_reply); + + return true; +} + +void ksGpuWindow_Destroy(ksGpuWindow *window) { + ksGpuContext_Destroy(&window->context); + ksGpuDevice_Destroy(&window->device); + +#if defined(OS_LINUX_XCB_GLX) + glXDestroyWindow(window->xDisplay, window->glxWindow); + XFlush(window->xDisplay); + XCloseDisplay(window->xDisplay); + window->xDisplay = NULL; +#else + xcb_glx_delete_window(window->connection, window->glxWindow); +#endif + + if (window->windowFullscreen) { + ChangeVideoMode_XcbRandR_1_4(window->connection, window->screen, NULL, NULL, NULL, &window->desktopWidth, + &window->desktopHeight, &window->desktopRefreshRate); + } + + xcb_destroy_window(window->connection, window->window); + xcb_free_colormap(window->connection, window->colormap); + xcb_flush(window->connection); + xcb_disconnect(window->connection); + xcb_key_symbols_free(window->key_symbols); +} + +bool ksGpuWindow_Create(ksGpuWindow *window, ksDriverInstance *instance, const ksGpuQueueInfo *queueInfo, const int queueIndex, + const ksGpuSurfaceColorFormat colorFormat, const ksGpuSurfaceDepthFormat depthFormat, + const ksGpuSampleCount sampleCount, const int width, const int height, const bool fullscreen) { + memset(window, 0, sizeof(ksGpuWindow)); + + window->colorFormat = colorFormat; + window->depthFormat = depthFormat; + window->sampleCount = sampleCount; + window->windowWidth = width; + window->windowHeight = height; + window->windowSwapInterval = 1; + window->windowRefreshRate = 60.0f; + window->windowFullscreen = fullscreen; + window->windowActive = false; + window->windowExit = false; + + const char *displayName = NULL; + int screen_number = 0; + window->connection = xcb_connect(displayName, &screen_number); + if (xcb_connection_has_error(window->connection)) { + ksGpuWindow_Destroy(window); + Error("Failed to open XCB connection."); + return false; + } + + const xcb_setup_t *setup = xcb_get_setup(window->connection); + xcb_screen_iterator_t iter = xcb_setup_roots_iterator(setup); + for (int i = 0; i < screen_number; i++) { + xcb_screen_next(&iter); + } + window->screen = iter.data; + + if (window->windowFullscreen) { + ChangeVideoMode_XcbRandR_1_4(window->connection, window->screen, &window->desktopWidth, &window->desktopHeight, + &window->desktopRefreshRate, &window->windowWidth, &window->windowHeight, + &window->windowRefreshRate); + } else { + ChangeVideoMode_XcbRandR_1_4(window->connection, window->screen, &window->desktopWidth, &window->desktopHeight, + &window->desktopRefreshRate, NULL, NULL, NULL); + window->windowRefreshRate = window->desktopRefreshRate; + } + + ksGpuDevice_Create(&window->device, instance, queueInfo); +#if defined(OS_LINUX_XCB_GLX) + window->xDisplay = XOpenDisplay(displayName); + ksGpuContext_CreateForSurface(&window->context, &window->device, queueIndex, colorFormat, depthFormat, sampleCount, + window->xDisplay, screen_number); +#else + ksGpuContext_CreateForSurface(&window->context, &window->device, queueIndex, colorFormat, depthFormat, sampleCount, + window->connection, screen_number); +#endif + + // Create the color map. + window->colormap = xcb_generate_id(window->connection); + xcb_create_colormap(window->connection, XCB_COLORMAP_ALLOC_NONE, window->colormap, window->screen->root, + window->context.visualid); + + // Create the window. + uint32_t value_mask = XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP; + uint32_t value_list[5]; + value_list[0] = window->screen->black_pixel; + value_list[1] = 0; + value_list[2] = XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_BUTTON_PRESS; + value_list[3] = window->colormap; + value_list[4] = 0; + + window->window = xcb_generate_id(window->connection); + xcb_create_window(window->connection, // xcb_connection_t * connection + XCB_COPY_FROM_PARENT, // uint8_t depth + window->window, // xcb_window_t wid + window->screen->root, // xcb_window_t parent + 0, // int16_t x + 0, // int16_t y + window->windowWidth, // uint16_t width + window->windowHeight, // uint16_t height + 0, // uint16_t border_width + XCB_WINDOW_CLASS_INPUT_OUTPUT, // uint16_t _class + window->context.visualid, // xcb_visualid_t visual + value_mask, // uint32_t value_mask + value_list); // const uint32_t * value_list + + // Change the window title. + xcb_change_property(window->connection, XCB_PROP_MODE_REPLACE, window->window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, + strlen(WINDOW_TITLE), WINDOW_TITLE); + + // Setup code that will send a notification when the window is destroyed. + xcb_intern_atom_cookie_t wm_protocols_cookie = xcb_intern_atom(window->connection, 1, 12, "WM_PROTOCOLS"); + xcb_intern_atom_cookie_t wm_delete_window_cookie = xcb_intern_atom(window->connection, 0, 16, "WM_DELETE_WINDOW"); + xcb_intern_atom_reply_t *wm_protocols_reply = xcb_intern_atom_reply(window->connection, wm_protocols_cookie, 0); + xcb_intern_atom_reply_t *wm_delete_window_reply = xcb_intern_atom_reply(window->connection, wm_delete_window_cookie, 0); + + window->wm_delete_window_atom = wm_delete_window_reply->atom; + xcb_change_property(window->connection, XCB_PROP_MODE_REPLACE, window->window, wm_protocols_reply->atom, XCB_ATOM_ATOM, 32, 1, + &wm_delete_window_reply->atom); + + free(wm_protocols_reply); + free(wm_delete_window_reply); + + if (window->windowFullscreen) { + // Change the window to fullscreen + xcb_intern_atom_cookie_t wm_state_cookie = xcb_intern_atom(window->connection, 0, 13, "_NET_WM_STATE"); + xcb_intern_atom_cookie_t wm_state_fullscreen_cookie = + xcb_intern_atom(window->connection, 0, 24, "_NET_WM_STATE_FULLSCREEN"); + xcb_intern_atom_reply_t *wm_state_reply = xcb_intern_atom_reply(window->connection, wm_state_cookie, 0); + xcb_intern_atom_reply_t *wm_state_fullscreen_reply = + xcb_intern_atom_reply(window->connection, wm_state_fullscreen_cookie, 0); + + xcb_client_message_event_t ev; + ev.response_type = XCB_CLIENT_MESSAGE; + ev.format = 32; + ev.sequence = 0; + ev.window = window->window; + ev.type = wm_state_reply->atom; + ev.data.data32[0] = _NET_WM_STATE_ADD; + ev.data.data32[1] = wm_state_fullscreen_reply->atom; + ev.data.data32[2] = XCB_ATOM_NONE; + ev.data.data32[3] = 0; + ev.data.data32[4] = 0; + + xcb_send_event(window->connection, 1, window->window, + XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY, (const char *)(&ev)); + + free(wm_state_reply); + free(wm_state_fullscreen_reply); + + xcb_map_window(window->connection, window->window); + xcb_flush(window->connection); + } else { + // Make the window fixed size. + xcb_size_hints_t hints; + memset(&hints, 0, sizeof(hints)); + hints.flags = XCB_SIZE_HINT_US_SIZE | XCB_SIZE_HINT_P_SIZE | XCB_SIZE_HINT_P_MIN_SIZE | XCB_SIZE_HINT_P_MAX_SIZE; + hints.min_width = window->windowWidth; + hints.max_width = window->windowWidth; + hints.min_height = window->windowHeight; + hints.max_height = window->windowHeight; + + xcb_change_property(window->connection, XCB_PROP_MODE_REPLACE, window->window, XCB_ATOM_WM_NORMAL_HINTS, + XCB_ATOM_WM_SIZE_HINTS, 32, sizeof(hints) / 4, &hints); + + // First map the window and then center the window on the screen. + xcb_map_window(window->connection, window->window); + const uint32_t coords[] = {(window->desktopWidth - window->windowWidth) / 2, + (window->desktopHeight - window->windowHeight) / 2}; + xcb_configure_window(window->connection, window->window, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, coords); + xcb_flush(window->connection); + } + + window->key_symbols = xcb_key_symbols_alloc(window->connection); + +#if defined(OS_LINUX_XCB_GLX) + window->glxWindow = glXCreateWindow(window->xDisplay, window->context.glxFBConfig, window->window, NULL); +#else + window->glxWindow = xcb_generate_id(window->connection); + xcb_glx_create_window(window->connection, screen_number, window->context.fbconfigid, window->window, window->glxWindow, 0, + NULL); +#endif + + window->context.glxDrawable = window->glxWindow; + + ksGpuContext_SetCurrent(&window->context); + + + return true; +} + +#elif defined(OS_LINUX_WAYLAND) + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif +static void _keyboard_keymap_cb(void *data, struct wl_keyboard *keyboard, uint32_t format, int fd, uint32_t size) { close(fd); } +static void _keyboard_modifiers_cb(void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed, + uint32_t mods_latched, uint32_t mods_locked, uint32_t group) {} + +static void _keyboard_enter_cb(void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface, + struct wl_array *keys) {} + +static void _keyboard_leave_cb(void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface) {} + +static void _pointer_leave_cb(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface) {} + +static void _pointer_enter_cb(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t sx, + wl_fixed_t sy) { + wl_pointer_set_cursor(pointer, serial, NULL, 0, 0); +} + +static void _pointer_motion_cb(void *data, struct wl_pointer *pointer, uint32_t time, wl_fixed_t x, wl_fixed_t y) { + ksGpuWindow *window = (ksGpuWindow *)data; + window->input.mouseInputX[0] = wl_fixed_to_int(x); + window->input.mouseInputY[0] = wl_fixed_to_int(y); +} + +static void _pointer_button_cb(void *data, struct wl_pointer *pointer, uint32_t serial, uint32_t time, uint32_t button, + uint32_t state) { + ksGpuWindow *window = (ksGpuWindow *)data; + + uint32_t button_id = 0; + switch (button) { + case BTN_LEFT: + button_id = 0; + break; + case BTN_MIDDLE: + button_id = 1; + break; + case BTN_RIGHT: + button_id = 2; + break; + } + + window->input.mouseInput[button_id] = state; +} + +static void _pointer_axis_cb(void *data, struct wl_pointer *pointer, uint32_t time, uint32_t axis, wl_fixed_t value) {} + +static void _keyboard_key_cb(void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, + uint32_t state) { + ksGpuWindow *window = (ksGpuWindow *)data; + if (key == KEY_ESC) window->windowExit = true; + + if (state) window->input.keyInput[key] = state; +} + +const struct wl_pointer_listener pointer_listener = { + _pointer_enter_cb, _pointer_leave_cb, _pointer_motion_cb, _pointer_button_cb, _pointer_axis_cb, +}; + +const struct wl_keyboard_listener keyboard_listener = { + _keyboard_keymap_cb, _keyboard_enter_cb, _keyboard_leave_cb, _keyboard_key_cb, _keyboard_modifiers_cb, +}; + +static void _seat_capabilities_cb(void *data, struct wl_seat *seat, uint32_t caps) { + ksGpuWindow *window = (ksGpuWindow *)data; + if ((caps & WL_SEAT_CAPABILITY_POINTER) && !window->pointer) { + window->pointer = wl_seat_get_pointer(seat); + wl_pointer_add_listener(window->pointer, &pointer_listener, window); + } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && window->pointer) { + wl_pointer_destroy(window->pointer); + window->pointer = NULL; + } + + if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !window->keyboard) { + window->keyboard = wl_seat_get_keyboard(seat); + wl_keyboard_add_listener(window->keyboard, &keyboard_listener, window); + } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && window->keyboard) { + wl_keyboard_destroy(window->keyboard); + window->keyboard = NULL; + } +} + +const struct wl_seat_listener seat_listener = { + _seat_capabilities_cb, +}; + +static void _xdg_surface_configure_cb(void *data, struct zxdg_surface_v6 *surface, uint32_t serial) { + zxdg_surface_v6_ack_configure(surface, serial); +} + +const struct zxdg_surface_v6_listener xdg_surface_listener = { + _xdg_surface_configure_cb, +}; + +static void _xdg_shell_ping_cb(void *data, struct zxdg_shell_v6 *shell, uint32_t serial) { zxdg_shell_v6_pong(shell, serial); } + +const struct zxdg_shell_v6_listener xdg_shell_listener = { + _xdg_shell_ping_cb, +}; + +static void _xdg_toplevel_configure_cb(void *data, struct zxdg_toplevel_v6 *toplevel, int32_t width, int32_t height, + struct wl_array *states) { + ksGpuWindow *window = (ksGpuWindow *)data; + + window->windowActive = false; + + enum zxdg_toplevel_v6_state *state; + wl_array_for_each(state, states) { + switch (*state) { + case ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN: + break; + case ZXDG_TOPLEVEL_V6_STATE_RESIZING: + window->windowWidth = width; + window->windowWidth = height; + break; + case ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED: + break; + case ZXDG_TOPLEVEL_V6_STATE_ACTIVATED: + window->windowActive = true; + break; + } + } +} + +static void _xdg_toplevel_close_cb(void *data, struct zxdg_toplevel_v6 *toplevel) { + ksGpuWindow *window = (ksGpuWindow *)data; + window->windowExit = true; +} + +const struct zxdg_toplevel_v6_listener xdg_toplevel_listener = { + _xdg_toplevel_configure_cb, + _xdg_toplevel_close_cb, +}; + +static void _registry_cb(void *data, struct wl_registry *registry, uint32_t id, const char *interface, uint32_t version) { + ksGpuWindow *window = (ksGpuWindow *)data; + + if (strcmp(interface, "wl_compositor") == 0) { + window->compositor = wl_registry_bind(registry, id, &wl_compositor_interface, 1); + } else if (strcmp(interface, "zxdg_shell_v6") == 0) { + window->shell = wl_registry_bind(registry, id, &zxdg_shell_v6_interface, 1); + zxdg_shell_v6_add_listener(window->shell, &xdg_shell_listener, NULL); + } else if (strcmp(interface, "wl_seat") == 0) { + window->seat = wl_registry_bind(registry, id, &wl_seat_interface, 1); + wl_seat_add_listener(window->seat, &seat_listener, window); + } +} + +static void _registry_remove_cb(void *data, struct wl_registry *registry, uint32_t id) {} + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +const struct wl_registry_listener registry_listener = {_registry_cb, _registry_remove_cb}; + +bool ksGpuWindow_Create(ksGpuWindow *window, ksDriverInstance *instance, const ksGpuQueueInfo *queueInfo, const int queueIndex, + const ksGpuSurfaceColorFormat colorFormat, const ksGpuSurfaceDepthFormat depthFormat, + const ksGpuSampleCount sampleCount, const int width, const int height, const bool fullscreen) { + (void)queueIndex; + memset(window, 0, sizeof(ksGpuWindow)); + + window->display = NULL; + window->surface = NULL; + window->registry = NULL; + window->compositor = NULL; + window->shell = NULL; + window->shell_surface = NULL; + + window->keyboard = NULL; + window->pointer = NULL; + window->seat = NULL; + + window->colorFormat = colorFormat; + window->depthFormat = depthFormat; + window->sampleCount = sampleCount; + window->windowWidth = width; + window->windowHeight = height; + window->windowSwapInterval = 1; + window->windowRefreshRate = 60.0f; + window->windowFullscreen = fullscreen; + window->windowActive = false; + window->windowExit = false; + + window->display = wl_display_connect(NULL); + if (window->display == NULL) { + Error("Can't connect to wayland display."); + return false; + } + + window->registry = wl_display_get_registry(window->display); + wl_registry_add_listener(window->registry, ®istry_listener, window); + + wl_display_roundtrip(window->display); + + if (window->compositor == NULL) { + Error("Compositor protocol failed to bind"); + return false; + } + + if (window->shell == NULL) { + Error("Compositor is missing support for zxdg_shell_v6."); + return false; + } + + window->surface = wl_compositor_create_surface(window->compositor); + if (window->surface == NULL) { + Error("Could not create compositor surface."); + return false; + } + + window->shell_surface = zxdg_shell_v6_get_xdg_surface(window->shell, window->surface); + if (window->shell_surface == NULL) { + Error("Could not get shell surface."); + return false; + } + + zxdg_surface_v6_add_listener(window->shell_surface, &xdg_surface_listener, window); + + struct zxdg_toplevel_v6 *toplevel = zxdg_surface_v6_get_toplevel(window->shell_surface); + if (toplevel == NULL) { + Error("Could not get surface toplevel."); + return false; + } + + zxdg_toplevel_v6_add_listener(toplevel, &xdg_toplevel_listener, window); + + zxdg_toplevel_v6_set_title(toplevel, WINDOW_TITLE); + zxdg_toplevel_v6_set_app_id(toplevel, APPLICATION_NAME); + zxdg_toplevel_v6_set_min_size(toplevel, width, height); + zxdg_toplevel_v6_set_max_size(toplevel, width, height); + + wl_surface_commit(window->surface); + + window->context.native_window = wl_egl_window_create(window->surface, width, height); + + if (window->context.native_window == EGL_NO_SURFACE) { + ksGpuWindow_Destroy(window); + Error("Could not create wayland egl window."); + return false; + } + + ksGpuDevice_Create(&window->device, instance, queueInfo); + + ksGpuContext_CreateForSurface(&window->context, &window->device, window->display); + + ksGpuContext_SetCurrent(&window->context); + + + return true; +} + +void ksGpuWindow_Destroy(ksGpuWindow *window) { + if (window->pointer != NULL) wl_pointer_destroy(window->pointer); + if (window->keyboard != NULL) wl_keyboard_destroy(window->keyboard); + if (window->seat != NULL) wl_seat_destroy(window->seat); + + wl_egl_window_destroy(window->context.native_window); + + if (window->compositor != NULL) wl_compositor_destroy(window->compositor); + if (window->registry != NULL) wl_registry_destroy(window->registry); + if (window->shell_surface != NULL) zxdg_surface_v6_destroy(window->shell_surface); + if (window->shell != NULL) zxdg_shell_v6_destroy(window->shell); + if (window->surface != NULL) wl_surface_destroy(window->surface); + if (window->display != NULL) wl_display_disconnect(window->display); + + ksGpuContext_Destroy(&window->context); + ksGpuDevice_Destroy(&window->device); +} + +/* + * TODO: + * This is a work around for ksKeyboardKey naming collision + * with the definitions from . + * The proper fix for this is to rename the key enums. + */ + +#undef KEY_A +#undef KEY_B +#undef KEY_C +#undef KEY_D +#undef KEY_E +#undef KEY_F +#undef KEY_G +#undef KEY_H +#undef KEY_I +#undef KEY_J +#undef KEY_K +#undef KEY_L +#undef KEY_M +#undef KEY_N +#undef KEY_O +#undef KEY_P +#undef KEY_Q +#undef KEY_R +#undef KEY_S +#undef KEY_T +#undef KEY_U +#undef KEY_V +#undef KEY_W +#undef KEY_X +#undef KEY_Y +#undef KEY_Z +#undef KEY_TAB + +typedef enum // from +{ KEY_A = 30, + KEY_B = 48, + KEY_C = 46, + KEY_D = 32, + KEY_E = 18, + KEY_F = 33, + KEY_G = 34, + KEY_H = 35, + KEY_I = 23, + KEY_J = 36, + KEY_K = 37, + KEY_L = 38, + KEY_M = 50, + KEY_N = 49, + KEY_O = 24, + KEY_P = 25, + KEY_Q = 16, + KEY_R = 19, + KEY_S = 31, + KEY_T = 20, + KEY_U = 22, + KEY_V = 47, + KEY_W = 17, + KEY_X = 45, + KEY_Y = 21, + KEY_Z = 44, + KEY_TAB = 15, + KEY_RETURN = KEY_ENTER, + KEY_ESCAPE = KEY_ESC, + KEY_SHIFT_LEFT = KEY_LEFTSHIFT, + KEY_CTRL_LEFT = KEY_LEFTCTRL, + KEY_ALT_LEFT = KEY_LEFTALT, + KEY_CURSOR_UP = KEY_UP, + KEY_CURSOR_DOWN = KEY_DOWN, + KEY_CURSOR_LEFT = KEY_LEFT, + KEY_CURSOR_RIGHT = KEY_RIGHT } ksKeyboardKey; + +typedef enum { MOUSE_LEFT = BTN_LEFT, MOUSE_MIDDLE = BTN_MIDDLE, MOUSE_RIGHT = BTN_RIGHT } ksMouseButton; + +#elif defined(OS_APPLE_MACOS) + +typedef enum { + KEY_A = 0x00, + KEY_B = 0x0B, + KEY_C = 0x08, + KEY_D = 0x02, + KEY_E = 0x0E, + KEY_F = 0x03, + KEY_G = 0x05, + KEY_H = 0x04, + KEY_I = 0x22, + KEY_J = 0x26, + KEY_K = 0x28, + KEY_L = 0x25, + KEY_M = 0x2E, + KEY_N = 0x2D, + KEY_O = 0x1F, + KEY_P = 0x23, + KEY_Q = 0x0C, + KEY_R = 0x0F, + KEY_S = 0x01, + KEY_T = 0x11, + KEY_U = 0x20, + KEY_V = 0x09, + KEY_W = 0x0D, + KEY_X = 0x07, + KEY_Y = 0x10, + KEY_Z = 0x06, + KEY_RETURN = 0x24, + KEY_TAB = 0x30, + KEY_ESCAPE = 0x35, + KEY_SHIFT_LEFT = 0x38, + KEY_CTRL_LEFT = 0x3B, + KEY_ALT_LEFT = 0x3A, + KEY_CURSOR_UP = 0x7E, + KEY_CURSOR_DOWN = 0x7D, + KEY_CURSOR_LEFT = 0x7B, + KEY_CURSOR_RIGHT = 0x7C +} ksKeyboardKey; + +typedef enum { MOUSE_LEFT = 0, MOUSE_RIGHT = 1 } ksMouseButton; + +NSAutoreleasePool *autoReleasePool; + +@interface MyNSWindow : NSWindow +- (BOOL)canBecomeMainWindow; +- (BOOL)canBecomeKeyWindow; +- (BOOL)acceptsFirstResponder; +- (void)keyDown:(NSEvent *)event; +@end + +@implementation MyNSWindow +- (BOOL)canBecomeMainWindow { + return YES; +} +- (BOOL)canBecomeKeyWindow { + return YES; +} +- (BOOL)acceptsFirstResponder { + return YES; +} +- (void)keyDown:(NSEvent *)event { + (void)event; +} +@end + +@interface MyNSView : NSView +- (BOOL)acceptsFirstResponder; +- (void)keyDown:(NSEvent *)event; +@end + +@implementation MyNSView +- (BOOL)acceptsFirstResponder { + return YES; +} +- (void)keyDown:(NSEvent *)event { + (void)event; +} +@end + +void ksGpuWindow_Destroy(ksGpuWindow *window) { + ksGpuContext_Destroy(&window->context); + ksGpuDevice_Destroy(&window->device); + + if (window->windowFullscreen) { + CGDisplaySetDisplayMode(window->display, window->desktopDisplayMode, NULL); + CGDisplayModeRelease(window->desktopDisplayMode); + window->desktopDisplayMode = NULL; + } + if (window->nsWindow) { + [window->nsWindow release]; + window->nsWindow = nil; + } + if (window->nsView) { + [window->nsView release]; + window->nsView = nil; + } +} + +bool ksGpuWindow_Create(ksGpuWindow *window, ksDriverInstance *instance, const ksGpuQueueInfo *queueInfo, const int queueIndex, + const ksGpuSurfaceColorFormat colorFormat, const ksGpuSurfaceDepthFormat depthFormat, + const ksGpuSampleCount sampleCount, const int width, const int height, const bool fullscreen) { + memset(window, 0, sizeof(ksGpuWindow)); + + window->colorFormat = colorFormat; + window->depthFormat = depthFormat; + window->sampleCount = sampleCount; + window->windowWidth = width; + window->windowHeight = height; + window->windowSwapInterval = 1; + window->windowRefreshRate = 60.0f; + window->windowFullscreen = fullscreen; + window->windowActive = false; + window->windowExit = false; + + // Get a list of all available displays. + CGDirectDisplayID displays[32]; + CGDisplayCount displayCount = 0; + CGDisplayErr err = CGGetActiveDisplayList(32, displays, &displayCount); + if (err != CGDisplayNoErr) { + return false; + } + // Use the main display. + window->display = displays[0]; + window->desktopDisplayMode = CGDisplayCopyDisplayMode(window->display); + + // If fullscreen then switch to the best matching display mode. + if (window->windowFullscreen) { + CFArrayRef displayModes = CGDisplayCopyAllDisplayModes(window->display, NULL); + CFIndex displayModeCount = CFArrayGetCount(displayModes); + CGDisplayModeRef bestDisplayMode = nil; + size_t bestDisplayWidth = 0; + size_t bestDisplayHeight = 0; + float bestDisplayRefreshRate = 0; + size_t bestError = 0x7FFFFFFF; + for (CFIndex i = 0; i < displayModeCount; i++) { + CGDisplayModeRef mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(displayModes, i); + + const size_t modeWidth = CGDisplayModeGetWidth(mode); + const size_t modeHeight = CGDisplayModeGetHeight(mode); + const double modeRefreshRate = CGDisplayModeGetRefreshRate(mode); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + CFStringRef modePixelEncoding = CGDisplayModeCopyPixelEncoding(mode); +#pragma GCC diagnostic pop + const bool modeBitsPerPixelIs32 = + (CFStringCompare(modePixelEncoding, CFSTR(IO32BitDirectPixels), 0) == kCFCompareEqualTo); + CFRelease(modePixelEncoding); + + if (modeBitsPerPixelIs32) { + const size_t dw = modeWidth - width; + const size_t dh = modeHeight - height; + const size_t error = dw * dw + dh * dh; + if (error < bestError) { + bestError = error; + bestDisplayMode = mode; + bestDisplayWidth = modeWidth; + bestDisplayHeight = modeHeight; + bestDisplayRefreshRate = (float)modeRefreshRate; + } + } + } + CGDisplayErr cgderr = CGDisplaySetDisplayMode(window->display, bestDisplayMode, NULL); + if (cgderr != CGDisplayNoErr) { + CFRelease(displayModes); + return false; + } + CFRelease(displayModes); + window->windowWidth = (int)bestDisplayWidth; + window->windowHeight = (int)bestDisplayHeight; + window->windowRefreshRate = (bestDisplayRefreshRate > 0.0f) ? bestDisplayRefreshRate : 60.0f; + } else { + const float desktopDisplayRefreshRate = (float)CGDisplayModeGetRefreshRate(window->desktopDisplayMode); + window->windowRefreshRate = (desktopDisplayRefreshRate > 0.0f) ? desktopDisplayRefreshRate : 60.0f; + } + + if (window->windowFullscreen) { + NSScreen *screen = [NSScreen mainScreen]; + NSRect screenRect = [screen frame]; + + window->nsView = [MyNSView alloc]; + [window->nsView initWithFrame:screenRect]; + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + const int style = NSBorderlessWindowMask; +#pragma GCC diagnostic pop + + window->nsWindow = [MyNSWindow alloc]; + [window->nsWindow initWithContentRect:screenRect styleMask:style backing:NSBackingStoreBuffered defer:NO screen:screen]; + [window->nsWindow setOpaque:YES]; + [window->nsWindow setLevel:NSMainMenuWindowLevel + 1]; + [window->nsWindow setContentView:window->nsView]; + [window->nsWindow makeMainWindow]; + [window->nsWindow makeKeyAndOrderFront:nil]; + [window->nsWindow makeFirstResponder:nil]; + } else { + NSScreen *screen = [NSScreen mainScreen]; + NSRect screenRect = [screen frame]; + + NSRect windowRect; + windowRect.origin.x = (screenRect.size.width - width) / 2; + windowRect.origin.y = (screenRect.size.height - height) / 2; + windowRect.size.width = width; + windowRect.size.height = height; + + window->nsView = [MyNSView alloc]; + [window->nsView initWithFrame:windowRect]; + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + // Fixed size window. + const int style = NSTitledWindowMask; // | NSClosableWindowMask | NSResizableWindowMask; +#pragma GCC diagnostic pop + + window->nsWindow = [MyNSWindow alloc]; + [window->nsWindow initWithContentRect:windowRect styleMask:style backing:NSBackingStoreBuffered defer:NO screen:screen]; + [window->nsWindow setTitle:@WINDOW_TITLE]; + [window->nsWindow setOpaque:YES]; + [window->nsWindow setContentView:window->nsView]; + [window->nsWindow makeMainWindow]; + [window->nsWindow makeKeyAndOrderFront:nil]; + [window->nsWindow makeFirstResponder:nil]; + } + + ksGpuDevice_Create(&window->device, instance, queueInfo); + ksGpuContext_CreateForSurface(&window->context, &window->device, queueIndex, colorFormat, depthFormat, sampleCount, + window->display); + + [window->context.nsContext setView:window->nsView]; + + ksGpuContext_SetCurrent(&window->context); + + + // The color buffers are not cleared by default. + for (int i = 0; i < 2; i++) { + GL(glClearColor(0.0f, 0.0f, 0.0f, 1.0f)); + GL(glClear(GL_COLOR_BUFFER_BIT)); + CGLFlushDrawable(window->context.cglContext); + } + + return true; +} + +#elif defined(OS_APPLE_IOS) + +typedef enum { + KEY_A = 0x00, + KEY_B = 0x0B, + KEY_C = 0x08, + KEY_D = 0x02, + KEY_E = 0x0E, + KEY_F = 0x03, + KEY_G = 0x05, + KEY_H = 0x04, + KEY_I = 0x22, + KEY_J = 0x26, + KEY_K = 0x28, + KEY_L = 0x25, + KEY_M = 0x2E, + KEY_N = 0x2D, + KEY_O = 0x1F, + KEY_P = 0x23, + KEY_Q = 0x0C, + KEY_R = 0x0F, + KEY_S = 0x01, + KEY_T = 0x11, + KEY_U = 0x20, + KEY_V = 0x09, + KEY_W = 0x0D, + KEY_X = 0x07, + KEY_Y = 0x10, + KEY_Z = 0x06, + KEY_RETURN = 0x24, + KEY_TAB = 0x30, + KEY_ESCAPE = 0x35, + KEY_SHIFT_LEFT = 0x38, + KEY_CTRL_LEFT = 0x3B, + KEY_ALT_LEFT = 0x3A, + KEY_CURSOR_UP = 0x7E, + KEY_CURSOR_DOWN = 0x7D, + KEY_CURSOR_LEFT = 0x7B, + KEY_CURSOR_RIGHT = 0x7C +} ksKeyboardKey; + +typedef enum { MOUSE_LEFT = 0, MOUSE_RIGHT = 1 } ksMouseButton; + +static UIView *myUIView; +static UIWindow *myUIWindow; + +@interface MyUIView : UIView +@end + +@implementation MyUIView + +- (instancetype)initWithFrame:(CGRect)frameRect { + self = [super initWithFrame:frameRect]; + if (self) { + self.contentScaleFactor = UIScreen.mainScreen.nativeScale; + } + return self; +} + ++ (Class)layerClass { + return [CAEAGLLayer class]; +} + +@end + +@interface MyUIViewController : UIViewController +@end + +@implementation MyUIViewController + +- (UIInterfaceOrientationMask)supportedInterfaceOrientations { + return UIInterfaceOrientationMaskLandscape; +} + +- (BOOL)shouldAutorotate { + return TRUE; +} + +@end + +void ksGpuWindow_Destroy(ksGpuWindow *window) { + ksGpuContext_Destroy(&window->context); + ksGpuDevice_Destroy(&window->device); + window->uiWindow = nil; + window->uiView = nil; +} + +bool ksGpuWindow_Create(ksGpuWindow *window, ksDriverInstance *instance, const ksGpuQueueInfo *queueInfo, const int queueIndex, + const ksGpuSurfaceColorFormat colorFormat, const ksGpuSurfaceDepthFormat depthFormat, + const ksGpuSampleCount sampleCount, const int width, const int height, const bool fullscreen) { + memset(window, 0, sizeof(ksGpuWindow)); + + window->colorFormat = colorFormat; + window->depthFormat = depthFormat; + window->sampleCount = sampleCount; + window->windowWidth = width; + window->windowHeight = height; + window->windowSwapInterval = 1; + window->windowRefreshRate = 60.0f; + window->windowFullscreen = fullscreen; + window->windowActive = false; + window->windowExit = false; + window->uiView = myUIView; + window->uiWindow = myUIWindow; + + ksGpuDevice_Create(&window->device, instance, queueInfo); + // ksGpuContext_CreateForSurface(&window->context, &window->device, queueIndex, colorFormat, depthFormat, sampleCount, + // window->display); + + return true; +} + +#elif defined(OS_ANDROID) + +void ksGpuWindow_Destroy(ksGpuWindow *window) { + ksGpuContext_Destroy(&window->context); + ksGpuDevice_Destroy(&window->device); + + if (window->display != 0) { + EGL(eglTerminate(window->display)); + window->display = 0; + } +} + +bool ksGpuWindow_Create(ksGpuWindow *window, ksDriverInstance *instance, const ksGpuQueueInfo *queueInfo, const int queueIndex, + const ksGpuSurfaceColorFormat colorFormat, const ksGpuSurfaceDepthFormat depthFormat, + const ksGpuSampleCount sampleCount, const int width, const int height, const bool fullscreen) { + memset(window, 0, sizeof(ksGpuWindow)); + (void)fullscreen; + + window->colorFormat = colorFormat; + window->depthFormat = depthFormat; + window->sampleCount = sampleCount; + window->windowWidth = width; + window->windowHeight = height; + window->windowSwapInterval = 1; + window->windowRefreshRate = 60.0f; + window->windowFullscreen = true; + window->windowActive = false; + window->windowExit = false; + window->display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (window->display == EGL_NO_DISPLAY) { + Error("Failed to get EGL_DEFAULT_DISPLAY."); + return false; + } + EGL(eglInitialize(window->display, &window->majorVersion, &window->minorVersion)); + + ksGpuDevice_Create(&window->device, instance, queueInfo); + ksGpuContext_CreateForSurface(&window->context, &window->device, queueIndex, colorFormat, depthFormat, sampleCount, + window->display); + ksGpuContext_SetCurrent(&window->context); + + return true; +} + +#endif diff --git a/base/oxr_utils/gfxwrapper_opengl.h b/base/oxr_utils/gfxwrapper_opengl.h new file mode 100644 index 0000000..00751e4 --- /dev/null +++ b/base/oxr_utils/gfxwrapper_opengl.h @@ -0,0 +1,693 @@ +// Copyright (c) 2017-2025 The Khronos Group Inc. +// Copyright (c) 2016, Oculus VR, LLC. +// Portions of macOS, iOS, functionality copyright (c) 2016, The Brenwill Workshop Ltd. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/* REUSE-IgnoreStart */ +/* The following has copyright notices that duplicate the header above */ + +/* +================================================================================================ + +Description : Convenient wrapper for the OpenGL API. +Orig. Author : J.M.P. van Waveren +Orig. Date : 12/21/2014 +Language : C99 +Copyright : Copyright (c) 2016 Oculus VR, LLC. All Rights reserved. + : Portions copyright (c) 2016 The Brenwill Workshop Ltd. All Rights reserved. + +IMPLEMENTATION +============== + +The code is written in an object-oriented style with a focus on minimizing state +and side effects. The majority of the functions manipulate self-contained objects +without modifying any global state (except for OpenGL state). The types +introduced in this file have no circular dependencies, and there are no forward +declarations. + +Even though an object-oriented style is used, the code is written in straight C99 for +maximum portability and readability. To further improve portability and to simplify +compilation, all source code is in a single file without any dependencies on third- +party code or non-standard libraries. The code does not use an OpenGL loading library +like GLEE, GLEW, GL3W, or an OpenGL toolkit like GLUT, FreeGLUT, GLFW, etc. Instead, +the code provides direct access to window and context creation for driver extension work. + +The code is written against version 4.3 of the Core Profile OpenGL Specification, +and version 3.1 of the OpenGL ES Specification. + +Supported platforms are: + + - Microsoft Windows 7 or later + - Ubuntu Linux 14.04 or later + - Apple macOS 10.11 or later + - Apple iOS 9.0 or later + - Android 5.0 or later + + +GRAPHICS API WRAPPER +==================== + +The code wraps the OpenGL API with a convenient wrapper that takes care of a +lot of the OpenGL intricacies. This wrapper does not expose the full OpenGL API +but can be easily extended to support more features. Some of the current +limitations are: + +- The wrapper is setup for forward rendering with a single render pass. This + can be easily extended if more complex rendering algorithms are desired. + +- A pipeline can only use 256 bytes worth of plain integer and floating-point + uniforms, including vectors and matrices. If more uniforms are needed then + it is advised to use a uniform buffer, which is the preferred approach for + exposing large amounts of data anyway. + +- Graphics programs currently consist of only a vertex and fragment shader. + This can be easily extended if there is a need for geometry shaders etc. + + +KNOWN ISSUES +============ + +OS : Apple Mac OS X 10.9.5 +GPU : Geforce GT 750M +DRIVER : NVIDIA 310.40.55b01 +----------------------------------------------- +- glGetQueryObjectui64v( query, GL_QUERY_RESULT, &time ) always returns zero for a timer query. +- glFlush() after a glFenceSync() stalls the CPU for many milliseconds. +- Creating a context fails when the share context is current on another thread. + +OS : Android 6.0.1 +GPU : Adreno (TM) 530 +DRIVER : OpenGL ES 3.1 V@145.0 +----------------------------------------------- +- Enabling OVR_multiview hangs the GPU. + + +WORK ITEMS +========== + +- Implement WGL, GLX and NSOpenGL equivalents of EGL_IMG_context_priority. +- Implement an extension that provides accurate display refresh timing (WGL_NV_delay_before_swap, D3DKMTGetScanLine). +- Implement an OpenGL extension that allows rendering directly to the front buffer. +- Implement an OpenGL extension that allows a compute shader to directly write to the front/back buffer images +(WGL_AMDX_drawable_view). +- Improve GPU task switching granularity. + +================================================================================================ +*/ + +#if !defined(KSGRAPHICSWRAPPER_OPENGL_H) +#define KSGRAPHICSWRAPPER_OPENGL_H + +#if !defined(__ANDROID__) +#error "This OpenGL wrapper now only supports Android builds." +#endif + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define OS_ANDROID + +/* +================================ +Platform headers / declarations +================================ +*/ + +#if defined(OS_WINDOWS) + +#define XR_USE_PLATFORM_WIN32 1 + +#if !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#if defined(_MSC_VER) +#pragma warning(disable : 4204) // nonstandard extension used : non-constant aggregate initializer +#pragma warning(disable : 4221) // nonstandard extension used: 'layers': cannot be initialized using address of automatic variable + // 'layerProjection' +#pragma warning(disable : 4255) // '' : no function prototype given: converting '()' to '(void)' +#pragma warning(disable : 4668) // '__cplusplus' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' +#pragma warning(disable : 4710) // 'int printf(const char *const ,...)': function not inlined +#pragma warning(disable : 4711) // function '' selected for automatic inline expansion +#pragma warning(disable : 4738) // storing 32-bit float result in memory, possible loss of performance +#pragma warning(disable : 4820) // '' : 'X' bytes padding added after data member '' +#pragma warning(disable : 4505) // unreferenced local function has been removed +#endif + +#if _MSC_VER >= 1900 +#pragma warning(disable : 4464) // relative include path contains '..' +#pragma warning(disable : 4774) // 'printf' : format string expected in argument 1 is not a string literal +#endif + +#define OPENGL_VERSION_MAJOR 4 +#define OPENGL_VERSION_MINOR 3 +#define GLSL_VERSION "430" +#define SPIRV_VERSION "99" +#define USE_SYNC_OBJECT 0 // 0 = GLsync, 1 = EGLSyncKHR, 2 = storage buffer + +#include + +#define GRAPHICS_API_OPENGL 1 +#define OUTPUT_PATH "" + +#elif defined(OS_LINUX) + +#define OPENGL_VERSION_MAJOR 4 +#define OPENGL_VERSION_MINOR 5 +#define GLSL_VERSION "430" +#define SPIRV_VERSION "99" +#define USE_SYNC_OBJECT 0 // 0 = GLsync, 1 = EGLSyncKHR, 2 = storage buffer + +#if !defined(_XOPEN_SOURCE) +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define _XOPEN_SOURCE 600 +#else +#define _XOPEN_SOURCE 500 +#endif +#endif + +#include // for timespec +#include // for gettimeofday() +#if !defined(__USE_UNIX98) +#define __USE_UNIX98 1 // for pthread_mutexattr_settype +#endif +#include // for pthread_create() etc. +#include // for memalign + +#if defined(OS_LINUX_XLIB) +#define XR_USE_PLATFORM_XLIB 1 + +#include +#include +#include // for fullscreen video mode +#include // for resolution changes + +#ifdef Success +#undef Success +#endif // Success + +#ifdef Always +#undef Always +#endif // Always + +#ifdef None +#undef None +#endif // None + +#elif defined(OS_LINUX_XCB) || defined(OS_LINUX_XCB_GLX) +#define XR_USE_PLATFORM_XCB 1 + +#include +#include +#include +#include +#include +#include +#include + +#ifdef Success +#undef Success +#endif // Success + +#ifdef Always +#undef Always +#endif // Always + +#ifdef None +#undef None +#endif // None + +#elif defined(OS_LINUX_WAYLAND) +#define XR_USE_PLATFORM_WAYLAND 1 + +#include +#include +#include +#include +#include +#include +#include "xdg-shell-unstable-v6.h" + +#endif + +#define GRAPHICS_API_OPENGL 1 +#define OUTPUT_PATH "" + +#elif defined(OS_APPLE_MACOS) + +// Apple is still at OpenGL 4.1 +#define OPENGL_VERSION_MAJOR 4 +#define OPENGL_VERSION_MINOR 1 +#define GLSL_VERSION "410" +#define SPIRV_VERSION "99" +#define USE_SYNC_OBJECT 0 // 0 = GLsync, 1 = EGLSyncKHR, 2 = storage buffer +#define XR_USE_PLATFORM_MACOS 1 + +#include +#include +#include +#include +#include +#if defined(__OBJC__) +#import +#endif + +#undef MAX +#undef MIN + +#define GRAPHICS_API_OPENGL 1 +#define OUTPUT_PATH "" + +// Undocumented CGS and CGL +typedef void *CGSConnectionID; +typedef int CGSWindowID; +typedef int CGSSurfaceID; + +CGLError CGLSetSurface(CGLContextObj ctx, CGSConnectionID cid, CGSWindowID wid, CGSSurfaceID sid); +CGLError CGLGetSurface(CGLContextObj ctx, CGSConnectionID *cid, CGSWindowID *wid, CGSSurfaceID *sid); +CGLError CGLUpdateContext(CGLContextObj ctx); + +#elif defined(OS_APPLE_IOS) + +// Assume iOS 7+ which is GLES 3.0 +#define OPENGL_VERSION_MAJOR 3 +#define OPENGL_VERSION_MINOR 0 +#define GLSL_VERSION "300 es" +#define SPIRV_VERSION "99" +#define USE_SYNC_OBJECT 0 // 0 = GLsync, 1 = EGLSyncKHR, 2 = storage buffer +#define XR_USE_PLATFORM_IOS 1 + +#import +#import +#import +#include + +#define GRAPHICS_API_OPENGL_ES 1 + +#elif defined(OS_ANDROID) + +#define OPENGL_VERSION_MAJOR 3 +#define OPENGL_VERSION_MINOR 2 +#define GLSL_VERSION "320 es" +#define SPIRV_VERSION "99" +#define USE_SYNC_OBJECT 1 // 0 = GLsync, 1 = EGLSyncKHR, 2 = storage buffer + +#include +#include +#include // for opendir/closedir +#include +#include // for memalign +#include // for dlopen +#include // for prctl( PR_SET_NAME ) +#include // for gettid +#include // for syscall +#include // for __android_log_print +#include // for AKEYCODE_ etc. +#include // for AWINDOW_FLAG_KEEP_SCREEN_ON +#include // for native window JNI + +#define GRAPHICS_API_OPENGL_ES 1 +#define OUTPUT_PATH "/sdcard/" + +typedef struct { + JavaVM *vm; // Java Virtual Machine + JNIEnv *env; // Thread specific environment + jobject activity; // Java activity object +} Java_t; + +#endif + +/* +================================ +Common headers +================================ +*/ + +#include +#include +#include +#include +#include +#include +#include +#include // for memset +#include // for EBUSY, ETIMEDOUT etc. +#include // for isspace, isdigit + +/* +================================ +Common defines +================================ +*/ + +#define UNUSED_PARM(x) \ + { (void)(x); } +#define ARRAY_SIZE(a) (sizeof((a)) / sizeof((a)[0])) +#define OFFSETOF_MEMBER(type, member) (size_t) & ((type *)0)->member +#define SIZEOF_MEMBER(type, member) sizeof(((type *)0)->member) +#define BIT(x) (1 << (x)) +#ifndef MAX +#define MAX(x, y) ((x > y) ? (x) : (y)) +#endif +#ifndef MIN +#define MIN(x, y) ((x < y) ? (x) : (y)) +#endif +#define CLAMP(x, min, max) (((x) < (min)) ? (min) : (((x) > (max)) ? (max) : (x))) +#define STRINGIFY_EXPANDED(a) #a +#define STRINGIFY(a) STRINGIFY_EXPANDED(a) + +#define APPLICATION_NAME "OpenGL SI" +#define WINDOW_TITLE "OpenGL SI" + +#define PROGRAM(name) name##GLSL + +#define GLSL_EXTENSIONS "#extension GL_EXT_shader_io_blocks : enable\n" +#define GL_FINISH_SYNC 1 + +#if defined(OS_ANDROID) +#define ES_HIGHP "highp" // GLSL "310 es" requires a precision qualifier on a image2D +#else +#define ES_HIGHP "" // GLSL "430" disallows a precision qualifier on a image2D +#endif + +/* +================================================================================================================================ + +Driver Instance. + +ksDriverInstance + +bool ksDriverInstance_Create( ksDriverInstance * instance ); +void ksDriverInstance_Destroy( ksDriverInstance * instance ); + +================================================================================================================================ +*/ + +typedef struct { + int placeholder; +} ksDriverInstance; + +bool ksDriverInstance_Create(ksDriverInstance *instance); +void ksDriverInstance_Destroy(ksDriverInstance *instance); + +/* +================================================================================================================================ + +GPU device. + +ksGpuQueueProperty +ksGpuQueuePriority +ksGpuQueueInfo +ksGpuDevice + +bool ksGpuDevice_Create( ksGpuDevice * device, ksDriverInstance * instance, const ksGpuQueueInfo * queueInfo ); +void ksGpuDevice_Destroy( ksGpuDevice * device ); + +================================================================================================================================ +*/ + +typedef enum { + KS_GPU_QUEUE_PROPERTY_GRAPHICS = BIT(0), + KS_GPU_QUEUE_PROPERTY_COMPUTE = BIT(1), + KS_GPU_QUEUE_PROPERTY_TRANSFER = BIT(2) +} ksGpuQueueProperty; + +typedef enum { KS_GPU_QUEUE_PRIORITY_LOW, KS_GPU_QUEUE_PRIORITY_MEDIUM, KS_GPU_QUEUE_PRIORITY_HIGH } ksGpuQueuePriority; + +#define MAX_QUEUES 16 + +typedef struct { + int queueCount; // number of queues + ksGpuQueueProperty queueProperties; // desired queue family properties + ksGpuQueuePriority queuePriorities[MAX_QUEUES]; // individual queue priorities +} ksGpuQueueInfo; + +typedef struct { + ksDriverInstance *instance; + ksGpuQueueInfo queueInfo; +} ksGpuDevice; + +bool ksGpuDevice_Create(ksGpuDevice *device, ksDriverInstance *instance, const ksGpuQueueInfo *queueInfo); +void ksGpuDevice_Destroy(ksGpuDevice *device); + +/* +================================================================================================================================ + +GPU context. + +A context encapsulates a queue that is used to submit command buffers. +A context can only be used by a single thread. +For optimal performance a context should only be created at load time, not at runtime. + +ksGpuContext +ksGpuSurfaceColorFormat +ksGpuSurfaceDepthFormat +ksGpuSampleCount + +bool ksGpuContext_CreateShared( ksGpuContext * context, const ksGpuContext * other, const int queueIndex ); +void ksGpuContext_Destroy( ksGpuContext * context ); +void ksGpuContext_SetCurrent( ksGpuContext * context ); +void ksGpuContext_UnsetCurrent( ksGpuContext * context ); +bool ksGpuContext_CheckCurrent( ksGpuContext * context ); + +bool ksGpuContext_CreateForSurface( ksGpuContext * context, const ksGpuDevice * device, const int queueIndex, + const ksGpuSurfaceColorFormat colorFormat, + const ksGpuSurfaceDepthFormat depthFormat, + const ksGpuSampleCount sampleCount, + ... ); + +================================================================================================================================ +*/ + +typedef enum { + KS_GPU_SURFACE_COLOR_FORMAT_R5G6B5, + KS_GPU_SURFACE_COLOR_FORMAT_B5G6R5, + KS_GPU_SURFACE_COLOR_FORMAT_R8G8B8A8, + KS_GPU_SURFACE_COLOR_FORMAT_B8G8R8A8, + KS_GPU_SURFACE_COLOR_FORMAT_MAX +} ksGpuSurfaceColorFormat; + +typedef enum { + KS_GPU_SURFACE_DEPTH_FORMAT_NONE, + KS_GPU_SURFACE_DEPTH_FORMAT_D16, + KS_GPU_SURFACE_DEPTH_FORMAT_D24, + KS_GPU_SURFACE_DEPTH_FORMAT_MAX +} ksGpuSurfaceDepthFormat; + +typedef enum { + KS_GPU_SAMPLE_COUNT_1 = 1, + KS_GPU_SAMPLE_COUNT_2 = 2, + KS_GPU_SAMPLE_COUNT_4 = 4, + KS_GPU_SAMPLE_COUNT_8 = 8, + KS_GPU_SAMPLE_COUNT_16 = 16, + KS_GPU_SAMPLE_COUNT_32 = 32, + KS_GPU_SAMPLE_COUNT_64 = 64, +} ksGpuSampleCount; + +typedef struct ksGpuLimits { + size_t maxPushConstantsSize; + int maxSamples; +} ksGpuLimits; + +typedef struct { + const ksGpuDevice *device; +#if defined(OS_WINDOWS) + HDC hDC; + HGLRC hGLRC; +#elif defined(OS_LINUX_XLIB) || defined(OS_LINUX_XCB_GLX) + Display *xDisplay; + uint32_t visualid; + GLXFBConfig glxFBConfig; + GLXDrawable glxDrawable; + GLXContext glxContext; +#elif defined(OS_LINUX_XCB) + xcb_connection_t *connection; + uint32_t screen_number; + xcb_glx_fbconfig_t fbconfigid; + xcb_visualid_t visualid; + xcb_glx_drawable_t glxDrawable; + xcb_glx_context_t glxContext; + xcb_glx_context_tag_t glxContextTag; +#elif defined(OS_LINUX_WAYLAND) + EGLNativeWindowType native_window; + EGLDisplay display; + EGLContext context; + EGLConfig config; + EGLSurface mainSurface; +#elif defined(OS_APPLE_MACOS) +#if defined(__OBJC__) + NSOpenGLContext *nsContext; +#else + void *nsContext; +#endif // defined(__OBJC__) + CGLContextObj cglContext; +#elif defined(OS_ANDROID) + EGLDisplay display; + EGLConfig config; + EGLSurface tinySurface; + EGLSurface mainSurface; + EGLContext context; +#endif +} ksGpuContext; + +typedef struct { + unsigned char redBits; + unsigned char greenBits; + unsigned char blueBits; + unsigned char alphaBits; + unsigned char colorBits; + unsigned char depthBits; +} ksGpuSurfaceBits; + +bool ksGpuContext_CreateShared(ksGpuContext *context, const ksGpuContext *other, int queueIndex); +void ksGpuContext_Destroy(ksGpuContext *context); +void ksGpuContext_SetCurrent(ksGpuContext *context); +void ksGpuContext_UnsetCurrent(ksGpuContext *context); +bool ksGpuContext_CheckCurrent(ksGpuContext *context); + +/* +================================================================================================================================ + +GPU Window. + +Window with associated GPU context for GPU accelerated rendering. +For optimal performance a window should only be created at load time, not at runtime. +Because on some platforms the OS/drivers use thread local storage, ksGpuWindow *must* be created +and destroyed on the same thread that will actually render to the window and swap buffers. + +ksGpuWindow +ksGpuWindowEvent +ksGpuWindowInput +ksKeyboardKey +ksMouseButton + +bool ksGpuWindow_Create( ksGpuWindow * window, ksDriverInstance * instance, + const ksGpuQueueInfo * queueInfo, const int queueIndex, + const ksGpuSurfaceColorFormat colorFormat, const ksGpuSurfaceDepthFormat +depthFormat, + const ksGpuSampleCount sampleCount, const int width, const int height, const bool +fullscreen ); +void ksGpuWindow_Destroy( ksGpuWindow * window ); + +================================================================================================================================ +*/ + +typedef enum { + KS_GPU_WINDOW_EVENT_NONE, + KS_GPU_WINDOW_EVENT_ACTIVATED, + KS_GPU_WINDOW_EVENT_DEACTIVATED, + KS_GPU_WINDOW_EVENT_EXIT +} ksGpuWindowEvent; + +typedef struct { + bool keyInput[256]; + bool mouseInput[8]; + int mouseInputX[8]; + int mouseInputY[8]; +} ksGpuWindowInput; + +typedef struct { + ksGpuDevice device; + ksGpuContext context; + ksGpuSurfaceColorFormat colorFormat; + ksGpuSurfaceDepthFormat depthFormat; + ksGpuSampleCount sampleCount; + int windowWidth; + int windowHeight; + int windowSwapInterval; + float windowRefreshRate; + bool windowFullscreen; + bool windowActive; + bool windowExit; + ksGpuWindowInput input; + +#if defined(OS_WINDOWS) + HINSTANCE hInstance; + HDC hDC; + HWND hWnd; + bool windowActiveState; +#elif defined(OS_LINUX_XLIB) + Display *xDisplay; + int xScreen; + Window xRoot; + XVisualInfo *xVisual; + Colormap xColormap; + Window xWindow; + int desktopWidth; + int desktopHeight; + float desktopRefreshRate; +#elif defined(OS_LINUX_XCB) || defined(OS_LINUX_XCB_GLX) + Display *xDisplay; + xcb_connection_t *connection; + xcb_screen_t *screen; + xcb_colormap_t colormap; + xcb_window_t window; + xcb_atom_t wm_delete_window_atom; + xcb_key_symbols_t *key_symbols; + xcb_glx_window_t glxWindow; + int desktopWidth; + int desktopHeight; + float desktopRefreshRate; +#elif defined(OS_LINUX_WAYLAND) + struct wl_display *display; + + struct wl_surface *surface; + + struct wl_registry *registry; + struct wl_compositor *compositor; + struct zxdg_shell_v6 *shell; + struct zxdg_surface_v6 *shell_surface; + + struct wl_keyboard *keyboard; + struct wl_pointer *pointer; + struct wl_seat *seat; +#elif defined(OS_APPLE_MACOS) + CGDirectDisplayID display; + CGDisplayModeRef desktopDisplayMode; +#if defined(__OBJC__) + NSWindow *nsWindow; + NSView *nsView; +#else + void *nsWindow; + void *nsView; +#endif // defined(__OBJC__) +#elif defined(OS_APPLE_IOS) + UIWindow *uiWindow; + UIView *uiView; +#elif defined(OS_ANDROID) + EGLDisplay display; + EGLint majorVersion; + EGLint minorVersion; + Java_t java; +#endif +} ksGpuWindow; + +bool ksGpuWindow_Create(ksGpuWindow *window, ksDriverInstance *instance, const ksGpuQueueInfo *queueInfo, int queueIndex, + ksGpuSurfaceColorFormat colorFormat, ksGpuSurfaceDepthFormat depthFormat, ksGpuSampleCount sampleCount, + int width, int height, bool fullscreen); +void ksGpuWindow_Destroy(ksGpuWindow *window); + +#ifdef __cplusplus +} +#endif + +#endif // !KSGRAPHICSWRAPPER_OPENGL_H diff --git a/base/oxr_utils/graphicsplugin.h b/base/oxr_utils/graphicsplugin.h index 523c8b3..f5bd22d 100644 --- a/base/oxr_utils/graphicsplugin.h +++ b/base/oxr_utils/graphicsplugin.h @@ -4,6 +4,10 @@ #pragma once +namespace Geometry { +struct Vertex; +} + struct Cube { XrPosef Pose; XrVector3f Scale; @@ -35,6 +39,18 @@ struct IGraphicsPlugin { const XrSwapchainImageBaseHeader* swapchainImage, int64_t swapchainFormat, const std::vector& cubes) = 0; + // Optional: render a user-provided mesh (Position+Color vertices) onto the same view's swapchain image. + // Default: no-op (implement only for backends that support it). + virtual void RenderUserMesh(const XrCompositionLayerProjectionView& /*layerView*/, + const XrSwapchainImageBaseHeader* /*swapchainImage*/, int64_t /*swapchainFormat*/, + const struct Geometry::Vertex* /*vertices*/, uint32_t /*vcount*/, + const uint16_t* /*indices*/, uint32_t /*icount*/, const XrPosef& /*worldPose*/) {} + + // Upload an RGBA8 image (width x height) to a vector of swapchain images. + // Implementations may assume images belong to this graphics API. + virtual void UploadRgbaToSwapchainImages(const std::vector& images, int width, + int height, const std::vector& rgba) {} + // Get recommended number of sub-data element samples in view (recommendedSwapchainSampleCount) // if supported by the graphics plugin. A supported value otherwise. virtual uint32_t GetSupportedSwapchainSampleCount(const XrViewConfigurationView& view) { diff --git a/base/oxr_utils/graphicsplugin_d3d11.cpp b/base/oxr_utils/graphicsplugin_d3d11.cpp index 20e48d1..97c5c29 100644 --- a/base/oxr_utils/graphicsplugin_d3d11.cpp +++ b/base/oxr_utils/graphicsplugin_d3d11.cpp @@ -10,7 +10,7 @@ #if defined(XR_USE_GRAPHICS_API_D3D11) -#include +#include "xr_linear.h" #include #include diff --git a/base/oxr_utils/graphicsplugin_d3d12.cpp b/base/oxr_utils/graphicsplugin_d3d12.cpp index 2cb78fb..fe4864e 100644 --- a/base/oxr_utils/graphicsplugin_d3d12.cpp +++ b/base/oxr_utils/graphicsplugin_d3d12.cpp @@ -10,7 +10,7 @@ #if defined(XR_USE_GRAPHICS_API_D3D12) -#include +#include "xr_linear.h" #include #include diff --git a/base/oxr_utils/graphicsplugin_metal.cpp b/base/oxr_utils/graphicsplugin_metal.cpp index aad3d1e..101e313 100644 --- a/base/oxr_utils/graphicsplugin_metal.cpp +++ b/base/oxr_utils/graphicsplugin_metal.cpp @@ -17,7 +17,7 @@ #include #include -#include +#include "xr_linear.h" #include struct MetalGraphicsPlugin : public IGraphicsPlugin { diff --git a/base/oxr_utils/graphicsplugin_opengl.cpp b/base/oxr_utils/graphicsplugin_opengl.cpp index d93f271..74d34d5 100644 --- a/base/oxr_utils/graphicsplugin_opengl.cpp +++ b/base/oxr_utils/graphicsplugin_opengl.cpp @@ -10,8 +10,8 @@ #ifdef XR_USE_GRAPHICS_API_OPENGL -#include -#include +#include "oxr_utils/gfxwrapper_opengl.h" +#include "xr_linear.h" namespace { diff --git a/base/oxr_utils/graphicsplugin_opengles.cpp b/base/oxr_utils/graphicsplugin_opengles.cpp index 859792e..71b932c 100644 --- a/base/oxr_utils/graphicsplugin_opengles.cpp +++ b/base/oxr_utils/graphicsplugin_opengles.cpp @@ -10,8 +10,8 @@ #ifdef XR_USE_GRAPHICS_API_OPENGL_ES -#include "common/gfxwrapper_opengl.h" -#include +#include "oxr_utils/gfxwrapper_opengl.h" +#include "xr_linear.h" namespace { diff --git a/base/oxr_utils/graphicsplugin_vulkan.cpp b/base/oxr_utils/graphicsplugin_vulkan.cpp index c362916..071a067 100644 --- a/base/oxr_utils/graphicsplugin_vulkan.cpp +++ b/base/oxr_utils/graphicsplugin_vulkan.cpp @@ -29,6 +29,8 @@ #define SPV_PREFIX #define SPV_SUFFIX #endif +VkDevice g_device; +VkPhysicalDevice g_pdevice; namespace { @@ -247,6 +249,7 @@ struct CmdBuffer { CHECK_CBSTATE(CmdBufferState::Undefined); m_vkDevice = device; + g_device = m_vkDevice; // Create a command pool to allocate our command buffer from VkCommandPoolCreateInfo cmdPoolInfo{VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO}; @@ -450,6 +453,28 @@ struct VertexBufferBase { VertexBufferBase& operator=(const VertexBufferBase&) = delete; VertexBufferBase(VertexBufferBase&&) = delete; VertexBufferBase& operator=(VertexBufferBase&&) = delete; + void Reset() { + if (m_vkDevice != nullptr) { + if (idxBuf != VK_NULL_HANDLE) { + vkDestroyBuffer(m_vkDevice, idxBuf, nullptr); + } + if (idxMem != VK_NULL_HANDLE) { + vkFreeMemory(m_vkDevice, idxMem, nullptr); + } + if (vtxBuf != VK_NULL_HANDLE) { + vkDestroyBuffer(m_vkDevice, vtxBuf, nullptr); + } + if (vtxMem != VK_NULL_HANDLE) { + vkFreeMemory(m_vkDevice, vtxMem, nullptr); + } + } + idxBuf = VK_NULL_HANDLE; + idxMem = VK_NULL_HANDLE; + vtxBuf = VK_NULL_HANDLE; + vtxMem = VK_NULL_HANDLE; + bindDesc = {}; + count = {0, 0}; + } void Init(VkDevice device, const MemoryAllocator* memAllocator, const std::vector& attr) { m_vkDevice = device; @@ -522,7 +547,9 @@ struct RenderPass { RenderPass() = default; - bool Create(const VulkanDebugObjectNamer& namer, VkDevice device, VkFormat aColorFmt, VkFormat aDepthFmt) { + bool Create(const VulkanDebugObjectNamer& namer, VkDevice device, VkFormat aColorFmt, VkFormat aDepthFmt, + VkAttachmentLoadOp colorLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, + VkAttachmentLoadOp depthLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR) { m_vkDevice = device; colorFmt = aColorFmt; depthFmt = aDepthFmt; @@ -546,7 +573,7 @@ struct RenderPass { at[colorRef.attachment].format = colorFmt; at[colorRef.attachment].samples = VK_SAMPLE_COUNT_1_BIT; - at[colorRef.attachment].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + at[colorRef.attachment].loadOp = colorLoadOp; at[colorRef.attachment].storeOp = VK_ATTACHMENT_STORE_OP_STORE; at[colorRef.attachment].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; at[colorRef.attachment].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; @@ -562,7 +589,7 @@ struct RenderPass { at[depthRef.attachment].format = depthFmt; at[depthRef.attachment].samples = VK_SAMPLE_COUNT_1_BIT; - at[depthRef.attachment].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + at[depthRef.attachment].loadOp = depthLoadOp; at[depthRef.attachment].storeOp = VK_ATTACHMENT_STORE_OP_STORE; at[depthRef.attachment].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; at[depthRef.attachment].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; @@ -603,14 +630,19 @@ struct RenderTarget { VkImage depthImage{VK_NULL_HANDLE}; VkImageView colorView{VK_NULL_HANDLE}; VkImageView depthView{VK_NULL_HANDLE}; - VkFramebuffer fb{VK_NULL_HANDLE}; + // Separate framebuffers for clear-pass and load-pass render passes + VkFramebuffer fbClear{VK_NULL_HANDLE}; + VkFramebuffer fbLoad{VK_NULL_HANDLE}; RenderTarget() = default; ~RenderTarget() { if (m_vkDevice != nullptr) { - if (fb != VK_NULL_HANDLE) { - vkDestroyFramebuffer(m_vkDevice, fb, nullptr); + if (fbClear != VK_NULL_HANDLE) { + vkDestroyFramebuffer(m_vkDevice, fbClear, nullptr); + } + if (fbLoad != VK_NULL_HANDLE) { + vkDestroyFramebuffer(m_vkDevice, fbLoad, nullptr); } if (colorView != VK_NULL_HANDLE) { vkDestroyImageView(m_vkDevice, colorView, nullptr); @@ -625,7 +657,8 @@ struct RenderTarget { depthImage = VK_NULL_HANDLE; colorView = VK_NULL_HANDLE; depthView = VK_NULL_HANDLE; - fb = VK_NULL_HANDLE; + fbClear = VK_NULL_HANDLE; + fbLoad = VK_NULL_HANDLE; m_vkDevice = nullptr; } @@ -635,7 +668,8 @@ struct RenderTarget { swap(depthImage, other.depthImage); swap(colorView, other.colorView); swap(depthView, other.depthView); - swap(fb, other.fb); + swap(fbClear, other.fbClear); + swap(fbLoad, other.fbLoad); swap(m_vkDevice, other.m_vkDevice); } RenderTarget& operator=(RenderTarget&& other) noexcept { @@ -649,7 +683,8 @@ struct RenderTarget { swap(depthImage, other.depthImage); swap(colorView, other.colorView); swap(depthView, other.depthView); - swap(fb, other.fb); + swap(fbClear, other.fbClear); + swap(fbLoad, other.fbLoad); swap(m_vkDevice, other.m_vkDevice); return *this; } @@ -704,14 +739,24 @@ struct RenderTarget { } VkFramebufferCreateInfo fbInfo{VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO}; - fbInfo.renderPass = renderPass.pass; fbInfo.attachmentCount = attachmentCount; fbInfo.pAttachments = attachments.data(); fbInfo.width = size.width; fbInfo.height = size.height; fbInfo.layers = 1; - CHECK_VKCMD(vkCreateFramebuffer(m_vkDevice, &fbInfo, nullptr, &fb)); - CHECK_VKCMD(namer.SetName(VK_OBJECT_TYPE_FRAMEBUFFER, (uint64_t)fb, "hello_xr framebuffer")); + // Create framebuffer for the provided renderPass + fbInfo.renderPass = renderPass.pass; + VkFramebuffer* targetFb = nullptr; + // Heuristic: if the provided renderPass was created with LOAD ops, put it in fbLoad; else fbClear + // We cannot query loadOp easily here; choose fbClear if unset. + if (fbClear == VK_NULL_HANDLE) targetFb = &fbClear; + if (renderPass.pass != VK_NULL_HANDLE) { + // If fbClear already used and this is a different pass, use fbLoad + if (fbClear != VK_NULL_HANDLE) targetFb = &fbLoad; + } + if (targetFb == nullptr) targetFb = &fbClear; + CHECK_VKCMD(vkCreateFramebuffer(m_vkDevice, &fbInfo, nullptr, targetFb)); + CHECK_VKCMD(namer.SetName(VK_OBJECT_TYPE_FRAMEBUFFER, (uint64_t)*targetFb, "hello_xr framebuffer")); } RenderTarget(const RenderTarget&) = delete; @@ -766,6 +811,7 @@ struct Pipeline { VkPipeline pipe{VK_NULL_HANDLE}; VkPrimitiveTopology topology{VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST}; std::vector dynamicStateEnables; + VkCullModeFlags cullMode{VK_CULL_MODE_BACK_BIT}; Pipeline() = default; @@ -791,7 +837,7 @@ struct Pipeline { VkPipelineRasterizationStateCreateInfo rs{VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO}; rs.polygonMode = VK_POLYGON_MODE_FILL; - rs.cullMode = VK_CULL_MODE_BACK_BIT; + rs.cullMode = cullMode; rs.frontFace = VK_FRONT_FACE_CLOCKWISE; rs.depthClampEnable = VK_FALSE; rs.rasterizerDiscardEnable = VK_FALSE; @@ -993,8 +1039,10 @@ struct SwapchainImageContext { std::vector renderTarget; VkExtent2D size{}; DepthBuffer depthBuffer{}; - RenderPass rp{}; + RenderPass rp{}; // clear pass + RenderPass rpLoad{}; // load pass for user mesh overlay Pipeline pipe{}; + Pipeline pipeLoad{}; XrStructureType swapchainImageType; SwapchainImageContext() = default; @@ -1013,8 +1061,13 @@ struct SwapchainImageContext { // XXX handle swapchainCreateInfo.sampleCount depthBuffer.Create(namer, m_vkDevice, memAllocator, depthFormat, swapchainCreateInfo); - rp.Create(namer, m_vkDevice, colorFormat, depthFormat); + rp.Create(namer, m_vkDevice, colorFormat, depthFormat, VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_LOAD_OP_CLEAR); + pipe.cullMode = VK_CULL_MODE_BACK_BIT; pipe.Create(m_vkDevice, size, layout, rp, sp, vb); + // load pass/pipeline for user mesh overlay (no clear) + rpLoad.Create(namer, m_vkDevice, colorFormat, depthFormat, VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_LOAD_OP_LOAD); + pipeLoad.cullMode = VK_CULL_MODE_NONE; // user mesh: avoid facing issues across faces + pipeLoad.Create(m_vkDevice, size, layout, rpLoad, sp, vb); swapchainImages.resize(capacity); renderTarget.resize(capacity); @@ -1033,11 +1086,32 @@ struct SwapchainImageContext { } void BindRenderTarget(uint32_t index, VkRenderPassBeginInfo* renderPassBeginInfo) { - if (renderTarget[index].fb == VK_NULL_HANDLE) { + if (renderTarget[index].fbClear == VK_NULL_HANDLE) { renderTarget[index].Create(m_namer, m_vkDevice, swapchainImages[index].image, depthBuffer.depthImage, size, rp); } renderPassBeginInfo->renderPass = rp.pass; - renderPassBeginInfo->framebuffer = renderTarget[index].fb; + renderPassBeginInfo->framebuffer = renderTarget[index].fbClear; + renderPassBeginInfo->renderArea.offset = {0, 0}; + renderPassBeginInfo->renderArea.extent = size; + } + + void BindRenderTargetWithPass(uint32_t index, VkRenderPassBeginInfo* renderPassBeginInfo, RenderPass& useRp) { + // Create the matching framebuffer for the requested render pass + if (useRp.pass == rp.pass) { + if (renderTarget[index].fbClear == VK_NULL_HANDLE) { + renderTarget[index].Create(m_namer, m_vkDevice, swapchainImages[index].image, depthBuffer.depthImage, size, + rp); + } + renderPassBeginInfo->renderPass = rp.pass; + renderPassBeginInfo->framebuffer = renderTarget[index].fbClear; + } else { + if (renderTarget[index].fbLoad == VK_NULL_HANDLE) { + renderTarget[index].Create(m_namer, m_vkDevice, swapchainImages[index].image, depthBuffer.depthImage, size, + useRp); + } + renderPassBeginInfo->renderPass = useRp.pass; + renderPassBeginInfo->framebuffer = renderTarget[index].fbLoad; + } renderPassBeginInfo->renderArea.offset = {0, 0}; renderPassBeginInfo->renderArea.extent = size; } @@ -1431,7 +1505,7 @@ struct VulkanGraphicsPlugin : public IGraphicsPlugin { deviceGetInfo.systemId = systemId; deviceGetInfo.vulkanInstance = m_vkInstance; CHECK_XRCMD(GetVulkanGraphicsDevice2KHR(instance, &deviceGetInfo, &m_vkPhysicalDevice)); - + g_pdevice = m_vkPhysicalDevice; VkDeviceQueueCreateInfo queueInfo{VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO}; float queuePriorities = 0; queueInfo.queueCount = 1; @@ -1551,6 +1625,13 @@ struct VulkanGraphicsPlugin : public IGraphicsPlugin { m_drawBuffer.UpdateIndices(Geometry::c_cubeIndices, numCubeIdicies, 0); m_drawBuffer.UpdateVertices(Geometry::c_cubeVertices, numCubeVerticies, 0); + // Create a reasonably large dynamic buffer for user mesh overlay (no reallocation in typical cases) + m_userBuffer.Init(m_vkDevice, &m_memAllocator, + {{0, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Geometry::Vertex, Position)}, + {1, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Geometry::Vertex, Color)}}); + // Capacity: up to 4096 verts / 8192 indices (uint16) + m_userBuffer.Create(/*idxCount*/ 8192, /*vtxCount*/ 4096); + #if defined(USE_MIRROR_WINDOW) m_swapchain.Create(m_vkInstance, m_vkPhysicalDevice, m_vkDevice, m_graphicsBinding.queueFamilyIndex); @@ -1683,10 +1764,170 @@ struct VulkanGraphicsPlugin : public IGraphicsPlugin { uint32_t GetSupportedSwapchainSampleCount(const XrViewConfigurationView&) override { return VK_SAMPLE_COUNT_1_BIT; } + void RenderUserMesh(const XrCompositionLayerProjectionView& layerView, + const XrSwapchainImageBaseHeader* swapchainImage, int64_t /*swapchainFormat*/, + const Geometry::Vertex* vertices, uint32_t vcount, const uint16_t* indices, uint32_t icount, + const XrPosef& worldPose) override { + if (vcount == 0 || icount == 0) return; + + auto swapchainContext = m_swapchainImageContextMap[swapchainImage]; + uint32_t imageIndex = swapchainContext->ImageIndex(swapchainImage); + + // Ensure capacity (recreate dynamic buffer if needed, with headroom) + auto nextPow2 = [](uint32_t x) { + if (x == 0) return 1u; + --x; x |= x >> 1; x |= x >> 2; x |= x >> 4; x |= x >> 8; x |= x >> 16; return x + 1; + }; + if (vcount > m_userBuffer.count.vtx || icount > m_userBuffer.count.idx) { + uint32_t newV = std::min(1u << 16, nextPow2(std::max(vcount, m_userBuffer.count.vtx * 2 + 1))); + uint32_t newI = std::min(1u << 16, nextPow2(std::max(icount, m_userBuffer.count.idx * 2 + 1))); + m_userBuffer.Reset(); + m_userBuffer.Init(m_vkDevice, &m_memAllocator, + {{0, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Geometry::Vertex, Position)}, + {1, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Geometry::Vertex, Color)}}); + m_userBuffer.Create(newI, newV); + Log::Write(Log::Level::Info, + Fmt("Vulkan: resized user mesh buffer to vtx=%u idx=%u", m_userBuffer.count.vtx, m_userBuffer.count.idx)); + } + + // Flush previous work and begin a new command buffer + m_cmdBuffer.Wait(); + m_cmdBuffer.Reset(); + m_cmdBuffer.Begin(); + + // Ensure depth is in the right layout + swapchainContext->depthBuffer.TransitionLayout(&m_cmdBuffer, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + + // Bind load-pass render target (no clear) + VkRenderPassBeginInfo renderPassBeginInfo{VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO}; + // No clear values (load existing) + renderPassBeginInfo.clearValueCount = 0; + renderPassBeginInfo.pClearValues = nullptr; + swapchainContext->BindRenderTargetWithPass(imageIndex, &renderPassBeginInfo, swapchainContext->rpLoad); + vkCmdBeginRenderPass(m_cmdBuffer.buf, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + + vkCmdBindPipeline(m_cmdBuffer.buf, VK_PIPELINE_BIND_POINT_GRAPHICS, swapchainContext->pipeLoad.pipe); + + // Upload data to user buffer + m_userBuffer.UpdateVertices(vertices, vcount, 0); + m_userBuffer.UpdateIndices(indices, icount, 0); + + // Bind index and vertex buffers + vkCmdBindIndexBuffer(m_cmdBuffer.buf, m_userBuffer.idxBuf, 0, VK_INDEX_TYPE_UINT16); + VkDeviceSize offset = 0; + vkCmdBindVertexBuffers(m_cmdBuffer.buf, 0, 1, &m_userBuffer.vtxBuf, &offset); + + // Compute VP and model + const auto& pose = layerView.pose; + XrMatrix4x4f proj; + XrMatrix4x4f_CreateProjectionFov(&proj, GRAPHICS_VULKAN, layerView.fov, 0.05f, 100.0f); + XrMatrix4x4f toView; + XrMatrix4x4f_CreateFromRigidTransform(&toView, &pose); + XrMatrix4x4f view; + XrMatrix4x4f_InvertRigidBody(&view, &toView); + XrMatrix4x4f vp; + XrMatrix4x4f_Multiply(&vp, &proj, &view); + + XrVector3f unitScale{1.0f, 1.0f, 1.0f}; + XrMatrix4x4f model; + XrMatrix4x4f_CreateTranslationRotationScale(&model, &worldPose.position, &worldPose.orientation, &unitScale); + XrMatrix4x4f mvp; + XrMatrix4x4f_Multiply(&mvp, &vp, &model); + vkCmdPushConstants(m_cmdBuffer.buf, m_pipelineLayout.layout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(mvp.m), + &mvp.m[0]); + + vkCmdDrawIndexed(m_cmdBuffer.buf, icount, 1, 0, 0, 0); + + vkCmdEndRenderPass(m_cmdBuffer.buf); + m_cmdBuffer.End(); + m_cmdBuffer.Exec(m_vkQueue); + } + void UpdateOptions(const std::shared_ptr& options) override { m_clearColor = options->GetBackgroundClearColor(); } + void UploadRgbaToSwapchainImages(const std::vector& images, int width, int height, + const std::vector& rgba) override { + // Create staging buffer + VkBuffer stagingBuf = VK_NULL_HANDLE; + VkDeviceMemory stagingMem = VK_NULL_HANDLE; + VkBufferCreateInfo bufInfo{VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO}; + bufInfo.size = static_cast(rgba.size()); + bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + CHECK_VKCMD(vkCreateBuffer(m_vkDevice, &bufInfo, nullptr, &stagingBuf)); + VkMemoryRequirements memReqs{}; + vkGetBufferMemoryRequirements(m_vkDevice, stagingBuf, &memReqs); + m_memAllocator.Allocate(memReqs, &stagingMem, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + CHECK_VKCMD(vkBindBufferMemory(m_vkDevice, stagingBuf, stagingMem, 0)); + // Upload data + void* mapped = nullptr; + CHECK_VKCMD(vkMapMemory(m_vkDevice, stagingMem, 0, bufInfo.size, 0, &mapped)); + std::memcpy(mapped, rgba.data(), rgba.size()); + vkUnmapMemory(m_vkDevice, stagingMem); + + // Record copy for each image + m_cmdBuffer.Wait(); + m_cmdBuffer.Reset(); + m_cmdBuffer.Begin(); + + for (auto* base : images) { + const auto* vkImgHdr = reinterpret_cast(base); + VkImage image = vkImgHdr->image; + + VkImageSubresourceRange range{}; + range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + range.baseMipLevel = 0; + range.levelCount = 1; + range.baseArrayLayer = 0; + range.layerCount = 1; + + VkImageMemoryBarrier barrierToDst{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER}; + barrierToDst.srcAccessMask = 0; + barrierToDst.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrierToDst.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + barrierToDst.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barrierToDst.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrierToDst.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrierToDst.image = image; + barrierToDst.subresourceRange = range; + vkCmdPipelineBarrier(m_cmdBuffer.buf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, + nullptr, 0, nullptr, 1, &barrierToDst); + + VkBufferImageCopy region{}; + region.bufferOffset = 0; + region.bufferRowLength = 0; // tightly packed + region.bufferImageHeight = 0; // tightly packed + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.mipLevel = 0; + region.imageSubresource.baseArrayLayer = 0; + region.imageSubresource.layerCount = 1; + region.imageOffset = {0, 0, 0}; + region.imageExtent = {static_cast(width), static_cast(height), 1}; + vkCmdCopyBufferToImage(m_cmdBuffer.buf, stagingBuf, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + + VkImageMemoryBarrier barrierToReadable{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER}; + barrierToReadable.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrierToReadable.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + barrierToReadable.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barrierToReadable.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + barrierToReadable.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrierToReadable.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrierToReadable.image = image; + barrierToReadable.subresourceRange = range; + vkCmdPipelineBarrier(m_cmdBuffer.buf, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, + nullptr, 0, nullptr, 1, &barrierToReadable); + } + + m_cmdBuffer.End(); + m_cmdBuffer.Exec(m_vkQueue); + m_cmdBuffer.Wait(); + + vkDestroyBuffer(m_vkDevice, stagingBuf, nullptr); + vkFreeMemory(m_vkDevice, stagingMem, nullptr); + } + protected: XrGraphicsBindingVulkan2KHR m_graphicsBinding{XR_TYPE_GRAPHICS_BINDING_VULKAN2_KHR}; std::list m_swapchainImageContexts; @@ -1705,6 +1946,7 @@ struct VulkanGraphicsPlugin : public IGraphicsPlugin { CmdBuffer m_cmdBuffer{}; PipelineLayout m_pipelineLayout{}; VertexBuffer m_drawBuffer{}; + VertexBuffer m_userBuffer{}; std::array m_clearColor; #if defined(USE_MIRROR_WINDOW) diff --git a/base/oxr_utils/logger.cpp b/base/oxr_utils/logger.cpp index 27e463a..8a3eaf6 100644 --- a/base/oxr_utils/logger.cpp +++ b/base/oxr_utils/logger.cpp @@ -43,13 +43,22 @@ void Write(Level severity, const std::string& msg) { const int64_t milliseconds = std::chrono::duration_cast(secondRemainder).count(); static std::map severityName = { - {Level::Verbose, "Verbose"}, {Level::Info, "Info "}, {Level::Warning, "Warning"}, {Level::Error, "Error "}}; + {Level::Verbose, "Verbose"}, + {Level::Debug, "Debug "}, + {Level::Info, "Info "}, + {Level::Warning, "Warning"}, + {Level::Error, "Error "}}; std::ostringstream out; out.fill('0'); + const char* sevName = "Unknown"; + if (auto it = severityName.find(severity); it != severityName.end()) { + sevName = it->second; + } + out << "[" << std::setw(2) << now_tm.tm_hour << ":" << std::setw(2) << now_tm.tm_min << ":" << std::setw(2) << now_tm.tm_sec << "." << std::setw(3) << milliseconds << "]" - << "[" << severityName[severity] << "] " << msg << std::endl; + << "[" << sevName << "] " << msg << std::endl; std::lock_guard lock(g_logLock); // Ensure output is serialized ((severity == Level::Error) ? std::clog : std::cout) << out.str(); diff --git a/base/securemr_base.h b/base/securemr_base.h index 0a03ff7..3302ca5 100644 --- a/base/securemr_base.h +++ b/base/securemr_base.h @@ -16,6 +16,10 @@ #define SECURE_MR_DEMOS_SECUREMR_BASE_H #include "pch.h" + +namespace Geometry { +struct Vertex; +} #include #include #include "logger.h" @@ -30,6 +34,10 @@ namespace SecureMR { class ISecureMR { public: virtual void UpdateHandPose(const XrVector3f* leftHandDelta, const XrVector3f* rightHandDelta) {} + // Optional: per-frame head pose for applications that anchor content in front of the user. + virtual void UpdateHeadPose(const XrPosef& /*pose*/) {} + // Optional: head motion hint between frames; deltas are per-frame magnitudes in meters and radians + virtual void UpdateHeadMotion(float /*linearDeltaM*/, float /*angularDeltaRad*/) {} virtual ~ISecureMR() = default; @@ -79,6 +87,44 @@ class ISecureMR { * in this method's implementation, which submits the desired pipelines timely. */ virtual void RunPipelines() = 0; + /** + * This method will be called every frame, after the OpenXR instance and session are ready. + * + * The method's implementation is expected to complete the following tasks: + *
    + *
  1. A Secure MR Framework handle which holds the MR resources and serves as a camera provider will be created + * and
  2. The camera resolution shall be determined
  3. + *
+ * + * The calling of this method starts the lifecycle of a camera client. + */ + virtual void RequestPermission(struct android_app* app) {} + + // Optional: inform app of overlay swapchain size so it can align camera/readback sizes. + virtual void SetOverlaySize(int /*width*/, int /*height*/) {} + + /** + * This method is called once on every iteration of the OpenXR app's main loop. + * + * Implementations should perform only lightweight, non-blocking per-frame work here, such as: + *
    + *
  • Polling results produced asynchronously by pipelines started in RunPipelines and + * updating any shared state used by rendering.
  • + *
  • Uploading per-frame inputs (for example, time values, poses, hand deltas) into shared buffers/tensors or + * placeholders to prepare for the next pipeline execution.
  • + *
  • Advancing app-side animations or housekeeping logic that interacts with Secure MR content.
  • + *
+ * + * Notes: + *
    + *
  • This method runs on the OpenXR program's main thread before events are polled and before a frame is + * rendered, so avoid heavy computations or long blocking operations.
  • + *
  • If interacting with worker threads created in CreatePipelines or RunPipelines, + * ensure proper synchronization and keep critical sections short.
  • + *
  • Do not submit pipelines in this method; use RunPipelines for pipeline submission.
  • + *
+ */ + virtual void Tick() {} /** * This method is designed to indicate the OpenXR app's main loop whether the Secure MR @@ -91,6 +137,29 @@ class ISecureMR { * @return True if all Secure MR resources are ready. */ [[nodiscard]] virtual bool LoadingFinished() const = 0; + + // Optional: request a head-locked 2D overlay (quad) during rendering. + // Default: no overlay. + [[nodiscard]] virtual bool WantsScanOverlay() const { return false; } + + // Optional: provide dynamic RGBA data for the head-locked overlay. + // Return true if outRgba is filled/updated and needs uploading to the overlay swapchain. + // The buffer must have size width*height*4 in RGBA (8-bit) format. + virtual bool UpdateOverlayRgba(int /*width*/, int /*height*/, std::vector& /*outRgba*/) { + return false; + } + + // Optional: user 3D mesh (for native procedural content). Default: disabled. + // If enabled, UpdateUserMesh should fill outVerts/outIdx and set outWorldPose. + // Geometry::Vertex uses Position+Color, and indices are 16-bit triangle indices. + [[nodiscard]] virtual bool WantsUserMesh() const { return false; } + virtual bool UpdateUserMesh(std::vector& /*outVerts*/, + std::vector& /*outIdx*/, XrPosef& /*outWorldPose*/) { + return false; + } + + // Optional: allow apps to suppress the default controller visualization cubes. + [[nodiscard]] virtual bool WantsControllerVisualization() const { return true; } }; std::shared_ptr CreateSecureMrProgram(const XrInstance& instance, const XrSession& session); diff --git a/base/securemr_utils/pipeline.cpp b/base/securemr_utils/pipeline.cpp index e859610..de8ae2f 100644 --- a/base/securemr_utils/pipeline.cpp +++ b/base/securemr_utils/pipeline.cpp @@ -57,7 +57,16 @@ bool Pipeline::verifyPipelineTensor(const std::shared_ptr& candi return candidateTensor != nullptr && candidateTensor->getPipeline().get() == this; } -Pipeline::~Pipeline(){CHECK_XRCMD(xrDestroySecureMrPipelinePICO(m_handle))} +Pipeline::~Pipeline() { + // Destructors must not throw. Guard invalid or already-destroyed handles + if (xrDestroySecureMrPipelinePICO != nullptr && m_handle != XR_NULL_HANDLE) { + const XrResult res = xrDestroySecureMrPipelinePICO(m_handle); + if (XR_FAILED(res) && res != XR_ERROR_HANDLE_INVALID) { + Log::Write(Log::Level::Warning, Fmt("xrDestroySecureMrPipelinePICO failed: %s", to_string(res))); + } + m_handle = XR_NULL_HANDLE; + } +} Pipeline& Pipeline::typeConvert(const std::shared_ptr& src, const std::shared_ptr& dst) { @@ -707,6 +716,20 @@ Pipeline& Pipeline::execRenderCommand(const std::shared_ptr& comm return *this; } +Pipeline& Pipeline::debugRenderText( + const std::shared_ptr& gltfPlaceholder, + const std::variant, std::string>& text, + int canvasWidth, int canvasHeight, + const std::variant, std::tuple>& startPosition, + const std::variant, float>& fontSize, + const std::variant, std::array, 2>>& colors, + const std::variant, uint16_t>& textureId) { + auto cmd = std::make_shared( + gltfPlaceholder, std::string("en-US"), RenderCommand_DrawText::TypeFaceTypes::MONOSPACE, canvasWidth, + canvasHeight, text, startPosition, fontSize, colors, textureId); + return execRenderCommand(cmd); +} + static std::vector prepareIoMap( const std::unordered_map>& tensors, const std::unordered_map& aliasing) { @@ -787,6 +810,38 @@ Pipeline& Pipeline::runAlgorithm(char* algPackageBuf, size_t algPackageSize, return *this; } +Pipeline& Pipeline::runJavascript(char* javascriptBuf, size_t javascriptSize, + const std::unordered_map>& scriptOperands, + const std::unordered_map>& scriptResults) { + + XrSecureMrOperatorPICO opHandle = XR_NULL_HANDLE; + + XrSecureMrOperatorJavascriptPICO jsConfig{ + .type = XR_TYPE_SECURE_MR_OPERATOR_JAVASCRIPT_PICO, + .configText = javascriptBuf, + .configLength = static_cast(javascriptSize) + }; + + XrSecureMrOperatorCreateInfoPICO operatorCreateInfo{ + .type = XR_TYPE_SECURE_MR_OPERATOR_CREATE_INFO_PICO, + .operatorInfo = reinterpret_cast(&jsConfig), + .operatorType = XR_SECURE_MR_OPERATOR_TYPE_JAVASCRIPT_PICO + }; + + CHECK_XRCMD(xrCreateSecureMrOperatorPICO(m_handle, &operatorCreateInfo, &opHandle)) + + for (auto& operand : scriptOperands) { + xrSetSecureMrOperatorOperandByNamePICO( + m_handle, opHandle, static_cast(*operand.second), operand.first.c_str()); + } + for (auto& result : scriptResults) { + xrSetSecureMrOperatorResultByNamePICO(m_handle, opHandle, static_cast(*result.second), + result.first.c_str()); + } + + return *this; +} + XrSecureMrPipelineRunPICO Pipeline::submit( const std::map, std::shared_ptr>& argumentMap, XrSecureMrPipelineRunPICO waitFor, const std::shared_ptr& condition) { diff --git a/base/securemr_utils/pipeline.h b/base/securemr_utils/pipeline.h index 0715a5c..63b8101 100644 --- a/base/securemr_utils/pipeline.h +++ b/base/securemr_utils/pipeline.h @@ -27,6 +27,7 @@ namespace SecureMR { class PipelineTensor; struct RenderCommand; +struct RenderCommand_DrawText; /** * Pipeline, an adapter for XrSecureMrPipelinePICO handle. By using the class: @@ -71,7 +72,7 @@ class Pipeline final : public XrHandleAdapter, public st public: friend struct RenderCommand; - + /** * Pipeline must be constructed in association with a FrameworkSession, which performs as the camera provider and * the manager for resources. @@ -160,7 +161,26 @@ class Pipeline final : public XrHandleAdapter, public st * evaluate an arithmetic expression, such as {0} + {1} / 2. * Encapsulating operator of type XR_SECURE_MR_OPERATOR_TYPE_ARITHMETIC_COMPOSE_PICO * @param expression The arithmetic expression, where you can use {IDX} to refer to the No. IDX - * operands. We support +, -, *, / and (). + * operands. We support +, -, *, /, ^, T, inv, sin, cos, tan, asin, acos, atan, + * sinh, cosh, tanh, (, and), where + * T means transpose of a matrix, + * inv means the inverse of a matrix, and + * ^ means the exponential of a matrix. + * For example, you can use the method like: + *
    + *
  • Linear arithmetic: pipeline.arithmetic("{0}+{1}*2", + * std::vector{tensor1, tensor2}, result);
  • + *
  • Parentheses: pipeline.arithmetic("({0}+{1})*2", + * std::vector{tensor1, tensor2}, result);
  • + *
  • Complex parentheses pipeline.arithmetic("4.0 - ( ( {0} * {1}) + 2)", + * std::vector{tensor1, tensor2}, result);
  • + *
  • Exponential: pipeline.arithmetic("4.0 - ({0} * {1}) ^ 2", + * std::vector{tensor1, tensor2}, result);
  • + *
  • Inverse: pipeline.arithmetic("4.0 -inv({0} * {1})", + * std::vector{tensor1, tensor2}, result);
  • + *
  • Transpose: pipeline.arithmetic("4.0 -inv({0} * {1})", + * std::vector{tensor1, tensor2}, result);
  • + *
* @param ops Operands to the arithmetic expression. The usage type of all the operands must be declared as * XR_SECURE_MR_TENSOR_TYPE_MAT_PICO * @param result The result from the arithmetic expression, whose usage type must also be @@ -530,6 +550,35 @@ class Pipeline final : public XrHandleAdapter, public st * @return Reference to this pipeline */ Pipeline& execRenderCommand(const std::shared_ptr& command); + + /** + * Add to the pipeline an operator that draws debug text onto a texture associated with a glTF object for display. + * + * The text is rendered using a monospace typeface and the specified canvas size. + * Callers may provide most arguments either as immediate values (string, tuple, float, + * arrays) or as pipeline tensors to drive the content dynamically at runtime. + * + * @param gltfPlaceholder Placeholder referring to the target glTF object + * @param text The text content to render; either a pipeline tensor (scalar/UTF-8 buffer) + * or a std::string + * @param canvasWidth Width of the text canvas in pixels + * @param canvasHeight Height of the text canvas in pixels + * @param startPosition The start position on the canvas (x,y in [0,1]) as either a 2‑tuple + * of floats or a pipeline tensor providing the pair + * @param fontSize The font size (in pixels) as either a float or a pipeline tensor + * @param colors Two RGBA colors used by the renderer (foreground, background), provided either + * as a pipeline tensor or a std::array,2> + * @param textureId The texture ID of the texture belonging to the glTF object to draw into; either a UINT16 pipeline tensor (usage + * XR_SECURE_MR_TENSOR_TYPE_SCALAR_PICO) or an immediate uint16 value + * @return Reference to this pipeline + */ + Pipeline& debugRenderText(const std::shared_ptr& gltfPlaceholder, + const std::variant, std::string>& text, + int canvasWidth, int canvasHeight, + const std::variant, std::tuple>& startPosition, + const std::variant, float>& fontSize, + const std::variant, std::array, 2>>& colors, + const std::variant, uint16_t>& textureId); /** * Add a customized algorithm operator the pipeline from a loaded binary algorithm package. * @@ -554,6 +603,24 @@ class Pipeline final : public XrHandleAdapter, public st const std::unordered_map& resultAliasing, const std::string& modelName); + /** + * Executes a JavaScript operator inside the pipeline. + * + * The JavaScript source is provided as a memory buffer. Named operands and results are + * mapped to pipeline tensors; names must match the identifiers referenced by the script. + * At execution time, operand tensors are made available to the script environment, and + * the script writes outputs into the provided result tensors. + * + * @param javscriptBuf Pointer to a UTF‑8 JavaScript source buffer + * @param javascriptSize Size in bytes of the JavaScript source buffer + * @param scriptOperands Mapping of operand names to pipeline tensors accessible from the script + * @param scriptResults Mapping of result names to pipeline tensors written by the script + * @return Reference to this pipeline + */ + Pipeline& runJavascript(char* javscriptBuf, size_t javascriptSize, + const std::unordered_map>& scriptOperands, + const std::unordered_map>& scriptResults); + /** * Submit the pipeline to be executed. The architecture (pipeline tensors, operators) of the pipeline will be frozen * until the execution is finished. Executions submitted from the same pipeline will be executed in the submission diff --git a/base/securemr_utils/readback.cpp b/base/securemr_utils/readback.cpp new file mode 100644 index 0000000..d83e4c6 --- /dev/null +++ b/base/securemr_utils/readback.cpp @@ -0,0 +1,142 @@ +// Copyright (2025) Bytedance Ltd. and/or its affiliates +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "readback.h" + +namespace SecureMR { +ReadbackController::ReadbackController(std::shared_ptr frameworkSession, std::shared_ptr readbackTensor) +{ + mReadbackTensor = readbackTensor; + + xrCreateBufferFromGlobalTensorAsyncPICO = + frameworkSession->getAPIFromXrInstance( + "xrCreateBufferFromGlobalTensorAsyncPICO"); + xrCreateBufferFromGlobalTensorCompletePICO = + frameworkSession->getAPIFromXrInstance( + "xrCreateBufferFromGlobalTensorCompletePICO"); + xrCreateTextureFromGlobalTensorAsyncPICO = + frameworkSession->getAPIFromXrInstance( + "xrCreateTextureFromGlobalTensorAsyncPICO"); + xrCreateTextureFromGlobalTensorCompletePICO = + frameworkSession->getAPIFromXrInstance( + "xrCreateTextureFromGlobalTensorCompletePICO"); + xrGetReadbackTextureImagePICO = + frameworkSession->getAPIFromXrInstance("xrGetReadbackTextureImagePICO"); + xrReleaseReadbackTexturePICO = + frameworkSession->getAPIFromXrInstance("xrReleaseReadbackTexturePICO"); + } + + + bool ReadbackController::RequestReadbackBuffer(ReadbackRequest* &request) { + auto new_future = new XrFutureEXT(); + auto res = xrCreateBufferFromGlobalTensorAsyncPICO(static_cast(*mReadbackTensor), new_future); + if (res != XR_SUCCESS) + { + LOGE("Create Readback Buffer failed! res = %d", res); + return false; + } + mPendingFutures.emplace((uint64_t)new_future, new_future); + request = new ReadbackRequest((ReadbackRequest)new_future); + return true; + } + + bool ReadbackController::RequestReadbackTexture(ReadbackRequest * &request) { + auto new_future = new XrFutureEXT(); + + auto res = xrCreateTextureFromGlobalTensorAsyncPICO(static_cast(*mReadbackTensor), new_future); + if (res != XR_SUCCESS) + { + LOGE("Create Readback Texture failed! res = %d", res); + return false; + } + mPendingFutures.emplace((uint64_t)new_future, new_future); + request = new ReadbackRequest((ReadbackRequest)new_future); + return true; + } + + bool ReadbackController::TryAcquireReadbackTexture(const SecureMR::ReadbackController::ReadbackRequest &req, XrReadbackTexturePICO &out) { + auto key = (uint64_t)(req); + if (mPendingFutures.find(key) == mPendingFutures.end()) + { + LOGE("Readback Request invalid!"); + return false; + } + XrCreateTextureFromGlobalTensorCompletionPICO completion; + auto ret = xrCreateTextureFromGlobalTensorCompletePICO( + XrSecureMrTensorPICO(*mReadbackTensor), + *mPendingFutures[key], + &completion); + if (ret != XR_SUCCESS) { + return false; + } + mPendingFutures.erase(key); + out = completion.texture; + return true; + } + + bool ReadbackController::TryAcquireReadbackBuffer(const SecureMR::ReadbackController::ReadbackRequest &req, XrReadbackTensorBufferPICO* &out) { + auto key = (uint64_t)(req); + if (mPendingFutures.find(key) == mPendingFutures.end()) + { + LOGE("Readback Request invalid!"); + return false; + } + + XrCreateBufferFromGlobalTensorCompletionPICO completion; + completion.tensorBuffer = new XrReadbackTensorBufferPICO(); + + completion.tensorBuffer->bufferCapacityInput = 0; + auto ret = + xrCreateBufferFromGlobalTensorCompletePICO(static_cast(*mReadbackTensor), *mPendingFutures[key], &completion); + if (ret != XR_SUCCESS) { + if (ret != XR_ERROR_FUTURE_PENDING_EXT) + { + LOGE("TryAcquireReadbackBuffer failed! ret = %d", ret); + } + return false; + } + completion.tensorBuffer->bufferCapacityInput = completion.tensorBuffer->bufferCountOutput; + completion.tensorBuffer->buffer = new char[completion.tensorBuffer->bufferCapacityInput]; + ret = xrCreateBufferFromGlobalTensorCompletePICO(static_cast(*mReadbackTensor), *mPendingFutures[key], &completion); + if (ret != XR_SUCCESS) + { + LOGE("TryAcquireReadbackBuffer failed! ret = %d", ret); + return false; + } + mPendingFutures.erase(key); + out = completion.tensorBuffer; + return true; + } + + bool ReadbackController::RetrieveTexture(XrReadbackTexturePICO texture, XrReadbackTextureImageBaseHeaderPICO* textureRes) + { + XrResult ret; + ret = xrGetReadbackTextureImagePICO(texture, textureRes); + if (ret != XR_SUCCESS) + { + LOGE("RetrieveTexture failed! ret = %d", ret); + return false; + } + return true; + } + + void ReadbackController::ReleaseReadbackTexture(XrReadbackTexturePICO texture) + { + xrReleaseReadbackTexturePICO(texture); + } + + void ReadbackController::ResetPendingRequests() { + mPendingFutures.clear(); + } +} \ No newline at end of file diff --git a/base/securemr_utils/readback.h b/base/securemr_utils/readback.h new file mode 100644 index 0000000..fd03702 --- /dev/null +++ b/base/securemr_utils/readback.h @@ -0,0 +1,122 @@ +// Copyright (2025) Bytedance Ltd. and/or its affiliates +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tensor.h" +#include "pch.h" + + +#define LOG_TAG "Readback" + +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) + +#include "graphicsplugin.h" + +namespace SecureMR { + +class FrameworkSession; + +class ReadbackController { + public: + + typedef uint64_t ReadbackRequest; + + /** + * Constructor: initializes the readback controller. + * @param frameworkSession OpenXR framework session object + * @param readbackTensor global tensor to read back + */ + ReadbackController(std::shared_ptr frameworkSession, std::shared_ptr readbackTensor); + + /** + * Issue an asynchronous buffer readback request. + * @param request output parameter; returns a request handle used to retrieve the result later + * @return true if the request is issued successfully; false otherwise + */ + bool RequestReadbackBuffer(ReadbackRequest* &request); + + /** + * Attempt to complete buffer readback and retrieve the result (non-blocking). + * @param req request handle + * @param out output parameter; pointer to the readback tensor buffer + * @return true if acquired; false if not ready or failed + */ + bool TryAcquireReadbackBuffer(const ReadbackRequest& req, XrReadbackTensorBufferPICO* &out); + + /** + * Issue an asynchronous texture readback/creation request. + * @param request output parameter; returns a request handle + * @return true on success; false otherwise + */ + bool RequestReadbackTexture(ReadbackRequest* &request); + + /** + * Attempt to complete texture readback/creation and return the texture object (non-blocking). + * @param req request handle + * @param out output parameter; the resulting `XrReadbackTexturePICO` + * @return true if acquired; false if not ready or failed + */ + bool TryAcquireReadbackTexture(const ReadbackRequest& req, XrReadbackTexturePICO &out); + + /** + * Retrieve the image content of the readback texture to a file. + * @param texture readback texture handle + * @param textureRes output parameter; returns the real texture resource based on graphics platform. + * @return true on success; false otherwise + */ + bool RetrieveTexture(XrReadbackTexturePICO texture, XrReadbackTextureImageBaseHeaderPICO* textureRes); + + /** + * Release texture resources produced by readback. + * @param texture texture handle to release + */ + void ReleaseReadbackTexture(XrReadbackTexturePICO texture); + + /** + * Reset pending requests. + */ + void ResetPendingRequests(); + + private: + void initializeGraphicsContext(); + + std::shared_ptr mReadbackTensor; + std::map mPendingFutures; + // buffer openxr interfaces + PFN_xrCreateBufferFromGlobalTensorAsyncPICO xrCreateBufferFromGlobalTensorAsyncPICO; + PFN_xrCreateBufferFromGlobalTensorCompletePICO xrCreateBufferFromGlobalTensorCompletePICO; + + // texture openxr interfaces + PFN_xrCreateTextureFromGlobalTensorCompletePICO xrCreateTextureFromGlobalTensorCompletePICO; + PFN_xrCreateTextureFromGlobalTensorAsyncPICO xrCreateTextureFromGlobalTensorAsyncPICO; + PFN_xrGetReadbackTextureImagePICO xrGetReadbackTextureImagePICO; + PFN_xrReleaseReadbackTexturePICO xrReleaseReadbackTexturePICO; + +}; +} diff --git a/base/securemr_utils/readback_async.cpp b/base/securemr_utils/readback_async.cpp new file mode 100644 index 0000000..43f37c8 --- /dev/null +++ b/base/securemr_utils/readback_async.cpp @@ -0,0 +1,262 @@ +// Copyright (2025) Bytedance Ltd. and/or its affiliates +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "readback_async.h" + +#include + +#include "common.h" +#include "logger.h" +#include "check.h" +#include "session.h" + +namespace SecureMR { + +TensorReadback::TensorReadback(std::shared_ptr session, std::vector targets) + : TensorReadback(std::move(session), std::move(targets), Config{}) {} + +TensorReadback::TensorReadback(std::shared_ptr session, std::vector targets, Config config) + : session_(std::move(session)), config_(config) { + CHECK_MSG(session_ != nullptr, "TensorReadback requires a valid FrameworkSession"); + + createAsync_ = + session_->getAPIFromXrInstance("xrCreateBufferFromGlobalTensorAsyncPICO"); + complete_ = + session_->getAPIFromXrInstance("xrCreateBufferFromGlobalTensorCompletePICO"); + CHECK_MSG(createAsync_ != nullptr, "Failed to resolve xrCreateBufferFromGlobalTensorAsyncPICO"); + CHECK_MSG(complete_ != nullptr, "Failed to resolve xrCreateBufferFromGlobalTensorCompletePICO"); + + targets_.reserve(targets.size()); + for (auto& target : targets) { + CHECK_MSG(target.tensor != nullptr, "TensorReadback target tensor must not be null"); + targets_.emplace_back(TargetState{.target = std::move(target)}); + } +} + +TensorReadback::~TensorReadback() { + Stop(); +} + +void TensorReadback::Start() { + if (targets_.empty()) { + Log::Write(Log::Level::Warning, "TensorReadback::Start called with no targets; skipping thread creation."); + return; + } + bool expected = false; + if (!running_.compare_exchange_strong(expected, true)) { + return; + } + worker_ = std::thread(&TensorReadback::Loop, this); +} + +void TensorReadback::Stop() { + bool wasRunning = running_.exchange(false); + if (!wasRunning) { + return; + } + stateCv_.notify_all(); + if (worker_.joinable()) { + worker_.join(); + } + pendingFutures_.clear(); + for (auto& state : targets_) { + state.inFlight = false; + } +} + +void TensorReadback::Loop() { + auto nextSchedule = std::chrono::steady_clock::now(); + while (running_.load(std::memory_order_acquire)) { + if (pendingFutures_.empty()) { + auto now = std::chrono::steady_clock::now(); + if (now < nextSchedule) { + std::unique_lock lock(stateMutex_); + stateCv_.wait_until(lock, nextSchedule, [this]() { return !running_.load(std::memory_order_acquire); }); + continue; + } + } + + if (!running_.load(std::memory_order_acquire)) { + break; + } + + auto now = std::chrono::steady_clock::now(); + if (now >= nextSchedule) { + ScheduleFutures(); + nextSchedule = std::chrono::steady_clock::now() + config_.pollingInterval; + } + + // Process queue + while (running_.load(std::memory_order_acquire) && ProcessSingleFuture()) {} + } + + pendingFutures_.clear(); + for (auto& state : targets_) { + state.inFlight = false; + } +} + +void TensorReadback::ScheduleFutures() { + for (auto& state : targets_) { + if (!running_.load(std::memory_order_acquire)) { + return; + } + if (state.inFlight) { + continue; + } + + while (pendingFutures_.size() >= kMaxQueueDepth && running_.load(std::memory_order_acquire)) { + if (!ProcessSingleFuture()) { + break; + } + } + + if (pendingFutures_.size() >= kMaxQueueDepth) { + break; + } + + EnqueueFuture(state); + } +} + +bool TensorReadback::EnqueueFuture(TargetState& state) { + if (state.inFlight || state.target.tensor == nullptr) { + return false; + } + + auto tensorHandle = static_cast(*state.target.tensor); + XrFutureEXT future = XR_NULL_HANDLE; + XrResult result = createAsync_(tensorHandle, &future); + if (XR_FAILED(result) || future == XR_NULL_HANDLE) { + Log::Write(Log::Level::Error, + Fmt("xrCreateBufferFromGlobalTensorAsyncPICO failed for %s (result=%d)", + state.target.name.c_str(), result)); + return false; + } + + pendingFutures_.push_back(PendingFuture{.state = &state, .future = future}); + state.inFlight = true; + return true; +} + + +bool TensorReadback::ProcessSingleFuture() { + if (pendingFutures_.empty()) { + return false; + } + + PendingFuture pending = std::move(pendingFutures_.front()); + pendingFutures_.pop_front(); + + if (!running_.load(std::memory_order_acquire)) { + if (pending.state) { + pending.state->inFlight = false; + } + return false; + } + + if (pending.state == nullptr || pending.state->target.tensor == nullptr || pending.future == XR_NULL_HANDLE) { + if (pending.state) { + pending.state->inFlight = false; + } + return true; + } + + ProcessFuture(*pending.state, pending.future); + pending.state->inFlight = false; + return true; +} + +bool TensorReadback::WaitCompletion(const TargetState& state, XrSecureMrTensorPICO tensorHandle, XrFutureEXT future, + XrReadbackTensorBufferPICO& buffer, + XrCreateBufferFromGlobalTensorCompletionPICO& completion) { + XrResult result; + while (true) { + completion = {.type = XR_TYPE_CREATE_BUFFER_FROM_GLOBAL_TENSOR_COMPLETION_PICO, + .next = nullptr, + .futureResult = XR_SUCCESS, + .tensorBuffer = &buffer}; + while (running_.load(std::memory_order_acquire)) { + result = complete_(tensorHandle, future, &completion); + if (result == XR_SUCCESS) { + return true; + } + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + } + Log::Write(Log::Level::Warning, + Fmt("xrCreateBufferFromGlobalTensorCompletePICO failed for %s (result=%d)", + state.target.name.c_str(), result)); + } + return false; +} + +void TensorReadback::ProcessFuture(TargetState& state, XrFutureEXT future) { + auto tensorHandle = static_cast(*state.target.tensor); + + XrReadbackTensorBufferPICO buffer{ + .type = XR_TYPE_READBACK_TENSOR_BUFFER_PICO, + .next = nullptr, + .bufferCapacityInput = 0, + .bufferCountOutput = 0, + .buffer = nullptr}; + + XrCreateBufferFromGlobalTensorCompletionPICO completion{ + .type = XR_TYPE_CREATE_BUFFER_FROM_GLOBAL_TENSOR_COMPLETION_PICO, + .next = nullptr, + .futureResult = XR_SUCCESS, + .tensorBuffer = &buffer}; + + + Log::Write(Log::Level::Verbose, Fmt("WaitCompletion_1 start")); + if (!WaitCompletion(state, tensorHandle, future, buffer, completion)) { + return; + } + Log::Write(Log::Level::Verbose, Fmt("WaitCompletion_1 end")); + + buffer.bufferCapacityInput = buffer.bufferCountOutput; + std::vector payload(buffer.bufferCapacityInput); + if (!payload.empty()) { + buffer.buffer = payload.data(); + } + + Log::Write(Log::Level::Verbose, Fmt("WaitCompletion_2 start")); + if (!WaitCompletion(state, tensorHandle, future, buffer, completion)) { + return; + } + Log::Write(Log::Level::Verbose, Fmt("WaitCompletion_2 end")); + + auto attrVariant = state.target.tensor->getAttribute(); + std::vector dimensions; + int channels = 0; + XrSecureMrTensorDataTypePICO dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_MAX_ENUM_PICO; + if (auto* attr = std::get_if(&attrVariant)) { + dimensions = attr->dimensions; + channels = attr->channels; + dataType = attr->dataType; + } + + if (state.target.callback) { + TensorReadbackResult resultPayload{.name = state.target.name, + .tensor = state.target.tensor, + .data = std::move(payload), + .dimensions = std::move(dimensions), + .channels = channels, + .dataType = dataType, + .futureResult = completion.futureResult}; + state.target.callback(std::move(resultPayload)); + } +} + + +} // namespace SecureMR diff --git a/base/securemr_utils/readback_async.h b/base/securemr_utils/readback_async.h new file mode 100644 index 0000000..da9ebc4 --- /dev/null +++ b/base/securemr_utils/readback_async.h @@ -0,0 +1,104 @@ +// Copyright (2025) Bytedance Ltd. and/or its affiliates +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tensor.h" + +namespace SecureMR { + +class FrameworkSession; + +struct TensorReadbackResult { + std::string_view name; + std::shared_ptr tensor; + std::vector data; + std::vector dimensions; + int channels = 0; + XrSecureMrTensorDataTypePICO dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_MAX_ENUM_PICO; + XrResult futureResult = XR_SUCCESS; +}; + +class TensorReadback { + public: + using Callback = std::function; + + struct Target { + std::shared_ptr tensor; + Callback callback; + std::string name; + }; + + struct Config { + std::chrono::milliseconds pollingInterval{33}; + }; + + TensorReadback(std::shared_ptr session, std::vector targets); + TensorReadback(std::shared_ptr session, std::vector targets, Config config); + TensorReadback(const TensorReadback&) = delete; + TensorReadback& operator=(const TensorReadback&) = delete; + TensorReadback(TensorReadback&&) = delete; + TensorReadback& operator=(TensorReadback&&) = delete; + ~TensorReadback(); + + void Start(); + void Stop(); + [[nodiscard]] bool IsRunning() const { return running_.load(std::memory_order_acquire); } + + private: + struct TargetState { + Target target; + bool inFlight = false; + }; + + struct PendingFuture { + TargetState* state = nullptr; + XrFutureEXT future = XR_NULL_HANDLE; + }; + + void Loop(); + void ScheduleFutures(); + bool EnqueueFuture(TargetState& state); + bool ProcessSingleFuture(); + bool WaitCompletion(const TargetState& state, XrSecureMrTensorPICO tensorHandle, XrFutureEXT future, + XrReadbackTensorBufferPICO& buffer, XrCreateBufferFromGlobalTensorCompletionPICO& completion); + void ProcessFuture(TargetState& state, XrFutureEXT future); + + std::shared_ptr session_; + std::vector targets_; + Config config_; + PFN_xrCreateBufferFromGlobalTensorAsyncPICO createAsync_ = nullptr; + PFN_xrCreateBufferFromGlobalTensorCompletePICO complete_ = nullptr; + + std::atomic running_{false}; + std::thread worker_; + std::deque pendingFutures_; + static constexpr std::size_t kMaxQueueDepth = 100; + std::mutex stateMutex_; + std::condition_variable stateCv_; +}; + +} // namespace SecureMR diff --git a/base/securemr_utils/rendercommand.h b/base/securemr_utils/rendercommand.h index b3fae1d..1e1502b 100644 --- a/base/securemr_utils/rendercommand.h +++ b/base/securemr_utils/rendercommand.h @@ -94,7 +94,7 @@ struct RenderCommand { opTensor = std::make_shared( gltfTensor->getPipeline(), TensorAttribute{.dimensions = {static_cast(arg.size())}, .channels = 4, - .usage = XR_SECURE_MR_TENSOR_TYPE_SCALAR_PICO, + .usage = XR_SECURE_MR_TENSOR_TYPE_COLOR_PICO, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_UINT8_PICO}); std::vector flattedArg; for (auto& eachColor : arg) { @@ -108,7 +108,7 @@ struct RenderCommand { opTensor = std::make_shared( gltfTensor->getPipeline(), TensorAttribute{.dimensions = {2}, .channels = 4, - .usage = XR_SECURE_MR_TENSOR_TYPE_SCALAR_PICO, + .usage = XR_SECURE_MR_TENSOR_TYPE_COLOR_PICO, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_UINT8_PICO}); uint8_t flattedArg[]{arg[0][0], arg[0][1], arg[0][2], arg[0][3], arg[1][0], arg[1][1], arg[1][2], arg[1][3]}; diff --git a/base/securemr_utils/serialization.cpp b/base/securemr_utils/serialization.cpp new file mode 100644 index 0000000..0bf751d --- /dev/null +++ b/base/securemr_utils/serialization.cpp @@ -0,0 +1,613 @@ +// Copyright (2025) Bytedance Ltd. and/or its affiliates +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "securemr_utils/serialization.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "oxr_utils/common.h" +#include "oxr_utils/logger.h" +#include "pipeline.h" +#include "tensor.h" + +#ifdef XR_USE_PLATFORM_ANDROID +#include +extern AAssetManager* g_assetManager; +#endif + +namespace SecureMR { + +Json TensorAttributeToJson(const TensorAttribute& attr) { + Json j; + j["dimensions"] = attr.dimensions; + j["channels"] = attr.channels; + j["usage"] = static_cast(attr.usage); + j["data_type"] = static_cast(attr.dataType); + return j; +} + +Json TensorAttributeVariantToJson(const std::variant& attr) { + Json j; + if (std::holds_alternative(attr)) { + j = TensorAttributeToJson(std::get(attr)); + } else { + j["is_gltf"] = true; + } + return j; +} + +Json TensorListToJson(const std::vector& tensors) { + Json arr = Json::array(); + for (const auto& name : tensors) { + arr.push_back(name); + } + return arr; +} + +Json MappedTensorListToJson(const std::vector>& mapping) { + Json arr = Json::array(); + for (const auto& [alias, tensor] : mapping) { + Json entry; + entry["name"] = alias; + entry["tensor"] = tensor; + arr.push_back(entry); + } + return arr; +} + +void SetInputs(Json& spec, const std::vector& inputs) { + spec["inputs"] = TensorListToJson(inputs); +} + +void SetOutputs(Json& spec, const std::vector& outputs) { + spec["outputs"] = TensorListToJson(outputs); +} + +bool WriteJsonToFile(const std::filesystem::path& filePath, const Json& spec) { + if (filePath.empty()) { + Log::Write(Log::Level::Error, "WriteJsonToFile failed: writable path unavailable"); + return false; + } + std::error_code ec; + std::filesystem::create_directories(filePath.parent_path(), ec); + std::ofstream ofs(filePath); + if (!ofs) { + Log::Write(Log::Level::Error, + Fmt("WriteJsonToFile failed: cannot open %s", filePath.string().c_str())); + return false; + } + ofs << spec.dump(2); + return true; +} + +bool JsonToTensorAttribute(const Json& j, TensorAttribute& out) { + if (j.find("dimensions") == j.end() || j.find("channels") == j.end() || j.find("usage") == j.end() || + j.find("data_type") == j.end()) { + return false; + } + if (!j["dimensions"].is_array()) { + return false; + } + out.dimensions.clear(); + for (const auto& dim : j["dimensions"]) { + if (!dim.is_number_integer()) { + return false; + } + out.dimensions.push_back(dim.get()); + } + out.channels = static_cast(j["channels"].get()); + out.usage = static_cast(j["usage"].get()); + out.dataType = static_cast(j["data_type"].get()); + return true; +} + +std::vector ParseTensorList(const Json& arr) { + std::vector tensors; + if (!arr.is_array()) { + return tensors; + } + tensors.reserve(arr.size()); + for (const auto& each : arr) { + if (each.is_string()) { + tensors.push_back(each.get()); + } else if (each.is_object()) { + if (auto tensorIt = each.find("tensor"); tensorIt != each.end() && tensorIt->is_string()) { + tensors.push_back(tensorIt->get()); + } + } + } + return tensors; +} + +std::vector> ParseMappedTensorList(const Json& arr) { + std::vector> mapping; + if (!arr.is_array()) { + return mapping; + } + mapping.reserve(arr.size()); + for (const auto& each : arr) { + std::string tensorName; + std::string alias; + if (each.is_object()) { + if (auto aliasIt = each.find("name"); aliasIt != each.end() && aliasIt->is_string()) { + alias = aliasIt->get(); + } + if (auto tensorIt = each.find("tensor"); tensorIt != each.end() && tensorIt->is_string()) { + tensorName = tensorIt->get(); + } + } else if (each.is_string()) { + tensorName = each.get(); + alias = tensorName; + } + if (!tensorName.empty()) { + if (alias.empty()) { + alias = tensorName; + } + mapping.emplace_back(alias, tensorName); + } + } + return mapping; +} + +bool JsonToFloatArray(const Json& arr, std::array& dest) { + if (!arr.is_array() || arr.size() != dest.size()) { + return false; + } + for (size_t i = 0; i < dest.size(); ++i) { + if (!arr[i].is_number()) { + return false; + } + dest[i] = arr[i].get(); + } + return true; +} + +Json LoadJsonFromFile(const std::filesystem::path& filePath) { + Json parsed; + if (filePath.empty()) { + Log::Write(Log::Level::Error, "LoadJsonFromFile failed: path empty"); + return parsed; + } + std::ifstream ifs(filePath); + if (!ifs) { + Log::Write(Log::Level::Error, + Fmt("LoadJsonFromFile failed: cannot open %s", filePath.string().c_str())); + return parsed; + } + try { + ifs >> parsed; + } catch (const std::exception& e) { + Log::Write(Log::Level::Error, Fmt("LoadJsonFromFile failed: %s", e.what())); + parsed = Json(); + } + return parsed; +} + +std::string FormatOperatorType(const std::string& typeName) { + if (typeName.empty()) { + return typeName; + } + static const std::unordered_map kAliases = { + {"camera_access", "camera_access"}, + {"XR_SECURE_MR_OPERATOR_TYPE_RECTIFIED_VST_ACCESS_PICO", "camera_access"}, + {"get_affine", "get_affine"}, + {"XR_SECURE_MR_OPERATOR_TYPE_GET_AFFINE_PICO", "get_affine"}, + {"apply_affine", "apply_affine"}, + {"XR_SECURE_MR_OPERATOR_TYPE_APPLY_AFFINE_PICO", "apply_affine"}, + {"cvt_color", "cvt_color"}, + {"XR_SECURE_MR_OPERATOR_TYPE_CONVERT_COLOR_PICO", "cvt_color"}, + {"assignment", "assignment"}, + {"type_convert", "type_convert"}, + {"XR_SECURE_MR_OPERATOR_TYPE_ASSIGNMENT_PICO", "assignment"}, + {"arithmetic", "arithmetic"}, + {"XR_SECURE_MR_OPERATOR_TYPE_ARITHMETIC_COMPOSE_PICO", "arithmetic"}, + {"run_algorithm", "run_algorithm"}, + {"XR_SECURE_MR_OPERATOR_TYPE_RUN_MODEL_INFERENCE_PICO", "run_algorithm"}, + }; + + if (auto it = kAliases.find(typeName); it != kAliases.end()) { + return it->second; + } + return typeName; +} + +bool DeserializePipelineFromJson(const Json& spec, + const std::shared_ptr& session, + PipelineDeserializationResult& outResult, + std::string& outError, + const PipelineDeserializationOptions& options) { + outResult = {}; + outError.clear(); + if (!spec.is_object()) { + outError = "JSON is not an object"; + return false; + } + + const auto tensorsIt = spec.find("tensors"); + if (tensorsIt == spec.end() || !tensorsIt->is_object()) { + outError = "tensors section missing or invalid"; + return false; + } + + auto pipeline = std::make_shared(session); + for (auto it = tensorsIt->begin(); it != tensorsIt->end(); ++it) { + const std::string tensorName = it.key(); + const Json& tensorSpec = *it; + const bool isPlaceholder = tensorSpec.value("is_placeholder", false); + const bool isGltf = tensorSpec.value("is_gltf", false); + std::shared_ptr tensor; + TensorAttribute attr{}; + try { + if (isPlaceholder && isGltf) { + tensor = PipelineTensor::PipelineGLTFPlaceholder(pipeline); + } else { + if (!JsonToTensorAttribute(tensorSpec, attr)) { + outError = Fmt("invalid tensor attribute for %s", tensorName.c_str()); + return false; + } + tensor = std::make_shared(pipeline, attr, isPlaceholder); + if (!isPlaceholder && !isGltf) { + auto valueIt = tensorSpec.find("value"); + if (valueIt != tensorSpec.end() && !valueIt->is_null()) { + if (!valueIt->is_array()) { + outError = Fmt("invalid tensor value for %s: expected array", tensorName.c_str()); + return false; + } + if (attr.channels <= 0) { + outError = Fmt("invalid tensor attribute for %s: channels must be positive", tensorName.c_str()); + return false; + } + size_t elementCount = 1; + for (int dim : attr.dimensions) { + if (dim <= 0) { + outError = Fmt("invalid tensor attribute for %s: non-positive dimension", tensorName.c_str()); + return false; + } + elementCount *= static_cast(dim); + } + const size_t expectedValues = elementCount * static_cast(attr.channels); + if (valueIt->size() != expectedValues) { + outError = Fmt("invalid tensor value for %s: expected %zu entries but found %zu", tensorName.c_str(), + expectedValues, valueIt->size()); + return false; + } + + auto ensureInteger = [&](const Json& value, const char* what, int64_t minValue, + int64_t maxValue) -> std::optional { + if (!value.is_number_integer()) { + outError = Fmt("invalid tensor value for %s: expected integer for %s", tensorName.c_str(), what); + return std::nullopt; + } + const int64_t numeric = value.get(); + if (numeric < minValue || numeric > maxValue) { + outError = Fmt("invalid tensor value for %s: %s out of range [%lld, %lld]", tensorName.c_str(), what, + static_cast(minValue), static_cast(maxValue)); + return std::nullopt; + } + return numeric; + }; + + switch (attr.dataType) { + case XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO: { + std::vector buffer(expectedValues); + for (size_t idx = 0; idx < expectedValues; ++idx) { + const auto& value = (*valueIt)[idx]; + if (!value.is_number()) { + outError = Fmt("invalid tensor value for %s: non-numeric entry at index %zu", tensorName.c_str(), + idx); + return false; + } + buffer[idx] = static_cast(value.get()); + } + tensor->setData(reinterpret_cast(buffer.data()), buffer.size() * sizeof(float)); + break; + } + case XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT64_PICO: { + std::vector buffer(expectedValues); + for (size_t idx = 0; idx < expectedValues; ++idx) { + const auto& value = (*valueIt)[idx]; + if (!value.is_number()) { + outError = Fmt("invalid tensor value for %s: non-numeric entry at index %zu", tensorName.c_str(), + idx); + return false; + } + buffer[idx] = value.get(); + } + tensor->setData(reinterpret_cast(buffer.data()), buffer.size() * sizeof(double)); + break; + } + case XR_SECURE_MR_TENSOR_DATA_TYPE_INT32_PICO: { + std::vector buffer(expectedValues); + for (size_t idx = 0; idx < expectedValues; ++idx) { + auto numeric = ensureInteger((*valueIt)[idx], "INT32", std::numeric_limits::min(), + std::numeric_limits::max()); + if (!numeric.has_value()) { + return false; + } + buffer[idx] = static_cast(*numeric); + } + tensor->setData(reinterpret_cast(buffer.data()), buffer.size() * sizeof(int32_t)); + break; + } + case XR_SECURE_MR_TENSOR_DATA_TYPE_INT16_PICO: { + std::vector buffer(expectedValues); + for (size_t idx = 0; idx < expectedValues; ++idx) { + auto numeric = ensureInteger((*valueIt)[idx], "INT16", std::numeric_limits::min(), + std::numeric_limits::max()); + if (!numeric.has_value()) { + return false; + } + buffer[idx] = static_cast(*numeric); + } + tensor->setData(reinterpret_cast(buffer.data()), buffer.size() * sizeof(int16_t)); + break; + } + case XR_SECURE_MR_TENSOR_DATA_TYPE_INT8_PICO: { + std::vector buffer(expectedValues); + for (size_t idx = 0; idx < expectedValues; ++idx) { + auto numeric = ensureInteger((*valueIt)[idx], "INT8", std::numeric_limits::min(), + std::numeric_limits::max()); + if (!numeric.has_value()) { + return false; + } + buffer[idx] = static_cast(*numeric); + } + tensor->setData(reinterpret_cast(buffer.data()), buffer.size() * sizeof(int8_t)); + break; + } + case XR_SECURE_MR_TENSOR_DATA_TYPE_UINT16_PICO: { + std::vector buffer(expectedValues); + for (size_t idx = 0; idx < expectedValues; ++idx) { + auto numeric = ensureInteger((*valueIt)[idx], "UINT16", 0, std::numeric_limits::max()); + if (!numeric.has_value()) { + return false; + } + buffer[idx] = static_cast(*numeric); + } + tensor->setData(reinterpret_cast(buffer.data()), buffer.size() * sizeof(uint16_t)); + break; + } + case XR_SECURE_MR_TENSOR_DATA_TYPE_UINT8_PICO: { + std::vector buffer(expectedValues); + for (size_t idx = 0; idx < expectedValues; ++idx) { + auto numeric = ensureInteger((*valueIt)[idx], "UINT8", 0, std::numeric_limits::max()); + if (!numeric.has_value()) { + return false; + } + buffer[idx] = static_cast(*numeric); + } + tensor->setData(reinterpret_cast(buffer.data()), buffer.size() * sizeof(uint8_t)); + break; + } + default: + Log::Write(Log::Level::Warning, + Fmt("DeserializePipelineFromJson: unsupported data_type %d for tensor %s initial value", + static_cast(attr.dataType), tensorName.c_str())); + break; + } + } + } + } + } catch (const std::exception& e) { + outError = Fmt("failed to create tensor '%s': %s", tensorName.c_str(), e.what()); + return false; + } + outResult.tensorMap.emplace(tensorName, std::move(tensor)); + } + + const auto requireTensor = [&](const std::string& name) -> std::shared_ptr { + auto it = outResult.tensorMap.find(name); + if (it == outResult.tensorMap.end()) { + throw std::runtime_error(Fmt("tensor '%s' not found", name.c_str())); + } + return it->second; + }; + + const auto operatorsIt = spec.find("operators"); + if (operatorsIt == spec.end() || !operatorsIt->is_array()) { + outError = "operators section missing or invalid"; + return false; + } + + try { + for (const auto& opSpec : *operatorsIt) { + const std::string rawType = opSpec.value("type", ""); + const std::string type = FormatOperatorType(rawType); + const auto inputs = ParseTensorList(opSpec.value("inputs", Json::array())); + const auto outputs = ParseTensorList(opSpec.value("outputs", Json::array())); + + auto requireByIndex = [&](const std::vector& container, size_t index, + const char* what) -> std::shared_ptr { + if (index >= container.size()) { + throw std::runtime_error(Fmt("%s index %zu out of range", what, index)); + } + return requireTensor(container[index]); + }; + + if (type == "camera_access") { + if (outputs.size() != 4) { + throw std::runtime_error("camera_access outputs malformed"); + } + pipeline->cameraAccess(requireByIndex(outputs, 0, "camera_access output"), + requireByIndex(outputs, 1, "camera_access output"), + requireByIndex(outputs, 2, "camera_access output"), + requireByIndex(outputs, 3, "camera_access output")); + } else if (type == "get_affine") { + if (outputs.empty()) { + throw std::runtime_error("get_affine requires output tensor"); + } + + // Support two forms: + // 1) src_points/dst_points provided as float arrays in JSON attributes + // 2) src/dst provided as input tensors via inputs[0], inputs[1] + if (opSpec.contains("src_points") && opSpec.contains("dst_points")) { + std::array src{}; + std::array dst{}; + if (!JsonToFloatArray(opSpec["src_points"], src) || !JsonToFloatArray(opSpec["dst_points"], dst)) { + throw std::runtime_error("get_affine points malformed"); + } + pipeline->getAffine(src, dst, requireTensor(outputs.front())); + } else if (inputs.size() >= 2) { + // Fallback: take src/dst points from input tensors + const auto srcTensor = requireByIndex(inputs, 0, "get_affine input"); + const auto dstTensor = requireByIndex(inputs, 1, "get_affine input"); + pipeline->getAffine(srcTensor, dstTensor, requireTensor(outputs.front())); + } else { + throw std::runtime_error("get_affine requires src/dst points or two input tensors"); + } + } else if (type == "apply_affine") { + if (inputs.size() < 2 || outputs.empty()) { + throw std::runtime_error("apply_affine requires two inputs and one output"); + } + pipeline->applyAffine(requireByIndex(inputs, 0, "apply_affine input"), + requireByIndex(inputs, 1, "apply_affine input"), + requireByIndex(outputs, 0, "apply_affine output")); + } else if (type == "assignment") { + if (inputs.empty() || outputs.empty()) { + throw std::runtime_error("assignment requires input and output tensors"); + } + pipeline->assignment(requireByIndex(inputs, 0, "assignment input"), + requireByIndex(outputs, 0, "assignment output")); + } else if (type == "cvt_color") { + const int flag = opSpec.value("flag", 0); + if (inputs.empty() || outputs.empty()) { + throw std::runtime_error("cvt_color requires input and output tensors"); + } + pipeline->cvtColor(flag, requireByIndex(inputs, 0, "cvt_color input"), + requireByIndex(outputs, 0, "cvt_color output")); + } else if (type == "type_convert") { + if (inputs.empty() || outputs.empty()) { + throw std::runtime_error("type_convert requires input and output tensors"); + } + pipeline->typeConvert(requireByIndex(inputs, 0, "type_convert input"), + requireByIndex(outputs, 0, "type_convert output")); + } else if (type == "arithmetic") { + const std::string expression = opSpec.value("expression", ""); + std::vector> operands; + operands.reserve(inputs.size()); + for (size_t idx = 0; idx < inputs.size(); ++idx) { + operands.push_back(requireByIndex(inputs, idx, "arithmetic input")); + } + if (outputs.empty()) { + throw std::runtime_error("arithmetic requires output tensor"); + } + pipeline->arithmetic(expression, operands, requireByIndex(outputs, 0, "arithmetic output")); + } else if (type == "run_algorithm") { + // Parse mapped inputs/outputs + auto mappedInputs = ParseMappedTensorList(opSpec.value("inputs", Json::array())); + auto mappedOutputs = ParseMappedTensorList(opSpec.value("outputs", Json::array())); + if (mappedInputs.empty() || mappedOutputs.empty()) { + throw std::runtime_error("run_algorithm inputs/outputs malformed"); + } + + std::unordered_map> inputMap; + for (const auto& [alias, tensorName] : mappedInputs) { + inputMap.emplace(alias, requireTensor(tensorName)); + } + std::unordered_map> outputMap; + for (const auto& [alias, tensorName] : mappedOutputs) { + outputMap.emplace(alias, requireTensor(tensorName)); + } + + const std::string modelName = opSpec.value("model_name", ""); + if (modelName.empty()) { + throw std::runtime_error("run_algorithm requires 'model_name'"); + } + + // Load model from asset (Android) or file (if provided) + std::vector modelBuffer; + if (auto assetIt = opSpec.find("model_asset"); assetIt != opSpec.end() && assetIt->is_string()) { + const std::string assetName = assetIt->get(); +#ifdef XR_USE_PLATFORM_ANDROID + if (g_assetManager == nullptr) { + throw std::runtime_error("run_algorithm: AssetManager not available for 'model_asset'"); + } + AAsset* asset = AAssetManager_open(g_assetManager, assetName.c_str(), AASSET_MODE_BUFFER); + if (asset == nullptr) { + throw std::runtime_error(Fmt("run_algorithm: unable to open asset '%s'", assetName.c_str())); + } + const off_t length = AAsset_getLength(asset); + modelBuffer.resize(static_cast(length)); + const int64_t read = AAsset_read(asset, modelBuffer.data(), length); + AAsset_close(asset); + if (read != length) { + modelBuffer.clear(); + throw std::runtime_error(Fmt("run_algorithm: read %ld of %ld bytes from asset '%s'", + static_cast(read), static_cast(length), assetName.c_str())); + } +#else + throw std::runtime_error("run_algorithm: 'model_asset' only supported on Android builds"); +#endif + } else if (auto fileIt = opSpec.find("model_file"); fileIt != opSpec.end() && fileIt->is_string()) { + const std::string filePath = fileIt->get(); + std::ifstream ifs(filePath, std::ios::binary); + if (!ifs) { + throw std::runtime_error(Fmt("run_algorithm: cannot open file '%s'", filePath.c_str())); + } + modelBuffer.assign(std::istreambuf_iterator(ifs), std::istreambuf_iterator()); + if (modelBuffer.empty()) { + throw std::runtime_error(Fmt("run_algorithm: file '%s' is empty or read failed", filePath.c_str())); + } + } else { + throw std::runtime_error("run_algorithm requires 'model_asset' (Android) or 'model_file'"); + } + + std::unordered_map operandAliasing; + std::unordered_map resultAliasing; + if (auto inAlias = opSpec.find("input_aliasing"); inAlias != opSpec.end() && inAlias->is_object()) { + for (auto it = inAlias->begin(); it != inAlias->end(); ++it) { + if (it.value().is_string()) { + operandAliasing.emplace(it.key(), it.value().get()); + } + } + } + if (auto outAlias = opSpec.find("output_aliasing"); outAlias != opSpec.end() && outAlias->is_object()) { + for (auto it = outAlias->begin(); it != outAlias->end(); ++it) { + if (it.value().is_string()) { + resultAliasing.emplace(it.key(), it.value().get()); + } + } + } + + pipeline->runAlgorithm(modelBuffer.data(), modelBuffer.size(), inputMap, operandAliasing, outputMap, + resultAliasing, modelName); + } else { + bool handled = false; + if (options.customOperatorHandler) { + handled = options.customOperatorHandler(opSpec, requireTensor, pipeline, outError); + } + if (!handled) { + throw std::runtime_error(Fmt("unsupported operator type '%s'", + rawType.empty() ? type.c_str() : rawType.c_str())); + } + } + } + } catch (const std::exception& e) { + if (outError.empty()) { + outError = e.what(); + } + return false; + } + + outResult.pipeline = std::move(pipeline); + return true; +} + +} // namespace SecureMR diff --git a/base/securemr_utils/serialization.h b/base/securemr_utils/serialization.h new file mode 100644 index 0000000..176b4c7 --- /dev/null +++ b/base/securemr_utils/serialization.h @@ -0,0 +1,76 @@ +// Copyright (2025) Bytedance Ltd. and/or its affiliates +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SECUREMR_UTILS_SERIALIZATION_H_ +#define SECUREMR_UTILS_SERIALIZATION_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace SecureMR { +struct TensorAttribute; +class FrameworkSession; +class Pipeline; +class PipelineTensor; + +using Json = nlohmann::json; + +Json TensorAttributeToJson(const TensorAttribute& attr); +Json TensorAttributeVariantToJson(const std::variant& attr); +Json TensorListToJson(const std::vector& tensors); +Json MappedTensorListToJson(const std::vector>& mapping); +bool WriteJsonToFile(const std::filesystem::path& filePath, const Json& spec); + +// Set top-level pipeline inputs/outputs in a JSON spec +void SetInputs(Json& spec, const std::vector& inputs); +void SetOutputs(Json& spec, const std::vector& outputs); + +bool JsonToTensorAttribute(const Json& j, TensorAttribute& out); +std::vector ParseTensorList(const Json& arr); +std::vector> ParseMappedTensorList(const Json& arr); +bool JsonToFloatArray(const Json& arr, std::array& dest); +Json LoadJsonFromFile(const std::filesystem::path& filePath); +std::string FormatOperatorType(const std::string& typeName); + +struct PipelineDeserializationResult { + std::shared_ptr pipeline; + std::unordered_map> tensorMap; +}; + +struct PipelineDeserializationOptions { + std::function(const std::string&)>& requireTensor, + const std::shared_ptr& pipeline, + std::string& error)> + customOperatorHandler; +}; + +bool DeserializePipelineFromJson(const Json& spec, + const std::shared_ptr& session, + PipelineDeserializationResult& outResult, + std::string& outError, + const PipelineDeserializationOptions& options = {}); + +} // namespace SecureMR + +#endif // SECUREMR_UTILS_SERIALIZATION_H_ diff --git a/base/securemr_utils/session.h b/base/securemr_utils/session.h index 67ed584..d61f227 100644 --- a/base/securemr_utils/session.h +++ b/base/securemr_utils/session.h @@ -16,7 +16,6 @@ #define SESSION_H #include #include - #include "openxr/openxr.h" namespace SecureMR { diff --git a/base/securemr_utils/tensor.cpp b/base/securemr_utils/tensor.cpp index 7b40bf2..c902345 100644 --- a/base/securemr_utils/tensor.cpp +++ b/base/securemr_utils/tensor.cpp @@ -15,6 +15,7 @@ // #include "rendercommand.h" #include "pipeline.h" #include "tensor.h" +#include "oxr_utils/logger.h" #include @@ -185,6 +186,7 @@ PipelineTensor::PipelineTensor(std::shared_ptr pipeline, TensorAttribu XrSecureMrTensorFormatPICO format = { .dataType = attribute.dataType, .channel = attribute.channels, .tensorType = attribute.usage}; + XrSecureMrTensorCreateInfoShapePICO createInfo = { .type = XR_TYPE_SECURE_MR_TENSOR_CREATE_INFO_SHAPE_PICO, .placeHolder = isPlaceholder, @@ -229,6 +231,8 @@ std::shared_ptr PipelineTensor::PipelinePlaceholderLike(const st return PipelineGLTFPlaceholder(root); } auto& tensorAttr = std::get(attr); + + Log::Write(Log::Level::Verbose, Fmt("tensorAttr.dataType: %d", tensorAttr.dataType)); return std::make_shared(root, tensorAttr, true); } diff --git a/base/securemr_utils/tensor.h b/base/securemr_utils/tensor.h index b1ecfbf..e8cd218 100644 --- a/base/securemr_utils/tensor.h +++ b/base/securemr_utils/tensor.h @@ -20,7 +20,6 @@ #include #include "adapter.hpp" -#include "pipeline.h" #include "check.h" namespace SecureMR { diff --git a/base/securemr_utils/utils.cpp b/base/securemr_utils/utils.cpp new file mode 100644 index 0000000..029bdba --- /dev/null +++ b/base/securemr_utils/utils.cpp @@ -0,0 +1,254 @@ +// Copyright (2025) Bytedance Ltd. and/or its affiliates +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "securemr_utils/utils.h" + +#include +#include +#include + +#include "common.h" +#include "logger.h" + +namespace SecureMR { +namespace { + +std::string JoinInts(const std::vector& values) { + std::string out; + for (size_t i = 0; i < values.size(); ++i) { + out.append(std::to_string(values[i])); + if (i + 1 < values.size()) { + out.push_back('x'); + } + } + return out; +} + +XrSecureMrTensorDataTypePICO MapQnnType(const std::string& type, bool& warnedFloat16) { + if (type == "QNN_DATATYPE_FLOAT_32") { + return XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO; + } + if (type == "QNN_DATATYPE_FLOAT_16") { + warnedFloat16 = true; + return XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO; + } + if (type == "QNN_DATATYPE_INT_32") { + return XR_SECURE_MR_TENSOR_DATA_TYPE_INT32_PICO; + } + if (type == "QNN_DATATYPE_INT_16") { + return XR_SECURE_MR_TENSOR_DATA_TYPE_INT16_PICO; + } + if (type == "QNN_DATATYPE_INT_8") { + return XR_SECURE_MR_TENSOR_DATA_TYPE_INT8_PICO; + } + if (type == "QNN_DATATYPE_UINT_16") { + return XR_SECURE_MR_TENSOR_DATA_TYPE_UINT16_PICO; + } + if (type == "QNN_DATATYPE_UINT_8") { + return XR_SECURE_MR_TENSOR_DATA_TYPE_UINT8_PICO; + } + return XR_SECURE_MR_TENSOR_DATA_TYPE_MAX_ENUM_PICO; +} + +std::optional BuildBinding(const Json& info) { + if (!info.is_object()) { + return std::nullopt; + } + + TensorBinding binding; + if (auto nameIt = info.find("name"); nameIt != info.end() && nameIt->is_string()) { + binding.name = nameIt->get(); + } + if (auto typeIt = info.find("dataType"); typeIt != info.end() && typeIt->is_string()) { + binding.qnnType = typeIt->get(); + } + if (auto dimsIt = info.find("dimensions"); dimsIt != info.end() && dimsIt->is_array()) { + for (const auto& dim : *dimsIt) { + if (dim.is_number_integer()) { + binding.qnnDims.push_back(dim.get()); + } + } + } + if (binding.name.empty() || binding.qnnType.empty() || binding.qnnDims.empty()) { + return std::nullopt; + } + + bool warnedFloat16 = false; + binding.attr.dataType = MapQnnType(binding.qnnType, warnedFloat16); + if (binding.attr.dataType == XR_SECURE_MR_TENSOR_DATA_TYPE_MAX_ENUM_PICO) { + Log::Write(Log::Level::Error, + Fmt("Tensor %s has unsupported data type %s", binding.name.c_str(), binding.qnnType.c_str())); + return std::nullopt; + } + + std::vector dims = binding.qnnDims; + if (dims.size() > 1 && dims.front() == 1) { + dims.erase(dims.begin()); + } + + int channels = 1; + if (dims.size() >= 2) { + channels = dims.back(); + dims.pop_back(); + } else if (!dims.empty()) { + channels = 1; + } + if (dims.empty()) { + dims.push_back(1); + } + + if (channels <= 0 || channels > std::numeric_limits::max()) { + Log::Write(Log::Level::Error, + Fmt("Tensor %s has unsupported channel count %d", binding.name.c_str(), channels)); + return std::nullopt; + } + + if (channels > 4) { + dims.push_back(channels); + channels = 1; + } + + binding.attr.dimensions = dims; + binding.attr.channels = static_cast(channels); + + if (binding.attr.dimensions.size() <= 1 && binding.attr.channels == 1) { + binding.attr.usage = XR_SECURE_MR_TENSOR_TYPE_SCALAR_PICO; + } else { + binding.attr.usage = XR_SECURE_MR_TENSOR_TYPE_MAT_PICO; + } + + if (binding.attr.usage == XR_SECURE_MR_TENSOR_TYPE_MAT_PICO && binding.attr.dimensions.size() < 2) { + binding.attr.dimensions.insert(binding.attr.dimensions.begin(), 1); + Log::Write(Log::Level::Warning, + Fmt("Tensor %s mapped to MAT but had 1 dimension; promoting shape to 1x%d to satisfy MAT requirements", + binding.name.c_str(), binding.attr.dimensions.back())); + } + + if (warnedFloat16) { + Log::Write(Log::Level::Warning, + Fmt("Tensor %s uses QNN float16; mapping to FLOAT32 for SecureMR tensor", binding.name.c_str())); + } + + Log::Write(Log::Level::Info, + Fmt("Tensor %s | qnn dims=%s type=%s -> attr dims=%s channels=%d type=%d", + binding.name.c_str(), JoinInts(binding.qnnDims).c_str(), binding.qnnType.c_str(), + JoinInts(binding.attr.dimensions).c_str(), binding.attr.channels, binding.attr.dataType)); + + return binding; +} + +bool ParseBindings(const Json& graphInfo, const char* key, std::vector& outBindings) { + auto tensorsIt = graphInfo.find(key); + if (tensorsIt == graphInfo.end() || !tensorsIt->is_array()) { + Log::Write(Log::Level::Error, Fmt("Model JSON missing %s array", key)); + return false; + } + + for (const auto& entry : *tensorsIt) { + if (!entry.is_object()) { + continue; + } + auto infoIt = entry.find("info"); + if (infoIt == entry.end()) { + continue; + } + auto binding = BuildBinding(*infoIt); + if (binding.has_value()) { + outBindings.emplace_back(std::move(*binding)); + } + } + if (outBindings.empty()) { + Log::Write(Log::Level::Error, Fmt("No valid %s entries found", key)); + return false; + } + return true; +} + +} // namespace + +size_t SecureMrUtils::BytesPerElement(XrSecureMrTensorDataTypePICO dataType) { + switch (dataType) { + case XR_SECURE_MR_TENSOR_DATA_TYPE_UINT8_PICO: + case XR_SECURE_MR_TENSOR_DATA_TYPE_INT8_PICO: + case XR_SECURE_MR_TENSOR_DATA_TYPE_DYNAMIC_TEXTURE_UINT8_PICO: + return 1; + case XR_SECURE_MR_TENSOR_DATA_TYPE_UINT16_PICO: + case XR_SECURE_MR_TENSOR_DATA_TYPE_INT16_PICO: + return 2; + case XR_SECURE_MR_TENSOR_DATA_TYPE_INT32_PICO: + case XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO: + case XR_SECURE_MR_TENSOR_DATA_TYPE_DYNAMIC_TEXTURE_FLOAT32_PICO: + return 4; + case XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT64_PICO: + return 8; + default: + return 0; + } +} + +size_t SecureMrUtils::ElementCount(const TensorAttribute& attr) { + size_t count = 1; + for (int dim : attr.dimensions) { + count *= static_cast(dim); + } + count *= static_cast(attr.channels); + return count; +} + +std::optional SecureMrUtils::LoadModelJson(const std::filesystem::path& jsonPath) { + try { + return LoadJsonFromFile(jsonPath); + } catch (const std::exception& e) { + Log::Write(Log::Level::Error, Fmt("Failed to read %s: %s", jsonPath.string().c_str(), e.what())); + return std::nullopt; + } +} + +bool SecureMrUtils::PrepareBindings(const Json& jsonSpec, + std::vector& inputBindings, + std::vector& outputBindings, + std::string& modelName) { + inputBindings.clear(); + outputBindings.clear(); + + auto infoIt = jsonSpec.find("info"); + if (infoIt == jsonSpec.end() || !infoIt->is_object()) { + Log::Write(Log::Level::Error, "ModelInspect: model JSON missing top-level info"); + return false; + } + auto graphsIt = infoIt->find("graphs"); + if (graphsIt == infoIt->end() || !graphsIt->is_array() || graphsIt->empty()) { + Log::Write(Log::Level::Error, "ModelInspect: model JSON missing graphs array"); + return false; + } + const auto& graph = (*graphsIt)[0]; + auto graphInfoIt = graph.find("info"); + if (graphInfoIt == graph.end() || !graphInfoIt->is_object()) { + Log::Write(Log::Level::Error, "ModelInspect: model JSON graph missing info"); + return false; + } + if (auto nameIt = graphInfoIt->find("graphName"); nameIt != graphInfoIt->end() && nameIt->is_string()) { + modelName = nameIt->get(); + } + + if (!ParseBindings(*graphInfoIt, "graphInputs", inputBindings)) { + return false; + } + if (!ParseBindings(*graphInfoIt, "graphOutputs", outputBindings)) { + return false; + } + return true; +} + +} // namespace SecureMR diff --git a/base/securemr_utils/utils.h b/base/securemr_utils/utils.h new file mode 100644 index 0000000..25a2fad --- /dev/null +++ b/base/securemr_utils/utils.h @@ -0,0 +1,50 @@ +// Copyright (2025) Bytedance Ltd. and/or its affiliates +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SECUREMR_UTILS_UTILS_H_ +#define SECUREMR_UTILS_UTILS_H_ + +#include +#include +#include +#include +#include + +#include "serialization.h" +#include "tensor.h" + +namespace SecureMR { + +struct TensorBinding { + std::string name; + std::vector qnnDims; + std::string qnnType; + TensorAttribute attr{}; + std::shared_ptr global; +}; + +class SecureMrUtils { + public: + static size_t BytesPerElement(XrSecureMrTensorDataTypePICO dataType); + static size_t ElementCount(const TensorAttribute& attr); + static std::optional LoadModelJson(const std::filesystem::path& jsonPath); + static bool PrepareBindings(const Json& jsonSpec, + std::vector& inputBindings, + std::vector& outputBindings, + std::string& modelName); +}; + +} // namespace SecureMR + +#endif // SECUREMR_UTILS_UTILS_H_ diff --git a/cmake/kociemba.cmake b/cmake/kociemba.cmake new file mode 100644 index 0000000..765b2a1 --- /dev/null +++ b/cmake/kociemba.cmake @@ -0,0 +1,42 @@ +# Fetch and build Kociemba C solver via CMake FetchContent + +include(FetchContent) + +if(NOT DEFINED KOCIEEMBA_GIT_REPOSITORY) + set(KOCIEEMBA_GIT_REPOSITORY https://github.com/muodov/kociemba.git) +endif() + +# You can override this at configure time: -DKOCIEMBA_GIT_TAG= +if(NOT DEFINED KOCIEMBA_GIT_TAG) + # A known stable commit; update as needed + set(KOCIEMBA_GIT_TAG master) +endif() + +set(FETCHCONTENT_QUIET OFF) +FetchContent_Declare( + kociemba_src + GIT_REPOSITORY ${KOCIEEMBA_GIT_REPOSITORY} + GIT_TAG ${KOCIEMBA_GIT_TAG} +) + +FetchContent_GetProperties(kociemba_src) +if(NOT kociemba_src_POPULATED) + message(STATUS "Fetching Kociemba from ${KOCIEEMBA_GIT_REPOSITORY} @ ${KOCIEMBA_GIT_TAG}") + FetchContent_Populate(kociemba_src) +endif() + +set(_KOCIEMBA_C_DIR ${kociemba_src_SOURCE_DIR}/kociemba/ckociemba) +if(NOT EXISTS ${_KOCIEMBA_C_DIR}/search.c) + message(FATAL_ERROR "Kociemba ckociemba sources not found under ${_KOCIEMBA_C_DIR}") +endif() + +add_library(kociemba_c STATIC + ${_KOCIEMBA_C_DIR}/coordcube.c + ${_KOCIEMBA_C_DIR}/cubiecube.c + ${_KOCIEMBA_C_DIR}/facecube.c + ${_KOCIEMBA_C_DIR}/search.c + ${_KOCIEMBA_C_DIR}/prunetable_helpers.c +) + +target_include_directories(kociemba_c PUBLIC ${_KOCIEMBA_C_DIR}/include) + diff --git a/cmake/opencv.cmake b/cmake/opencv.cmake new file mode 100644 index 0000000..b43bf76 --- /dev/null +++ b/cmake/opencv.cmake @@ -0,0 +1,53 @@ +# Lightweight OpenCV Android SDK integration for CMake + +if(NOT ANDROID) + message(FATAL_ERROR "OpenCV Android SDK integration only supports Android builds.") +endif() + +if(NOT DEFINED OpencvVersion) + set(OpencvVersion 4.10.0) +endif() + +include(FetchContent) +set(_opencv_url https://github.com/opencv/opencv/releases/download/${OpencvVersion}/opencv-${OpencvVersion}-android-sdk.zip) + +# Download once into the build tree +set(_opencv_root ${CMAKE_BINARY_DIR}/_deps/opencv) +set(_opencv_zip ${_opencv_root}/opencv-${OpencvVersion}-android-sdk.zip) +set(_opencv_sdk_dir "") + +file(MAKE_DIRECTORY ${_opencv_root}) + +if(NOT EXISTS ${_opencv_root}/OpenCV-android-sdk AND NOT EXISTS ${_opencv_root}/opencv-${OpencvVersion}-android-sdk) + message(STATUS "Downloading OpenCV Android SDK ${OpencvVersion} ...") + file(DOWNLOAD ${_opencv_url} ${_opencv_zip} SHOW_PROGRESS STATUS _dl_status TLS_VERIFY ON) + list(GET _dl_status 0 _dl_status_code) + if(NOT _dl_status_code EQUAL 0) + message(FATAL_ERROR "Failed to download OpenCV SDK: ${_dl_status}") + endif() + execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf ${_opencv_zip} WORKING_DIRECTORY ${_opencv_root} + RESULT_VARIABLE _unzip_rv) + if(NOT _unzip_rv EQUAL 0) + message(FATAL_ERROR "Failed to extract OpenCV SDK archive") + endif() +endif() + +# Detect extracted SDK directory name +foreach(_cand OpenCV-android-sdk opencv-${OpencvVersion}-android-sdk) + if(EXISTS ${_opencv_root}/${_cand}/sdk/native) + set(_opencv_sdk_dir ${_opencv_root}/${_cand}) + endif() +endforeach() + +if(NOT _opencv_sdk_dir) + message(FATAL_ERROR "OpenCV SDK directory not found under ${_opencv_root}") +endif() + +set(OpenCV_DIR ${_opencv_sdk_dir}/sdk/native) +message(STATUS "OpenCV_DIR=${OpenCV_DIR}") + +include_directories(${OpenCV_DIR}/jni/include) + +add_library(lib_opencv SHARED IMPORTED GLOBAL) +set_target_properties(lib_opencv PROPERTIES + IMPORTED_LOCATION ${OpenCV_DIR}/libs/arm64-v8a/libopencv_java4.so) diff --git a/docs/mnistwild.gif b/docs/mnistwild.gif new file mode 100644 index 0000000..0d5f368 Binary files /dev/null and b/docs/mnistwild.gif differ diff --git a/docs/rubics_cube.gif b/docs/rubics_cube.gif new file mode 100644 index 0000000..9ad4f92 Binary files /dev/null and b/docs/rubics_cube.gif differ diff --git a/external/openxr/include/openxr/openxr.h b/external/openxr/include/openxr/openxr.h index 36eae0e..faf7d12 100644 --- a/external/openxr/include/openxr/openxr.h +++ b/external/openxr/include/openxr/openxr.h @@ -26,7 +26,7 @@ extern "C" { ((((major) & 0xffffULL) << 48) | (((minor) & 0xffffULL) << 32) | ((patch) & 0xffffffffULL)) // OpenXR current version number. -#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(1, 1, 49) +#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(1, 1, 53) // OpenXR 1.0 version number #define XR_API_VERSION_1_0 XR_MAKE_VERSION(1, 0, XR_VERSION_PATCH(XR_CURRENT_API_VERSION)) @@ -255,26 +255,40 @@ typedef enum XrResult { XR_ERROR_SPACE_NETWORK_TIMEOUT_FB = -1000169002, XR_ERROR_SPACE_NETWORK_REQUEST_FAILED_FB = -1000169003, XR_ERROR_SPACE_CLOUD_STORAGE_DISABLED_FB = -1000169004, + XR_ERROR_SPACE_INSUFFICIENT_RESOURCES_META = -1000259000, + XR_ERROR_SPACE_STORAGE_AT_CAPACITY_META = -1000259001, + XR_ERROR_SPACE_INSUFFICIENT_VIEW_META = -1000259002, + XR_ERROR_SPACE_PERMISSION_INSUFFICIENT_META = -1000259003, + XR_ERROR_SPACE_RATE_LIMITED_META = -1000259004, + XR_ERROR_SPACE_TOO_DARK_META = -1000259005, + XR_ERROR_SPACE_TOO_BRIGHT_META = -1000259006, XR_ERROR_PASSTHROUGH_COLOR_LUT_BUFFER_SIZE_MISMATCH_META = -1000266000, XR_ENVIRONMENT_DEPTH_NOT_AVAILABLE_META = 1000291000, - XR_ERROR_RENDER_MODEL_ID_INVALID_EXT = -1000300000, - XR_ERROR_RENDER_MODEL_ASSET_UNAVAILABLE_EXT = -1000300001, - XR_ERROR_RENDER_MODEL_GLTF_EXTENSION_REQUIRED_EXT = -1000300002, - XR_ERROR_NOT_INTERACTION_RENDER_MODEL_EXT = -1000301000, - XR_ERROR_HINT_ALREADY_SET_QCOM = -1000306000, + XR_ERROR_RENDER_MODEL_ID_INVALID_EXT = -1000300000, + XR_ERROR_RENDER_MODEL_ASSET_UNAVAILABLE_EXT = -1000300001, + XR_ERROR_RENDER_MODEL_GLTF_EXTENSION_REQUIRED_EXT = -1000300002, + XR_ERROR_NOT_INTERACTION_RENDER_MODEL_EXT = -1000301000, + XR_ERROR_HINT_ALREADY_SET_QCOM = -1000306000, XR_ERROR_NOT_AN_ANCHOR_HTC = -1000319000, - XR_ERROR_SPATIAL_ENTITY_ID_INVALID_BD = -1000389000, - XR_ERROR_SPATIAL_SENSING_SERVICE_UNAVAILABLE_BD = -1000389001, - XR_ERROR_ANCHOR_NOT_SUPPORTED_FOR_ENTITY_BD = -1000389002, - XR_ERROR_SPATIAL_ANCHOR_NOT_FOUND_BD = -1000390000, - XR_ERROR_SPATIAL_ANCHOR_SHARING_NETWORK_TIMEOUT_BD = -1000391000, - XR_ERROR_SPATIAL_ANCHOR_SHARING_AUTHENTICATION_FAILURE_BD = -1000391001, - XR_ERROR_SPATIAL_ANCHOR_SHARING_NETWORK_FAILURE_BD = -1000391002, - XR_ERROR_SPATIAL_ANCHOR_SHARING_LOCALIZATION_FAIL_BD = -1000391003, - XR_ERROR_SPATIAL_ANCHOR_SHARING_MAP_INSUFFICIENT_BD = -1000391004, - XR_ERROR_SCENE_CAPTURE_FAILURE_BD = -1000392000, - XR_ERROR_SPACE_NOT_LOCATABLE_EXT = -1000429000, + XR_ERROR_SPATIAL_ENTITY_ID_INVALID_BD = -1000389000, + XR_ERROR_SPATIAL_SENSING_SERVICE_UNAVAILABLE_BD = -1000389001, + XR_ERROR_ANCHOR_NOT_SUPPORTED_FOR_ENTITY_BD = -1000389002, + XR_ERROR_SPATIAL_ANCHOR_NOT_FOUND_BD = -1000390000, + XR_ERROR_SPATIAL_ANCHOR_SHARING_NETWORK_TIMEOUT_BD = -1000391000, + XR_ERROR_SPATIAL_ANCHOR_SHARING_AUTHENTICATION_FAILURE_BD = -1000391001, + XR_ERROR_SPATIAL_ANCHOR_SHARING_NETWORK_FAILURE_BD = -1000391002, + XR_ERROR_SPATIAL_ANCHOR_SHARING_LOCALIZATION_FAIL_BD = -1000391003, + XR_ERROR_SPATIAL_ANCHOR_SHARING_MAP_INSUFFICIENT_BD = -1000391004, + XR_ERROR_SCENE_CAPTURE_FAILURE_BD = -1000392000, + XR_ERROR_SPACE_NOT_LOCATABLE_EXT = -1000429000, XR_ERROR_PLANE_DETECTION_PERMISSION_DENIED_EXT = -1000429001, + XR_ERROR_MISMATCHING_TRACKABLE_TYPE_ANDROID = -1000455000, + XR_ERROR_TRACKABLE_TYPE_NOT_SUPPORTED_ANDROID = -1000455001, + XR_ERROR_ANCHOR_ID_NOT_FOUND_ANDROID = -1000457000, + XR_ERROR_ANCHOR_ALREADY_PERSISTED_ANDROID = -1000457001, + XR_ERROR_ANCHOR_NOT_TRACKING_ANDROID = -1000457002, + XR_ERROR_PERSISTED_DATA_NOT_READY_ANDROID = -1000457003, + XR_ERROR_SERVICE_NOT_READY_ANDROID = -1000458000, XR_ERROR_FUTURE_PENDING_EXT = -1000469001, XR_ERROR_FUTURE_INVALID_EXT = -1000469002, XR_ERROR_SYSTEM_NOTIFICATION_PERMISSION_DENIED_ML = -1000473000, @@ -287,13 +301,14 @@ typedef enum XrResult { XR_COLOCATION_DISCOVERY_ALREADY_ADVERTISING_META = 1000571003, XR_COLOCATION_DISCOVERY_ALREADY_DISCOVERING_META = 1000571004, XR_ERROR_SPACE_GROUP_NOT_FOUND_META = -1000572002, - XR_ERROR_SPATIAL_CAPABILITY_UNSUPPORTED_EXT = -1000740001, - XR_ERROR_SPATIAL_ENTITY_ID_INVALID_EXT = -1000740002, - XR_ERROR_SPATIAL_BUFFER_ID_INVALID_EXT = -1000740003, - XR_ERROR_SPATIAL_COMPONENT_UNSUPPORTED_FOR_CAPABILITY_EXT = -1000740004, - XR_ERROR_SPATIAL_CAPABILITY_CONFIGURATION_INVALID_EXT = -1000740005, - XR_ERROR_SPATIAL_COMPONENT_NOT_ENABLED_EXT = -1000740006, - XR_ERROR_VIRTUAL_BOUNDARY_TRIGGER_NODE_TYPE_UNSUPPORTED_PICO = -1010001000, + XR_ERROR_ANCHOR_NOT_OWNED_BY_CALLER_ANDROID = -1000701000, + XR_ERROR_SPATIAL_CAPABILITY_UNSUPPORTED_EXT = -1000740001, + XR_ERROR_SPATIAL_ENTITY_ID_INVALID_EXT = -1000740002, + XR_ERROR_SPATIAL_BUFFER_ID_INVALID_EXT = -1000740003, + XR_ERROR_SPATIAL_COMPONENT_UNSUPPORTED_FOR_CAPABILITY_EXT = -1000740004, + XR_ERROR_SPATIAL_CAPABILITY_CONFIGURATION_INVALID_EXT = -1000740005, + XR_ERROR_SPATIAL_COMPONENT_NOT_ENABLED_EXT = -1000740006, + XR_ERROR_VIRTUAL_BOUNDARY_TRIGGER_NODE_TYPE_UNSUPPORTED_PICO = -1010001000, XR_ERROR_MOTION_TRACKER_TYPE_MISMATCH_PICO = -1010002000, XR_ERROR_MOTION_TRACKER_COUNT_EXCEEDED_PICO = -1010002001, XR_ERROR_MOTION_TRACKING_MODE_MISMATCH_PICO = -1010002002, @@ -304,15 +319,24 @@ typedef enum XrResult { XR_ERROR_SECURE_MR_USAGE_BEFORE_INIT_PICO = -1010007003, XR_ERROR_SECURE_MR_OTHER_INTERNAL_ERROR_PICO = -1010007004, XR_ERROR_EXPAND_TRACKER_ID_INVALID_PICO = -1010008000, - XR_ERROR_SPATIAL_SENSING_SERVICE_UNAVAILABLE_PICO = -1200389027, - XR_ERROR_SPATIAL_ANCHOR_SHARING_NETWORK_TIMEOUT_PICO = -1200391101, - XR_ERROR_SPATIAL_ANCHOR_SHARING_AUTHENTICATION_FAILURE_PICO = -1200391102, - XR_ERROR_SPATIAL_ANCHOR_SHARING_NETWORK_FAILURE_PICO = -1200391103, - XR_ERROR_SPATIAL_ANCHOR_SHARING_LOCALIZATION_FAIL_PICO = -1200391104, - XR_ERROR_SPATIAL_ANCHOR_SHARING_MAP_INSUFFICIENT_PICO = -1200391105, - XR_ERROR_SPATIAL_PERSISTENCE_SCOPE_UNSUPPORTED_EXT = -1000763001, - XR_ERROR_SPATIAL_PERSISTENCE_SCOPE_INCOMPATIBLE_EXT = -1000781001, - XR_ERROR_EXTENSION_DEPENDENCY_NOT_ENABLED_KHR = XR_ERROR_EXTENSION_DEPENDENCY_NOT_ENABLED, + XR_ERROR_CAMERA_UNAVAILABLE_PICO = -1010033000, + XR_ERROR_CAMERA_OCCUPIED_PICO = -1010033001, + XR_ERROR_CAMERA_CAPTURE_SESSION_CAPTURING_PICO = -1010033002, + XR_ERROR_CAMERA_CAPTURE_SESSION_NOT_CAPTURING_PICO = -1010033003, + XR_ERROR_CAMERA_ID_INVALID_PICO = -1010033004, + XR_ERROR_CAMERA_IMAGE_ID_INVALID_PICO = -1010033005, + XR_ERROR_CAMERA_PROPERTY_TYPE_INVALID_PICO = -1010033006, + XR_ERROR_CAMERA_CAPABILITY_TYPE_INVALID_PICO = -1010033007, + XR_CAMERA_IMAGE_NO_UPDATE_PICO = 1010033000, + XR_ERROR_SPATIAL_SENSING_SERVICE_UNAVAILABLE_PICO = -1200389027, + XR_ERROR_SPATIAL_ANCHOR_SHARING_NETWORK_TIMEOUT_PICO = -1200391101, + XR_ERROR_SPATIAL_ANCHOR_SHARING_AUTHENTICATION_FAILURE_PICO = -1200391102, + XR_ERROR_SPATIAL_ANCHOR_SHARING_NETWORK_FAILURE_PICO = -1200391103, + XR_ERROR_SPATIAL_ANCHOR_SHARING_LOCALIZATION_FAIL_PICO = -1200391104, + XR_ERROR_SPATIAL_ANCHOR_SHARING_MAP_INSUFFICIENT_PICO = -1200391105, + XR_ERROR_SPATIAL_PERSISTENCE_SCOPE_UNSUPPORTED_EXT = -1000763001, + XR_ERROR_SPATIAL_PERSISTENCE_SCOPE_INCOMPATIBLE_EXT = -1000781001, + XR_ERROR_EXTENSION_DEPENDENCY_NOT_ENABLED_KHR = XR_ERROR_EXTENSION_DEPENDENCY_NOT_ENABLED, XR_ERROR_PERMISSION_INSUFFICIENT_KHR = XR_ERROR_PERMISSION_INSUFFICIENT, XR_RESULT_MAX_ENUM = 0x7FFFFFFF } XrResult; @@ -644,8 +668,21 @@ typedef enum XrStructureType { XR_TYPE_EVENT_DATA_SPACE_LIST_SAVE_COMPLETE_FB = 1000238001, XR_TYPE_SPACE_USER_CREATE_INFO_FB = 1000241001, XR_TYPE_SYSTEM_HEADSET_ID_PROPERTIES_META = 1000245000, + XR_TYPE_SYSTEM_SPACE_DISCOVERY_PROPERTIES_META = 1000247000, + XR_TYPE_SPACE_DISCOVERY_INFO_META = 1000247001, + XR_TYPE_SPACE_FILTER_UUID_META = 1000247003, + XR_TYPE_SPACE_FILTER_COMPONENT_META = 1000247004, + XR_TYPE_SPACE_DISCOVERY_RESULT_META = 1000247005, + XR_TYPE_SPACE_DISCOVERY_RESULTS_META = 1000247006, + XR_TYPE_EVENT_DATA_SPACE_DISCOVERY_RESULTS_AVAILABLE_META = 1000247007, + XR_TYPE_EVENT_DATA_SPACE_DISCOVERY_COMPLETE_META = 1000247008, XR_TYPE_RECOMMENDED_LAYER_RESOLUTION_META = 1000254000, XR_TYPE_RECOMMENDED_LAYER_RESOLUTION_GET_INFO_META = 1000254001, + XR_TYPE_SYSTEM_SPACE_PERSISTENCE_PROPERTIES_META = 1000259000, + XR_TYPE_SPACES_SAVE_INFO_META = 1000259001, + XR_TYPE_EVENT_DATA_SPACES_SAVE_RESULT_META = 1000259002, + XR_TYPE_SPACES_ERASE_INFO_META = 1000259003, + XR_TYPE_EVENT_DATA_SPACES_ERASE_RESULT_META = 1000259004, XR_TYPE_SYSTEM_PASSTHROUGH_COLOR_LUT_PROPERTIES_META = 1000266000, XR_TYPE_PASSTHROUGH_COLOR_LUT_CREATE_INFO_META = 1000266001, XR_TYPE_PASSTHROUGH_COLOR_LUT_UPDATE_INFO_META = 1000266002, @@ -653,8 +690,11 @@ typedef enum XrStructureType { XR_TYPE_PASSTHROUGH_COLOR_MAP_INTERPOLATED_LUT_META = 1000266101, XR_TYPE_SPACE_TRIANGLE_MESH_GET_INFO_META = 1000269001, XR_TYPE_SPACE_TRIANGLE_MESH_META = 1000269002, - XR_TYPE_SYSTEM_PROPERTIES_BODY_TRACKING_FULL_BODY_META = 1000274000, - XR_TYPE_EVENT_DATA_PASSTHROUGH_LAYER_RESUMED_META = 1000282000, + XR_TYPE_SYSTEM_PROPERTIES_BODY_TRACKING_FULL_BODY_META = 1000274000, + XR_TYPE_EVENT_DATA_PASSTHROUGH_LAYER_RESUMED_META = 1000282000, + XR_TYPE_BODY_TRACKING_CALIBRATION_INFO_META = 1000283002, + XR_TYPE_BODY_TRACKING_CALIBRATION_STATUS_META = 1000283003, + XR_TYPE_SYSTEM_PROPERTIES_BODY_TRACKING_CALIBRATION_META = 1000283004, XR_TYPE_SYSTEM_FACE_TRACKING_PROPERTIES2_FB = 1000287013, XR_TYPE_FACE_TRACKER_CREATE_INFO2_FB = 1000287014, XR_TYPE_FACE_EXPRESSION_INFO2_FB = 1000287015, @@ -670,22 +710,22 @@ typedef enum XrStructureType { XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_META = 1000291005, XR_TYPE_ENVIRONMENT_DEPTH_HAND_REMOVAL_SET_INFO_META = 1000291006, XR_TYPE_SYSTEM_ENVIRONMENT_DEPTH_PROPERTIES_META = 1000291007, - XR_TYPE_RENDER_MODEL_CREATE_INFO_EXT = 1000300000, - XR_TYPE_RENDER_MODEL_PROPERTIES_GET_INFO_EXT = 1000300001, - XR_TYPE_RENDER_MODEL_PROPERTIES_EXT = 1000300002, - XR_TYPE_RENDER_MODEL_SPACE_CREATE_INFO_EXT = 1000300003, - XR_TYPE_RENDER_MODEL_STATE_GET_INFO_EXT = 1000300004, - XR_TYPE_RENDER_MODEL_STATE_EXT = 1000300005, - XR_TYPE_RENDER_MODEL_ASSET_CREATE_INFO_EXT = 1000300006, - XR_TYPE_RENDER_MODEL_ASSET_DATA_GET_INFO_EXT = 1000300007, - XR_TYPE_RENDER_MODEL_ASSET_DATA_EXT = 1000300008, - XR_TYPE_RENDER_MODEL_ASSET_PROPERTIES_GET_INFO_EXT = 1000300009, - XR_TYPE_RENDER_MODEL_ASSET_PROPERTIES_EXT = 1000300010, - XR_TYPE_INTERACTION_RENDER_MODEL_IDS_ENUMERATE_INFO_EXT = 1000301000, - XR_TYPE_INTERACTION_RENDER_MODEL_SUBACTION_PATH_INFO_EXT = 1000301001, - XR_TYPE_EVENT_DATA_INTERACTION_RENDER_MODELS_CHANGED_EXT = 1000301002, - XR_TYPE_INTERACTION_RENDER_MODEL_TOP_LEVEL_USER_PATH_GET_INFO_EXT = 1000301003, - XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC = 1000317001, + XR_TYPE_RENDER_MODEL_CREATE_INFO_EXT = 1000300000, + XR_TYPE_RENDER_MODEL_PROPERTIES_GET_INFO_EXT = 1000300001, + XR_TYPE_RENDER_MODEL_PROPERTIES_EXT = 1000300002, + XR_TYPE_RENDER_MODEL_SPACE_CREATE_INFO_EXT = 1000300003, + XR_TYPE_RENDER_MODEL_STATE_GET_INFO_EXT = 1000300004, + XR_TYPE_RENDER_MODEL_STATE_EXT = 1000300005, + XR_TYPE_RENDER_MODEL_ASSET_CREATE_INFO_EXT = 1000300006, + XR_TYPE_RENDER_MODEL_ASSET_DATA_GET_INFO_EXT = 1000300007, + XR_TYPE_RENDER_MODEL_ASSET_DATA_EXT = 1000300008, + XR_TYPE_RENDER_MODEL_ASSET_PROPERTIES_GET_INFO_EXT = 1000300009, + XR_TYPE_RENDER_MODEL_ASSET_PROPERTIES_EXT = 1000300010, + XR_TYPE_INTERACTION_RENDER_MODEL_IDS_ENUMERATE_INFO_EXT = 1000301000, + XR_TYPE_INTERACTION_RENDER_MODEL_SUBACTION_PATH_INFO_EXT = 1000301001, + XR_TYPE_EVENT_DATA_INTERACTION_RENDER_MODELS_CHANGED_EXT = 1000301002, + XR_TYPE_INTERACTION_RENDER_MODEL_TOP_LEVEL_USER_PATH_GET_INFO_EXT = 1000301003, + XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC = 1000317001, XR_TYPE_PASSTHROUGH_COLOR_HTC = 1000317002, XR_TYPE_PASSTHROUGH_MESH_TRANSFORM_INFO_HTC = 1000317003, XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_HTC = 1000317004, @@ -706,45 +746,50 @@ typedef enum XrStructureType { XR_TYPE_BODY_JOINTS_LOCATE_INFO_BD = 1000385002, XR_TYPE_BODY_JOINT_LOCATIONS_BD = 1000385003, XR_TYPE_SYSTEM_BODY_TRACKING_PROPERTIES_BD = 1000385004, - XR_TYPE_SYSTEM_SPATIAL_SENSING_PROPERTIES_BD = 1000389000, - XR_TYPE_SPATIAL_ENTITY_COMPONENT_GET_INFO_BD = 1000389001, - XR_TYPE_SPATIAL_ENTITY_LOCATION_GET_INFO_BD = 1000389002, - XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_LOCATION_BD = 1000389003, - XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_SEMANTIC_BD = 1000389004, - XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_BOUNDING_BOX_2D_BD = 1000389005, - XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_POLYGON_BD = 1000389006, - XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_BOUNDING_BOX_3D_BD = 1000389007, - XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_TRIANGLE_MESH_BD = 1000389008, - XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_BD = 1000389009, - XR_TYPE_SENSE_DATA_PROVIDER_START_INFO_BD = 1000389010, - XR_TYPE_EVENT_DATA_SENSE_DATA_PROVIDER_STATE_CHANGED_BD = 1000389011, - XR_TYPE_EVENT_DATA_SENSE_DATA_UPDATED_BD = 1000389012, - XR_TYPE_SENSE_DATA_QUERY_INFO_BD = 1000389013, - XR_TYPE_SENSE_DATA_QUERY_COMPLETION_BD = 1000389014, - XR_TYPE_SENSE_DATA_FILTER_UUID_BD = 1000389015, - XR_TYPE_SENSE_DATA_FILTER_SEMANTIC_BD = 1000389016, - XR_TYPE_QUERIED_SENSE_DATA_GET_INFO_BD = 1000389017, - XR_TYPE_QUERIED_SENSE_DATA_BD = 1000389018, - XR_TYPE_SPATIAL_ENTITY_STATE_BD = 1000389019, - XR_TYPE_SPATIAL_ENTITY_ANCHOR_CREATE_INFO_BD = 1000389020, - XR_TYPE_ANCHOR_SPACE_CREATE_INFO_BD = 1000389021, - XR_TYPE_SYSTEM_SPATIAL_ANCHOR_PROPERTIES_BD = 1000390000, - XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_BD = 1000390001, - XR_TYPE_SPATIAL_ANCHOR_CREATE_COMPLETION_BD = 1000390002, - XR_TYPE_SPATIAL_ANCHOR_PERSIST_INFO_BD = 1000390003, - XR_TYPE_SPATIAL_ANCHOR_UNPERSIST_INFO_BD = 1000390004, - XR_TYPE_SYSTEM_SPATIAL_ANCHOR_SHARING_PROPERTIES_BD = 1000391000, - XR_TYPE_SPATIAL_ANCHOR_SHARE_INFO_BD = 1000391001, - XR_TYPE_SHARED_SPATIAL_ANCHOR_DOWNLOAD_INFO_BD = 1000391002, - XR_TYPE_SYSTEM_SPATIAL_SCENE_PROPERTIES_BD = 1000392000, - XR_TYPE_SCENE_CAPTURE_INFO_BD = 1000392001, - XR_TYPE_SYSTEM_SPATIAL_MESH_PROPERTIES_BD = 1000393000, - XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SPATIAL_MESH_BD = 1000393001, - XR_TYPE_FUTURE_POLL_RESULT_PROGRESS_BD = 1000394001, - XR_TYPE_SYSTEM_SPATIAL_PLANE_PROPERTIES_BD = 1000396000, - XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_PLANE_ORIENTATION_BD = 1000396001, - XR_TYPE_SENSE_DATA_FILTER_PLANE_ORIENTATION_BD = 1000396002, - XR_TYPE_HAND_TRACKING_DATA_SOURCE_INFO_EXT = 1000428000, + XR_TYPE_SYSTEM_FACIAL_SIMULATION_PROPERTIES_BD = 1000386001, + XR_TYPE_FACE_TRACKER_CREATE_INFO_BD = 1000386002, + XR_TYPE_FACIAL_SIMULATION_DATA_GET_INFO_BD = 1000386003, + XR_TYPE_FACIAL_SIMULATION_DATA_BD = 1000386004, + XR_TYPE_LIP_EXPRESSION_DATA_BD = 1000386005, + XR_TYPE_SYSTEM_SPATIAL_SENSING_PROPERTIES_BD = 1000389000, + XR_TYPE_SPATIAL_ENTITY_COMPONENT_GET_INFO_BD = 1000389001, + XR_TYPE_SPATIAL_ENTITY_LOCATION_GET_INFO_BD = 1000389002, + XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_LOCATION_BD = 1000389003, + XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_SEMANTIC_BD = 1000389004, + XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_BOUNDING_BOX_2D_BD = 1000389005, + XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_POLYGON_BD = 1000389006, + XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_BOUNDING_BOX_3D_BD = 1000389007, + XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_TRIANGLE_MESH_BD = 1000389008, + XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_BD = 1000389009, + XR_TYPE_SENSE_DATA_PROVIDER_START_INFO_BD = 1000389010, + XR_TYPE_EVENT_DATA_SENSE_DATA_PROVIDER_STATE_CHANGED_BD = 1000389011, + XR_TYPE_EVENT_DATA_SENSE_DATA_UPDATED_BD = 1000389012, + XR_TYPE_SENSE_DATA_QUERY_INFO_BD = 1000389013, + XR_TYPE_SENSE_DATA_QUERY_COMPLETION_BD = 1000389014, + XR_TYPE_SENSE_DATA_FILTER_UUID_BD = 1000389015, + XR_TYPE_SENSE_DATA_FILTER_SEMANTIC_BD = 1000389016, + XR_TYPE_QUERIED_SENSE_DATA_GET_INFO_BD = 1000389017, + XR_TYPE_QUERIED_SENSE_DATA_BD = 1000389018, + XR_TYPE_SPATIAL_ENTITY_STATE_BD = 1000389019, + XR_TYPE_SPATIAL_ENTITY_ANCHOR_CREATE_INFO_BD = 1000389020, + XR_TYPE_ANCHOR_SPACE_CREATE_INFO_BD = 1000389021, + XR_TYPE_SYSTEM_SPATIAL_ANCHOR_PROPERTIES_BD = 1000390000, + XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_BD = 1000390001, + XR_TYPE_SPATIAL_ANCHOR_CREATE_COMPLETION_BD = 1000390002, + XR_TYPE_SPATIAL_ANCHOR_PERSIST_INFO_BD = 1000390003, + XR_TYPE_SPATIAL_ANCHOR_UNPERSIST_INFO_BD = 1000390004, + XR_TYPE_SYSTEM_SPATIAL_ANCHOR_SHARING_PROPERTIES_BD = 1000391000, + XR_TYPE_SPATIAL_ANCHOR_SHARE_INFO_BD = 1000391001, + XR_TYPE_SHARED_SPATIAL_ANCHOR_DOWNLOAD_INFO_BD = 1000391002, + XR_TYPE_SYSTEM_SPATIAL_SCENE_PROPERTIES_BD = 1000392000, + XR_TYPE_SCENE_CAPTURE_INFO_BD = 1000392001, + XR_TYPE_SYSTEM_SPATIAL_MESH_PROPERTIES_BD = 1000393000, + XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SPATIAL_MESH_BD = 1000393001, + XR_TYPE_FUTURE_POLL_RESULT_PROGRESS_BD = 1000394001, + XR_TYPE_SYSTEM_SPATIAL_PLANE_PROPERTIES_BD = 1000396000, + XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_PLANE_ORIENTATION_BD = 1000396001, + XR_TYPE_SENSE_DATA_FILTER_PLANE_ORIENTATION_BD = 1000396002, + XR_TYPE_HAND_TRACKING_DATA_SOURCE_INFO_EXT = 1000428000, XR_TYPE_HAND_TRACKING_DATA_SOURCE_STATE_EXT = 1000428001, XR_TYPE_PLANE_DETECTOR_CREATE_INFO_EXT = 1000429001, XR_TYPE_PLANE_DETECTOR_BEGIN_INFO_EXT = 1000429002, @@ -753,6 +798,25 @@ typedef enum XrStructureType { XR_TYPE_PLANE_DETECTOR_LOCATION_EXT = 1000429005, XR_TYPE_PLANE_DETECTOR_POLYGON_BUFFER_EXT = 1000429006, XR_TYPE_SYSTEM_PLANE_DETECTION_PROPERTIES_EXT = 1000429007, + XR_TYPE_TRACKABLE_GET_INFO_ANDROID = 1000455000, + XR_TYPE_ANCHOR_SPACE_CREATE_INFO_ANDROID = 1000455001, + XR_TYPE_TRACKABLE_PLANE_ANDROID = 1000455003, + XR_TYPE_TRACKABLE_TRACKER_CREATE_INFO_ANDROID = 1000455004, + XR_TYPE_SYSTEM_TRACKABLES_PROPERTIES_ANDROID = 1000455005, + XR_TYPE_PERSISTED_ANCHOR_SPACE_CREATE_INFO_ANDROID = 1000457001, + XR_TYPE_PERSISTED_ANCHOR_SPACE_INFO_ANDROID = 1000457002, + XR_TYPE_DEVICE_ANCHOR_PERSISTENCE_CREATE_INFO_ANDROID = 1000457003, + XR_TYPE_SYSTEM_DEVICE_ANCHOR_PERSISTENCE_PROPERTIES_ANDROID = 1000457004, + XR_TYPE_FACE_TRACKER_CREATE_INFO_ANDROID = 1000458000, + XR_TYPE_FACE_STATE_GET_INFO_ANDROID = 1000458001, + XR_TYPE_FACE_STATE_ANDROID = 1000458002, + XR_TYPE_SYSTEM_FACE_TRACKING_PROPERTIES_ANDROID = 1000458003, + XR_TYPE_PASSTHROUGH_CAMERA_STATE_GET_INFO_ANDROID = 1000460000, + XR_TYPE_SYSTEM_PASSTHROUGH_CAMERA_STATE_PROPERTIES_ANDROID = 1000460001, + XR_TYPE_RAYCAST_INFO_ANDROID = 1000463000, + XR_TYPE_RAYCAST_HIT_RESULTS_ANDROID = 1000463001, + XR_TYPE_TRACKABLE_OBJECT_ANDROID = 1000466000, + XR_TYPE_TRACKABLE_OBJECT_CONFIGURATION_ANDROID = 1000466001, XR_TYPE_FUTURE_CANCEL_INFO_EXT = 1000469000, XR_TYPE_FUTURE_POLL_INFO_EXT = 1000469001, XR_TYPE_FUTURE_COMPLETION_EXT = 1000469002, @@ -776,10 +840,10 @@ typedef enum XrStructureType { XR_TYPE_FACIAL_EXPRESSION_CLIENT_CREATE_INFO_ML = 1000482005, XR_TYPE_FACIAL_EXPRESSION_BLEND_SHAPE_GET_INFO_ML = 1000482006, XR_TYPE_FACIAL_EXPRESSION_BLEND_SHAPE_PROPERTIES_ML = 1000482007, - XR_TYPE_SYSTEM_SIMULTANEOUS_HANDS_AND_CONTROLLERS_PROPERTIES_META = 1000532001, - XR_TYPE_SIMULTANEOUS_HANDS_AND_CONTROLLERS_TRACKING_RESUME_INFO_META = 1000532002, - XR_TYPE_SIMULTANEOUS_HANDS_AND_CONTROLLERS_TRACKING_PAUSE_INFO_META = 1000532003, - XR_TYPE_COLOCATION_DISCOVERY_START_INFO_META = 1000571010, + XR_TYPE_SYSTEM_SIMULTANEOUS_HANDS_AND_CONTROLLERS_PROPERTIES_META = 1000532001, + XR_TYPE_SIMULTANEOUS_HANDS_AND_CONTROLLERS_TRACKING_RESUME_INFO_META = 1000532002, + XR_TYPE_SIMULTANEOUS_HANDS_AND_CONTROLLERS_TRACKING_PAUSE_INFO_META = 1000532003, + XR_TYPE_COLOCATION_DISCOVERY_START_INFO_META = 1000571010, XR_TYPE_COLOCATION_DISCOVERY_STOP_INFO_META = 1000571011, XR_TYPE_COLOCATION_ADVERTISEMENT_START_INFO_META = 1000571012, XR_TYPE_COLOCATION_ADVERTISEMENT_STOP_INFO_META = 1000571013, @@ -794,36 +858,42 @@ typedef enum XrStructureType { XR_TYPE_SHARE_SPACES_RECIPIENT_GROUPS_META = 1000572000, XR_TYPE_SPACE_GROUP_UUID_FILTER_INFO_META = 1000572001, XR_TYPE_SYSTEM_SPATIAL_ENTITY_GROUP_SHARING_PROPERTIES_META = 1000572100, - XR_TYPE_SPATIAL_CAPABILITY_COMPONENT_TYPES_EXT = 1000740000, - XR_TYPE_SPATIAL_CONTEXT_CREATE_INFO_EXT = 1000740001, - XR_TYPE_CREATE_SPATIAL_CONTEXT_COMPLETION_EXT = 1000740002, - XR_TYPE_SPATIAL_DISCOVERY_SNAPSHOT_CREATE_INFO_EXT = 1000740003, - XR_TYPE_CREATE_SPATIAL_DISCOVERY_SNAPSHOT_COMPLETION_INFO_EXT = 1000740004, - XR_TYPE_CREATE_SPATIAL_DISCOVERY_SNAPSHOT_COMPLETION_EXT = 1000740005, - XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_CONDITION_EXT = 1000740006, - XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_RESULT_EXT = 1000740007, - XR_TYPE_SPATIAL_BUFFER_GET_INFO_EXT = 1000740008, - XR_TYPE_SPATIAL_COMPONENT_BOUNDED_2D_LIST_EXT = 1000740009, - XR_TYPE_SPATIAL_COMPONENT_BOUNDED_3D_LIST_EXT = 1000740010, - XR_TYPE_SPATIAL_COMPONENT_PARENT_LIST_EXT = 1000740011, - XR_TYPE_SPATIAL_COMPONENT_MESH_3D_LIST_EXT = 1000740012, - XR_TYPE_SPATIAL_ENTITY_FROM_ID_CREATE_INFO_EXT = 1000740013, - XR_TYPE_SPATIAL_UPDATE_SNAPSHOT_CREATE_INFO_EXT = 1000740014, - XR_TYPE_EVENT_DATA_SPATIAL_DISCOVERY_RECOMMENDED_EXT = 1000740015, - XR_TYPE_SPATIAL_FILTER_TRACKING_STATE_EXT = 1000740016, - XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_PLANE_TRACKING_EXT = 1000741000, - XR_TYPE_SPATIAL_COMPONENT_PLANE_ALIGNMENT_LIST_EXT = 1000741001, - XR_TYPE_SPATIAL_COMPONENT_MESH_2D_LIST_EXT = 1000741002, - XR_TYPE_SPATIAL_COMPONENT_POLYGON_2D_LIST_EXT = 1000741003, - XR_TYPE_SPATIAL_COMPONENT_PLANE_SEMANTIC_LABEL_LIST_EXT = 1000741004, - XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_QR_CODE_EXT = 1000743000, - XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_MICRO_QR_CODE_EXT = 1000743001, - XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_ARUCO_MARKER_EXT = 1000743002, - XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_APRIL_TAG_EXT = 1000743003, - XR_TYPE_SPATIAL_MARKER_SIZE_EXT = 1000743004, - XR_TYPE_SPATIAL_MARKER_STATIC_OPTIMIZATION_EXT = 1000743005, - XR_TYPE_SPATIAL_COMPONENT_MARKER_LIST_EXT = 1000743006, - XR_TYPE_EVENT_DATA_MRC_STATUS_CHANGED_PICO = 1010000000, + XR_TYPE_ANCHOR_SHARING_INFO_ANDROID = 1000701000, + XR_TYPE_ANCHOR_SHARING_TOKEN_ANDROID = 1000701001, + XR_TYPE_SYSTEM_ANCHOR_SHARING_EXPORT_PROPERTIES_ANDROID = 1000701002, + XR_TYPE_SYSTEM_MARKER_TRACKING_PROPERTIES_ANDROID = 1000707000, + XR_TYPE_TRACKABLE_MARKER_CONFIGURATION_ANDROID = 1000707001, + XR_TYPE_TRACKABLE_MARKER_ANDROID = 1000707002, + XR_TYPE_SPATIAL_CAPABILITY_COMPONENT_TYPES_EXT = 1000740000, + XR_TYPE_SPATIAL_CONTEXT_CREATE_INFO_EXT = 1000740001, + XR_TYPE_CREATE_SPATIAL_CONTEXT_COMPLETION_EXT = 1000740002, + XR_TYPE_SPATIAL_DISCOVERY_SNAPSHOT_CREATE_INFO_EXT = 1000740003, + XR_TYPE_CREATE_SPATIAL_DISCOVERY_SNAPSHOT_COMPLETION_INFO_EXT = 1000740004, + XR_TYPE_CREATE_SPATIAL_DISCOVERY_SNAPSHOT_COMPLETION_EXT = 1000740005, + XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_CONDITION_EXT = 1000740006, + XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_RESULT_EXT = 1000740007, + XR_TYPE_SPATIAL_BUFFER_GET_INFO_EXT = 1000740008, + XR_TYPE_SPATIAL_COMPONENT_BOUNDED_2D_LIST_EXT = 1000740009, + XR_TYPE_SPATIAL_COMPONENT_BOUNDED_3D_LIST_EXT = 1000740010, + XR_TYPE_SPATIAL_COMPONENT_PARENT_LIST_EXT = 1000740011, + XR_TYPE_SPATIAL_COMPONENT_MESH_3D_LIST_EXT = 1000740012, + XR_TYPE_SPATIAL_ENTITY_FROM_ID_CREATE_INFO_EXT = 1000740013, + XR_TYPE_SPATIAL_UPDATE_SNAPSHOT_CREATE_INFO_EXT = 1000740014, + XR_TYPE_EVENT_DATA_SPATIAL_DISCOVERY_RECOMMENDED_EXT = 1000740015, + XR_TYPE_SPATIAL_FILTER_TRACKING_STATE_EXT = 1000740016, + XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_PLANE_TRACKING_EXT = 1000741000, + XR_TYPE_SPATIAL_COMPONENT_PLANE_ALIGNMENT_LIST_EXT = 1000741001, + XR_TYPE_SPATIAL_COMPONENT_MESH_2D_LIST_EXT = 1000741002, + XR_TYPE_SPATIAL_COMPONENT_POLYGON_2D_LIST_EXT = 1000741003, + XR_TYPE_SPATIAL_COMPONENT_PLANE_SEMANTIC_LABEL_LIST_EXT = 1000741004, + XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_QR_CODE_EXT = 1000743000, + XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_MICRO_QR_CODE_EXT = 1000743001, + XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_ARUCO_MARKER_EXT = 1000743002, + XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_APRIL_TAG_EXT = 1000743003, + XR_TYPE_SPATIAL_MARKER_SIZE_EXT = 1000743004, + XR_TYPE_SPATIAL_MARKER_STATIC_OPTIMIZATION_EXT = 1000743005, + XR_TYPE_SPATIAL_COMPONENT_MARKER_LIST_EXT = 1000743006, + XR_TYPE_EVENT_DATA_MRC_STATUS_CHANGED_PICO = 1010000000, XR_TYPE_MRC_SPACE_CREATE_INFO_PICO = 1010000001, XR_TYPE_EXTERNAL_CAMERA_PARAMETER_PICO = 1010000002, XR_TYPE_VIRTUAL_BOUNDARY_INFO_PICO = 1010001001, @@ -840,10 +910,14 @@ typedef enum XrStructureType { XR_TYPE_EVENT_DATA_MOTION_TRACKER_POWER_KEY_EVENT_PICO = 1010002006, XR_TYPE_COMPOSITION_LAYER_FISHEYE_PICO = 1010003000, XR_TYPE_LAYER_SETTINGS_PICO = 1010004000, - XR_TYPE_EYE_TRACKER_CREATE_INFO_PICO = 1010006000, - XR_TYPE_EYE_TRACKER_DATA_INFO_PICO = 1010006001, - XR_TYPE_EYE_TRACKER_DATA_PICO = 1010006002, - XR_TYPE_EYE_DATA_PICO = 1010006003, + XR_TYPE_EYE_TRACKER_CREATE_INFO_PICO = 1010006001, + XR_TYPE_EYE_TRACKER_DATA_INFO_PICO = 1010006002, + XR_TYPE_EYE_TRACKER_DATA_PICO = 1010006003, + XR_TYPE_EYE_DATA_PICO = 1010006004, + XR_TYPE_EYE_TRACKER_GAZE_INFO_PICO = 1010006005, + XR_TYPE_EYE_TRACKER_GAZE_PICO = 1010006006, + XR_TYPE_EYE_GAZE_PICO = 1010006007, + XR_TYPE_EYE_TRACKER_GAZE_DEPTH_PICO = 1010006008, XR_TYPE_SECURE_MR_FRAMEWORK_CREATE_INFO_PICO = 1010007000, XR_TYPE_SECURE_MR_PIPELINE_CREATE_INFO_PICO = 1010007001, XR_TYPE_SECURE_MR_OPERATOR_BASE_HEADER_PICO = 1010007002, @@ -865,6 +939,7 @@ typedef enum XrStructureType { XR_TYPE_SECURE_MR_OPERATOR_IO_MAP_PICO = 1010007018, XR_TYPE_SECURE_MR_OPERATOR_SORT_MATRIX_PICO = 1010007019, XR_TYPE_SECURE_MR_OPERATOR_COLOR_CONVERT_PICO = 1010007020, + XR_TYPE_SECURE_MR_OPERATOR_JAVASCRIPT_PICO = 1010007021, XR_TYPE_EXPAND_DEVICE_MOTOR_VIBRATE_PICO = 1010008000, XR_TYPE_EXPAND_DEVICE_CUSTOM_DATA_PICO = 1010008001, XR_TYPE_EXPAND_DEVICE_BATTERY_STATE_PICO = 1010008002, @@ -877,76 +952,111 @@ typedef enum XrStructureType { XR_TYPE_BODY_JOINT_ACCELERATIONS_PICO = 1010009004, XR_TYPE_BODY_TRACKING_STATE_PICO = 1010009005, XR_TYPE_SYMMETRIC_FOV_REPROJECTION_VIEW_CONFIGURATION_PICO = 1010016000, - XR_TYPE_SYSTEM_DYNAMIC_OBJECT_TRACKING_PROPERTIES_PICO = 1010017000, - XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_DYNAMIC_OBJECT_PICO = 1010017001, - XR_TYPE_SPATIAL_ENTITY_DYNAMIC_OBJECT_GET_INFO_PICO = 1010017002, - XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_DYNAMIC_OBJECT_PICO = 1010017003, - XR_TYPE_DYNAMIC_OBJECT_DATA_PICO = 1010017004, - XR_TYPE_SENSE_DATA_FILTER_DYNAMIC_OBJECT_TYPE_PICO = 1010017005, - XR_TYPE_SYSTEM_DYNAMIC_OBJECT_KEYBOARD_PROPERTIES_PICO = 1010018000, - XR_TYPE_SYSTEM_DYNAMIC_OBJECT_MOUSE_PROPERTIES_PICO = 1010019000, - XR_TYPE_LAYER_COLOR_MATRIX_PICO = 1010026000, - XR_TYPE_SYSTEM_SPATIAL_SENSING_PROPERTIES_PICO = 1200389000, - XR_TYPE_SPATIAL_ENTITY_LOCATION_GET_INFO_PICO = 1200389002, - XR_TYPE_SPATIAL_ENTITY_LOCATION_DATA_PICO = 1200389003, - XR_TYPE_SPATIAL_ENTITY_SEMANTIC_GET_INFO_PICO = 1200389004, - XR_TYPE_SPATIAL_ENTITY_SEMANTIC_DATA_PICO = 1200389005, - XR_TYPE_SPATIAL_ENTITY_BOUNDING_BOX_2D_GET_INFO_PICO = 1200389006, - XR_TYPE_SPATIAL_ENTITY_BOUNDING_BOX_2D_DATA_PICO = 1200389007, - XR_TYPE_SPATIAL_ENTITY_POLYGON_GET_INFO_PICO = 1200389008, - XR_TYPE_SPATIAL_ENTITY_POLYGON_DATA_PICO = 1200389009, - XR_TYPE_SPATIAL_ENTITY_BOUNDING_BOX_3D_GET_INFO_PICO = 1200389010, - XR_TYPE_SPATIAL_ENTITY_BOUNDING_BOX_3D_DATA_PICO = 1200389011, - XR_TYPE_SPATIAL_ENTITY_TRIANGLE_MESH_GET_INFO_PICO = 1200389012, - XR_TYPE_SPATIAL_ENTITY_TRIANGLE_MESH_DATA_PICO = 1200389013, - XR_TYPE_SENSE_DATA_PROVIDER_START_COMPLETION_PICO = 1200389014, - XR_TYPE_EVENT_DATA_SENSE_DATA_PROVIDER_STATE_CHANGED_PICO = 1200389015, - XR_TYPE_SENSE_DATA_FILTER_UUID_PICO = 1200389016, - XR_TYPE_SENSE_DATA_FILTER_SEMANTIC_PICO = 1200389017, - XR_TYPE_SENSE_DATA_QUERY_INFO_PICO = 1200389018, - XR_TYPE_SENSE_DATA_QUERY_COMPLETION_PICO = 1200389019, - XR_TYPE_QUERIED_SENSE_DATA_GET_INFO_PICO = 1200389020, - XR_TYPE_SPATIAL_ENTITY_STATE_PICO = 1200389021, - XR_TYPE_QUERIED_SENSE_DATA_PICO = 1200389022, - XR_TYPE_EVENT_DATA_SENSE_DATA_UPDATED_PICO = 1200389023, - XR_TYPE_SENSE_DATA_PROVIDER_START_INFO_PICO = 1200389024, - XR_TYPE_SPATIAL_ENTITY_ANCHOR_RETRIEVE_INFO_PICO = 1200389025, - XR_TYPE_ANCHOR_LOCATE_INFO_PICO = 1200389026, - XR_TYPE_FUTURE_POLL_RESULT_PROGRESS_PICO = 1200389028, - XR_TYPE_SPATIAL_ENTITY_SPHERE_GET_INFO_PICO = 1200389029, - XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_SPHERE_PICO = 1200389030, - XR_TYPE_SYSTEM_SPATIAL_ANCHOR_PROPERTIES_PICO = 1200390000, - XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SPATIAL_ANCHOR_PICO = 1200390001, - XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_PICO = 1200390002, - XR_TYPE_SPATIAL_ANCHOR_CREATE_COMPLETION_PICO = 1200390003, - XR_TYPE_SPATIAL_ANCHOR_PERSIST_INFO_PICO = 1200390004, - XR_TYPE_SPATIAL_ANCHOR_PERSIST_COMPLETION_PICO = 1200390005, - XR_TYPE_SPATIAL_ANCHOR_UNPERSIST_INFO_PICO = 1200390006, - XR_TYPE_SPATIAL_ANCHOR_UNPERSIST_COMPLETION_PICO = 1200390007, - XR_TYPE_SYSTEM_SPATIAL_ANCHOR_SHARING_PROPERTIES_PICO = 1200391000, - XR_TYPE_SPATIAL_ANCHOR_SHARE_INFO_PICO = 1200391001, - XR_TYPE_SPATIAL_ANCHOR_SHARE_COMPLETION_PICO = 1200391002, - XR_TYPE_SPATIAL_ANCHOR_DOWNLOAD_INFO_PICO = 1200391003, - XR_TYPE_SPATIAL_ANCHOR_DOWNLOAD_COMPLETION_PICO = 1200391004, - XR_TYPE_SYSTEM_SCENE_CAPTURE_PROPERTIES_PICO = 1200392000, - XR_TYPE_SCENE_CAPTURE_START_INFO_PICO = 1200392001, - XR_TYPE_SCENE_CAPTURE_START_COMPLETION_PICO = 1200392002, - XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SCENE_CAPTURE_PICO = 1200392003, - XR_TYPE_SYSTEM_SPATIAL_MESH_PROPERTIES_PICO = 1200393000, - XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SPATIAL_MESH_PICO = 1200393001, - XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_ANCHOR_EXT = 1000762000, - XR_TYPE_SPATIAL_COMPONENT_ANCHOR_LIST_EXT = 1000762001, - XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_EXT = 1000762002, - XR_TYPE_SPATIAL_PERSISTENCE_CONTEXT_CREATE_INFO_EXT = 1000763000, - XR_TYPE_CREATE_SPATIAL_PERSISTENCE_CONTEXT_COMPLETION_EXT = 1000763001, - XR_TYPE_SPATIAL_CONTEXT_PERSISTENCE_CONFIG_EXT = 1000763002, - XR_TYPE_SPATIAL_DISCOVERY_PERSISTENCE_UUID_FILTER_EXT = 1000763003, - XR_TYPE_SPATIAL_COMPONENT_PERSISTENCE_LIST_EXT = 1000763004, - XR_TYPE_SPATIAL_ENTITY_PERSIST_INFO_EXT = 1000781000, - XR_TYPE_PERSIST_SPATIAL_ENTITY_COMPLETION_EXT = 1000781001, - XR_TYPE_SPATIAL_ENTITY_UNPERSIST_INFO_EXT = 1000781002, - XR_TYPE_UNPERSIST_SPATIAL_ENTITY_COMPLETION_EXT = 1000781003, - XR_TYPE_GRAPHICS_BINDING_VULKAN2_KHR = XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR, + XR_TYPE_SYSTEM_DYNAMIC_OBJECT_TRACKING_PROPERTIES_PICO = 1010017000, + XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_DYNAMIC_OBJECT_PICO = 1010017001, + XR_TYPE_SPATIAL_ENTITY_DYNAMIC_OBJECT_GET_INFO_PICO = 1010017002, + XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_DYNAMIC_OBJECT_PICO = 1010017003, + XR_TYPE_DYNAMIC_OBJECT_DATA_PICO = 1010017004, + XR_TYPE_SENSE_DATA_FILTER_DYNAMIC_OBJECT_TYPE_PICO = 1010017005, + XR_TYPE_SYSTEM_DYNAMIC_OBJECT_KEYBOARD_PROPERTIES_PICO = 1010018000, + XR_TYPE_SYSTEM_DYNAMIC_OBJECT_MOUSE_PROPERTIES_PICO = 1010019000, + XR_TYPE_LAYER_COLOR_MATRIX_PICO = 1010026000, + XR_TYPE_AVAILABLE_CAMERAS_ENUMERATE_INFO_PICO = 1010033000, + XR_TYPE_CAMERA_PROPERTIES_GET_INFO_PICO = 1010033001, + XR_TYPE_CAMERA_PROPERTIES_PICO = 1010033002, + XR_TYPE_CAMERA_PROPERTY_FACING_PICO = 1010033003, + XR_TYPE_CAMERA_PROPERTY_POSITION_PICO = 1010033004, + XR_TYPE_CAMERA_PROPERTY_CAMERA_TYPE_PICO = 1010033005, + XR_TYPE_CAMERA_SUPPORTED_CAPABILITIES_GET_INFO_PICO = 1010033006, + XR_TYPE_CAMERA_SUPPORTED_CAPABILITIES_PICO = 1010033007, + XR_TYPE_CAMERA_SUPPORTED_CAPABILITY_IMAGE_RESOLUTION_PICO = 1010033008, + XR_TYPE_CAMERA_CAPABILITY_IMAGE_RESOLUTION_PICO = 1010033009, + XR_TYPE_CAMERA_SUPPORTED_CAPABILITY_DATA_TRANSFER_TYPE_PICO = 1010033010, + XR_TYPE_CAMERA_CAPABILITY_DATA_TRANSFER_TYPE_PICO = 1010033011, + XR_TYPE_CAMERA_SUPPORTED_CAPABILITY_IMAGE_FORMAT_PICO = 1010033012, + XR_TYPE_CAMERA_CAPABILITY_IMAGE_FORMAT_PICO = 1010033013, + XR_TYPE_CAMERA_SUPPORTED_CAPABILITY_CAMERA_MODEL_PICO = 1010033014, + XR_TYPE_CAMERA_CAPABILITY_CAMERA_MODEL_PICO = 1010033015, + XR_TYPE_CAMERA_SUPPORTED_CAPABILITY_IMAGE_FPS_PICO = 1010033016, + XR_TYPE_CAMERA_CAPABILITY_IMAGE_FPS_PICO = 1010033017, + XR_TYPE_CAMERA_DEVICE_CREATE_INFO_PICO = 1010033018, + XR_TYPE_CREATE_CAMERA_DEVICE_COMPLETION_PICO = 1010033019, + XR_TYPE_CAMERA_CAPTURE_SESSION_CREATE_INFO_PICO = 1010033020, + XR_TYPE_CREATE_CAMERA_CAPTURE_SESSION_COMPLETION_PICO = 1010033021, + XR_TYPE_CAMERA_INTRINSICS_PICO = 1010033022, + XR_TYPE_CAMERA_EXTRINSICS_PICO = 1010033023, + XR_TYPE_CAMERA_CAPTURE_BEGIN_INFO_PICO = 1010033024, + XR_TYPE_CAMERA_IMAGE_ACQUIRE_INFO_PICO = 1010033025, + XR_TYPE_CAMERA_IMAGE_PICO = 1010033026, + XR_TYPE_CAMERA_IMAGE_DATA_RAW_BUFFER_PICO = 1010033027, + XR_TYPE_CAMERA_CAPABILITIES_PICO = 1010033028, + XR_TYPE_READBACK_TENSOR_BUFFER_PICO = 1010027000, + XR_TYPE_CREATE_BUFFER_FROM_GLOBAL_TENSOR_COMPLETION_PICO = 1010027001, + XR_TYPE_CREATE_TEXTURE_FROM_GLOBAL_TENSOR_COMPLETION_PICO = 1010027002, + XR_TYPE_READBACK_TEXTURE_IMAGE_VULKAN_PICO = 1010028000, + XR_TYPE_READBACK_TEXTURE_IMAGE_OPENGL_PICO = 1010029000, + XR_TYPE_SYSTEM_SPATIAL_SENSING_PROPERTIES_PICO = 1200389000, + XR_TYPE_SPATIAL_ENTITY_LOCATION_GET_INFO_PICO = 1200389002, + XR_TYPE_SPATIAL_ENTITY_LOCATION_DATA_PICO = 1200389003, + XR_TYPE_SPATIAL_ENTITY_SEMANTIC_GET_INFO_PICO = 1200389004, + XR_TYPE_SPATIAL_ENTITY_SEMANTIC_DATA_PICO = 1200389005, + XR_TYPE_SPATIAL_ENTITY_BOUNDING_BOX_2D_GET_INFO_PICO = 1200389006, + XR_TYPE_SPATIAL_ENTITY_BOUNDING_BOX_2D_DATA_PICO = 1200389007, + XR_TYPE_SPATIAL_ENTITY_POLYGON_GET_INFO_PICO = 1200389008, + XR_TYPE_SPATIAL_ENTITY_POLYGON_DATA_PICO = 1200389009, + XR_TYPE_SPATIAL_ENTITY_BOUNDING_BOX_3D_GET_INFO_PICO = 1200389010, + XR_TYPE_SPATIAL_ENTITY_BOUNDING_BOX_3D_DATA_PICO = 1200389011, + XR_TYPE_SPATIAL_ENTITY_TRIANGLE_MESH_GET_INFO_PICO = 1200389012, + XR_TYPE_SPATIAL_ENTITY_TRIANGLE_MESH_DATA_PICO = 1200389013, + XR_TYPE_SENSE_DATA_PROVIDER_START_COMPLETION_PICO = 1200389014, + XR_TYPE_EVENT_DATA_SENSE_DATA_PROVIDER_STATE_CHANGED_PICO = 1200389015, + XR_TYPE_SENSE_DATA_FILTER_UUID_PICO = 1200389016, + XR_TYPE_SENSE_DATA_FILTER_SEMANTIC_PICO = 1200389017, + XR_TYPE_SENSE_DATA_QUERY_INFO_PICO = 1200389018, + XR_TYPE_SENSE_DATA_QUERY_COMPLETION_PICO = 1200389019, + XR_TYPE_QUERIED_SENSE_DATA_GET_INFO_PICO = 1200389020, + XR_TYPE_SPATIAL_ENTITY_STATE_PICO = 1200389021, + XR_TYPE_QUERIED_SENSE_DATA_PICO = 1200389022, + XR_TYPE_EVENT_DATA_SENSE_DATA_UPDATED_PICO = 1200389023, + XR_TYPE_SENSE_DATA_PROVIDER_START_INFO_PICO = 1200389024, + XR_TYPE_SPATIAL_ENTITY_ANCHOR_RETRIEVE_INFO_PICO = 1200389025, + XR_TYPE_ANCHOR_LOCATE_INFO_PICO = 1200389026, + XR_TYPE_FUTURE_POLL_RESULT_PROGRESS_PICO = 1200389028, + XR_TYPE_SPATIAL_ENTITY_SPHERE_GET_INFO_PICO = 1200389029, + XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_SPHERE_PICO = 1200389030, + XR_TYPE_SYSTEM_SPATIAL_ANCHOR_PROPERTIES_PICO = 1200390000, + XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SPATIAL_ANCHOR_PICO = 1200390001, + XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_PICO = 1200390002, + XR_TYPE_SPATIAL_ANCHOR_CREATE_COMPLETION_PICO = 1200390003, + XR_TYPE_SPATIAL_ANCHOR_PERSIST_INFO_PICO = 1200390004, + XR_TYPE_SPATIAL_ANCHOR_PERSIST_COMPLETION_PICO = 1200390005, + XR_TYPE_SPATIAL_ANCHOR_UNPERSIST_INFO_PICO = 1200390006, + XR_TYPE_SPATIAL_ANCHOR_UNPERSIST_COMPLETION_PICO = 1200390007, + XR_TYPE_SYSTEM_SPATIAL_ANCHOR_SHARING_PROPERTIES_PICO = 1200391000, + XR_TYPE_SPATIAL_ANCHOR_SHARE_INFO_PICO = 1200391001, + XR_TYPE_SPATIAL_ANCHOR_SHARE_COMPLETION_PICO = 1200391002, + XR_TYPE_SHARED_SPATIAL_ANCHOR_DOWNLOAD_INFO_PICO = 1200391003, + XR_TYPE_SHARED_SPATIAL_ANCHOR_DOWNLOAD_COMPLETION_PICO = 1200391004, + XR_TYPE_SYSTEM_SCENE_CAPTURE_PROPERTIES_PICO = 1200392000, + XR_TYPE_SCENE_CAPTURE_START_INFO_PICO = 1200392001, + XR_TYPE_SCENE_CAPTURE_START_COMPLETION_PICO = 1200392002, + XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SCENE_CAPTURE_PICO = 1200392003, + XR_TYPE_SYSTEM_SPATIAL_MESH_PROPERTIES_PICO = 1200393000, + XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SPATIAL_MESH_PICO = 1200393001, + XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_ANCHOR_EXT = 1000762000, + XR_TYPE_SPATIAL_COMPONENT_ANCHOR_LIST_EXT = 1000762001, + XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_EXT = 1000762002, + XR_TYPE_SPATIAL_PERSISTENCE_CONTEXT_CREATE_INFO_EXT = 1000763000, + XR_TYPE_CREATE_SPATIAL_PERSISTENCE_CONTEXT_COMPLETION_EXT = 1000763001, + XR_TYPE_SPATIAL_CONTEXT_PERSISTENCE_CONFIG_EXT = 1000763002, + XR_TYPE_SPATIAL_DISCOVERY_PERSISTENCE_UUID_FILTER_EXT = 1000763003, + XR_TYPE_SPATIAL_COMPONENT_PERSISTENCE_LIST_EXT = 1000763004, + XR_TYPE_SPATIAL_ENTITY_PERSIST_INFO_EXT = 1000781000, + XR_TYPE_PERSIST_SPATIAL_ENTITY_COMPLETION_EXT = 1000781001, + XR_TYPE_SPATIAL_ENTITY_UNPERSIST_INFO_EXT = 1000781002, + XR_TYPE_UNPERSIST_SPATIAL_ENTITY_COMPLETION_EXT = 1000781003, + XR_TYPE_LOADER_INIT_INFO_PROPERTIES_EXT = 1000838000, + XR_TYPE_GRAPHICS_BINDING_VULKAN2_KHR = XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR, XR_TYPE_SWAPCHAIN_IMAGE_VULKAN2_KHR = XR_TYPE_SWAPCHAIN_IMAGE_VULKAN_KHR, XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN2_KHR = XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN_KHR, XR_TYPE_DEVICE_PCM_SAMPLE_RATE_GET_INFO_FB = XR_TYPE_DEVICE_PCM_SAMPLE_RATE_STATE_FB, @@ -1054,29 +1164,36 @@ typedef enum XrObjectType { XR_OBJECT_TYPE_FACE_TRACKER2_FB = 1000287012, XR_OBJECT_TYPE_ENVIRONMENT_DEPTH_PROVIDER_META = 1000291000, XR_OBJECT_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_META = 1000291001, - XR_OBJECT_TYPE_RENDER_MODEL_EXT = 1000300000, - XR_OBJECT_TYPE_RENDER_MODEL_ASSET_EXT = 1000300001, - XR_OBJECT_TYPE_PASSTHROUGH_HTC = 1000317000, + XR_OBJECT_TYPE_RENDER_MODEL_EXT = 1000300000, + XR_OBJECT_TYPE_RENDER_MODEL_ASSET_EXT = 1000300001, + XR_OBJECT_TYPE_PASSTHROUGH_HTC = 1000317000, XR_OBJECT_TYPE_BODY_TRACKER_HTC = 1000320000, XR_OBJECT_TYPE_BODY_TRACKER_BD = 1000385000, - XR_OBJECT_TYPE_SENSE_DATA_PROVIDER_BD = 1000389000, - XR_OBJECT_TYPE_SENSE_DATA_SNAPSHOT_BD = 1000389001, - XR_OBJECT_TYPE_ANCHOR_BD = 1000389002, - XR_OBJECT_TYPE_PLANE_DETECTOR_EXT = 1000429000, + XR_OBJECT_TYPE_FACE_TRACKER_BD = 1000386000, + XR_OBJECT_TYPE_SENSE_DATA_PROVIDER_BD = 1000389000, + XR_OBJECT_TYPE_SENSE_DATA_SNAPSHOT_BD = 1000389001, + XR_OBJECT_TYPE_ANCHOR_BD = 1000389002, + XR_OBJECT_TYPE_PLANE_DETECTOR_EXT = 1000429000, + XR_OBJECT_TYPE_TRACKABLE_TRACKER_ANDROID = 1000455001, + XR_OBJECT_TYPE_DEVICE_ANCHOR_PERSISTENCE_ANDROID = 1000457000, + XR_OBJECT_TYPE_FACE_TRACKER_ANDROID = 1000458000, XR_OBJECT_TYPE_WORLD_MESH_DETECTOR_ML = 1000474000, XR_OBJECT_TYPE_FACIAL_EXPRESSION_CLIENT_ML = 1000482000, - XR_OBJECT_TYPE_SPATIAL_ENTITY_EXT = 1000740000, - XR_OBJECT_TYPE_SPATIAL_CONTEXT_EXT = 1000740001, - XR_OBJECT_TYPE_SPATIAL_SNAPSHOT_EXT = 1000740002, - XR_OBJECT_TYPE_EYE_TRACKER_PICO = 1010006000, + XR_OBJECT_TYPE_SPATIAL_ENTITY_EXT = 1000740000, + XR_OBJECT_TYPE_SPATIAL_CONTEXT_EXT = 1000740001, + XR_OBJECT_TYPE_SPATIAL_SNAPSHOT_EXT = 1000740002, + XR_OBJECT_TYPE_EYE_TRACKER_PICO = 1010006000, XR_OBJECT_TYPE_SECURE_MR_FRAMEWORK_PICO = 1010007000, XR_OBJECT_TYPE_SECURE_MR_PIPELINE_PICO = 1010007001, XR_OBJECT_TYPE_SECURE_MR_TENSOR_PICO = 1010007002, - XR_OBJECT_TYPE_SENSE_DATA_PROVIDER_PICO = 1200389000, - XR_OBJECT_TYPE_SENSE_DATA_SNAPSHOT_PICO = 1200389001, - XR_OBJECT_TYPE_ANCHOR_PICO = 1200389002, - XR_OBJECT_TYPE_SPATIAL_PERSISTENCE_CONTEXT_EXT = 1000763000, - XR_OBJECT_TYPE_MAX_ENUM = 0x7FFFFFFF + XR_OBJECT_TYPE_CAMERA_DEVICE_PICO = 1010033000, + XR_OBJECT_TYPE_CAMERA_CAPTURE_SESSION_PICO = 1010033001, + XR_OBJECT_TYPE_READBACK_TEXTURE_PICO = 1010027000, + XR_OBJECT_TYPE_SENSE_DATA_PROVIDER_PICO = 1200389000, + XR_OBJECT_TYPE_SENSE_DATA_SNAPSHOT_PICO = 1200389001, + XR_OBJECT_TYPE_ANCHOR_PICO = 1200389002, + XR_OBJECT_TYPE_SPATIAL_PERSISTENCE_CONTEXT_EXT = 1000763000, + XR_OBJECT_TYPE_MAX_ENUM = 0x7FFFFFFF } XrObjectType; typedef XrFlags64 XrInstanceCreateFlags; @@ -2260,21 +2377,25 @@ typedef struct XrBindingModificationsKHR { const XrBindingModificationBaseHeaderKHR* const* bindingModifications; } XrBindingModificationsKHR; + + // XR_KHR_extended_struct_name_lengths is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_extended_struct_name_lengths 1 #define XR_KHR_extended_struct_name_lengths_SPEC_VERSION 1 #define XR_KHR_EXTENDED_STRUCT_NAME_LENGTHS_EXTENSION_NAME "XR_KHR_extended_struct_name_lengths" #define XR_MAX_STRUCTURE_NAME_SIZE_EXTENDED_KHR 256 -typedef XrResult(XRAPI_PTR* PFN_xrStructureTypeToString2KHR)(XrInstance instance, XrStructureType value, - char buffer[XR_MAX_STRUCTURE_NAME_SIZE_EXTENDED_KHR]); +typedef XrResult (XRAPI_PTR *PFN_xrStructureTypeToString2KHR)(XrInstance instance, XrStructureType value, char buffer[XR_MAX_STRUCTURE_NAME_SIZE_EXTENDED_KHR]); #ifndef XR_NO_PROTOTYPES #ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrStructureTypeToString2KHR(XrInstance instance, XrStructureType value, - char buffer[XR_MAX_STRUCTURE_NAME_SIZE_EXTENDED_KHR]); +XRAPI_ATTR XrResult XRAPI_CALL xrStructureTypeToString2KHR( + XrInstance instance, + XrStructureType value, + char buffer[XR_MAX_STRUCTURE_NAME_SIZE_EXTENDED_KHR]); #endif /* XR_EXTENSION_PROTOTYPES */ #endif /* !XR_NO_PROTOTYPES */ + // XR_KHR_swapchain_usage_input_attachment_bit is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_swapchain_usage_input_attachment_bit 1 #define XR_KHR_swapchain_usage_input_attachment_bit_SPEC_VERSION 3 @@ -2323,6 +2444,12 @@ typedef XrFrustumf XrFrustumfKHR; +// XR_KHR_generic_controller is a preprocessor guard. Do not pass it to API calls. +#define XR_KHR_generic_controller 1 +#define XR_KHR_generic_controller_SPEC_VERSION 1 +#define XR_KHR_GENERIC_CONTROLLER_EXTENSION_NAME "XR_KHR_generic_controller" + + // XR_EXT_performance_settings is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_performance_settings 1 #define XR_EXT_performance_settings_SPEC_VERSION 4 @@ -2650,7 +2777,7 @@ typedef struct XrCompositionLayerAlphaBlendFB { // XR_MND_headless is a preprocessor guard. Do not pass it to API calls. #define XR_MND_headless 1 -#define XR_MND_headless_SPEC_VERSION 2 +#define XR_MND_headless_SPEC_VERSION 3 #define XR_MND_HEADLESS_EXTENSION_NAME "XR_MND_headless" @@ -3336,8 +3463,8 @@ typedef enum XrBodyJointFB { typedef enum XrBodyJointSetFB { XR_BODY_JOINT_SET_DEFAULT_FB = 0, - XR_BODY_JOINT_SET_FULL_BODY_META = 1000274000, - XR_BODY_JOINT_SET_MAX_ENUM_FB = 0x7FFFFFFF + XR_BODY_JOINT_SET_FULL_BODY_META = 1000274000, + XR_BODY_JOINT_SET_MAX_ENUM_FB = 0x7FFFFFFF } XrBodyJointSetFB; typedef struct XrBodyJointLocationFB { XrSpaceLocationFlags locationFlags; @@ -7010,11 +7137,13 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSaveSpaceListFB( #endif /* XR_EXTENSION_PROTOTYPES */ #endif /* !XR_NO_PROTOTYPES */ + // XR_META_detached_controllers is a preprocessor guard. Do not pass it to API calls. #define XR_META_detached_controllers 1 #define XR_META_detached_controllers_SPEC_VERSION 1 #define XR_META_DETACHED_CONTROLLERS_EXTENSION_NAME "XR_META_detached_controllers" + // XR_FB_spatial_entity_user is a preprocessor guard. Do not pass it to API calls. #define XR_FB_spatial_entity_user 1 typedef uint64_t XrSpaceUserIdFB; @@ -7058,11 +7187,94 @@ typedef struct XrSystemHeadsetIdPropertiesMETA { XrUuidEXT id; } XrSystemHeadsetIdPropertiesMETA; + + +// XR_META_spatial_entity_discovery is a preprocessor guard. Do not pass it to API calls. +#define XR_META_spatial_entity_discovery 1 +#define XR_META_spatial_entity_discovery_SPEC_VERSION 1 +#define XR_META_SPATIAL_ENTITY_DISCOVERY_EXTENSION_NAME "XR_META_spatial_entity_discovery" +// XrSystemSpaceDiscoveryPropertiesMETA extends XrSystemProperties +typedef struct XrSystemSpaceDiscoveryPropertiesMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrBool32 supportsSpaceDiscovery; +} XrSystemSpaceDiscoveryPropertiesMETA; + +typedef struct XR_MAY_ALIAS XrSpaceFilterBaseHeaderMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; +} XrSpaceFilterBaseHeaderMETA; + +typedef struct XrSpaceDiscoveryInfoMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t filterCount; + const XrSpaceFilterBaseHeaderMETA* const * filters; +} XrSpaceDiscoveryInfoMETA; + +typedef struct XrSpaceFilterUuidMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t uuidCount; + const XrUuidEXT* uuids; +} XrSpaceFilterUuidMETA; + +typedef struct XrSpaceFilterComponentMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpaceComponentTypeFB componentType; +} XrSpaceFilterComponentMETA; + +typedef struct XrSpaceDiscoveryResultMETA { + XrSpace space; + XrUuidEXT uuid; +} XrSpaceDiscoveryResultMETA; + +typedef struct XrSpaceDiscoveryResultsMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t resultCapacityInput; + uint32_t resultCountOutput; + XrSpaceDiscoveryResultMETA* results; +} XrSpaceDiscoveryResultsMETA; + +typedef struct XrEventDataSpaceDiscoveryResultsAvailableMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrAsyncRequestIdFB requestId; +} XrEventDataSpaceDiscoveryResultsAvailableMETA; + +typedef struct XrEventDataSpaceDiscoveryCompleteMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrAsyncRequestIdFB requestId; + XrResult result; +} XrEventDataSpaceDiscoveryCompleteMETA; + +typedef XrResult (XRAPI_PTR *PFN_xrDiscoverSpacesMETA)(XrSession session, const XrSpaceDiscoveryInfoMETA* info, XrAsyncRequestIdFB* requestId); +typedef XrResult (XRAPI_PTR *PFN_xrRetrieveSpaceDiscoveryResultsMETA)(XrSession session, XrAsyncRequestIdFB requestId, XrSpaceDiscoveryResultsMETA* results); + +#ifndef XR_NO_PROTOTYPES +#ifdef XR_EXTENSION_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrDiscoverSpacesMETA( + XrSession session, + const XrSpaceDiscoveryInfoMETA* info, + XrAsyncRequestIdFB* requestId); + +XRAPI_ATTR XrResult XRAPI_CALL xrRetrieveSpaceDiscoveryResultsMETA( + XrSession session, + XrAsyncRequestIdFB requestId, + XrSpaceDiscoveryResultsMETA* results); +#endif /* XR_EXTENSION_PROTOTYPES */ +#endif /* !XR_NO_PROTOTYPES */ + + // XR_META_hand_tracking_microgestures is a preprocessor guard. Do not pass it to API calls. #define XR_META_hand_tracking_microgestures 1 #define XR_META_hand_tracking_microgestures_SPEC_VERSION 1 #define XR_META_HAND_TRACKING_MICROGESTURES_EXTENSION_NAME "XR_META_hand_tracking_microgestures" + // XR_META_recommended_layer_resolution is a preprocessor guard. Do not pass it to API calls. #define XR_META_recommended_layer_resolution 1 #define XR_META_recommended_layer_resolution_SPEC_VERSION 1 @@ -7093,6 +7305,65 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetRecommendedLayerResolutionMETA( #endif /* !XR_NO_PROTOTYPES */ +// XR_META_spatial_entity_persistence is a preprocessor guard. Do not pass it to API calls. +#define XR_META_spatial_entity_persistence 1 +#define XR_META_spatial_entity_persistence_SPEC_VERSION 1 +#define XR_META_SPATIAL_ENTITY_PERSISTENCE_EXTENSION_NAME "XR_META_spatial_entity_persistence" +// XrSystemSpacePersistencePropertiesMETA extends XrSystemProperties +typedef struct XrSystemSpacePersistencePropertiesMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrBool32 supportsSpacePersistence; +} XrSystemSpacePersistencePropertiesMETA; + +typedef struct XrSpacesSaveInfoMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t spaceCount; + XrSpace* spaces; +} XrSpacesSaveInfoMETA; + +typedef struct XrEventDataSpacesSaveResultMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrAsyncRequestIdFB requestId; + XrResult result; +} XrEventDataSpacesSaveResultMETA; + +typedef struct XrSpacesEraseInfoMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t spaceCount; + XrSpace* spaces; + uint32_t uuidCount; + XrUuidEXT* uuids; +} XrSpacesEraseInfoMETA; + +typedef struct XrEventDataSpacesEraseResultMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrAsyncRequestIdFB requestId; + XrResult result; +} XrEventDataSpacesEraseResultMETA; + +typedef XrResult (XRAPI_PTR *PFN_xrSaveSpacesMETA)(XrSession session, const XrSpacesSaveInfoMETA* info, XrAsyncRequestIdFB* requestId); +typedef XrResult (XRAPI_PTR *PFN_xrEraseSpacesMETA)(XrSession session, const XrSpacesEraseInfoMETA* info, XrAsyncRequestIdFB* requestId); + +#ifndef XR_NO_PROTOTYPES +#ifdef XR_EXTENSION_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrSaveSpacesMETA( + XrSession session, + const XrSpacesSaveInfoMETA* info, + XrAsyncRequestIdFB* requestId); + +XRAPI_ATTR XrResult XRAPI_CALL xrEraseSpacesMETA( + XrSession session, + const XrSpacesEraseInfoMETA* info, + XrAsyncRequestIdFB* requestId); +#endif /* XR_EXTENSION_PROTOTYPES */ +#endif /* !XR_NO_PROTOTYPES */ + + // XR_META_passthrough_color_lut is a preprocessor guard. Do not pass it to API calls. #define XR_META_passthrough_color_lut 1 XR_DEFINE_HANDLE(XrPassthroughColorLutMETA) @@ -7205,107 +7476,110 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetSpaceTriangleMeshMETA( #define XR_META_automatic_layer_filter_SPEC_VERSION 1 #define XR_META_AUTOMATIC_LAYER_FILTER_EXTENSION_NAME "XR_META_automatic_layer_filter" + // XR_META_body_tracking_full_body is a preprocessor guard. Do not pass it to API calls. #define XR_META_body_tracking_full_body 1 #define XR_META_body_tracking_full_body_SPEC_VERSION 1 #define XR_META_BODY_TRACKING_FULL_BODY_EXTENSION_NAME "XR_META_body_tracking_full_body" typedef enum XrFullBodyJointMETA { - XR_FULL_BODY_JOINT_ROOT_META = 0, - XR_FULL_BODY_JOINT_HIPS_META = 1, - XR_FULL_BODY_JOINT_SPINE_LOWER_META = 2, - XR_FULL_BODY_JOINT_SPINE_MIDDLE_META = 3, - XR_FULL_BODY_JOINT_SPINE_UPPER_META = 4, - XR_FULL_BODY_JOINT_CHEST_META = 5, - XR_FULL_BODY_JOINT_NECK_META = 6, - XR_FULL_BODY_JOINT_HEAD_META = 7, - XR_FULL_BODY_JOINT_LEFT_SHOULDER_META = 8, - XR_FULL_BODY_JOINT_LEFT_SCAPULA_META = 9, - XR_FULL_BODY_JOINT_LEFT_ARM_UPPER_META = 10, - XR_FULL_BODY_JOINT_LEFT_ARM_LOWER_META = 11, - XR_FULL_BODY_JOINT_LEFT_HAND_WRIST_TWIST_META = 12, - XR_FULL_BODY_JOINT_RIGHT_SHOULDER_META = 13, - XR_FULL_BODY_JOINT_RIGHT_SCAPULA_META = 14, - XR_FULL_BODY_JOINT_RIGHT_ARM_UPPER_META = 15, - XR_FULL_BODY_JOINT_RIGHT_ARM_LOWER_META = 16, - XR_FULL_BODY_JOINT_RIGHT_HAND_WRIST_TWIST_META = 17, - XR_FULL_BODY_JOINT_LEFT_HAND_PALM_META = 18, - XR_FULL_BODY_JOINT_LEFT_HAND_WRIST_META = 19, - XR_FULL_BODY_JOINT_LEFT_HAND_THUMB_METACARPAL_META = 20, - XR_FULL_BODY_JOINT_LEFT_HAND_THUMB_PROXIMAL_META = 21, - XR_FULL_BODY_JOINT_LEFT_HAND_THUMB_DISTAL_META = 22, - XR_FULL_BODY_JOINT_LEFT_HAND_THUMB_TIP_META = 23, - XR_FULL_BODY_JOINT_LEFT_HAND_INDEX_METACARPAL_META = 24, - XR_FULL_BODY_JOINT_LEFT_HAND_INDEX_PROXIMAL_META = 25, - XR_FULL_BODY_JOINT_LEFT_HAND_INDEX_INTERMEDIATE_META = 26, - XR_FULL_BODY_JOINT_LEFT_HAND_INDEX_DISTAL_META = 27, - XR_FULL_BODY_JOINT_LEFT_HAND_INDEX_TIP_META = 28, - XR_FULL_BODY_JOINT_LEFT_HAND_MIDDLE_METACARPAL_META = 29, - XR_FULL_BODY_JOINT_LEFT_HAND_MIDDLE_PROXIMAL_META = 30, - XR_FULL_BODY_JOINT_LEFT_HAND_MIDDLE_INTERMEDIATE_META = 31, - XR_FULL_BODY_JOINT_LEFT_HAND_MIDDLE_DISTAL_META = 32, - XR_FULL_BODY_JOINT_LEFT_HAND_MIDDLE_TIP_META = 33, - XR_FULL_BODY_JOINT_LEFT_HAND_RING_METACARPAL_META = 34, - XR_FULL_BODY_JOINT_LEFT_HAND_RING_PROXIMAL_META = 35, - XR_FULL_BODY_JOINT_LEFT_HAND_RING_INTERMEDIATE_META = 36, - XR_FULL_BODY_JOINT_LEFT_HAND_RING_DISTAL_META = 37, - XR_FULL_BODY_JOINT_LEFT_HAND_RING_TIP_META = 38, - XR_FULL_BODY_JOINT_LEFT_HAND_LITTLE_METACARPAL_META = 39, - XR_FULL_BODY_JOINT_LEFT_HAND_LITTLE_PROXIMAL_META = 40, - XR_FULL_BODY_JOINT_LEFT_HAND_LITTLE_INTERMEDIATE_META = 41, - XR_FULL_BODY_JOINT_LEFT_HAND_LITTLE_DISTAL_META = 42, - XR_FULL_BODY_JOINT_LEFT_HAND_LITTLE_TIP_META = 43, - XR_FULL_BODY_JOINT_RIGHT_HAND_PALM_META = 44, - XR_FULL_BODY_JOINT_RIGHT_HAND_WRIST_META = 45, - XR_FULL_BODY_JOINT_RIGHT_HAND_THUMB_METACARPAL_META = 46, - XR_FULL_BODY_JOINT_RIGHT_HAND_THUMB_PROXIMAL_META = 47, - XR_FULL_BODY_JOINT_RIGHT_HAND_THUMB_DISTAL_META = 48, - XR_FULL_BODY_JOINT_RIGHT_HAND_THUMB_TIP_META = 49, - XR_FULL_BODY_JOINT_RIGHT_HAND_INDEX_METACARPAL_META = 50, - XR_FULL_BODY_JOINT_RIGHT_HAND_INDEX_PROXIMAL_META = 51, - XR_FULL_BODY_JOINT_RIGHT_HAND_INDEX_INTERMEDIATE_META = 52, - XR_FULL_BODY_JOINT_RIGHT_HAND_INDEX_DISTAL_META = 53, - XR_FULL_BODY_JOINT_RIGHT_HAND_INDEX_TIP_META = 54, - XR_FULL_BODY_JOINT_RIGHT_HAND_MIDDLE_METACARPAL_META = 55, - XR_FULL_BODY_JOINT_RIGHT_HAND_MIDDLE_PROXIMAL_META = 56, - XR_FULL_BODY_JOINT_RIGHT_HAND_MIDDLE_INTERMEDIATE_META = 57, - XR_FULL_BODY_JOINT_RIGHT_HAND_MIDDLE_DISTAL_META = 58, - XR_FULL_BODY_JOINT_RIGHT_HAND_MIDDLE_TIP_META = 59, - XR_FULL_BODY_JOINT_RIGHT_HAND_RING_METACARPAL_META = 60, - XR_FULL_BODY_JOINT_RIGHT_HAND_RING_PROXIMAL_META = 61, - XR_FULL_BODY_JOINT_RIGHT_HAND_RING_INTERMEDIATE_META = 62, - XR_FULL_BODY_JOINT_RIGHT_HAND_RING_DISTAL_META = 63, - XR_FULL_BODY_JOINT_RIGHT_HAND_RING_TIP_META = 64, - XR_FULL_BODY_JOINT_RIGHT_HAND_LITTLE_METACARPAL_META = 65, - XR_FULL_BODY_JOINT_RIGHT_HAND_LITTLE_PROXIMAL_META = 66, - XR_FULL_BODY_JOINT_RIGHT_HAND_LITTLE_INTERMEDIATE_META = 67, - XR_FULL_BODY_JOINT_RIGHT_HAND_LITTLE_DISTAL_META = 68, - XR_FULL_BODY_JOINT_RIGHT_HAND_LITTLE_TIP_META = 69, - XR_FULL_BODY_JOINT_LEFT_UPPER_LEG_META = 70, - XR_FULL_BODY_JOINT_LEFT_LOWER_LEG_META = 71, - XR_FULL_BODY_JOINT_LEFT_FOOT_ANKLE_TWIST_META = 72, - XR_FULL_BODY_JOINT_LEFT_FOOT_ANKLE_META = 73, - XR_FULL_BODY_JOINT_LEFT_FOOT_SUBTALAR_META = 74, - XR_FULL_BODY_JOINT_LEFT_FOOT_TRANSVERSE_META = 75, - XR_FULL_BODY_JOINT_LEFT_FOOT_BALL_META = 76, - XR_FULL_BODY_JOINT_RIGHT_UPPER_LEG_META = 77, - XR_FULL_BODY_JOINT_RIGHT_LOWER_LEG_META = 78, - XR_FULL_BODY_JOINT_RIGHT_FOOT_ANKLE_TWIST_META = 79, - XR_FULL_BODY_JOINT_RIGHT_FOOT_ANKLE_META = 80, - XR_FULL_BODY_JOINT_RIGHT_FOOT_SUBTALAR_META = 81, - XR_FULL_BODY_JOINT_RIGHT_FOOT_TRANSVERSE_META = 82, - XR_FULL_BODY_JOINT_RIGHT_FOOT_BALL_META = 83, - XR_FULL_BODY_JOINT_COUNT_META = 84, - XR_FULL_BODY_JOINT_NONE_META = 85, - XR_FULL_BODY_JOINT_MAX_ENUM_META = 0x7FFFFFFF + XR_FULL_BODY_JOINT_ROOT_META = 0, + XR_FULL_BODY_JOINT_HIPS_META = 1, + XR_FULL_BODY_JOINT_SPINE_LOWER_META = 2, + XR_FULL_BODY_JOINT_SPINE_MIDDLE_META = 3, + XR_FULL_BODY_JOINT_SPINE_UPPER_META = 4, + XR_FULL_BODY_JOINT_CHEST_META = 5, + XR_FULL_BODY_JOINT_NECK_META = 6, + XR_FULL_BODY_JOINT_HEAD_META = 7, + XR_FULL_BODY_JOINT_LEFT_SHOULDER_META = 8, + XR_FULL_BODY_JOINT_LEFT_SCAPULA_META = 9, + XR_FULL_BODY_JOINT_LEFT_ARM_UPPER_META = 10, + XR_FULL_BODY_JOINT_LEFT_ARM_LOWER_META = 11, + XR_FULL_BODY_JOINT_LEFT_HAND_WRIST_TWIST_META = 12, + XR_FULL_BODY_JOINT_RIGHT_SHOULDER_META = 13, + XR_FULL_BODY_JOINT_RIGHT_SCAPULA_META = 14, + XR_FULL_BODY_JOINT_RIGHT_ARM_UPPER_META = 15, + XR_FULL_BODY_JOINT_RIGHT_ARM_LOWER_META = 16, + XR_FULL_BODY_JOINT_RIGHT_HAND_WRIST_TWIST_META = 17, + XR_FULL_BODY_JOINT_LEFT_HAND_PALM_META = 18, + XR_FULL_BODY_JOINT_LEFT_HAND_WRIST_META = 19, + XR_FULL_BODY_JOINT_LEFT_HAND_THUMB_METACARPAL_META = 20, + XR_FULL_BODY_JOINT_LEFT_HAND_THUMB_PROXIMAL_META = 21, + XR_FULL_BODY_JOINT_LEFT_HAND_THUMB_DISTAL_META = 22, + XR_FULL_BODY_JOINT_LEFT_HAND_THUMB_TIP_META = 23, + XR_FULL_BODY_JOINT_LEFT_HAND_INDEX_METACARPAL_META = 24, + XR_FULL_BODY_JOINT_LEFT_HAND_INDEX_PROXIMAL_META = 25, + XR_FULL_BODY_JOINT_LEFT_HAND_INDEX_INTERMEDIATE_META = 26, + XR_FULL_BODY_JOINT_LEFT_HAND_INDEX_DISTAL_META = 27, + XR_FULL_BODY_JOINT_LEFT_HAND_INDEX_TIP_META = 28, + XR_FULL_BODY_JOINT_LEFT_HAND_MIDDLE_METACARPAL_META = 29, + XR_FULL_BODY_JOINT_LEFT_HAND_MIDDLE_PROXIMAL_META = 30, + XR_FULL_BODY_JOINT_LEFT_HAND_MIDDLE_INTERMEDIATE_META = 31, + XR_FULL_BODY_JOINT_LEFT_HAND_MIDDLE_DISTAL_META = 32, + XR_FULL_BODY_JOINT_LEFT_HAND_MIDDLE_TIP_META = 33, + XR_FULL_BODY_JOINT_LEFT_HAND_RING_METACARPAL_META = 34, + XR_FULL_BODY_JOINT_LEFT_HAND_RING_PROXIMAL_META = 35, + XR_FULL_BODY_JOINT_LEFT_HAND_RING_INTERMEDIATE_META = 36, + XR_FULL_BODY_JOINT_LEFT_HAND_RING_DISTAL_META = 37, + XR_FULL_BODY_JOINT_LEFT_HAND_RING_TIP_META = 38, + XR_FULL_BODY_JOINT_LEFT_HAND_LITTLE_METACARPAL_META = 39, + XR_FULL_BODY_JOINT_LEFT_HAND_LITTLE_PROXIMAL_META = 40, + XR_FULL_BODY_JOINT_LEFT_HAND_LITTLE_INTERMEDIATE_META = 41, + XR_FULL_BODY_JOINT_LEFT_HAND_LITTLE_DISTAL_META = 42, + XR_FULL_BODY_JOINT_LEFT_HAND_LITTLE_TIP_META = 43, + XR_FULL_BODY_JOINT_RIGHT_HAND_PALM_META = 44, + XR_FULL_BODY_JOINT_RIGHT_HAND_WRIST_META = 45, + XR_FULL_BODY_JOINT_RIGHT_HAND_THUMB_METACARPAL_META = 46, + XR_FULL_BODY_JOINT_RIGHT_HAND_THUMB_PROXIMAL_META = 47, + XR_FULL_BODY_JOINT_RIGHT_HAND_THUMB_DISTAL_META = 48, + XR_FULL_BODY_JOINT_RIGHT_HAND_THUMB_TIP_META = 49, + XR_FULL_BODY_JOINT_RIGHT_HAND_INDEX_METACARPAL_META = 50, + XR_FULL_BODY_JOINT_RIGHT_HAND_INDEX_PROXIMAL_META = 51, + XR_FULL_BODY_JOINT_RIGHT_HAND_INDEX_INTERMEDIATE_META = 52, + XR_FULL_BODY_JOINT_RIGHT_HAND_INDEX_DISTAL_META = 53, + XR_FULL_BODY_JOINT_RIGHT_HAND_INDEX_TIP_META = 54, + XR_FULL_BODY_JOINT_RIGHT_HAND_MIDDLE_METACARPAL_META = 55, + XR_FULL_BODY_JOINT_RIGHT_HAND_MIDDLE_PROXIMAL_META = 56, + XR_FULL_BODY_JOINT_RIGHT_HAND_MIDDLE_INTERMEDIATE_META = 57, + XR_FULL_BODY_JOINT_RIGHT_HAND_MIDDLE_DISTAL_META = 58, + XR_FULL_BODY_JOINT_RIGHT_HAND_MIDDLE_TIP_META = 59, + XR_FULL_BODY_JOINT_RIGHT_HAND_RING_METACARPAL_META = 60, + XR_FULL_BODY_JOINT_RIGHT_HAND_RING_PROXIMAL_META = 61, + XR_FULL_BODY_JOINT_RIGHT_HAND_RING_INTERMEDIATE_META = 62, + XR_FULL_BODY_JOINT_RIGHT_HAND_RING_DISTAL_META = 63, + XR_FULL_BODY_JOINT_RIGHT_HAND_RING_TIP_META = 64, + XR_FULL_BODY_JOINT_RIGHT_HAND_LITTLE_METACARPAL_META = 65, + XR_FULL_BODY_JOINT_RIGHT_HAND_LITTLE_PROXIMAL_META = 66, + XR_FULL_BODY_JOINT_RIGHT_HAND_LITTLE_INTERMEDIATE_META = 67, + XR_FULL_BODY_JOINT_RIGHT_HAND_LITTLE_DISTAL_META = 68, + XR_FULL_BODY_JOINT_RIGHT_HAND_LITTLE_TIP_META = 69, + XR_FULL_BODY_JOINT_LEFT_UPPER_LEG_META = 70, + XR_FULL_BODY_JOINT_LEFT_LOWER_LEG_META = 71, + XR_FULL_BODY_JOINT_LEFT_FOOT_ANKLE_TWIST_META = 72, + XR_FULL_BODY_JOINT_LEFT_FOOT_ANKLE_META = 73, + XR_FULL_BODY_JOINT_LEFT_FOOT_SUBTALAR_META = 74, + XR_FULL_BODY_JOINT_LEFT_FOOT_TRANSVERSE_META = 75, + XR_FULL_BODY_JOINT_LEFT_FOOT_BALL_META = 76, + XR_FULL_BODY_JOINT_RIGHT_UPPER_LEG_META = 77, + XR_FULL_BODY_JOINT_RIGHT_LOWER_LEG_META = 78, + XR_FULL_BODY_JOINT_RIGHT_FOOT_ANKLE_TWIST_META = 79, + XR_FULL_BODY_JOINT_RIGHT_FOOT_ANKLE_META = 80, + XR_FULL_BODY_JOINT_RIGHT_FOOT_SUBTALAR_META = 81, + XR_FULL_BODY_JOINT_RIGHT_FOOT_TRANSVERSE_META = 82, + XR_FULL_BODY_JOINT_RIGHT_FOOT_BALL_META = 83, + XR_FULL_BODY_JOINT_COUNT_META = 84, + XR_FULL_BODY_JOINT_NONE_META = 85, + XR_FULL_BODY_JOINT_MAX_ENUM_META = 0x7FFFFFFF } XrFullBodyJointMETA; // XrSystemPropertiesBodyTrackingFullBodyMETA extends XrSystemProperties typedef struct XrSystemPropertiesBodyTrackingFullBodyMETA { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrBool32 supportsFullBodyTracking; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 supportsFullBodyTracking; } XrSystemPropertiesBodyTrackingFullBodyMETA; + + // XR_META_touch_controller_plus is a preprocessor guard. Do not pass it to API calls. #define XR_META_touch_controller_plus 1 #define XR_META_touch_controller_plus_SPEC_VERSION 1 @@ -7324,6 +7598,52 @@ typedef struct XrEventDataPassthroughLayerResumedMETA { +// XR_META_body_tracking_calibration is a preprocessor guard. Do not pass it to API calls. +#define XR_META_body_tracking_calibration 1 +#define XR_META_body_tracking_calibration_SPEC_VERSION 1 +#define XR_META_BODY_TRACKING_CALIBRATION_EXTENSION_NAME "XR_META_body_tracking_calibration" + +typedef enum XrBodyTrackingCalibrationStateMETA { + XR_BODY_TRACKING_CALIBRATION_STATE_VALID_META = 1, + XR_BODY_TRACKING_CALIBRATION_STATE_CALIBRATING_META = 2, + XR_BODY_TRACKING_CALIBRATION_STATE_INVALID_META = 3, + XR_BODY_TRACKING_CALIBRATION_STATE_MAX_ENUM_META = 0x7FFFFFFF +} XrBodyTrackingCalibrationStateMETA; +// XrBodyTrackingCalibrationStatusMETA extends XrBodyJointLocationsFB +typedef struct XrBodyTrackingCalibrationStatusMETA { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBodyTrackingCalibrationStateMETA status; +} XrBodyTrackingCalibrationStatusMETA; + +typedef struct XrBodyTrackingCalibrationInfoMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + float bodyHeight; +} XrBodyTrackingCalibrationInfoMETA; + +// XrSystemPropertiesBodyTrackingCalibrationMETA extends XrSystemProperties +typedef struct XrSystemPropertiesBodyTrackingCalibrationMETA { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 supportsHeightOverride; +} XrSystemPropertiesBodyTrackingCalibrationMETA; + +typedef XrResult (XRAPI_PTR *PFN_xrSuggestBodyTrackingCalibrationOverrideMETA)(XrBodyTrackerFB bodyTracker, const XrBodyTrackingCalibrationInfoMETA* calibrationInfo); +typedef XrResult (XRAPI_PTR *PFN_xrResetBodyTrackingCalibrationMETA)(XrBodyTrackerFB bodyTracker); + +#ifndef XR_NO_PROTOTYPES +#ifdef XR_EXTENSION_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrSuggestBodyTrackingCalibrationOverrideMETA( + XrBodyTrackerFB bodyTracker, + const XrBodyTrackingCalibrationInfoMETA* calibrationInfo); + +XRAPI_ATTR XrResult XRAPI_CALL xrResetBodyTrackingCalibrationMETA( + XrBodyTrackerFB bodyTracker); +#endif /* XR_EXTENSION_PROTOTYPES */ +#endif /* !XR_NO_PROTOTYPES */ + + // XR_FB_face_tracking2 is a preprocessor guard. Do not pass it to API calls. #define XR_FB_face_tracking2 1 XR_DEFINE_HANDLE(XrFaceTracker2FB) @@ -7656,207 +7976,208 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSetEnvironmentDepthHandRemovalMETA( #define XR_EXT_UUID_EXTENSION_NAME "XR_EXT_uuid" #define XR_UUID_SIZE_EXT 16 + // XR_EXT_render_model is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_render_model 1 XR_DEFINE_ATOM(XrRenderModelIdEXT) XR_DEFINE_HANDLE(XrRenderModelEXT) XR_DEFINE_HANDLE(XrRenderModelAssetEXT) #define XR_MAX_RENDER_MODEL_ASSET_NODE_NAME_SIZE_EXT 64 -#define XR_EXT_render_model_SPEC_VERSION 1 +#define XR_EXT_render_model_SPEC_VERSION 1 #define XR_EXT_RENDER_MODEL_EXTENSION_NAME "XR_EXT_render_model" -#define XR_NULL_RENDER_MODEL_ID_EXT 0 +#define XR_NULL_RENDER_MODEL_ID_EXT 0 typedef struct XrRenderModelCreateInfoEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrRenderModelIdEXT renderModelId; - uint32_t gltfExtensionCount; - const char* const* gltfExtensions; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrRenderModelIdEXT renderModelId; + uint32_t gltfExtensionCount; + const char* const* gltfExtensions; } XrRenderModelCreateInfoEXT; typedef struct XrRenderModelPropertiesGetInfoEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; + XrStructureType type; + const void* XR_MAY_ALIAS next; } XrRenderModelPropertiesGetInfoEXT; typedef struct XrRenderModelPropertiesEXT { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrUuidEXT cacheId; - uint32_t animatableNodeCount; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrUuidEXT cacheId; + uint32_t animatableNodeCount; } XrRenderModelPropertiesEXT; typedef struct XrRenderModelSpaceCreateInfoEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrRenderModelEXT renderModel; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrRenderModelEXT renderModel; } XrRenderModelSpaceCreateInfoEXT; typedef struct XrRenderModelStateGetInfoEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrTime displayTime; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrTime displayTime; } XrRenderModelStateGetInfoEXT; typedef struct XrRenderModelNodeStateEXT { - XrPosef nodePose; - XrBool32 isVisible; + XrPosef nodePose; + XrBool32 isVisible; } XrRenderModelNodeStateEXT; typedef struct XrRenderModelStateEXT { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t nodeStateCount; - XrRenderModelNodeStateEXT* nodeStates; + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t nodeStateCount; + XrRenderModelNodeStateEXT* nodeStates; } XrRenderModelStateEXT; typedef struct XrRenderModelAssetCreateInfoEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrUuidEXT cacheId; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrUuidEXT cacheId; } XrRenderModelAssetCreateInfoEXT; typedef struct XrRenderModelAssetDataGetInfoEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; + XrStructureType type; + const void* XR_MAY_ALIAS next; } XrRenderModelAssetDataGetInfoEXT; typedef struct XrRenderModelAssetDataEXT { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t bufferCapacityInput; - uint32_t bufferCountOutput; - uint8_t* buffer; + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t bufferCapacityInput; + uint32_t bufferCountOutput; + uint8_t* buffer; } XrRenderModelAssetDataEXT; typedef struct XrRenderModelAssetPropertiesGetInfoEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; + XrStructureType type; + const void* XR_MAY_ALIAS next; } XrRenderModelAssetPropertiesGetInfoEXT; typedef struct XrRenderModelAssetNodePropertiesEXT { - char uniqueName[XR_MAX_RENDER_MODEL_ASSET_NODE_NAME_SIZE_EXT]; + char uniqueName[XR_MAX_RENDER_MODEL_ASSET_NODE_NAME_SIZE_EXT]; } XrRenderModelAssetNodePropertiesEXT; typedef struct XrRenderModelAssetPropertiesEXT { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t nodePropertyCount; - XrRenderModelAssetNodePropertiesEXT* nodeProperties; + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t nodePropertyCount; + XrRenderModelAssetNodePropertiesEXT* nodeProperties; } XrRenderModelAssetPropertiesEXT; -typedef XrResult(XRAPI_PTR* PFN_xrCreateRenderModelEXT)(XrSession session, const XrRenderModelCreateInfoEXT* createInfo, - XrRenderModelEXT* renderModel); -typedef XrResult(XRAPI_PTR* PFN_xrDestroyRenderModelEXT)(XrRenderModelEXT renderModel); -typedef XrResult(XRAPI_PTR* PFN_xrGetRenderModelPropertiesEXT)(XrRenderModelEXT renderModel, - const XrRenderModelPropertiesGetInfoEXT* getInfo, - XrRenderModelPropertiesEXT* properties); -typedef XrResult(XRAPI_PTR* PFN_xrCreateRenderModelSpaceEXT)(XrSession session, - const XrRenderModelSpaceCreateInfoEXT* createInfo, - XrSpace* space); -typedef XrResult(XRAPI_PTR* PFN_xrCreateRenderModelAssetEXT)(XrSession session, - const XrRenderModelAssetCreateInfoEXT* createInfo, - XrRenderModelAssetEXT* asset); -typedef XrResult(XRAPI_PTR* PFN_xrDestroyRenderModelAssetEXT)(XrRenderModelAssetEXT asset); -typedef XrResult(XRAPI_PTR* PFN_xrGetRenderModelAssetDataEXT)(XrRenderModelAssetEXT asset, - const XrRenderModelAssetDataGetInfoEXT* getInfo, - XrRenderModelAssetDataEXT* buffer); -typedef XrResult(XRAPI_PTR* PFN_xrGetRenderModelAssetPropertiesEXT)( - XrRenderModelAssetEXT asset, const XrRenderModelAssetPropertiesGetInfoEXT* getInfo, - XrRenderModelAssetPropertiesEXT* properties); -typedef XrResult(XRAPI_PTR* PFN_xrGetRenderModelStateEXT)(XrRenderModelEXT renderModel, - const XrRenderModelStateGetInfoEXT* getInfo, - XrRenderModelStateEXT* state); +typedef XrResult (XRAPI_PTR *PFN_xrCreateRenderModelEXT)(XrSession session, const XrRenderModelCreateInfoEXT* createInfo, XrRenderModelEXT* renderModel); +typedef XrResult (XRAPI_PTR *PFN_xrDestroyRenderModelEXT)(XrRenderModelEXT renderModel); +typedef XrResult (XRAPI_PTR *PFN_xrGetRenderModelPropertiesEXT)(XrRenderModelEXT renderModel, const XrRenderModelPropertiesGetInfoEXT* getInfo, XrRenderModelPropertiesEXT* properties); +typedef XrResult (XRAPI_PTR *PFN_xrCreateRenderModelSpaceEXT)(XrSession session, const XrRenderModelSpaceCreateInfoEXT* createInfo, XrSpace* space); +typedef XrResult (XRAPI_PTR *PFN_xrCreateRenderModelAssetEXT)(XrSession session, const XrRenderModelAssetCreateInfoEXT* createInfo, XrRenderModelAssetEXT* asset); +typedef XrResult (XRAPI_PTR *PFN_xrDestroyRenderModelAssetEXT)(XrRenderModelAssetEXT asset); +typedef XrResult (XRAPI_PTR *PFN_xrGetRenderModelAssetDataEXT)(XrRenderModelAssetEXT asset, const XrRenderModelAssetDataGetInfoEXT* getInfo, XrRenderModelAssetDataEXT* buffer); +typedef XrResult (XRAPI_PTR *PFN_xrGetRenderModelAssetPropertiesEXT)(XrRenderModelAssetEXT asset, const XrRenderModelAssetPropertiesGetInfoEXT* getInfo, XrRenderModelAssetPropertiesEXT* properties); +typedef XrResult (XRAPI_PTR *PFN_xrGetRenderModelStateEXT)(XrRenderModelEXT renderModel, const XrRenderModelStateGetInfoEXT* getInfo, XrRenderModelStateEXT* state); #ifndef XR_NO_PROTOTYPES #ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrCreateRenderModelEXT(XrSession session, const XrRenderModelCreateInfoEXT* createInfo, - XrRenderModelEXT* renderModel); +XRAPI_ATTR XrResult XRAPI_CALL xrCreateRenderModelEXT( + XrSession session, + const XrRenderModelCreateInfoEXT* createInfo, + XrRenderModelEXT* renderModel); -XRAPI_ATTR XrResult XRAPI_CALL xrDestroyRenderModelEXT(XrRenderModelEXT renderModel); +XRAPI_ATTR XrResult XRAPI_CALL xrDestroyRenderModelEXT( + XrRenderModelEXT renderModel); -XRAPI_ATTR XrResult XRAPI_CALL xrGetRenderModelPropertiesEXT(XrRenderModelEXT renderModel, - const XrRenderModelPropertiesGetInfoEXT* getInfo, - XrRenderModelPropertiesEXT* properties); +XRAPI_ATTR XrResult XRAPI_CALL xrGetRenderModelPropertiesEXT( + XrRenderModelEXT renderModel, + const XrRenderModelPropertiesGetInfoEXT* getInfo, + XrRenderModelPropertiesEXT* properties); -XRAPI_ATTR XrResult XRAPI_CALL xrCreateRenderModelSpaceEXT(XrSession session, - const XrRenderModelSpaceCreateInfoEXT* createInfo, - XrSpace* space); +XRAPI_ATTR XrResult XRAPI_CALL xrCreateRenderModelSpaceEXT( + XrSession session, + const XrRenderModelSpaceCreateInfoEXT* createInfo, + XrSpace* space); -XRAPI_ATTR XrResult XRAPI_CALL xrCreateRenderModelAssetEXT(XrSession session, - const XrRenderModelAssetCreateInfoEXT* createInfo, - XrRenderModelAssetEXT* asset); +XRAPI_ATTR XrResult XRAPI_CALL xrCreateRenderModelAssetEXT( + XrSession session, + const XrRenderModelAssetCreateInfoEXT* createInfo, + XrRenderModelAssetEXT* asset); -XRAPI_ATTR XrResult XRAPI_CALL xrDestroyRenderModelAssetEXT(XrRenderModelAssetEXT asset); +XRAPI_ATTR XrResult XRAPI_CALL xrDestroyRenderModelAssetEXT( + XrRenderModelAssetEXT asset); -XRAPI_ATTR XrResult XRAPI_CALL xrGetRenderModelAssetDataEXT(XrRenderModelAssetEXT asset, - const XrRenderModelAssetDataGetInfoEXT* getInfo, - XrRenderModelAssetDataEXT* buffer); +XRAPI_ATTR XrResult XRAPI_CALL xrGetRenderModelAssetDataEXT( + XrRenderModelAssetEXT asset, + const XrRenderModelAssetDataGetInfoEXT* getInfo, + XrRenderModelAssetDataEXT* buffer); -XRAPI_ATTR XrResult XRAPI_CALL xrGetRenderModelAssetPropertiesEXT(XrRenderModelAssetEXT asset, - const XrRenderModelAssetPropertiesGetInfoEXT* getInfo, - XrRenderModelAssetPropertiesEXT* properties); +XRAPI_ATTR XrResult XRAPI_CALL xrGetRenderModelAssetPropertiesEXT( + XrRenderModelAssetEXT asset, + const XrRenderModelAssetPropertiesGetInfoEXT* getInfo, + XrRenderModelAssetPropertiesEXT* properties); -XRAPI_ATTR XrResult XRAPI_CALL xrGetRenderModelStateEXT(XrRenderModelEXT renderModel, - const XrRenderModelStateGetInfoEXT* getInfo, - XrRenderModelStateEXT* state); +XRAPI_ATTR XrResult XRAPI_CALL xrGetRenderModelStateEXT( + XrRenderModelEXT renderModel, + const XrRenderModelStateGetInfoEXT* getInfo, + XrRenderModelStateEXT* state); #endif /* XR_EXTENSION_PROTOTYPES */ #endif /* !XR_NO_PROTOTYPES */ + // XR_EXT_interaction_render_model is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_interaction_render_model 1 #define XR_EXT_interaction_render_model_SPEC_VERSION 1 #define XR_EXT_INTERACTION_RENDER_MODEL_EXTENSION_NAME "XR_EXT_interaction_render_model" typedef struct XrInteractionRenderModelIdsEnumerateInfoEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; + XrStructureType type; + const void* XR_MAY_ALIAS next; } XrInteractionRenderModelIdsEnumerateInfoEXT; typedef struct XrInteractionRenderModelSubactionPathInfoEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; + XrStructureType type; + const void* XR_MAY_ALIAS next; } XrInteractionRenderModelSubactionPathInfoEXT; typedef struct XrInteractionRenderModelTopLevelUserPathGetInfoEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - uint32_t topLevelUserPathCount; - const XrPath* topLevelUserPaths; + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t topLevelUserPathCount; + const XrPath* topLevelUserPaths; } XrInteractionRenderModelTopLevelUserPathGetInfoEXT; typedef struct XrEventDataInteractionRenderModelsChangedEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; + XrStructureType type; + const void* XR_MAY_ALIAS next; } XrEventDataInteractionRenderModelsChangedEXT; -typedef XrResult(XRAPI_PTR* PFN_xrEnumerateInteractionRenderModelIdsEXT)( - XrSession session, const XrInteractionRenderModelIdsEnumerateInfoEXT* getInfo, uint32_t renderModelIdCapacityInput, - uint32_t* renderModelIdCountOutput, XrRenderModelIdEXT* renderModelIds); -typedef XrResult(XRAPI_PTR* PFN_xrEnumerateRenderModelSubactionPathsEXT)( - XrRenderModelEXT renderModel, const XrInteractionRenderModelSubactionPathInfoEXT* info, uint32_t pathCapacityInput, - uint32_t* pathCountOutput, XrPath* paths); -typedef XrResult(XRAPI_PTR* PFN_xrGetRenderModelPoseTopLevelUserPathEXT)( - XrRenderModelEXT renderModel, const XrInteractionRenderModelTopLevelUserPathGetInfoEXT* info, - XrPath* topLevelUserPath); +typedef XrResult (XRAPI_PTR *PFN_xrEnumerateInteractionRenderModelIdsEXT)(XrSession session, const XrInteractionRenderModelIdsEnumerateInfoEXT* getInfo, uint32_t renderModelIdCapacityInput, uint32_t* renderModelIdCountOutput, XrRenderModelIdEXT* renderModelIds); +typedef XrResult (XRAPI_PTR *PFN_xrEnumerateRenderModelSubactionPathsEXT)(XrRenderModelEXT renderModel, const XrInteractionRenderModelSubactionPathInfoEXT* info, uint32_t pathCapacityInput, uint32_t* pathCountOutput, XrPath* paths); +typedef XrResult (XRAPI_PTR *PFN_xrGetRenderModelPoseTopLevelUserPathEXT)(XrRenderModelEXT renderModel, const XrInteractionRenderModelTopLevelUserPathGetInfoEXT* info, XrPath* topLevelUserPath); #ifndef XR_NO_PROTOTYPES #ifdef XR_EXTENSION_PROTOTYPES XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateInteractionRenderModelIdsEXT( - XrSession session, const XrInteractionRenderModelIdsEnumerateInfoEXT* getInfo, uint32_t renderModelIdCapacityInput, - uint32_t* renderModelIdCountOutput, XrRenderModelIdEXT* renderModelIds); + XrSession session, + const XrInteractionRenderModelIdsEnumerateInfoEXT* getInfo, + uint32_t renderModelIdCapacityInput, + uint32_t* renderModelIdCountOutput, + XrRenderModelIdEXT* renderModelIds); XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateRenderModelSubactionPathsEXT( - XrRenderModelEXT renderModel, const XrInteractionRenderModelSubactionPathInfoEXT* info, uint32_t pathCapacityInput, - uint32_t* pathCountOutput, XrPath* paths); + XrRenderModelEXT renderModel, + const XrInteractionRenderModelSubactionPathInfoEXT* info, + uint32_t pathCapacityInput, + uint32_t* pathCountOutput, + XrPath* paths); XRAPI_ATTR XrResult XRAPI_CALL xrGetRenderModelPoseTopLevelUserPathEXT( - XrRenderModelEXT renderModel, const XrInteractionRenderModelTopLevelUserPathGetInfoEXT* info, - XrPath* topLevelUserPath); + XrRenderModelEXT renderModel, + const XrInteractionRenderModelTopLevelUserPathGetInfoEXT* info, + XrPath* topLevelUserPath); #endif /* XR_EXTENSION_PROTOTYPES */ #endif /* !XR_NO_PROTOTYPES */ + // XR_EXT_hand_interaction is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_hand_interaction 1 -#define XR_EXT_hand_interaction_SPEC_VERSION 1 +#define XR_EXT_hand_interaction_SPEC_VERSION 2 #define XR_EXT_HAND_INTERACTION_EXTENSION_NAME "XR_EXT_hand_interaction" @@ -8352,6 +8673,181 @@ XRAPI_ATTR XrResult XRAPI_CALL xrLocateBodyJointsBD( #endif /* XR_EXTENSION_PROTOTYPES */ #endif /* !XR_NO_PROTOTYPES */ + +// XR_BD_facial_simulation is a preprocessor guard. Do not pass it to API calls. +#define XR_BD_facial_simulation 1 + +#define XR_FACE_EXPRESSION_COUNT_BD 52 + + +#define XR_LIP_EXPRESSION_COUNT_BD 20 + + XR_DEFINE_HANDLE(XrFaceTrackerBD) +#define XR_BD_facial_simulation_SPEC_VERSION 1 +#define XR_BD_FACIAL_SIMULATION_EXTENSION_NAME "XR_BD_facial_simulation" + +typedef enum XrFacialSimulationModeBD { + XR_FACIAL_SIMULATION_MODE_DEFAULT_BD = 0, + XR_FACIAL_SIMULATION_MODE_COMBINED_AUDIO_BD = 1, + XR_FACIAL_SIMULATION_MODE_COMBINED_AUDIO_WITH_LIP_BD = 2, + XR_FACIAL_SIMULATION_MODE_ONLY_AUDIO_WITH_LIP_BD = 3, + XR_FACIAL_SIMULATION_MODE_MAX_ENUM_BD = 0x7FFFFFFF +} XrFacialSimulationModeBD; + +typedef enum XrFaceExpressionBD { + XR_FACE_EXPRESSION_BROW_DROP_L_BD = 0, + XR_FACE_EXPRESSION_BROW_DROP_R_BD = 1, + XR_FACE_EXPRESSION_BROW_INNER_UPWARDS_BD = 2, + XR_FACE_EXPRESSION_BROW_OUTER_UPWARDS_L_BD = 3, + XR_FACE_EXPRESSION_BROW_OUTER_UPWARDS_R_BD = 4, + XR_FACE_EXPRESSION_EYE_BLINK_L_BD = 5, + XR_FACE_EXPRESSION_EYE_LOOK_DROP_L_BD = 6, + XR_FACE_EXPRESSION_EYE_LOOK_IN_L_BD = 7, + XR_FACE_EXPRESSION_EYE_LOOK_OUT_L_BD = 8, + XR_FACE_EXPRESSION_EYE_LOOK_UPWARDS_L_BD = 9, + XR_FACE_EXPRESSION_EYE_LOOK_SQUINT_L_BD = 10, + XR_FACE_EXPRESSION_EYE_LOOK_WIDE_L_BD = 11, + XR_FACE_EXPRESSION_EYE_BLINK_R_BD = 12, + XR_FACE_EXPRESSION_EYE_LOOK_DROP_R_BD = 13, + XR_FACE_EXPRESSION_EYE_LOOK_IN_R_BD = 14, + XR_FACE_EXPRESSION_EYE_LOOK_OUT_R_BD = 15, + XR_FACE_EXPRESSION_EYE_LOOK_UPWARDS_R_BD = 16, + XR_FACE_EXPRESSION_EYE_LOOK_SQUINT_R_BD = 17, + XR_FACE_EXPRESSION_EYE_LOOK_WIDE_R_BD = 18, + XR_FACE_EXPRESSION_NOSE_SNEER_L_BD = 19, + XR_FACE_EXPRESSION_NOSE_SNEER_R_BD = 20, + XR_FACE_EXPRESSION_CHEEK_PUFF_BD = 21, + XR_FACE_EXPRESSION_CHEEK_SQUINT_L_BD = 22, + XR_FACE_EXPRESSION_CHEEK_SQUINT_R_BD = 23, + XR_FACE_EXPRESSION_MOUTH_CLOSE_BD = 24, + XR_FACE_EXPRESSION_MOUTH_FUNNEL_BD = 25, + XR_FACE_EXPRESSION_MOUTH_PUCKER_BD = 26, + XR_FACE_EXPRESSION_MOUTH_L_BD = 27, + XR_FACE_EXPRESSION_MOUTH_R_BD = 28, + XR_FACE_EXPRESSION_MOUTH_SMILE_L_BD = 29, + XR_FACE_EXPRESSION_MOUTH_SMILE_R_BD = 30, + XR_FACE_EXPRESSION_MOUTH_FROWN_L_BD = 31, + XR_FACE_EXPRESSION_MOUTH_FROWN_R_BD = 32, + XR_FACE_EXPRESSION_MOUTH_DIMPLE_L_BD = 33, + XR_FACE_EXPRESSION_MOUTH_DIMPLE_R_BD = 34, + XR_FACE_EXPRESSION_MOUTH_STRETCH_L_BD = 35, + XR_FACE_EXPRESSION_MOUTH_STRETCH_R_BD = 36, + XR_FACE_EXPRESSION_MOUTH_ROLL_LOWER_BD = 37, + XR_FACE_EXPRESSION_MOUTH_ROLL_UPPER_BD = 38, + XR_FACE_EXPRESSION_MOUTH_SHRUG_LOWER_BD = 39, + XR_FACE_EXPRESSION_MOUTH_SHRUG_UPPER_BD = 40, + XR_FACE_EXPRESSION_MOUTH_PRESS_L_BD = 41, + XR_FACE_EXPRESSION_MOUTH_PRESS_R_BD = 42, + XR_FACE_EXPRESSION_MOUTH_LOWER_DROP_L_BD = 43, + XR_FACE_EXPRESSION_MOUTH_LOWER_DROP_R_BD = 44, + XR_FACE_EXPRESSION_MOUTH_UPPER_UPWARDS_L_BD = 45, + XR_FACE_EXPRESSION_MOUTH_UPPER_UPWARDS_R_BD = 46, + XR_FACE_EXPRESSION_JAW_FORWARD_BD = 47, + XR_FACE_EXPRESSION_JAW_L_BD = 48, + XR_FACE_EXPRESSION_JAW_R_BD = 49, + XR_FACE_EXPRESSION_JAW_OPEN_BD = 50, + XR_FACE_EXPRESSION_TONGUE_OUT_BD = 51, + XR_FACE_EXPRESSION_MAX_ENUM_BD = 0x7FFFFFFF +} XrFaceExpressionBD; + +typedef enum XrLipExpressionBD { + XR_LIP_EXPRESSION_PP_BD = 0, + XR_LIP_EXPRESSION_CH_BD = 1, + XR_LIP_EXPRESSION_LO_BD = 2, + XR_LIP_EXPRESSION_O_BD = 3, + XR_LIP_EXPRESSION_I_BD = 4, + XR_LIP_EXPRESSION_LU_BD = 5, + XR_LIP_EXPRESSION_RR_BD = 6, + XR_LIP_EXPRESSION_XX_BD = 7, + XR_LIP_EXPRESSION_LAA_BD = 8, + XR_LIP_EXPRESSION_LI_BD = 9, + XR_LIP_EXPRESSION_FF_BD = 10, + XR_LIP_EXPRESSION_U_BD = 11, + XR_LIP_EXPRESSION_TH_BD = 12, + XR_LIP_EXPRESSION_LKK_BD = 13, + XR_LIP_EXPRESSION_SS_BD = 14, + XR_LIP_EXPRESSION_LE_BD = 15, + XR_LIP_EXPRESSION_DD_BD = 16, + XR_LIP_EXPRESSION_E_BD = 17, + XR_LIP_EXPRESSION_LNN_BD = 18, + XR_LIP_EXPRESSION_SIL_BD = 19, + XR_LIP_EXPRESSION_MAX_ENUM_BD = 0x7FFFFFFF +} XrLipExpressionBD; +// XrSystemFacialSimulationPropertiesBD extends XrSystemProperties +typedef struct XrSystemFacialSimulationPropertiesBD { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 supportsFaceTracking; +} XrSystemFacialSimulationPropertiesBD; + +typedef struct XrFaceTrackerCreateInfoBD { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrFacialSimulationModeBD mode; +} XrFaceTrackerCreateInfoBD; + +typedef struct XrFacialSimulationDataGetInfoBD { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrTime time; +} XrFacialSimulationDataGetInfoBD; + +typedef struct XrFacialSimulationDataBD { + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t faceExpressionWeightCount; + float* faceExpressionWeights; + XrBool32 isUpperFaceDataValid; + XrBool32 isLowerFaceDataValid; + XrTime time; +} XrFacialSimulationDataBD; + +typedef struct XrLipExpressionDataBD { + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t lipsyncExpressionWeightCount; + float* lipsyncExpressionWeights; +} XrLipExpressionDataBD; + +typedef XrResult (XRAPI_PTR *PFN_xrEnumerateFacialSimulationModesBD)(XrSession session, uint32_t modeCapacityInput, uint32_t* modeCountOutput, XrFacialSimulationModeBD* modes); +typedef XrResult (XRAPI_PTR *PFN_xrCreateFaceTrackerBD)(XrSession session, const XrFaceTrackerCreateInfoBD* createInfo, XrFaceTrackerBD* tracker); +typedef XrResult (XRAPI_PTR *PFN_xrDestroyFaceTrackerBD)(XrFaceTrackerBD tracker); +typedef XrResult (XRAPI_PTR *PFN_xrGetFacialSimulationDataBD)(XrFaceTrackerBD tracker, const XrFacialSimulationDataGetInfoBD*info, XrFacialSimulationDataBD* facialData); +typedef XrResult (XRAPI_PTR *PFN_xrSetFacialSimulationModeBD)(XrFaceTrackerBD tracker, XrFacialSimulationModeBD mode); +typedef XrResult (XRAPI_PTR *PFN_xrGetFacialSimulationModeBD)(XrFaceTrackerBD tracker, XrFacialSimulationModeBD*mode); + +#ifndef XR_NO_PROTOTYPES +#ifdef XR_EXTENSION_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateFacialSimulationModesBD( + XrSession session, + uint32_t modeCapacityInput, + uint32_t* modeCountOutput, + XrFacialSimulationModeBD* modes); + +XRAPI_ATTR XrResult XRAPI_CALL xrCreateFaceTrackerBD( + XrSession session, + const XrFaceTrackerCreateInfoBD* createInfo, + XrFaceTrackerBD* tracker); + +XRAPI_ATTR XrResult XRAPI_CALL xrDestroyFaceTrackerBD( + XrFaceTrackerBD tracker); + +XRAPI_ATTR XrResult XRAPI_CALL xrGetFacialSimulationDataBD( + XrFaceTrackerBD tracker, + const XrFacialSimulationDataGetInfoBD* info, + XrFacialSimulationDataBD* facialData); + +XRAPI_ATTR XrResult XRAPI_CALL xrSetFacialSimulationModeBD( + XrFaceTrackerBD tracker, + XrFacialSimulationModeBD mode); + +XRAPI_ATTR XrResult XRAPI_CALL xrGetFacialSimulationModeBD( + XrFaceTrackerBD tracker, + XrFacialSimulationModeBD* mode); +#endif /* XR_EXTENSION_PROTOTYPES */ +#endif /* !XR_NO_PROTOTYPES */ + + // XR_BD_spatial_sensing is a preprocessor guard. Do not pass it to API calls. #define XR_BD_spatial_sensing 1 XR_DEFINE_ATOM(XrSpatialEntityIdBD) @@ -8362,503 +8858,513 @@ XR_DEFINE_HANDLE(XrAnchorBD) #define XR_BD_SPATIAL_SENSING_EXTENSION_NAME "XR_BD_spatial_sensing" typedef enum XrSpatialEntityComponentTypeBD { - XR_SPATIAL_ENTITY_COMPONENT_TYPE_LOCATION_BD = 0, - XR_SPATIAL_ENTITY_COMPONENT_TYPE_SEMANTIC_BD = 1, - XR_SPATIAL_ENTITY_COMPONENT_TYPE_BOUNDING_BOX_2D_BD = 2, - XR_SPATIAL_ENTITY_COMPONENT_TYPE_POLYGON_BD = 3, - XR_SPATIAL_ENTITY_COMPONENT_TYPE_BOUNDING_BOX_3D_BD = 4, - XR_SPATIAL_ENTITY_COMPONENT_TYPE_TRIANGLE_MESH_BD = 5, - XR_SPATIAL_ENTITY_COMPONENT_TYPE_PLANE_ORIENTATION_BD = 1000396000, - XR_SPATIAL_ENTITY_COMPONENT_TYPE_MAX_ENUM_BD = 0x7FFFFFFF + XR_SPATIAL_ENTITY_COMPONENT_TYPE_LOCATION_BD = 0, + XR_SPATIAL_ENTITY_COMPONENT_TYPE_SEMANTIC_BD = 1, + XR_SPATIAL_ENTITY_COMPONENT_TYPE_BOUNDING_BOX_2D_BD = 2, + XR_SPATIAL_ENTITY_COMPONENT_TYPE_POLYGON_BD = 3, + XR_SPATIAL_ENTITY_COMPONENT_TYPE_BOUNDING_BOX_3D_BD = 4, + XR_SPATIAL_ENTITY_COMPONENT_TYPE_TRIANGLE_MESH_BD = 5, + XR_SPATIAL_ENTITY_COMPONENT_TYPE_PLANE_ORIENTATION_BD = 1000396000, + XR_SPATIAL_ENTITY_COMPONENT_TYPE_MAX_ENUM_BD = 0x7FFFFFFF } XrSpatialEntityComponentTypeBD; typedef enum XrSemanticLabelBD { - XR_SEMANTIC_LABEL_UNKNOWN_BD = 0, - XR_SEMANTIC_LABEL_FLOOR_BD = 1, - XR_SEMANTIC_LABEL_CEILING_BD = 2, - XR_SEMANTIC_LABEL_WALL_BD = 3, - XR_SEMANTIC_LABEL_DOOR_BD = 4, - XR_SEMANTIC_LABEL_WINDOW_BD = 5, - XR_SEMANTIC_LABEL_OPENING_BD = 6, - XR_SEMANTIC_LABEL_TABLE_BD = 7, - XR_SEMANTIC_LABEL_SOFA_BD = 8, - XR_SEMANTIC_LABEL_CHAIR_BD = 9, - XR_SEMANTIC_LABEL_HUMAN_BD = 10, - XR_SEMANTIC_LABEL_BEAM_BD = 11, - XR_SEMANTIC_LABEL_COLUMN_BD = 12, - XR_SEMANTIC_LABEL_CURTAIN_BD = 13, - XR_SEMANTIC_LABEL_CABINET_BD = 14, - XR_SEMANTIC_LABEL_BED_BD = 15, - XR_SEMANTIC_LABEL_PLANT_BD = 16, - XR_SEMANTIC_LABEL_SCREEN_BD = 17, - XR_SEMANTIC_LABEL_VIRTUAL_WALL_BD = 18, - XR_SEMANTIC_LABEL_REFRIGERATOR_BD = 19, - XR_SEMANTIC_LABEL_WASHING_MACHINE_BD = 20, - XR_SEMANTIC_LABEL_AIR_CONDITIONER_BD = 21, - XR_SEMANTIC_LABEL_LAMP_BD = 22, - XR_SEMANTIC_LABEL_WALL_ART_BD = 23, - XR_SEMANTIC_LABEL_STAIRWAY_BD = 24, - XR_SEMANTIC_LABEL_MAX_ENUM_BD = 0x7FFFFFFF + XR_SEMANTIC_LABEL_UNKNOWN_BD = 0, + XR_SEMANTIC_LABEL_FLOOR_BD = 1, + XR_SEMANTIC_LABEL_CEILING_BD = 2, + XR_SEMANTIC_LABEL_WALL_BD = 3, + XR_SEMANTIC_LABEL_DOOR_BD = 4, + XR_SEMANTIC_LABEL_WINDOW_BD = 5, + XR_SEMANTIC_LABEL_OPENING_BD = 6, + XR_SEMANTIC_LABEL_TABLE_BD = 7, + XR_SEMANTIC_LABEL_SOFA_BD = 8, + XR_SEMANTIC_LABEL_CHAIR_BD = 9, + XR_SEMANTIC_LABEL_HUMAN_BD = 10, + XR_SEMANTIC_LABEL_BEAM_BD = 11, + XR_SEMANTIC_LABEL_COLUMN_BD = 12, + XR_SEMANTIC_LABEL_CURTAIN_BD = 13, + XR_SEMANTIC_LABEL_CABINET_BD = 14, + XR_SEMANTIC_LABEL_BED_BD = 15, + XR_SEMANTIC_LABEL_PLANT_BD = 16, + XR_SEMANTIC_LABEL_SCREEN_BD = 17, + XR_SEMANTIC_LABEL_VIRTUAL_WALL_BD = 18, + XR_SEMANTIC_LABEL_REFRIGERATOR_BD = 19, + XR_SEMANTIC_LABEL_WASHING_MACHINE_BD = 20, + XR_SEMANTIC_LABEL_AIR_CONDITIONER_BD = 21, + XR_SEMANTIC_LABEL_LAMP_BD = 22, + XR_SEMANTIC_LABEL_WALL_ART_BD = 23, + XR_SEMANTIC_LABEL_STAIRWAY_BD = 24, + XR_SEMANTIC_LABEL_MAX_ENUM_BD = 0x7FFFFFFF } XrSemanticLabelBD; typedef enum XrSenseDataProviderTypeBD { - XR_SENSE_DATA_PROVIDER_TYPE_ANCHOR_BD = 1000390000, - XR_SENSE_DATA_PROVIDER_TYPE_SCENE_BD = 1000392000, - XR_SENSE_DATA_PROVIDER_TYPE_MESH_BD = 1000393000, - XR_SENSE_DATA_PROVIDER_TYPE_PLANE_BD = 1000396000, - XR_SENSE_DATA_PROVIDER_TYPE_MAX_ENUM_BD = 0x7FFFFFFF + XR_SENSE_DATA_PROVIDER_TYPE_ANCHOR_BD = 1000390000, + XR_SENSE_DATA_PROVIDER_TYPE_SCENE_BD = 1000392000, + XR_SENSE_DATA_PROVIDER_TYPE_MESH_BD = 1000393000, + XR_SENSE_DATA_PROVIDER_TYPE_PLANE_BD = 1000396000, + XR_SENSE_DATA_PROVIDER_TYPE_MAX_ENUM_BD = 0x7FFFFFFF } XrSenseDataProviderTypeBD; typedef enum XrSenseDataProviderStateBD { - XR_SENSE_DATA_PROVIDER_STATE_INITIALIZED_BD = 0, - XR_SENSE_DATA_PROVIDER_STATE_RUNNING_BD = 1, - XR_SENSE_DATA_PROVIDER_STATE_STOPPED_BD = 2, - XR_SENSE_DATA_PROVIDER_STATE_MAX_ENUM_BD = 0x7FFFFFFF + XR_SENSE_DATA_PROVIDER_STATE_INITIALIZED_BD = 0, + XR_SENSE_DATA_PROVIDER_STATE_RUNNING_BD = 1, + XR_SENSE_DATA_PROVIDER_STATE_STOPPED_BD = 2, + XR_SENSE_DATA_PROVIDER_STATE_MAX_ENUM_BD = 0x7FFFFFFF } XrSenseDataProviderStateBD; // XrSystemSpatialSensingPropertiesBD extends XrSystemProperties typedef struct XrSystemSpatialSensingPropertiesBD { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrBool32 supportsSpatialSensing; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 supportsSpatialSensing; } XrSystemSpatialSensingPropertiesBD; typedef struct XrSpatialEntityComponentGetInfoBD { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpatialEntityIdBD entityId; - XrSpatialEntityComponentTypeBD componentType; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpatialEntityIdBD entityId; + XrSpatialEntityComponentTypeBD componentType; } XrSpatialEntityComponentGetInfoBD; typedef struct XrSpatialEntityComponentDataBaseHeaderBD { - XrStructureType type; - void* XR_MAY_ALIAS next; + XrStructureType type; + void* XR_MAY_ALIAS next; } XrSpatialEntityComponentDataBaseHeaderBD; typedef struct XrSpatialEntityLocationGetInfoBD { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpace baseSpace; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpace baseSpace; } XrSpatialEntityLocationGetInfoBD; // XrSpatialEntityComponentDataLocationBD extends XrSpatialEntityComponentDataBaseHeaderBD typedef struct XrSpatialEntityComponentDataLocationBD { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrSpaceLocation location; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrSpaceLocation location; } XrSpatialEntityComponentDataLocationBD; // XrSpatialEntityComponentDataSemanticBD extends XrSpatialEntityComponentDataBaseHeaderBD typedef struct XrSpatialEntityComponentDataSemanticBD { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t labelCapacityInput; - uint32_t labelCountOutput; - XrSemanticLabelBD* labels; + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t labelCapacityInput; + uint32_t labelCountOutput; + XrSemanticLabelBD* labels; } XrSpatialEntityComponentDataSemanticBD; // XrSpatialEntityComponentDataBoundingBox2DBD extends XrSpatialEntityComponentDataBaseHeaderBD typedef struct XrSpatialEntityComponentDataBoundingBox2DBD { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrRect2Df boundingBox2D; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrRect2Df boundingBox2D; } XrSpatialEntityComponentDataBoundingBox2DBD; // XrSpatialEntityComponentDataPolygonBD extends XrSpatialEntityComponentDataBaseHeaderBD typedef struct XrSpatialEntityComponentDataPolygonBD { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t vertexCapacityInput; - uint32_t vertexCountOutput; - XrVector2f* vertices; + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t vertexCapacityInput; + uint32_t vertexCountOutput; + XrVector2f* vertices; } XrSpatialEntityComponentDataPolygonBD; // XrSpatialEntityComponentDataBoundingBox3DBD extends XrSpatialEntityComponentDataBaseHeaderBD typedef struct XrSpatialEntityComponentDataBoundingBox3DBD { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrBoxf boundingBox3D; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBoxf boundingBox3D; } XrSpatialEntityComponentDataBoundingBox3DBD; // XrSpatialEntityComponentDataTriangleMeshBD extends XrSpatialEntityComponentDataBaseHeaderBD typedef struct XrSpatialEntityComponentDataTriangleMeshBD { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t vertexCapacityInput; - uint32_t vertexCountOutput; - XrVector3f* vertices; - uint32_t indexCapacityInput; - uint32_t indexCountOutput; - uint16_t* indices; + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t vertexCapacityInput; + uint32_t vertexCountOutput; + XrVector3f* vertices; + uint32_t indexCapacityInput; + uint32_t indexCountOutput; + uint16_t* indices; } XrSpatialEntityComponentDataTriangleMeshBD; typedef struct XrSenseDataProviderCreateInfoBD { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSenseDataProviderTypeBD providerType; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSenseDataProviderTypeBD providerType; } XrSenseDataProviderCreateInfoBD; typedef struct XrSenseDataProviderStartInfoBD { - XrStructureType type; - const void* XR_MAY_ALIAS next; + XrStructureType type; + const void* XR_MAY_ALIAS next; } XrSenseDataProviderStartInfoBD; typedef struct XrEventDataSenseDataProviderStateChangedBD { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSenseDataProviderBD provider; - XrSenseDataProviderStateBD newState; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSenseDataProviderBD provider; + XrSenseDataProviderStateBD newState; } XrEventDataSenseDataProviderStateChangedBD; typedef struct XrEventDataSenseDataUpdatedBD { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSenseDataProviderBD provider; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSenseDataProviderBD provider; } XrEventDataSenseDataUpdatedBD; typedef struct XrSenseDataQueryInfoBD { - XrStructureType type; - const void* XR_MAY_ALIAS next; + XrStructureType type; + const void* XR_MAY_ALIAS next; } XrSenseDataQueryInfoBD; typedef struct XrSenseDataQueryCompletionBD { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrResult futureResult; - XrSenseDataSnapshotBD snapshot; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrResult futureResult; + XrSenseDataSnapshotBD snapshot; } XrSenseDataQueryCompletionBD; typedef struct XrQueriedSenseDataGetInfoBD { - XrStructureType type; - const void* XR_MAY_ALIAS next; + XrStructureType type; + const void* XR_MAY_ALIAS next; } XrQueriedSenseDataGetInfoBD; typedef struct XrSpatialEntityStateBD { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrSpatialEntityIdBD entityId; - XrTime lastUpdateTime; - XrUuidEXT uuid; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrSpatialEntityIdBD entityId; + XrTime lastUpdateTime; + XrUuidEXT uuid; } XrSpatialEntityStateBD; typedef struct XrQueriedSenseDataBD { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t stateCapacityInput; - uint32_t stateCountOutput; - XrSpatialEntityStateBD* states; + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t stateCapacityInput; + uint32_t stateCountOutput; + XrSpatialEntityStateBD* states; } XrQueriedSenseDataBD; // XrSenseDataFilterUuidBD extends XrSenseDataQueryInfoBD typedef struct XrSenseDataFilterUuidBD { - XrStructureType type; - const void* XR_MAY_ALIAS next; - uint32_t uuidCount; - const XrUuidEXT* uuids; + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t uuidCount; + const XrUuidEXT* uuids; } XrSenseDataFilterUuidBD; // XrSenseDataFilterSemanticBD extends XrSenseDataQueryInfoBD typedef struct XrSenseDataFilterSemanticBD { - XrStructureType type; - const void* XR_MAY_ALIAS next; - uint32_t labelCount; - const XrSemanticLabelBD* labels; + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t labelCount; + const XrSemanticLabelBD* labels; } XrSenseDataFilterSemanticBD; typedef struct XrSpatialEntityAnchorCreateInfoBD { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSenseDataSnapshotBD snapshot; - XrSpatialEntityIdBD entityId; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSenseDataSnapshotBD snapshot; + XrSpatialEntityIdBD entityId; } XrSpatialEntityAnchorCreateInfoBD; typedef struct XrAnchorSpaceCreateInfoBD { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrAnchorBD anchor; - XrPosef poseInAnchorSpace; -} XrAnchorSpaceCreateInfoBD; - + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrAnchorBD anchor; + XrPosef poseInAnchorSpace; +} XrAnchorSpaceCreateInfoBD; + typedef struct XrFutureCompletionEXT { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrResult futureResult; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrResult futureResult; } XrFutureCompletionEXT; -typedef XrResult(XRAPI_PTR* PFN_xrEnumerateSpatialEntityComponentTypesBD)( - XrSenseDataSnapshotBD snapshot, XrSpatialEntityIdBD entityId, uint32_t componentTypeCapacityInput, - uint32_t* componentTypeCountOutput, XrSpatialEntityComponentTypeBD* componentTypes); -typedef XrResult(XRAPI_PTR* PFN_xrGetSpatialEntityUuidBD)(XrSenseDataSnapshotBD snapshot, XrSpatialEntityIdBD entityId, - XrUuidEXT* uuid); -typedef XrResult(XRAPI_PTR* PFN_xrGetSpatialEntityComponentDataBD)( - XrSenseDataSnapshotBD snapshot, const XrSpatialEntityComponentGetInfoBD* getInfo, - XrSpatialEntityComponentDataBaseHeaderBD* componentData); -typedef XrResult(XRAPI_PTR* PFN_xrCreateSenseDataProviderBD)(XrSession session, - const XrSenseDataProviderCreateInfoBD* createInfo, - XrSenseDataProviderBD* provider); -typedef XrResult(XRAPI_PTR* PFN_xrStartSenseDataProviderAsyncBD)(XrSenseDataProviderBD provider, - const XrSenseDataProviderStartInfoBD* startInfo, - XrFutureEXT* future); -typedef XrResult(XRAPI_PTR* PFN_xrStartSenseDataProviderCompleteBD)(XrSession session, XrFutureEXT future, - XrFutureCompletionEXT* completion); -typedef XrResult(XRAPI_PTR* PFN_xrGetSenseDataProviderStateBD)(XrSenseDataProviderBD provider, - XrSenseDataProviderStateBD* state); -typedef XrResult(XRAPI_PTR* PFN_xrQuerySenseDataAsyncBD)(XrSenseDataProviderBD provider, - const XrSenseDataQueryInfoBD* queryInfo, XrFutureEXT* future); -typedef XrResult(XRAPI_PTR* PFN_xrQuerySenseDataCompleteBD)(XrSenseDataProviderBD provider, XrFutureEXT future, - XrSenseDataQueryCompletionBD* completion); -typedef XrResult(XRAPI_PTR* PFN_xrDestroySenseDataSnapshotBD)(XrSenseDataSnapshotBD snapshot); -typedef XrResult(XRAPI_PTR* PFN_xrGetQueriedSenseDataBD)(XrSenseDataSnapshotBD snapshot, - XrQueriedSenseDataGetInfoBD* getInfo, - XrQueriedSenseDataBD* queriedSenseData); -typedef XrResult(XRAPI_PTR* PFN_xrStopSenseDataProviderBD)(XrSenseDataProviderBD provider); -typedef XrResult(XRAPI_PTR* PFN_xrDestroySenseDataProviderBD)(XrSenseDataProviderBD provider); -typedef XrResult(XRAPI_PTR* PFN_xrCreateSpatialEntityAnchorBD)(XrSenseDataProviderBD provider, - const XrSpatialEntityAnchorCreateInfoBD* createInfo, - XrAnchorBD* anchor); -typedef XrResult(XRAPI_PTR* PFN_xrDestroyAnchorBD)(XrAnchorBD anchor); -typedef XrResult(XRAPI_PTR* PFN_xrGetAnchorUuidBD)(XrAnchorBD anchor, XrUuidEXT* uuid); -typedef XrResult(XRAPI_PTR* PFN_xrCreateAnchorSpaceBD)(XrSession session, const XrAnchorSpaceCreateInfoBD* createInfo, - XrSpace* space); +typedef XrResult (XRAPI_PTR *PFN_xrEnumerateSpatialEntityComponentTypesBD)(XrSenseDataSnapshotBD snapshot, XrSpatialEntityIdBD entityId, uint32_t componentTypeCapacityInput, uint32_t* componentTypeCountOutput, XrSpatialEntityComponentTypeBD* componentTypes); +typedef XrResult (XRAPI_PTR *PFN_xrGetSpatialEntityUuidBD)(XrSenseDataSnapshotBD snapshot, XrSpatialEntityIdBD entityId, XrUuidEXT* uuid); +typedef XrResult (XRAPI_PTR *PFN_xrGetSpatialEntityComponentDataBD)(XrSenseDataSnapshotBD snapshot, const XrSpatialEntityComponentGetInfoBD* getInfo, XrSpatialEntityComponentDataBaseHeaderBD* componentData); +typedef XrResult (XRAPI_PTR *PFN_xrCreateSenseDataProviderBD)(XrSession session, const XrSenseDataProviderCreateInfoBD* createInfo, XrSenseDataProviderBD* provider); +typedef XrResult (XRAPI_PTR *PFN_xrStartSenseDataProviderAsyncBD)(XrSenseDataProviderBD provider, const XrSenseDataProviderStartInfoBD* startInfo, XrFutureEXT* future); +typedef XrResult (XRAPI_PTR *PFN_xrStartSenseDataProviderCompleteBD)(XrSession session, XrFutureEXT future, XrFutureCompletionEXT* completion); +typedef XrResult (XRAPI_PTR *PFN_xrGetSenseDataProviderStateBD)(XrSenseDataProviderBD provider, XrSenseDataProviderStateBD* state); +typedef XrResult (XRAPI_PTR *PFN_xrQuerySenseDataAsyncBD)(XrSenseDataProviderBD provider, const XrSenseDataQueryInfoBD* queryInfo, XrFutureEXT* future); +typedef XrResult (XRAPI_PTR *PFN_xrQuerySenseDataCompleteBD)(XrSenseDataProviderBD provider, XrFutureEXT future, XrSenseDataQueryCompletionBD* completion); +typedef XrResult (XRAPI_PTR *PFN_xrDestroySenseDataSnapshotBD)(XrSenseDataSnapshotBD snapshot); +typedef XrResult (XRAPI_PTR *PFN_xrGetQueriedSenseDataBD)(XrSenseDataSnapshotBD snapshot, XrQueriedSenseDataGetInfoBD* getInfo, XrQueriedSenseDataBD* queriedSenseData); +typedef XrResult (XRAPI_PTR *PFN_xrStopSenseDataProviderBD)(XrSenseDataProviderBD provider); +typedef XrResult (XRAPI_PTR *PFN_xrDestroySenseDataProviderBD)(XrSenseDataProviderBD provider); +typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialEntityAnchorBD)(XrSenseDataProviderBD provider, const XrSpatialEntityAnchorCreateInfoBD* createInfo, XrAnchorBD* anchor); +typedef XrResult (XRAPI_PTR *PFN_xrDestroyAnchorBD)(XrAnchorBD anchor); +typedef XrResult (XRAPI_PTR *PFN_xrGetAnchorUuidBD)(XrAnchorBD anchor, XrUuidEXT* uuid); +typedef XrResult (XRAPI_PTR *PFN_xrCreateAnchorSpaceBD)(XrSession session, const XrAnchorSpaceCreateInfoBD* createInfo, XrSpace* space); #ifndef XR_NO_PROTOTYPES #ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSpatialEntityComponentTypesBD(XrSenseDataSnapshotBD snapshot, - XrSpatialEntityIdBD entityId, - uint32_t componentTypeCapacityInput, - uint32_t* componentTypeCountOutput, - XrSpatialEntityComponentTypeBD* componentTypes); +XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSpatialEntityComponentTypesBD( + XrSenseDataSnapshotBD snapshot, + XrSpatialEntityIdBD entityId, + uint32_t componentTypeCapacityInput, + uint32_t* componentTypeCountOutput, + XrSpatialEntityComponentTypeBD* componentTypes); -XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialEntityUuidBD(XrSenseDataSnapshotBD snapshot, XrSpatialEntityIdBD entityId, - XrUuidEXT* uuid); +XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialEntityUuidBD( + XrSenseDataSnapshotBD snapshot, + XrSpatialEntityIdBD entityId, + XrUuidEXT* uuid); -XRAPI_ATTR XrResult XRAPI_CALL -xrGetSpatialEntityComponentDataBD(XrSenseDataSnapshotBD snapshot, const XrSpatialEntityComponentGetInfoBD* getInfo, - XrSpatialEntityComponentDataBaseHeaderBD* componentData); +XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialEntityComponentDataBD( + XrSenseDataSnapshotBD snapshot, + const XrSpatialEntityComponentGetInfoBD* getInfo, + XrSpatialEntityComponentDataBaseHeaderBD* componentData); -XRAPI_ATTR XrResult XRAPI_CALL xrCreateSenseDataProviderBD(XrSession session, - const XrSenseDataProviderCreateInfoBD* createInfo, - XrSenseDataProviderBD* provider); +XRAPI_ATTR XrResult XRAPI_CALL xrCreateSenseDataProviderBD( + XrSession session, + const XrSenseDataProviderCreateInfoBD* createInfo, + XrSenseDataProviderBD* provider); -XRAPI_ATTR XrResult XRAPI_CALL xrStartSenseDataProviderAsyncBD(XrSenseDataProviderBD provider, - const XrSenseDataProviderStartInfoBD* startInfo, - XrFutureEXT* future); +XRAPI_ATTR XrResult XRAPI_CALL xrStartSenseDataProviderAsyncBD( + XrSenseDataProviderBD provider, + const XrSenseDataProviderStartInfoBD* startInfo, + XrFutureEXT* future); -XRAPI_ATTR XrResult XRAPI_CALL xrStartSenseDataProviderCompleteBD(XrSession session, XrFutureEXT future, - XrFutureCompletionEXT* completion); +XRAPI_ATTR XrResult XRAPI_CALL xrStartSenseDataProviderCompleteBD( + XrSession session, + XrFutureEXT future, + XrFutureCompletionEXT* completion); -XRAPI_ATTR XrResult XRAPI_CALL xrGetSenseDataProviderStateBD(XrSenseDataProviderBD provider, - XrSenseDataProviderStateBD* state); +XRAPI_ATTR XrResult XRAPI_CALL xrGetSenseDataProviderStateBD( + XrSenseDataProviderBD provider, + XrSenseDataProviderStateBD* state); -XRAPI_ATTR XrResult XRAPI_CALL xrQuerySenseDataAsyncBD(XrSenseDataProviderBD provider, - const XrSenseDataQueryInfoBD* queryInfo, XrFutureEXT* future); +XRAPI_ATTR XrResult XRAPI_CALL xrQuerySenseDataAsyncBD( + XrSenseDataProviderBD provider, + const XrSenseDataQueryInfoBD* queryInfo, + XrFutureEXT* future); -XRAPI_ATTR XrResult XRAPI_CALL xrQuerySenseDataCompleteBD(XrSenseDataProviderBD provider, XrFutureEXT future, - XrSenseDataQueryCompletionBD* completion); +XRAPI_ATTR XrResult XRAPI_CALL xrQuerySenseDataCompleteBD( + XrSenseDataProviderBD provider, + XrFutureEXT future, + XrSenseDataQueryCompletionBD* completion); -XRAPI_ATTR XrResult XRAPI_CALL xrDestroySenseDataSnapshotBD(XrSenseDataSnapshotBD snapshot); +XRAPI_ATTR XrResult XRAPI_CALL xrDestroySenseDataSnapshotBD( + XrSenseDataSnapshotBD snapshot); -XRAPI_ATTR XrResult XRAPI_CALL xrGetQueriedSenseDataBD(XrSenseDataSnapshotBD snapshot, - XrQueriedSenseDataGetInfoBD* getInfo, - XrQueriedSenseDataBD* queriedSenseData); +XRAPI_ATTR XrResult XRAPI_CALL xrGetQueriedSenseDataBD( + XrSenseDataSnapshotBD snapshot, + XrQueriedSenseDataGetInfoBD* getInfo, + XrQueriedSenseDataBD* queriedSenseData); -XRAPI_ATTR XrResult XRAPI_CALL xrStopSenseDataProviderBD(XrSenseDataProviderBD provider); +XRAPI_ATTR XrResult XRAPI_CALL xrStopSenseDataProviderBD( + XrSenseDataProviderBD provider); -XRAPI_ATTR XrResult XRAPI_CALL xrDestroySenseDataProviderBD(XrSenseDataProviderBD provider); +XRAPI_ATTR XrResult XRAPI_CALL xrDestroySenseDataProviderBD( + XrSenseDataProviderBD provider); -XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialEntityAnchorBD(XrSenseDataProviderBD provider, - const XrSpatialEntityAnchorCreateInfoBD* createInfo, - XrAnchorBD* anchor); +XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialEntityAnchorBD( + XrSenseDataProviderBD provider, + const XrSpatialEntityAnchorCreateInfoBD* createInfo, + XrAnchorBD* anchor); -XRAPI_ATTR XrResult XRAPI_CALL xrDestroyAnchorBD(XrAnchorBD anchor); +XRAPI_ATTR XrResult XRAPI_CALL xrDestroyAnchorBD( + XrAnchorBD anchor); -XRAPI_ATTR XrResult XRAPI_CALL xrGetAnchorUuidBD(XrAnchorBD anchor, XrUuidEXT* uuid); +XRAPI_ATTR XrResult XRAPI_CALL xrGetAnchorUuidBD( + XrAnchorBD anchor, + XrUuidEXT* uuid); -XRAPI_ATTR XrResult XRAPI_CALL xrCreateAnchorSpaceBD(XrSession session, const XrAnchorSpaceCreateInfoBD* createInfo, - XrSpace* space); +XRAPI_ATTR XrResult XRAPI_CALL xrCreateAnchorSpaceBD( + XrSession session, + const XrAnchorSpaceCreateInfoBD* createInfo, + XrSpace* space); #endif /* XR_EXTENSION_PROTOTYPES */ #endif /* !XR_NO_PROTOTYPES */ + // XR_BD_spatial_anchor is a preprocessor guard. Do not pass it to API calls. #define XR_BD_spatial_anchor 1 -#define XR_BD_spatial_anchor_SPEC_VERSION 1 +#define XR_BD_spatial_anchor_SPEC_VERSION 2 #define XR_BD_SPATIAL_ANCHOR_EXTENSION_NAME "XR_BD_spatial_anchor" typedef enum XrPersistenceLocationBD { - XR_PERSISTENCE_LOCATION_LOCAL_BD = 0, - XR_PERSISTENCE_LOCATION_MAX_ENUM_BD = 0x7FFFFFFF + XR_PERSISTENCE_LOCATION_LOCAL_BD = 0, + XR_PERSISTENCE_LOCATION_MAX_ENUM_BD = 0x7FFFFFFF } XrPersistenceLocationBD; // XrSystemSpatialAnchorPropertiesBD extends XrSystemProperties typedef struct XrSystemSpatialAnchorPropertiesBD { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrBool32 supportsSpatialAnchor; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 supportsSpatialAnchor; } XrSystemSpatialAnchorPropertiesBD; typedef struct XrSpatialAnchorCreateInfoBD { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpace space; - XrPosef pose; - XrTime time; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpace space; + XrPosef pose; + XrTime time; } XrSpatialAnchorCreateInfoBD; typedef struct XrSpatialAnchorCreateCompletionBD { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrResult futureResult; - XrAnchorBD anchor; - XrUuidEXT uuid; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrResult futureResult; + XrUuidEXT uuid; + XrAnchorBD anchor; } XrSpatialAnchorCreateCompletionBD; typedef struct XrSpatialAnchorPersistInfoBD { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrPersistenceLocationBD location; - XrAnchorBD anchor; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrPersistenceLocationBD location; + XrAnchorBD anchor; } XrSpatialAnchorPersistInfoBD; typedef struct XrSpatialAnchorUnpersistInfoBD { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrPersistenceLocationBD location; - XrAnchorBD anchor; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrPersistenceLocationBD location; + XrAnchorBD anchor; } XrSpatialAnchorUnpersistInfoBD; -typedef XrResult(XRAPI_PTR* PFN_xrCreateSpatialAnchorAsyncBD)(XrSenseDataProviderBD provider, - const XrSpatialAnchorCreateInfoBD* info, - XrFutureEXT* future); -typedef XrResult(XRAPI_PTR* PFN_xrCreateSpatialAnchorCompleteBD)(XrSenseDataProviderBD provider, XrFutureEXT future, - XrSpatialAnchorCreateCompletionBD* completion); -typedef XrResult(XRAPI_PTR* PFN_xrPersistSpatialAnchorAsyncBD)(XrSenseDataProviderBD provider, - const XrSpatialAnchorPersistInfoBD* info, - XrFutureEXT* future); -typedef XrResult(XRAPI_PTR* PFN_xrPersistSpatialAnchorCompleteBD)(XrSenseDataProviderBD provider, XrFutureEXT future, - XrFutureCompletionEXT* completion); -typedef XrResult(XRAPI_PTR* PFN_xrUnpersistSpatialAnchorAsyncBD)(XrSenseDataProviderBD provider, - const XrSpatialAnchorUnpersistInfoBD* info, - XrFutureEXT* future); -typedef XrResult(XRAPI_PTR* PFN_xrUnpersistSpatialAnchorCompleteBD)(XrSenseDataProviderBD provider, XrFutureEXT future, - XrFutureCompletionEXT* completion); +typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialAnchorAsyncBD)(XrSenseDataProviderBD provider, const XrSpatialAnchorCreateInfoBD* info, XrFutureEXT* future); +typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialAnchorCompleteBD)(XrSenseDataProviderBD provider, XrFutureEXT future, XrSpatialAnchorCreateCompletionBD* completion); +typedef XrResult (XRAPI_PTR *PFN_xrPersistSpatialAnchorAsyncBD)(XrSenseDataProviderBD provider, const XrSpatialAnchorPersistInfoBD* info, XrFutureEXT* future); +typedef XrResult (XRAPI_PTR *PFN_xrPersistSpatialAnchorCompleteBD)(XrSenseDataProviderBD provider, XrFutureEXT future, XrFutureCompletionEXT* completion); +typedef XrResult (XRAPI_PTR *PFN_xrUnpersistSpatialAnchorAsyncBD)(XrSenseDataProviderBD provider, const XrSpatialAnchorUnpersistInfoBD* info, XrFutureEXT* future); +typedef XrResult (XRAPI_PTR *PFN_xrUnpersistSpatialAnchorCompleteBD)(XrSenseDataProviderBD provider, XrFutureEXT future, XrFutureCompletionEXT* completion); #ifndef XR_NO_PROTOTYPES #ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorAsyncBD(XrSenseDataProviderBD provider, - const XrSpatialAnchorCreateInfoBD* info, - XrFutureEXT* future); +XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorAsyncBD( + XrSenseDataProviderBD provider, + const XrSpatialAnchorCreateInfoBD* info, + XrFutureEXT* future); -XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorCompleteBD(XrSenseDataProviderBD provider, XrFutureEXT future, - XrSpatialAnchorCreateCompletionBD* completion); +XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorCompleteBD( + XrSenseDataProviderBD provider, + XrFutureEXT future, + XrSpatialAnchorCreateCompletionBD* completion); -XRAPI_ATTR XrResult XRAPI_CALL xrPersistSpatialAnchorAsyncBD(XrSenseDataProviderBD provider, - const XrSpatialAnchorPersistInfoBD* info, - XrFutureEXT* future); +XRAPI_ATTR XrResult XRAPI_CALL xrPersistSpatialAnchorAsyncBD( + XrSenseDataProviderBD provider, + const XrSpatialAnchorPersistInfoBD* info, + XrFutureEXT* future); -XRAPI_ATTR XrResult XRAPI_CALL xrPersistSpatialAnchorCompleteBD(XrSenseDataProviderBD provider, XrFutureEXT future, - XrFutureCompletionEXT* completion); +XRAPI_ATTR XrResult XRAPI_CALL xrPersistSpatialAnchorCompleteBD( + XrSenseDataProviderBD provider, + XrFutureEXT future, + XrFutureCompletionEXT* completion); -XRAPI_ATTR XrResult XRAPI_CALL xrUnpersistSpatialAnchorAsyncBD(XrSenseDataProviderBD provider, - const XrSpatialAnchorUnpersistInfoBD* info, - XrFutureEXT* future); +XRAPI_ATTR XrResult XRAPI_CALL xrUnpersistSpatialAnchorAsyncBD( + XrSenseDataProviderBD provider, + const XrSpatialAnchorUnpersistInfoBD* info, + XrFutureEXT* future); -XRAPI_ATTR XrResult XRAPI_CALL xrUnpersistSpatialAnchorCompleteBD(XrSenseDataProviderBD provider, XrFutureEXT future, - XrFutureCompletionEXT* completion); +XRAPI_ATTR XrResult XRAPI_CALL xrUnpersistSpatialAnchorCompleteBD( + XrSenseDataProviderBD provider, + XrFutureEXT future, + XrFutureCompletionEXT* completion); #endif /* XR_EXTENSION_PROTOTYPES */ #endif /* !XR_NO_PROTOTYPES */ + // XR_BD_spatial_anchor_sharing is a preprocessor guard. Do not pass it to API calls. #define XR_BD_spatial_anchor_sharing 1 #define XR_BD_spatial_anchor_sharing_SPEC_VERSION 2 #define XR_BD_SPATIAL_ANCHOR_SHARING_EXTENSION_NAME "XR_BD_spatial_anchor_sharing" // XrSystemSpatialAnchorSharingPropertiesBD extends XrSystemProperties typedef struct XrSystemSpatialAnchorSharingPropertiesBD { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrBool32 supportsSpatialAnchorSharing; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 supportsSpatialAnchorSharing; } XrSystemSpatialAnchorSharingPropertiesBD; typedef struct XrSpatialAnchorShareInfoBD { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrAnchorBD anchor; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrAnchorBD anchor; } XrSpatialAnchorShareInfoBD; typedef struct XrSharedSpatialAnchorDownloadInfoBD { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrUuidEXT uuid; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrUuidEXT uuid; } XrSharedSpatialAnchorDownloadInfoBD; -typedef XrResult(XRAPI_PTR* PFN_xrShareSpatialAnchorAsyncBD)(XrSenseDataProviderBD provider, - const XrSpatialAnchorShareInfoBD* info, - XrFutureEXT* future); -typedef XrResult(XRAPI_PTR* PFN_xrShareSpatialAnchorCompleteBD)(XrSenseDataProviderBD provider, XrFutureEXT future, - XrFutureCompletionEXT* completion); -typedef XrResult(XRAPI_PTR* PFN_xrDownloadSharedSpatialAnchorAsyncBD)(XrSenseDataProviderBD provider, - const XrSharedSpatialAnchorDownloadInfoBD* info, - XrFutureEXT* future); -typedef XrResult(XRAPI_PTR* PFN_xrDownloadSharedSpatialAnchorCompleteBD)(XrSenseDataProviderBD provider, - XrFutureEXT future, - XrFutureCompletionEXT* completion); +typedef XrResult (XRAPI_PTR *PFN_xrShareSpatialAnchorAsyncBD)(XrSenseDataProviderBD provider, const XrSpatialAnchorShareInfoBD* info, XrFutureEXT* future); +typedef XrResult (XRAPI_PTR *PFN_xrShareSpatialAnchorCompleteBD)(XrSenseDataProviderBD provider, XrFutureEXT future, XrFutureCompletionEXT* completion); +typedef XrResult (XRAPI_PTR *PFN_xrDownloadSharedSpatialAnchorAsyncBD)(XrSenseDataProviderBD provider, const XrSharedSpatialAnchorDownloadInfoBD* info, XrFutureEXT* future); +typedef XrResult (XRAPI_PTR *PFN_xrDownloadSharedSpatialAnchorCompleteBD)(XrSenseDataProviderBD provider, XrFutureEXT future, XrFutureCompletionEXT* completion); #ifndef XR_NO_PROTOTYPES #ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrShareSpatialAnchorAsyncBD(XrSenseDataProviderBD provider, - const XrSpatialAnchorShareInfoBD* info, XrFutureEXT* future); +XRAPI_ATTR XrResult XRAPI_CALL xrShareSpatialAnchorAsyncBD( + XrSenseDataProviderBD provider, + const XrSpatialAnchorShareInfoBD* info, + XrFutureEXT* future); -XRAPI_ATTR XrResult XRAPI_CALL xrShareSpatialAnchorCompleteBD(XrSenseDataProviderBD provider, XrFutureEXT future, - XrFutureCompletionEXT* completion); +XRAPI_ATTR XrResult XRAPI_CALL xrShareSpatialAnchorCompleteBD( + XrSenseDataProviderBD provider, + XrFutureEXT future, + XrFutureCompletionEXT* completion); -XRAPI_ATTR XrResult XRAPI_CALL xrDownloadSharedSpatialAnchorAsyncBD(XrSenseDataProviderBD provider, - const XrSharedSpatialAnchorDownloadInfoBD* info, - XrFutureEXT* future); +XRAPI_ATTR XrResult XRAPI_CALL xrDownloadSharedSpatialAnchorAsyncBD( + XrSenseDataProviderBD provider, + const XrSharedSpatialAnchorDownloadInfoBD* info, + XrFutureEXT* future); -XRAPI_ATTR XrResult XRAPI_CALL xrDownloadSharedSpatialAnchorCompleteBD(XrSenseDataProviderBD provider, - XrFutureEXT future, - XrFutureCompletionEXT* completion); +XRAPI_ATTR XrResult XRAPI_CALL xrDownloadSharedSpatialAnchorCompleteBD( + XrSenseDataProviderBD provider, + XrFutureEXT future, + XrFutureCompletionEXT* completion); #endif /* XR_EXTENSION_PROTOTYPES */ #endif /* !XR_NO_PROTOTYPES */ + // XR_BD_spatial_scene is a preprocessor guard. Do not pass it to API calls. #define XR_BD_spatial_scene 1 -#define XR_BD_spatial_scene_SPEC_VERSION 1 +#define XR_BD_spatial_scene_SPEC_VERSION 1 #define XR_BD_SPATIAL_SCENE_EXTENSION_NAME "XR_BD_spatial_scene" // XrSystemSpatialScenePropertiesBD extends XrSystemProperties typedef struct XrSystemSpatialScenePropertiesBD { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrBool32 supportsSpatialScene; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 supportsSpatialScene; } XrSystemSpatialScenePropertiesBD; typedef struct XrSceneCaptureInfoBD { - XrStructureType type; - const void* XR_MAY_ALIAS next; + XrStructureType type; + const void* XR_MAY_ALIAS next; } XrSceneCaptureInfoBD; -typedef XrResult(XRAPI_PTR* PFN_xrCaptureSceneAsyncBD)(XrSenseDataProviderBD provider, const XrSceneCaptureInfoBD* info, - XrFutureEXT* future); -typedef XrResult(XRAPI_PTR* PFN_xrCaptureSceneCompleteBD)(XrSenseDataProviderBD provider, XrFutureEXT future, - XrFutureCompletionEXT* completion); +typedef XrResult (XRAPI_PTR *PFN_xrCaptureSceneAsyncBD)(XrSenseDataProviderBD provider, const XrSceneCaptureInfoBD* info, XrFutureEXT* future); +typedef XrResult (XRAPI_PTR *PFN_xrCaptureSceneCompleteBD)(XrSenseDataProviderBD provider, XrFutureEXT future, XrFutureCompletionEXT* completion); #ifndef XR_NO_PROTOTYPES #ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrCaptureSceneAsyncBD(XrSenseDataProviderBD provider, const XrSceneCaptureInfoBD* info, - XrFutureEXT* future); +XRAPI_ATTR XrResult XRAPI_CALL xrCaptureSceneAsyncBD( + XrSenseDataProviderBD provider, + const XrSceneCaptureInfoBD* info, + XrFutureEXT* future); -XRAPI_ATTR XrResult XRAPI_CALL xrCaptureSceneCompleteBD(XrSenseDataProviderBD provider, XrFutureEXT future, - XrFutureCompletionEXT* completion); +XRAPI_ATTR XrResult XRAPI_CALL xrCaptureSceneCompleteBD( + XrSenseDataProviderBD provider, + XrFutureEXT future, + XrFutureCompletionEXT* completion); #endif /* XR_EXTENSION_PROTOTYPES */ #endif /* !XR_NO_PROTOTYPES */ + // XR_BD_spatial_mesh is a preprocessor guard. Do not pass it to API calls. #define XR_BD_spatial_mesh 1 -#define XR_BD_spatial_mesh_SPEC_VERSION 1 +#define XR_BD_spatial_mesh_SPEC_VERSION 1 #define XR_BD_SPATIAL_MESH_EXTENSION_NAME "XR_BD_spatial_mesh" typedef enum XrSpatialMeshLodBD { - XR_SPATIAL_MESH_LOD_COARSE_BD = 0, - XR_SPATIAL_MESH_LOD_MEDIUM_BD = 1, - XR_SPATIAL_MESH_LOD_FINE_BD = 2, - XR_SPATIAL_MESH_LOD_MAX_ENUM_BD = 0x7FFFFFFF + XR_SPATIAL_MESH_LOD_COARSE_BD = 0, + XR_SPATIAL_MESH_LOD_MEDIUM_BD = 1, + XR_SPATIAL_MESH_LOD_FINE_BD = 2, + XR_SPATIAL_MESH_LOD_MAX_ENUM_BD = 0x7FFFFFFF } XrSpatialMeshLodBD; typedef XrFlags64 XrSpatialMeshConfigFlagsBD; @@ -8868,65 +9374,77 @@ static const XrSpatialMeshConfigFlagsBD XR_SPATIAL_MESH_CONFIG_ALIGN_SEMANTIC_WI // XrSystemSpatialMeshPropertiesBD extends XrSystemProperties typedef struct XrSystemSpatialMeshPropertiesBD { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrBool32 supportsSpatialMesh; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 supportsSpatialMesh; } XrSystemSpatialMeshPropertiesBD; // XrSenseDataProviderCreateInfoSpatialMeshBD extends XrSenseDataProviderCreateInfoBD typedef struct XrSenseDataProviderCreateInfoSpatialMeshBD { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpatialMeshConfigFlagsBD configFlags; - XrSpatialMeshLodBD lod; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpatialMeshConfigFlagsBD configFlags; + XrSpatialMeshLodBD lod; } XrSenseDataProviderCreateInfoSpatialMeshBD; + + // XR_BD_future_progress is a preprocessor guard. Do not pass it to API calls. #define XR_BD_future_progress 1 #define XR_BD_future_progress_SPEC_VERSION 1 #define XR_BD_FUTURE_PROGRESS_EXTENSION_NAME "XR_BD_future_progress" // XrFuturePollResultProgressBD extends XrFuturePollResultEXT typedef struct XrFuturePollResultProgressBD { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrBool32 isSupported; - uint32_t progressPercentage; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 isSupported; + uint32_t progressPercentage; } XrFuturePollResultProgressBD; + + // XR_BD_spatial_plane is a preprocessor guard. Do not pass it to API calls. #define XR_BD_spatial_plane 1 -#define XR_BD_spatial_plane_SPEC_VERSION 1 +#define XR_BD_spatial_plane_SPEC_VERSION 1 #define XR_BD_SPATIAL_PLANE_EXTENSION_NAME "XR_BD_spatial_plane" typedef enum XrPlaneOrientationBD { - XR_PLANE_ORIENTATION_HORIZONTAL_UPWARD_BD = 0, - XR_PLANE_ORIENTATION_HORIZONTAL_DOWNWARD_BD = 1, - XR_PLANE_ORIENTATION_VERTICAL_BD = 2, - XR_PLANE_ORIENTATION_ARBITRARY_BD = 3, - XR_PLANE_ORIENTATION_MAX_ENUM_BD = 0x7FFFFFFF + XR_PLANE_ORIENTATION_HORIZONTAL_UPWARD_BD = 0, + XR_PLANE_ORIENTATION_HORIZONTAL_DOWNWARD_BD = 1, + XR_PLANE_ORIENTATION_VERTICAL_BD = 2, + XR_PLANE_ORIENTATION_ARBITRARY_BD = 3, + XR_PLANE_ORIENTATION_MAX_ENUM_BD = 0x7FFFFFFF } XrPlaneOrientationBD; // XrSystemSpatialPlanePropertiesBD extends XrSystemProperties typedef struct XrSystemSpatialPlanePropertiesBD { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrBool32 supportsSpatialPlane; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 supportsSpatialPlane; } XrSystemSpatialPlanePropertiesBD; // XrSpatialEntityComponentDataPlaneOrientationBD extends XrSpatialEntityComponentDataBaseHeaderBD typedef struct XrSpatialEntityComponentDataPlaneOrientationBD { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrPlaneOrientationBD orientation; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrPlaneOrientationBD orientation; } XrSpatialEntityComponentDataPlaneOrientationBD; // XrSenseDataFilterPlaneOrientationBD extends XrSenseDataQueryInfoBD typedef struct XrSenseDataFilterPlaneOrientationBD { - XrStructureType type; - const void* XR_MAY_ALIAS next; - uint32_t orientationCount; - XrPlaneOrientationBD* orientations; + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t orientationCount; + XrPlaneOrientationBD* orientations; } XrSenseDataFilterPlaneOrientationBD; + + +// XR_BD_ultra_controller_interaction is a preprocessor guard. Do not pass it to API calls. +#define XR_BD_ultra_controller_interaction 1 +#define XR_BD_ultra_controller_interaction_SPEC_VERSION 1 +#define XR_BD_ULTRA_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_BD_ultra_controller_interaction" + + // XR_EXT_local_floor is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_local_floor 1 #define XR_EXT_local_floor_SPEC_VERSION 1 @@ -9073,7 +9591,7 @@ typedef struct XrPlaneDetectorPolygonBufferEXT { XrVector2f* vertices; } XrPlaneDetectorPolygonBufferEXT; -typedef XrResult (XRAPI_PTR *PFN_xrCreatePlaneDetectorEXT)(XrSession session, const XrPlaneDetectorCreateInfoEXT* createInfo, XrPlaneDetectorEXT* planeDetector); +typedef XrResult (XRAPI_PTR *PFN_xrCreatePlaneDetectorEXT)(XrSession session, const XrPlaneDetectorCreateInfoEXT* createInfo, XrPlaneDetectorEXT* planeDetector); typedef XrResult (XRAPI_PTR *PFN_xrDestroyPlaneDetectorEXT)(XrPlaneDetectorEXT planeDetector); typedef XrResult (XRAPI_PTR *PFN_xrBeginPlaneDetectionEXT)(XrPlaneDetectorEXT planeDetector, const XrPlaneDetectorBeginInfoEXT* beginInfo); typedef XrResult (XRAPI_PTR *PFN_xrGetPlaneDetectionStateEXT)(XrPlaneDetectorEXT planeDetector, XrPlaneDetectionStateEXT* state); @@ -9118,6 +9636,515 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetPlanePolygonBufferEXT( #define XR_OPPO_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_OPPO_controller_interaction" +// XR_ANDROID_trackables is a preprocessor guard. Do not pass it to API calls. +#define XR_ANDROID_trackables 1 + +#define XR_NULL_TRACKABLE_ANDROID 0 + +XR_DEFINE_ATOM(XrTrackableANDROID) +XR_DEFINE_HANDLE(XrTrackableTrackerANDROID) +#define XR_ANDROID_trackables_SPEC_VERSION 2 +#define XR_ANDROID_TRACKABLES_EXTENSION_NAME "XR_ANDROID_trackables" + +typedef enum XrTrackingStateANDROID { + XR_TRACKING_STATE_PAUSED_ANDROID = 0, + XR_TRACKING_STATE_STOPPED_ANDROID = 1, + XR_TRACKING_STATE_TRACKING_ANDROID = 2, + XR_TRACKING_STATE_MAX_ENUM_ANDROID = 0x7FFFFFFF +} XrTrackingStateANDROID; + +typedef enum XrTrackableTypeANDROID { + XR_TRACKABLE_TYPE_NOT_VALID_ANDROID = 0, + XR_TRACKABLE_TYPE_PLANE_ANDROID = 1, + XR_TRACKABLE_TYPE_DEPTH_ANDROID = 1000463000, + XR_TRACKABLE_TYPE_OBJECT_ANDROID = 1000466000, + XR_TRACKABLE_TYPE_MARKER_ANDROID = 1000707000, + XR_TRACKABLE_TYPE_MAX_ENUM_ANDROID = 0x7FFFFFFF +} XrTrackableTypeANDROID; + +typedef enum XrPlaneTypeANDROID { + XR_PLANE_TYPE_HORIZONTAL_DOWNWARD_FACING_ANDROID = 0, + XR_PLANE_TYPE_HORIZONTAL_UPWARD_FACING_ANDROID = 1, + XR_PLANE_TYPE_VERTICAL_ANDROID = 2, + XR_PLANE_TYPE_ARBITRARY_ANDROID = 3, + XR_PLANE_TYPE_MAX_ENUM_ANDROID = 0x7FFFFFFF +} XrPlaneTypeANDROID; + +typedef enum XrPlaneLabelANDROID { + XR_PLANE_LABEL_UNKNOWN_ANDROID = 0, + XR_PLANE_LABEL_WALL_ANDROID = 1, + XR_PLANE_LABEL_FLOOR_ANDROID = 2, + XR_PLANE_LABEL_CEILING_ANDROID = 3, + XR_PLANE_LABEL_TABLE_ANDROID = 4, + XR_PLANE_LABEL_MAX_ENUM_ANDROID = 0x7FFFFFFF +} XrPlaneLabelANDROID; +typedef struct XrTrackableTrackerCreateInfoANDROID { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrTrackableTypeANDROID trackableType; +} XrTrackableTrackerCreateInfoANDROID; + +typedef struct XrTrackableGetInfoANDROID { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrTrackableANDROID trackable; + XrSpace baseSpace; + XrTime time; +} XrTrackableGetInfoANDROID; + +typedef struct XrTrackablePlaneANDROID { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrTrackingStateANDROID trackingState; + XrPosef centerPose; + XrExtent2Df extents; + XrPlaneTypeANDROID planeType; + XrPlaneLabelANDROID planeLabel; + XrTrackableANDROID subsumedByPlane; + XrTime lastUpdatedTime; + uint32_t vertexCapacityInput; + uint32_t* vertexCountOutput; + XrVector2f* vertices; +} XrTrackablePlaneANDROID; + +typedef struct XrAnchorSpaceCreateInfoANDROID { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpace space; + XrTime time; + XrPosef pose; + XrTrackableANDROID trackable; +} XrAnchorSpaceCreateInfoANDROID; + +// XrSystemTrackablesPropertiesANDROID extends XrSystemProperties +typedef struct XrSystemTrackablesPropertiesANDROID { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrBool32 supportsAnchor; + uint32_t maxAnchors; +} XrSystemTrackablesPropertiesANDROID; + +typedef XrResult (XRAPI_PTR *PFN_xrEnumerateSupportedTrackableTypesANDROID)(XrInstance instance, XrSystemId systemId, uint32_t trackableTypeCapacityInput, uint32_t* trackableTypeCountOutput, XrTrackableTypeANDROID* trackableTypes); +typedef XrResult (XRAPI_PTR *PFN_xrEnumerateSupportedAnchorTrackableTypesANDROID)(XrInstance instance, XrSystemId systemId, uint32_t trackableTypeCapacityInput, uint32_t* trackableTypeCountOutput, XrTrackableTypeANDROID* trackableTypes); +typedef XrResult (XRAPI_PTR *PFN_xrCreateTrackableTrackerANDROID)(XrSession session, const XrTrackableTrackerCreateInfoANDROID* createInfo, XrTrackableTrackerANDROID* trackableTracker); +typedef XrResult (XRAPI_PTR *PFN_xrDestroyTrackableTrackerANDROID)(XrTrackableTrackerANDROID trackableTracker); +typedef XrResult (XRAPI_PTR *PFN_xrGetAllTrackablesANDROID)(XrTrackableTrackerANDROID trackableTracker, uint32_t trackableCapacityInput, uint32_t* trackableCountOutput, XrTrackableANDROID* trackables); +typedef XrResult (XRAPI_PTR *PFN_xrGetTrackablePlaneANDROID)(XrTrackableTrackerANDROID trackableTracker, const XrTrackableGetInfoANDROID* getInfo, XrTrackablePlaneANDROID* planeOutput); +typedef XrResult (XRAPI_PTR *PFN_xrCreateAnchorSpaceANDROID)(XrSession session, const XrAnchorSpaceCreateInfoANDROID* createInfo, XrSpace* anchorOutput); + +#ifndef XR_NO_PROTOTYPES +#ifdef XR_EXTENSION_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSupportedTrackableTypesANDROID( + XrInstance instance, + XrSystemId systemId, + uint32_t trackableTypeCapacityInput, + uint32_t* trackableTypeCountOutput, + XrTrackableTypeANDROID* trackableTypes); + +XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSupportedAnchorTrackableTypesANDROID( + XrInstance instance, + XrSystemId systemId, + uint32_t trackableTypeCapacityInput, + uint32_t* trackableTypeCountOutput, + XrTrackableTypeANDROID* trackableTypes); + +XRAPI_ATTR XrResult XRAPI_CALL xrCreateTrackableTrackerANDROID( + XrSession session, + const XrTrackableTrackerCreateInfoANDROID* createInfo, + XrTrackableTrackerANDROID* trackableTracker); + +XRAPI_ATTR XrResult XRAPI_CALL xrDestroyTrackableTrackerANDROID( + XrTrackableTrackerANDROID trackableTracker); + +XRAPI_ATTR XrResult XRAPI_CALL xrGetAllTrackablesANDROID( + XrTrackableTrackerANDROID trackableTracker, + uint32_t trackableCapacityInput, + uint32_t* trackableCountOutput, + XrTrackableANDROID* trackables); + +XRAPI_ATTR XrResult XRAPI_CALL xrGetTrackablePlaneANDROID( + XrTrackableTrackerANDROID trackableTracker, + const XrTrackableGetInfoANDROID* getInfo, + XrTrackablePlaneANDROID* planeOutput); + +XRAPI_ATTR XrResult XRAPI_CALL xrCreateAnchorSpaceANDROID( + XrSession session, + const XrAnchorSpaceCreateInfoANDROID* createInfo, + XrSpace* anchorOutput); +#endif /* XR_EXTENSION_PROTOTYPES */ +#endif /* !XR_NO_PROTOTYPES */ + + +// XR_ANDROID_device_anchor_persistence is a preprocessor guard. Do not pass it to API calls. +#define XR_ANDROID_device_anchor_persistence 1 +XR_DEFINE_HANDLE(XrDeviceAnchorPersistenceANDROID) +#define XR_ANDROID_device_anchor_persistence_SPEC_VERSION 1 +#define XR_ANDROID_DEVICE_ANCHOR_PERSISTENCE_EXTENSION_NAME "XR_ANDROID_device_anchor_persistence" + +typedef enum XrAnchorPersistStateANDROID { + XR_ANCHOR_PERSIST_STATE_PERSIST_NOT_REQUESTED_ANDROID = 0, + XR_ANCHOR_PERSIST_STATE_PERSIST_PENDING_ANDROID = 1, + XR_ANCHOR_PERSIST_STATE_PERSISTED_ANDROID = 2, + XR_ANCHOR_PERSIST_STATE_MAX_ENUM_ANDROID = 0x7FFFFFFF +} XrAnchorPersistStateANDROID; +typedef struct XrDeviceAnchorPersistenceCreateInfoANDROID { + XrStructureType type; + const void* XR_MAY_ALIAS next; +} XrDeviceAnchorPersistenceCreateInfoANDROID; + +typedef struct XrPersistedAnchorSpaceCreateInfoANDROID { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrUuidEXT anchorId; +} XrPersistedAnchorSpaceCreateInfoANDROID; + +typedef struct XrPersistedAnchorSpaceInfoANDROID { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpace anchor; +} XrPersistedAnchorSpaceInfoANDROID; + +// XrSystemDeviceAnchorPersistencePropertiesANDROID extends XrSystemProperties +typedef struct XrSystemDeviceAnchorPersistencePropertiesANDROID { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 supportsAnchorPersistence; +} XrSystemDeviceAnchorPersistencePropertiesANDROID; + +typedef XrResult (XRAPI_PTR *PFN_xrEnumerateSupportedPersistenceAnchorTypesANDROID)(XrInstance instance, XrSystemId systemId, uint32_t trackableTypeCapacityInput, uint32_t* trackableTypeCountOutput, XrTrackableTypeANDROID* trackableTypes); +typedef XrResult (XRAPI_PTR *PFN_xrCreateDeviceAnchorPersistenceANDROID)(XrSession session, const XrDeviceAnchorPersistenceCreateInfoANDROID* createInfo, XrDeviceAnchorPersistenceANDROID* outHandle); +typedef XrResult (XRAPI_PTR *PFN_xrDestroyDeviceAnchorPersistenceANDROID)(XrDeviceAnchorPersistenceANDROID handle); +typedef XrResult (XRAPI_PTR *PFN_xrPersistAnchorANDROID)(XrDeviceAnchorPersistenceANDROID handle, const XrPersistedAnchorSpaceInfoANDROID* persistedInfo, XrUuidEXT* anchorIdOutput); +typedef XrResult (XRAPI_PTR *PFN_xrGetAnchorPersistStateANDROID)(XrDeviceAnchorPersistenceANDROID handle, const XrUuidEXT* anchorId, XrAnchorPersistStateANDROID* persistState); +typedef XrResult (XRAPI_PTR *PFN_xrCreatePersistedAnchorSpaceANDROID)(XrDeviceAnchorPersistenceANDROID handle, const XrPersistedAnchorSpaceCreateInfoANDROID* createInfo, XrSpace* anchorOutput); +typedef XrResult (XRAPI_PTR *PFN_xrEnumeratePersistedAnchorsANDROID)(XrDeviceAnchorPersistenceANDROID handle, uint32_t anchorIdCapacityInput, uint32_t* anchorIdCountOutput, XrUuidEXT* anchorIds); +typedef XrResult (XRAPI_PTR *PFN_xrUnpersistAnchorANDROID)(XrDeviceAnchorPersistenceANDROID handle, const XrUuidEXT* anchorId); + +#ifndef XR_NO_PROTOTYPES +#ifdef XR_EXTENSION_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSupportedPersistenceAnchorTypesANDROID( + XrInstance instance, + XrSystemId systemId, + uint32_t trackableTypeCapacityInput, + uint32_t* trackableTypeCountOutput, + XrTrackableTypeANDROID* trackableTypes); + +XRAPI_ATTR XrResult XRAPI_CALL xrCreateDeviceAnchorPersistenceANDROID( + XrSession session, + const XrDeviceAnchorPersistenceCreateInfoANDROID* createInfo, + XrDeviceAnchorPersistenceANDROID* outHandle); + +XRAPI_ATTR XrResult XRAPI_CALL xrDestroyDeviceAnchorPersistenceANDROID( + XrDeviceAnchorPersistenceANDROID handle); + +XRAPI_ATTR XrResult XRAPI_CALL xrPersistAnchorANDROID( + XrDeviceAnchorPersistenceANDROID handle, + const XrPersistedAnchorSpaceInfoANDROID* persistedInfo, + XrUuidEXT* anchorIdOutput); + +XRAPI_ATTR XrResult XRAPI_CALL xrGetAnchorPersistStateANDROID( + XrDeviceAnchorPersistenceANDROID handle, + const XrUuidEXT* anchorId, + XrAnchorPersistStateANDROID* persistState); + +XRAPI_ATTR XrResult XRAPI_CALL xrCreatePersistedAnchorSpaceANDROID( + XrDeviceAnchorPersistenceANDROID handle, + const XrPersistedAnchorSpaceCreateInfoANDROID* createInfo, + XrSpace* anchorOutput); + +XRAPI_ATTR XrResult XRAPI_CALL xrEnumeratePersistedAnchorsANDROID( + XrDeviceAnchorPersistenceANDROID handle, + uint32_t anchorIdCapacityInput, + uint32_t* anchorIdCountOutput, + XrUuidEXT* anchorIds); + +XRAPI_ATTR XrResult XRAPI_CALL xrUnpersistAnchorANDROID( + XrDeviceAnchorPersistenceANDROID handle, + const XrUuidEXT* anchorId); +#endif /* XR_EXTENSION_PROTOTYPES */ +#endif /* !XR_NO_PROTOTYPES */ + + +// XR_ANDROID_face_tracking is a preprocessor guard. Do not pass it to API calls. +#define XR_ANDROID_face_tracking 1 +XR_DEFINE_HANDLE(XrFaceTrackerANDROID) +#define XR_ANDROID_face_tracking_SPEC_VERSION 1 +#define XR_ANDROID_FACE_TRACKING_EXTENSION_NAME "XR_ANDROID_face_tracking" +#define XR_FACE_PARAMETER_COUNT_ANDROID 68 +#define XR_FACE_REGION_CONFIDENCE_COUNT_ANDROID 3 + +typedef enum XrFaceParameterIndicesANDROID { + XR_FACE_PARAMETER_INDICES_BROW_LOWERER_L_ANDROID = 0, + XR_FACE_PARAMETER_INDICES_BROW_LOWERER_R_ANDROID = 1, + XR_FACE_PARAMETER_INDICES_CHEEK_PUFF_L_ANDROID = 2, + XR_FACE_PARAMETER_INDICES_CHEEK_PUFF_R_ANDROID = 3, + XR_FACE_PARAMETER_INDICES_CHEEK_RAISER_L_ANDROID = 4, + XR_FACE_PARAMETER_INDICES_CHEEK_RAISER_R_ANDROID = 5, + XR_FACE_PARAMETER_INDICES_CHEEK_SUCK_L_ANDROID = 6, + XR_FACE_PARAMETER_INDICES_CHEEK_SUCK_R_ANDROID = 7, + XR_FACE_PARAMETER_INDICES_CHIN_RAISER_B_ANDROID = 8, + XR_FACE_PARAMETER_INDICES_CHIN_RAISER_T_ANDROID = 9, + XR_FACE_PARAMETER_INDICES_DIMPLER_L_ANDROID = 10, + XR_FACE_PARAMETER_INDICES_DIMPLER_R_ANDROID = 11, + XR_FACE_PARAMETER_INDICES_EYES_CLOSED_L_ANDROID = 12, + XR_FACE_PARAMETER_INDICES_EYES_CLOSED_R_ANDROID = 13, + XR_FACE_PARAMETER_INDICES_EYES_LOOK_DOWN_L_ANDROID = 14, + XR_FACE_PARAMETER_INDICES_EYES_LOOK_DOWN_R_ANDROID = 15, + XR_FACE_PARAMETER_INDICES_EYES_LOOK_LEFT_L_ANDROID = 16, + XR_FACE_PARAMETER_INDICES_EYES_LOOK_LEFT_R_ANDROID = 17, + XR_FACE_PARAMETER_INDICES_EYES_LOOK_RIGHT_L_ANDROID = 18, + XR_FACE_PARAMETER_INDICES_EYES_LOOK_RIGHT_R_ANDROID = 19, + XR_FACE_PARAMETER_INDICES_EYES_LOOK_UP_L_ANDROID = 20, + XR_FACE_PARAMETER_INDICES_EYES_LOOK_UP_R_ANDROID = 21, + XR_FACE_PARAMETER_INDICES_INNER_BROW_RAISER_L_ANDROID = 22, + XR_FACE_PARAMETER_INDICES_INNER_BROW_RAISER_R_ANDROID = 23, + XR_FACE_PARAMETER_INDICES_JAW_DROP_ANDROID = 24, + XR_FACE_PARAMETER_INDICES_JAW_SIDEWAYS_LEFT_ANDROID = 25, + XR_FACE_PARAMETER_INDICES_JAW_SIDEWAYS_RIGHT_ANDROID = 26, + XR_FACE_PARAMETER_INDICES_JAW_THRUST_ANDROID = 27, + XR_FACE_PARAMETER_INDICES_LID_TIGHTENER_L_ANDROID = 28, + XR_FACE_PARAMETER_INDICES_LID_TIGHTENER_R_ANDROID = 29, + XR_FACE_PARAMETER_INDICES_LIP_CORNER_DEPRESSOR_L_ANDROID = 30, + XR_FACE_PARAMETER_INDICES_LIP_CORNER_DEPRESSOR_R_ANDROID = 31, + XR_FACE_PARAMETER_INDICES_LIP_CORNER_PULLER_L_ANDROID = 32, + XR_FACE_PARAMETER_INDICES_LIP_CORNER_PULLER_R_ANDROID = 33, + XR_FACE_PARAMETER_INDICES_LIP_FUNNELER_LB_ANDROID = 34, + XR_FACE_PARAMETER_INDICES_LIP_FUNNELER_LT_ANDROID = 35, + XR_FACE_PARAMETER_INDICES_LIP_FUNNELER_RB_ANDROID = 36, + XR_FACE_PARAMETER_INDICES_LIP_FUNNELER_RT_ANDROID = 37, + XR_FACE_PARAMETER_INDICES_LIP_PRESSOR_L_ANDROID = 38, + XR_FACE_PARAMETER_INDICES_LIP_PRESSOR_R_ANDROID = 39, + XR_FACE_PARAMETER_INDICES_LIP_PUCKER_L_ANDROID = 40, + XR_FACE_PARAMETER_INDICES_LIP_PUCKER_R_ANDROID = 41, + XR_FACE_PARAMETER_INDICES_LIP_STRETCHER_L_ANDROID = 42, + XR_FACE_PARAMETER_INDICES_LIP_STRETCHER_R_ANDROID = 43, + XR_FACE_PARAMETER_INDICES_LIP_SUCK_LB_ANDROID = 44, + XR_FACE_PARAMETER_INDICES_LIP_SUCK_LT_ANDROID = 45, + XR_FACE_PARAMETER_INDICES_LIP_SUCK_RB_ANDROID = 46, + XR_FACE_PARAMETER_INDICES_LIP_SUCK_RT_ANDROID = 47, + XR_FACE_PARAMETER_INDICES_LIP_TIGHTENER_L_ANDROID = 48, + XR_FACE_PARAMETER_INDICES_LIP_TIGHTENER_R_ANDROID = 49, + XR_FACE_PARAMETER_INDICES_LIPS_TOWARD_ANDROID = 50, + XR_FACE_PARAMETER_INDICES_LOWER_LIP_DEPRESSOR_L_ANDROID = 51, + XR_FACE_PARAMETER_INDICES_LOWER_LIP_DEPRESSOR_R_ANDROID = 52, + XR_FACE_PARAMETER_INDICES_MOUTH_LEFT_ANDROID = 53, + XR_FACE_PARAMETER_INDICES_MOUTH_RIGHT_ANDROID = 54, + XR_FACE_PARAMETER_INDICES_NOSE_WRINKLER_L_ANDROID = 55, + XR_FACE_PARAMETER_INDICES_NOSE_WRINKLER_R_ANDROID = 56, + XR_FACE_PARAMETER_INDICES_OUTER_BROW_RAISER_L_ANDROID = 57, + XR_FACE_PARAMETER_INDICES_OUTER_BROW_RAISER_R_ANDROID = 58, + XR_FACE_PARAMETER_INDICES_UPPER_LID_RAISER_L_ANDROID = 59, + XR_FACE_PARAMETER_INDICES_UPPER_LID_RAISER_R_ANDROID = 60, + XR_FACE_PARAMETER_INDICES_UPPER_LIP_RAISER_L_ANDROID = 61, + XR_FACE_PARAMETER_INDICES_UPPER_LIP_RAISER_R_ANDROID = 62, + XR_FACE_PARAMETER_INDICES_TONGUE_OUT_ANDROID = 63, + XR_FACE_PARAMETER_INDICES_TONGUE_LEFT_ANDROID = 64, + XR_FACE_PARAMETER_INDICES_TONGUE_RIGHT_ANDROID = 65, + XR_FACE_PARAMETER_INDICES_TONGUE_UP_ANDROID = 66, + XR_FACE_PARAMETER_INDICES_TONGUE_DOWN_ANDROID = 67, + XR_FACE_PARAMETER_INDICES_MAX_ENUM_ANDROID = 0x7FFFFFFF +} XrFaceParameterIndicesANDROID; + +typedef enum XrFaceTrackingStateANDROID { + XR_FACE_TRACKING_STATE_PAUSED_ANDROID = 0, + XR_FACE_TRACKING_STATE_STOPPED_ANDROID = 1, + XR_FACE_TRACKING_STATE_TRACKING_ANDROID = 2, + XR_FACE_TRACKING_STATE_MAX_ENUM_ANDROID = 0x7FFFFFFF +} XrFaceTrackingStateANDROID; + +typedef enum XrFaceConfidenceRegionsANDROID { + XR_FACE_CONFIDENCE_REGIONS_LOWER_ANDROID = 0, + XR_FACE_CONFIDENCE_REGIONS_LEFT_UPPER_ANDROID = 1, + XR_FACE_CONFIDENCE_REGIONS_RIGHT_UPPER_ANDROID = 2, + XR_FACE_CONFIDENCE_REGIONS_MAX_ENUM_ANDROID = 0x7FFFFFFF +} XrFaceConfidenceRegionsANDROID; +typedef struct XrFaceTrackerCreateInfoANDROID { + XrStructureType type; + const void* XR_MAY_ALIAS next; +} XrFaceTrackerCreateInfoANDROID; + +typedef struct XrFaceStateGetInfoANDROID { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrTime time; +} XrFaceStateGetInfoANDROID; + +typedef struct XrFaceStateANDROID { + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t parametersCapacityInput; + uint32_t parametersCountOutput; + float* parameters; + XrFaceTrackingStateANDROID faceTrackingState; + XrTime sampleTime; + XrBool32 isValid; + uint32_t regionConfidencesCapacityInput; + uint32_t regionConfidencesCountOutput; + float* regionConfidences; +} XrFaceStateANDROID; + +// XrSystemFaceTrackingPropertiesANDROID extends XrSystemProperties +typedef struct XrSystemFaceTrackingPropertiesANDROID { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 supportsFaceTracking; +} XrSystemFaceTrackingPropertiesANDROID; + +typedef XrResult (XRAPI_PTR *PFN_xrCreateFaceTrackerANDROID)(XrSession session, const XrFaceTrackerCreateInfoANDROID* createInfo, XrFaceTrackerANDROID* faceTracker); +typedef XrResult (XRAPI_PTR *PFN_xrDestroyFaceTrackerANDROID)(XrFaceTrackerANDROID faceTracker); +typedef XrResult (XRAPI_PTR *PFN_xrGetFaceStateANDROID)(XrFaceTrackerANDROID faceTracker, const XrFaceStateGetInfoANDROID* getInfo, XrFaceStateANDROID* faceStateOutput); +typedef XrResult (XRAPI_PTR *PFN_xrGetFaceCalibrationStateANDROID)(XrFaceTrackerANDROID faceTracker, XrBool32* faceIsCalibratedOutput); + +#ifndef XR_NO_PROTOTYPES +#ifdef XR_EXTENSION_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrCreateFaceTrackerANDROID( + XrSession session, + const XrFaceTrackerCreateInfoANDROID* createInfo, + XrFaceTrackerANDROID* faceTracker); + +XRAPI_ATTR XrResult XRAPI_CALL xrDestroyFaceTrackerANDROID( + XrFaceTrackerANDROID faceTracker); + +XRAPI_ATTR XrResult XRAPI_CALL xrGetFaceStateANDROID( + XrFaceTrackerANDROID faceTracker, + const XrFaceStateGetInfoANDROID* getInfo, + XrFaceStateANDROID* faceStateOutput); + +XRAPI_ATTR XrResult XRAPI_CALL xrGetFaceCalibrationStateANDROID( + XrFaceTrackerANDROID faceTracker, + XrBool32* faceIsCalibratedOutput); +#endif /* XR_EXTENSION_PROTOTYPES */ +#endif /* !XR_NO_PROTOTYPES */ + + +// XR_ANDROID_passthrough_camera_state is a preprocessor guard. Do not pass it to API calls. +#define XR_ANDROID_passthrough_camera_state 1 +#define XR_ANDROID_passthrough_camera_state_SPEC_VERSION 1 +#define XR_ANDROID_PASSTHROUGH_CAMERA_STATE_EXTENSION_NAME "XR_ANDROID_passthrough_camera_state" + +typedef enum XrPassthroughCameraStateANDROID { + XR_PASSTHROUGH_CAMERA_STATE_DISABLED_ANDROID = 0, + XR_PASSTHROUGH_CAMERA_STATE_INITIALIZING_ANDROID = 1, + XR_PASSTHROUGH_CAMERA_STATE_READY_ANDROID = 2, + XR_PASSTHROUGH_CAMERA_STATE_ERROR_ANDROID = 3, + XR_PASSTHROUGH_CAMERA_STATE_MAX_ENUM_ANDROID = 0x7FFFFFFF +} XrPassthroughCameraStateANDROID; +// XrSystemPassthroughCameraStatePropertiesANDROID extends XrSystemProperties +typedef struct XrSystemPassthroughCameraStatePropertiesANDROID { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 supportsPassthroughCameraState; +} XrSystemPassthroughCameraStatePropertiesANDROID; + +typedef struct XrPassthroughCameraStateGetInfoANDROID { + XrStructureType type; + const void* XR_MAY_ALIAS next; +} XrPassthroughCameraStateGetInfoANDROID; + +typedef XrResult (XRAPI_PTR *PFN_xrGetPassthroughCameraStateANDROID)(XrSession session, const XrPassthroughCameraStateGetInfoANDROID* getInfo, XrPassthroughCameraStateANDROID* cameraStateOutput); + +#ifndef XR_NO_PROTOTYPES +#ifdef XR_EXTENSION_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrGetPassthroughCameraStateANDROID( + XrSession session, + const XrPassthroughCameraStateGetInfoANDROID* getInfo, + XrPassthroughCameraStateANDROID* cameraStateOutput); +#endif /* XR_EXTENSION_PROTOTYPES */ +#endif /* !XR_NO_PROTOTYPES */ + + +// XR_ANDROID_raycast is a preprocessor guard. Do not pass it to API calls. +#define XR_ANDROID_raycast 1 +#define XR_ANDROID_raycast_SPEC_VERSION 1 +#define XR_ANDROID_RAYCAST_EXTENSION_NAME "XR_ANDROID_raycast" +typedef struct XrRaycastInfoANDROID { + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t maxResults; + uint32_t trackerCount; + const XrTrackableTrackerANDROID* trackers; + XrVector3f origin; + XrVector3f trajectory; + XrSpace space; + XrTime time; +} XrRaycastInfoANDROID; + +typedef struct XrRaycastHitResultANDROID { + XrTrackableTypeANDROID type; + XrTrackableANDROID trackable; + XrPosef pose; +} XrRaycastHitResultANDROID; + +typedef struct XrRaycastHitResultsANDROID { + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t resultsCapacityInput; + uint32_t resultsCountOutput; + XrRaycastHitResultANDROID* results; +} XrRaycastHitResultsANDROID; + +typedef XrResult (XRAPI_PTR *PFN_xrEnumerateRaycastSupportedTrackableTypesANDROID)(XrInstance instance, XrSystemId systemId, uint32_t trackableTypeCapacityInput, uint32_t* trackableTypeCountOutput, XrTrackableTypeANDROID* trackableTypes); +typedef XrResult (XRAPI_PTR *PFN_xrRaycastANDROID)(XrSession session, const XrRaycastInfoANDROID* rayInfo, XrRaycastHitResultsANDROID* results); + +#ifndef XR_NO_PROTOTYPES +#ifdef XR_EXTENSION_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateRaycastSupportedTrackableTypesANDROID( + XrInstance instance, + XrSystemId systemId, + uint32_t trackableTypeCapacityInput, + uint32_t* trackableTypeCountOutput, + XrTrackableTypeANDROID* trackableTypes); + +XRAPI_ATTR XrResult XRAPI_CALL xrRaycastANDROID( + XrSession session, + const XrRaycastInfoANDROID* rayInfo, + XrRaycastHitResultsANDROID* results); +#endif /* XR_EXTENSION_PROTOTYPES */ +#endif /* !XR_NO_PROTOTYPES */ + + +// XR_ANDROID_trackables_object is a preprocessor guard. Do not pass it to API calls. +#define XR_ANDROID_trackables_object 1 +#define XR_ANDROID_trackables_object_SPEC_VERSION 2 +#define XR_ANDROID_TRACKABLES_OBJECT_EXTENSION_NAME "XR_ANDROID_trackables_object" + +typedef enum XrObjectLabelANDROID { + XR_OBJECT_LABEL_UNKNOWN_ANDROID = 0, + XR_OBJECT_LABEL_KEYBOARD_ANDROID = 1, + XR_OBJECT_LABEL_MOUSE_ANDROID = 2, + XR_OBJECT_LABEL_LAPTOP_ANDROID = 3, + XR_OBJECT_LABEL_MAX_ENUM_ANDROID = 0x7FFFFFFF +} XrObjectLabelANDROID; +typedef struct XrTrackableObjectANDROID { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrTrackingStateANDROID trackingState; + XrPosef centerPose; + XrExtent3DfEXT extents; + XrObjectLabelANDROID objectLabel; + XrTime lastUpdatedTime; +} XrTrackableObjectANDROID; + +// XrTrackableObjectConfigurationANDROID extends XrTrackableTrackerCreateInfoANDROID +typedef struct XrTrackableObjectConfigurationANDROID { + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t labelCount; + const XrObjectLabelANDROID* activeLabels; +} XrTrackableObjectConfigurationANDROID; + +typedef XrResult (XRAPI_PTR *PFN_xrGetTrackableObjectANDROID)(XrTrackableTrackerANDROID tracker, const XrTrackableGetInfoANDROID* getInfo, XrTrackableObjectANDROID* objectOutput); + +#ifndef XR_NO_PROTOTYPES +#ifdef XR_EXTENSION_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrGetTrackableObjectANDROID( + XrTrackableTrackerANDROID tracker, + const XrTrackableGetInfoANDROID* getInfo, + XrTrackableObjectANDROID* objectOutput); +#endif /* XR_EXTENSION_PROTOTYPES */ +#endif /* !XR_NO_PROTOTYPES */ + + // XR_EXT_future is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_future 1 #define XR_EXT_future_SPEC_VERSION 1 @@ -9583,42 +10610,44 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetFacialExpressionBlendShapePropertiesML( #define XR_YVR_controller_interaction_SPEC_VERSION 1 #define XR_YVR_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_YVR_controller_interaction" + // XR_META_simultaneous_hands_and_controllers is a preprocessor guard. Do not pass it to API calls. #define XR_META_simultaneous_hands_and_controllers 1 #define XR_META_simultaneous_hands_and_controllers_SPEC_VERSION 1 #define XR_META_SIMULTANEOUS_HANDS_AND_CONTROLLERS_EXTENSION_NAME "XR_META_simultaneous_hands_and_controllers" // XrSystemSimultaneousHandsAndControllersPropertiesMETA extends XrSystemProperties typedef struct XrSystemSimultaneousHandsAndControllersPropertiesMETA { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrBool32 supportsSimultaneousHandsAndControllers; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 supportsSimultaneousHandsAndControllers; } XrSystemSimultaneousHandsAndControllersPropertiesMETA; typedef struct XrSimultaneousHandsAndControllersTrackingResumeInfoMETA { - XrStructureType type; - const void* XR_MAY_ALIAS next; + XrStructureType type; + const void* XR_MAY_ALIAS next; } XrSimultaneousHandsAndControllersTrackingResumeInfoMETA; typedef struct XrSimultaneousHandsAndControllersTrackingPauseInfoMETA { - XrStructureType type; - const void* XR_MAY_ALIAS next; + XrStructureType type; + const void* XR_MAY_ALIAS next; } XrSimultaneousHandsAndControllersTrackingPauseInfoMETA; -typedef XrResult(XRAPI_PTR* PFN_xrResumeSimultaneousHandsAndControllersTrackingMETA)( - XrSession session, const XrSimultaneousHandsAndControllersTrackingResumeInfoMETA* resumeInfo); -typedef XrResult(XRAPI_PTR* PFN_xrPauseSimultaneousHandsAndControllersTrackingMETA)( - XrSession session, const XrSimultaneousHandsAndControllersTrackingPauseInfoMETA* pauseInfo); +typedef XrResult (XRAPI_PTR *PFN_xrResumeSimultaneousHandsAndControllersTrackingMETA)(XrSession session, const XrSimultaneousHandsAndControllersTrackingResumeInfoMETA* resumeInfo); +typedef XrResult (XRAPI_PTR *PFN_xrPauseSimultaneousHandsAndControllersTrackingMETA)(XrSession session, const XrSimultaneousHandsAndControllersTrackingPauseInfoMETA* pauseInfo); #ifndef XR_NO_PROTOTYPES #ifdef XR_EXTENSION_PROTOTYPES XRAPI_ATTR XrResult XRAPI_CALL xrResumeSimultaneousHandsAndControllersTrackingMETA( - XrSession session, const XrSimultaneousHandsAndControllersTrackingResumeInfoMETA* resumeInfo); + XrSession session, + const XrSimultaneousHandsAndControllersTrackingResumeInfoMETA* resumeInfo); XRAPI_ATTR XrResult XRAPI_CALL xrPauseSimultaneousHandsAndControllersTrackingMETA( - XrSession session, const XrSimultaneousHandsAndControllersTrackingPauseInfoMETA* pauseInfo); + XrSession session, + const XrSimultaneousHandsAndControllersTrackingPauseInfoMETA* pauseInfo); #endif /* XR_EXTENSION_PROTOTYPES */ #endif /* !XR_NO_PROTOTYPES */ + // XR_EXT_composition_layer_inverted_alpha is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_composition_layer_inverted_alpha 1 #define XR_EXT_composition_layer_inverted_alpha_SPEC_VERSION 1 @@ -9765,11 +10794,100 @@ typedef struct XrSpaceGroupUuidFilterInfoMETA { XrUuid groupUuid; } XrSpaceGroupUuidFilterInfoMETA; + + +// XR_ANDROID_trackables_marker is a preprocessor guard. Do not pass it to API calls. +#define XR_ANDROID_trackables_marker 1 +#define XR_ANDROID_trackables_marker_SPEC_VERSION 1 +#define XR_ANDROID_TRACKABLES_MARKER_EXTENSION_NAME "XR_ANDROID_trackables_marker" + +typedef enum XrTrackableMarkerTrackingModeANDROID { + XR_TRACKABLE_MARKER_TRACKING_MODE_DYNAMIC_ANDROID = 0, + XR_TRACKABLE_MARKER_TRACKING_MODE_STATIC_ANDROID = 1, + XR_TRACKABLE_MARKER_TRACKING_MODE_MAX_ENUM_ANDROID = 0x7FFFFFFF +} XrTrackableMarkerTrackingModeANDROID; + +typedef enum XrTrackableMarkerDictionaryANDROID { + XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_4X4_50_ANDROID = 0, + XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_4X4_100_ANDROID = 1, + XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_4X4_250_ANDROID = 2, + XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_4X4_1000_ANDROID = 3, + XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_5X5_50_ANDROID = 4, + XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_5X5_100_ANDROID = 5, + XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_5X5_250_ANDROID = 6, + XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_5X5_1000_ANDROID = 7, + XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_6X6_50_ANDROID = 8, + XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_6X6_100_ANDROID = 9, + XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_6X6_250_ANDROID = 10, + XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_6X6_1000_ANDROID = 11, + XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_7X7_50_ANDROID = 12, + XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_7X7_100_ANDROID = 13, + XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_7X7_250_ANDROID = 14, + XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_7X7_1000_ANDROID = 15, + XR_TRACKABLE_MARKER_DICTIONARY_APRILTAG_16H5_ANDROID = 16, + XR_TRACKABLE_MARKER_DICTIONARY_APRILTAG_25H9_ANDROID = 17, + XR_TRACKABLE_MARKER_DICTIONARY_APRILTAG_36H10_ANDROID = 18, + XR_TRACKABLE_MARKER_DICTIONARY_APRILTAG_36H11_ANDROID = 19, + XR_TRACKABLE_MARKER_DICTIONARY_MAX_ENUM_ANDROID = 0x7FFFFFFF +} XrTrackableMarkerDictionaryANDROID; +// XrSystemMarkerTrackingPropertiesANDROID extends XrSystemProperties +typedef struct XrSystemMarkerTrackingPropertiesANDROID { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 supportsMarkerTracking; + XrBool32 supportsMarkerSizeEstimation; + uint16_t maxMarkerCount; +} XrSystemMarkerTrackingPropertiesANDROID; + +typedef struct XrTrackableMarkerDatabaseEntryANDROID { + int32_t id; + float edgeSize; +} XrTrackableMarkerDatabaseEntryANDROID; + +typedef struct XrTrackableMarkerDatabaseANDROID { + XrTrackableMarkerDictionaryANDROID dictionary; + uint32_t entryCount; + const XrTrackableMarkerDatabaseEntryANDROID* entries; +} XrTrackableMarkerDatabaseANDROID; + +// XrTrackableMarkerConfigurationANDROID extends XrTrackableTrackerCreateInfoANDROID +typedef struct XrTrackableMarkerConfigurationANDROID { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrTrackableMarkerTrackingModeANDROID trackingMode; + uint32_t databaseCount; + const XrTrackableMarkerDatabaseANDROID* databases; +} XrTrackableMarkerConfigurationANDROID; + +typedef struct XrTrackableMarkerANDROID { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrTrackingStateANDROID trackingState; + XrTime lastUpdatedTime; + XrTrackableMarkerDictionaryANDROID dictionary; + int32_t markerId; + XrPosef centerPose; + XrExtent2Df extents; +} XrTrackableMarkerANDROID; + +typedef XrResult (XRAPI_PTR *PFN_xrGetTrackableMarkerANDROID)(XrTrackableTrackerANDROID tracker, const XrTrackableGetInfoANDROID* getInfo, XrTrackableMarkerANDROID* markerOutput); + +#ifndef XR_NO_PROTOTYPES +#ifdef XR_EXTENSION_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrGetTrackableMarkerANDROID( + XrTrackableTrackerANDROID tracker, + const XrTrackableGetInfoANDROID* getInfo, + XrTrackableMarkerANDROID* markerOutput); +#endif /* XR_EXTENSION_PROTOTYPES */ +#endif /* !XR_NO_PROTOTYPES */ + + // XR_EXT_spatial_entity is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_spatial_entity 1 #define XR_NULL_SPATIAL_ENTITY_ID_EXT 0 + #define XR_NULL_SPATIAL_BUFFER_ID_EXT 0 XR_DEFINE_ATOM(XrSpatialEntityIdEXT) @@ -9781,567 +10899,559 @@ XR_DEFINE_HANDLE(XrSpatialSnapshotEXT) #define XR_EXT_SPATIAL_ENTITY_EXTENSION_NAME "XR_EXT_spatial_entity" typedef enum XrSpatialCapabilityEXT { - XR_SPATIAL_CAPABILITY_PLANE_TRACKING_EXT = 1000741000, - XR_SPATIAL_CAPABILITY_MARKER_TRACKING_QR_CODE_EXT = 1000743000, - XR_SPATIAL_CAPABILITY_MARKER_TRACKING_MICRO_QR_CODE_EXT = 1000743001, - XR_SPATIAL_CAPABILITY_MARKER_TRACKING_ARUCO_MARKER_EXT = 1000743002, - XR_SPATIAL_CAPABILITY_MARKER_TRACKING_APRIL_TAG_EXT = 1000743003, - XR_SPATIAL_CAPABILITY_ANCHOR_EXT = 1000762000, - XR_SPATIAL_CAPABILITY_MAX_ENUM_EXT = 0x7FFFFFFF + XR_SPATIAL_CAPABILITY_PLANE_TRACKING_EXT = 1000741000, + XR_SPATIAL_CAPABILITY_MARKER_TRACKING_QR_CODE_EXT = 1000743000, + XR_SPATIAL_CAPABILITY_MARKER_TRACKING_MICRO_QR_CODE_EXT = 1000743001, + XR_SPATIAL_CAPABILITY_MARKER_TRACKING_ARUCO_MARKER_EXT = 1000743002, + XR_SPATIAL_CAPABILITY_MARKER_TRACKING_APRIL_TAG_EXT = 1000743003, + XR_SPATIAL_CAPABILITY_ANCHOR_EXT = 1000762000, + XR_SPATIAL_CAPABILITY_MAX_ENUM_EXT = 0x7FFFFFFF } XrSpatialCapabilityEXT; typedef enum XrSpatialCapabilityFeatureEXT { - XR_SPATIAL_CAPABILITY_FEATURE_MARKER_TRACKING_FIXED_SIZE_MARKERS_EXT = 1000743000, - XR_SPATIAL_CAPABILITY_FEATURE_MARKER_TRACKING_STATIC_MARKERS_EXT = 1000743001, - XR_SPATIAL_CAPABILITY_FEATURE_MAX_ENUM_EXT = 0x7FFFFFFF + XR_SPATIAL_CAPABILITY_FEATURE_MARKER_TRACKING_FIXED_SIZE_MARKERS_EXT = 1000743000, + XR_SPATIAL_CAPABILITY_FEATURE_MARKER_TRACKING_STATIC_MARKERS_EXT = 1000743001, + XR_SPATIAL_CAPABILITY_FEATURE_MAX_ENUM_EXT = 0x7FFFFFFF } XrSpatialCapabilityFeatureEXT; typedef enum XrSpatialComponentTypeEXT { - XR_SPATIAL_COMPONENT_TYPE_BOUNDED_2D_EXT = 1, - XR_SPATIAL_COMPONENT_TYPE_BOUNDED_3D_EXT = 2, - XR_SPATIAL_COMPONENT_TYPE_PARENT_EXT = 3, - XR_SPATIAL_COMPONENT_TYPE_MESH_3D_EXT = 4, - XR_SPATIAL_COMPONENT_TYPE_PLANE_ALIGNMENT_EXT = 1000741000, - XR_SPATIAL_COMPONENT_TYPE_MESH_2D_EXT = 1000741001, - XR_SPATIAL_COMPONENT_TYPE_POLYGON_2D_EXT = 1000741002, - XR_SPATIAL_COMPONENT_TYPE_PLANE_SEMANTIC_LABEL_EXT = 1000741003, - XR_SPATIAL_COMPONENT_TYPE_MARKER_EXT = 1000743000, - XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT = 1000762000, - XR_SPATIAL_COMPONENT_TYPE_PERSISTENCE_EXT = 1000763000, - XR_SPATIAL_COMPONENT_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF + XR_SPATIAL_COMPONENT_TYPE_BOUNDED_2D_EXT = 1, + XR_SPATIAL_COMPONENT_TYPE_BOUNDED_3D_EXT = 2, + XR_SPATIAL_COMPONENT_TYPE_PARENT_EXT = 3, + XR_SPATIAL_COMPONENT_TYPE_MESH_3D_EXT = 4, + XR_SPATIAL_COMPONENT_TYPE_PLANE_ALIGNMENT_EXT = 1000741000, + XR_SPATIAL_COMPONENT_TYPE_MESH_2D_EXT = 1000741001, + XR_SPATIAL_COMPONENT_TYPE_POLYGON_2D_EXT = 1000741002, + XR_SPATIAL_COMPONENT_TYPE_PLANE_SEMANTIC_LABEL_EXT = 1000741003, + XR_SPATIAL_COMPONENT_TYPE_MARKER_EXT = 1000743000, + XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT = 1000762000, + XR_SPATIAL_COMPONENT_TYPE_PERSISTENCE_EXT = 1000763000, + XR_SPATIAL_COMPONENT_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF } XrSpatialComponentTypeEXT; typedef enum XrSpatialEntityTrackingStateEXT { - XR_SPATIAL_ENTITY_TRACKING_STATE_STOPPED_EXT = 1, - XR_SPATIAL_ENTITY_TRACKING_STATE_PAUSED_EXT = 2, - XR_SPATIAL_ENTITY_TRACKING_STATE_TRACKING_EXT = 3, - XR_SPATIAL_ENTITY_TRACKING_STATE_MAX_ENUM_EXT = 0x7FFFFFFF + XR_SPATIAL_ENTITY_TRACKING_STATE_STOPPED_EXT = 1, + XR_SPATIAL_ENTITY_TRACKING_STATE_PAUSED_EXT = 2, + XR_SPATIAL_ENTITY_TRACKING_STATE_TRACKING_EXT = 3, + XR_SPATIAL_ENTITY_TRACKING_STATE_MAX_ENUM_EXT = 0x7FFFFFFF } XrSpatialEntityTrackingStateEXT; typedef enum XrSpatialBufferTypeEXT { - XR_SPATIAL_BUFFER_TYPE_UNKNOWN_EXT = 0, - XR_SPATIAL_BUFFER_TYPE_STRING_EXT = 1, - XR_SPATIAL_BUFFER_TYPE_UINT8_EXT = 2, - XR_SPATIAL_BUFFER_TYPE_UINT16_EXT = 3, - XR_SPATIAL_BUFFER_TYPE_UINT32_EXT = 4, - XR_SPATIAL_BUFFER_TYPE_FLOAT_EXT = 5, - XR_SPATIAL_BUFFER_TYPE_VECTOR2F_EXT = 6, - XR_SPATIAL_BUFFER_TYPE_VECTOR3F_EXT = 7, - XR_SPATIAL_BUFFER_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF + XR_SPATIAL_BUFFER_TYPE_UNKNOWN_EXT = 0, + XR_SPATIAL_BUFFER_TYPE_STRING_EXT = 1, + XR_SPATIAL_BUFFER_TYPE_UINT8_EXT = 2, + XR_SPATIAL_BUFFER_TYPE_UINT16_EXT = 3, + XR_SPATIAL_BUFFER_TYPE_UINT32_EXT = 4, + XR_SPATIAL_BUFFER_TYPE_FLOAT_EXT = 5, + XR_SPATIAL_BUFFER_TYPE_VECTOR2F_EXT = 6, + XR_SPATIAL_BUFFER_TYPE_VECTOR3F_EXT = 7, + XR_SPATIAL_BUFFER_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF } XrSpatialBufferTypeEXT; typedef struct XrSpatialCapabilityComponentTypesEXT { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t componentTypeCapacityInput; - uint32_t componentTypeCountOutput; - XrSpatialComponentTypeEXT* componentTypes; + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t componentTypeCapacityInput; + uint32_t componentTypeCountOutput; + XrSpatialComponentTypeEXT* componentTypes; } XrSpatialCapabilityComponentTypesEXT; typedef struct XR_MAY_ALIAS XrSpatialCapabilityConfigurationBaseHeaderEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpatialCapabilityEXT capability; - uint32_t enabledComponentCount; - const XrSpatialComponentTypeEXT* enabledComponents; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpatialCapabilityEXT capability; + uint32_t enabledComponentCount; + const XrSpatialComponentTypeEXT* enabledComponents; } XrSpatialCapabilityConfigurationBaseHeaderEXT; typedef struct XrSpatialContextCreateInfoEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - uint32_t capabilityConfigCount; - const XrSpatialCapabilityConfigurationBaseHeaderEXT* const* capabilityConfigs; + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t capabilityConfigCount; + const XrSpatialCapabilityConfigurationBaseHeaderEXT* const* capabilityConfigs; } XrSpatialContextCreateInfoEXT; typedef struct XrCreateSpatialContextCompletionEXT { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrResult futureResult; - XrSpatialContextEXT spatialContext; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrResult futureResult; + XrSpatialContextEXT spatialContext; } XrCreateSpatialContextCompletionEXT; typedef struct XrSpatialDiscoverySnapshotCreateInfoEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - uint32_t componentTypeCount; - const XrSpatialComponentTypeEXT* componentTypes; + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t componentTypeCount; + const XrSpatialComponentTypeEXT* componentTypes; } XrSpatialDiscoverySnapshotCreateInfoEXT; typedef struct XrCreateSpatialDiscoverySnapshotCompletionInfoEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpace baseSpace; - XrTime time; - XrFutureEXT future; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpace baseSpace; + XrTime time; + XrFutureEXT future; } XrCreateSpatialDiscoverySnapshotCompletionInfoEXT; typedef struct XrCreateSpatialDiscoverySnapshotCompletionEXT { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrResult futureResult; - XrSpatialSnapshotEXT snapshot; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrResult futureResult; + XrSpatialSnapshotEXT snapshot; } XrCreateSpatialDiscoverySnapshotCompletionEXT; typedef struct XrSpatialComponentDataQueryConditionEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - uint32_t componentTypeCount; - const XrSpatialComponentTypeEXT* componentTypes; -} XrSpatialComponentDataQueryConditionEXT; + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t componentTypeCount; + const XrSpatialComponentTypeEXT* componentTypes; +} XrSpatialComponentDataQueryConditionEXT; typedef struct XrSpatialComponentDataQueryResultEXT { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t entityIdCapacityInput; - uint32_t entityIdCountOutput; - XrSpatialEntityIdEXT* entityIds; - uint32_t entityStateCapacityInput; - uint32_t entityStateCountOutput; - XrSpatialEntityTrackingStateEXT* entityStates; + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t entityIdCapacityInput; + uint32_t entityIdCountOutput; + XrSpatialEntityIdEXT* entityIds; + uint32_t entityStateCapacityInput; + uint32_t entityStateCountOutput; + XrSpatialEntityTrackingStateEXT* entityStates; } XrSpatialComponentDataQueryResultEXT; typedef struct XrSpatialBufferEXT { - XrSpatialBufferIdEXT bufferId; - XrSpatialBufferTypeEXT bufferType; + XrSpatialBufferIdEXT bufferId; + XrSpatialBufferTypeEXT bufferType; } XrSpatialBufferEXT; typedef struct XrSpatialBufferGetInfoEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpatialBufferIdEXT bufferId; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpatialBufferIdEXT bufferId; } XrSpatialBufferGetInfoEXT; typedef struct XrSpatialBounded2DDataEXT { - XrPosef center; - XrExtent2Df extents; + XrPosef center; + XrExtent2Df extents; } XrSpatialBounded2DDataEXT; // XrSpatialComponentBounded2DListEXT extends XrSpatialComponentDataQueryResultEXT typedef struct XrSpatialComponentBounded2DListEXT { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t boundCount; - XrSpatialBounded2DDataEXT* bounds; + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t boundCount; + XrSpatialBounded2DDataEXT* bounds; } XrSpatialComponentBounded2DListEXT; // XrSpatialComponentBounded3DListEXT extends XrSpatialComponentDataQueryResultEXT typedef struct XrSpatialComponentBounded3DListEXT { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t boundCount; - XrBoxf* bounds; + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t boundCount; + XrBoxf* bounds; } XrSpatialComponentBounded3DListEXT; // XrSpatialComponentParentListEXT extends XrSpatialComponentDataQueryResultEXT typedef struct XrSpatialComponentParentListEXT { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t parentCount; - XrSpatialEntityIdEXT* parents; + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t parentCount; + XrSpatialEntityIdEXT* parents; } XrSpatialComponentParentListEXT; typedef struct XrSpatialMeshDataEXT { - XrPosef origin; - XrSpatialBufferEXT vertexBuffer; - XrSpatialBufferEXT indexBuffer; + XrPosef origin; + XrSpatialBufferEXT vertexBuffer; + XrSpatialBufferEXT indexBuffer; } XrSpatialMeshDataEXT; // XrSpatialComponentMesh3DListEXT extends XrSpatialComponentDataQueryResultEXT typedef struct XrSpatialComponentMesh3DListEXT { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t meshCount; - XrSpatialMeshDataEXT* meshes; + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t meshCount; + XrSpatialMeshDataEXT* meshes; } XrSpatialComponentMesh3DListEXT; typedef struct XrSpatialEntityFromIdCreateInfoEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpatialEntityIdEXT entityId; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpatialEntityIdEXT entityId; } XrSpatialEntityFromIdCreateInfoEXT; typedef struct XrSpatialUpdateSnapshotCreateInfoEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - uint32_t entityCount; - const XrSpatialEntityEXT* entities; - uint32_t componentTypeCount; - const XrSpatialComponentTypeEXT* componentTypes; - XrSpace baseSpace; - XrTime time; + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t entityCount; + const XrSpatialEntityEXT* entities; + uint32_t componentTypeCount; + const XrSpatialComponentTypeEXT* componentTypes; + XrSpace baseSpace; + XrTime time; } XrSpatialUpdateSnapshotCreateInfoEXT; typedef struct XrEventDataSpatialDiscoveryRecommendedEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpatialContextEXT spatialContext; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpatialContextEXT spatialContext; } XrEventDataSpatialDiscoveryRecommendedEXT; -// XrSpatialFilterTrackingStateEXT extends -// XrSpatialDiscoverySnapshotCreateInfoEXT,XrSpatialComponentDataQueryConditionEXT +// XrSpatialFilterTrackingStateEXT extends XrSpatialDiscoverySnapshotCreateInfoEXT,XrSpatialComponentDataQueryConditionEXT typedef struct XrSpatialFilterTrackingStateEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpatialEntityTrackingStateEXT trackingState; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpatialEntityTrackingStateEXT trackingState; } XrSpatialFilterTrackingStateEXT; -typedef XrResult(XRAPI_PTR* PFN_xrEnumerateSpatialCapabilitiesEXT)(XrInstance instance, XrSystemId systemId, - uint32_t capabilityCapacityInput, - uint32_t* capabilityCountOutput, - XrSpatialCapabilityEXT* capabilities); -typedef XrResult(XRAPI_PTR* PFN_xrEnumerateSpatialCapabilityComponentTypesEXT)( - XrInstance instance, XrSystemId systemId, XrSpatialCapabilityEXT capability, - XrSpatialCapabilityComponentTypesEXT* capabilityComponents); -typedef XrResult(XRAPI_PTR* PFN_xrEnumerateSpatialCapabilityFeaturesEXT)( - XrInstance instance, XrSystemId systemId, XrSpatialCapabilityEXT capability, - uint32_t capabilityFeatureCapacityInput, uint32_t* capabilityFeatureCountOutput, - XrSpatialCapabilityFeatureEXT* capabilityFeatures); -typedef XrResult(XRAPI_PTR* PFN_xrCreateSpatialContextAsyncEXT)(XrSession session, - const XrSpatialContextCreateInfoEXT* createInfo, - XrFutureEXT* future); -typedef XrResult(XRAPI_PTR* PFN_xrCreateSpatialContextCompleteEXT)(XrSession session, XrFutureEXT future, - XrCreateSpatialContextCompletionEXT* completion); -typedef XrResult(XRAPI_PTR* PFN_xrDestroySpatialContextEXT)(XrSpatialContextEXT spatialContext); -typedef XrResult(XRAPI_PTR* PFN_xrCreateSpatialDiscoverySnapshotAsyncEXT)( - XrSpatialContextEXT spatialContext, const XrSpatialDiscoverySnapshotCreateInfoEXT* createInfo, XrFutureEXT* future); -typedef XrResult(XRAPI_PTR* PFN_xrCreateSpatialDiscoverySnapshotCompleteEXT)( - XrSpatialContextEXT spatialContext, - const XrCreateSpatialDiscoverySnapshotCompletionInfoEXT* createSnapshotCompletionInfo, - XrCreateSpatialDiscoverySnapshotCompletionEXT* completion); -typedef XrResult(XRAPI_PTR* PFN_xrQuerySpatialComponentDataEXT)( - XrSpatialSnapshotEXT snapshot, const XrSpatialComponentDataQueryConditionEXT* queryCondition, - XrSpatialComponentDataQueryResultEXT* queryResult); -typedef XrResult(XRAPI_PTR* PFN_xrDestroySpatialSnapshotEXT)(XrSpatialSnapshotEXT snapshot); -typedef XrResult(XRAPI_PTR* PFN_xrCreateSpatialEntityFromIdEXT)(XrSpatialContextEXT spatialContext, - const XrSpatialEntityFromIdCreateInfoEXT* createInfo, - XrSpatialEntityEXT* spatialEntity); -typedef XrResult(XRAPI_PTR* PFN_xrDestroySpatialEntityEXT)(XrSpatialEntityEXT spatialEntity); -typedef XrResult(XRAPI_PTR* PFN_xrCreateSpatialUpdateSnapshotEXT)( - XrSpatialContextEXT spatialContext, const XrSpatialUpdateSnapshotCreateInfoEXT* createInfo, - XrSpatialSnapshotEXT* snapshot); -typedef XrResult(XRAPI_PTR* PFN_xrGetSpatialBufferStringEXT)(XrSpatialSnapshotEXT snapshot, - const XrSpatialBufferGetInfoEXT* info, - uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, - char* buffer); -typedef XrResult(XRAPI_PTR* PFN_xrGetSpatialBufferUint8EXT)(XrSpatialSnapshotEXT snapshot, - const XrSpatialBufferGetInfoEXT* info, - uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, - uint8_t* buffer); -typedef XrResult(XRAPI_PTR* PFN_xrGetSpatialBufferUint16EXT)(XrSpatialSnapshotEXT snapshot, - const XrSpatialBufferGetInfoEXT* info, - uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, - uint16_t* buffer); -typedef XrResult(XRAPI_PTR* PFN_xrGetSpatialBufferUint32EXT)(XrSpatialSnapshotEXT snapshot, - const XrSpatialBufferGetInfoEXT* info, - uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, - uint32_t* buffer); -typedef XrResult(XRAPI_PTR* PFN_xrGetSpatialBufferFloatEXT)(XrSpatialSnapshotEXT snapshot, - const XrSpatialBufferGetInfoEXT* info, - uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, - float* buffer); -typedef XrResult(XRAPI_PTR* PFN_xrGetSpatialBufferVector2fEXT)(XrSpatialSnapshotEXT snapshot, - const XrSpatialBufferGetInfoEXT* info, - uint32_t bufferCapacityInput, - uint32_t* bufferCountOutput, XrVector2f* buffer); -typedef XrResult(XRAPI_PTR* PFN_xrGetSpatialBufferVector3fEXT)(XrSpatialSnapshotEXT snapshot, - const XrSpatialBufferGetInfoEXT* info, - uint32_t bufferCapacityInput, - uint32_t* bufferCountOutput, XrVector3f* buffer); +typedef XrResult (XRAPI_PTR *PFN_xrEnumerateSpatialCapabilitiesEXT)(XrInstance instance, XrSystemId systemId, uint32_t capabilityCapacityInput, uint32_t* capabilityCountOutput, XrSpatialCapabilityEXT* capabilities); +typedef XrResult (XRAPI_PTR *PFN_xrEnumerateSpatialCapabilityComponentTypesEXT)(XrInstance instance, XrSystemId systemId, XrSpatialCapabilityEXT capability, XrSpatialCapabilityComponentTypesEXT* capabilityComponents); +typedef XrResult (XRAPI_PTR *PFN_xrEnumerateSpatialCapabilityFeaturesEXT)(XrInstance instance, XrSystemId systemId, XrSpatialCapabilityEXT capability, uint32_t capabilityFeatureCapacityInput, uint32_t* capabilityFeatureCountOutput, XrSpatialCapabilityFeatureEXT* capabilityFeatures); +typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialContextAsyncEXT)(XrSession session, const XrSpatialContextCreateInfoEXT* createInfo, XrFutureEXT* future); +typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialContextCompleteEXT)(XrSession session, XrFutureEXT future, XrCreateSpatialContextCompletionEXT* completion); +typedef XrResult (XRAPI_PTR *PFN_xrDestroySpatialContextEXT)(XrSpatialContextEXT spatialContext); +typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialDiscoverySnapshotAsyncEXT)(XrSpatialContextEXT spatialContext, const XrSpatialDiscoverySnapshotCreateInfoEXT* createInfo, XrFutureEXT* future); +typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialDiscoverySnapshotCompleteEXT)(XrSpatialContextEXT spatialContext, const XrCreateSpatialDiscoverySnapshotCompletionInfoEXT* createSnapshotCompletionInfo, XrCreateSpatialDiscoverySnapshotCompletionEXT* completion); +typedef XrResult (XRAPI_PTR *PFN_xrQuerySpatialComponentDataEXT)(XrSpatialSnapshotEXT snapshot, const XrSpatialComponentDataQueryConditionEXT* queryCondition, XrSpatialComponentDataQueryResultEXT* queryResult); +typedef XrResult (XRAPI_PTR *PFN_xrDestroySpatialSnapshotEXT)(XrSpatialSnapshotEXT snapshot); +typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialEntityFromIdEXT)(XrSpatialContextEXT spatialContext, const XrSpatialEntityFromIdCreateInfoEXT* createInfo, XrSpatialEntityEXT* spatialEntity); +typedef XrResult (XRAPI_PTR *PFN_xrDestroySpatialEntityEXT)(XrSpatialEntityEXT spatialEntity); +typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialUpdateSnapshotEXT)(XrSpatialContextEXT spatialContext, const XrSpatialUpdateSnapshotCreateInfoEXT* createInfo, XrSpatialSnapshotEXT* snapshot); +typedef XrResult (XRAPI_PTR *PFN_xrGetSpatialBufferStringEXT)(XrSpatialSnapshotEXT snapshot, const XrSpatialBufferGetInfoEXT* info, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, char* buffer); +typedef XrResult (XRAPI_PTR *PFN_xrGetSpatialBufferUint8EXT)(XrSpatialSnapshotEXT snapshot, const XrSpatialBufferGetInfoEXT* info, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, uint8_t* buffer); +typedef XrResult (XRAPI_PTR *PFN_xrGetSpatialBufferUint16EXT)(XrSpatialSnapshotEXT snapshot, const XrSpatialBufferGetInfoEXT* info, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, uint16_t* buffer); +typedef XrResult (XRAPI_PTR *PFN_xrGetSpatialBufferUint32EXT)(XrSpatialSnapshotEXT snapshot, const XrSpatialBufferGetInfoEXT* info, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, uint32_t* buffer); +typedef XrResult (XRAPI_PTR *PFN_xrGetSpatialBufferFloatEXT)(XrSpatialSnapshotEXT snapshot, const XrSpatialBufferGetInfoEXT* info, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, float* buffer); +typedef XrResult (XRAPI_PTR *PFN_xrGetSpatialBufferVector2fEXT)(XrSpatialSnapshotEXT snapshot, const XrSpatialBufferGetInfoEXT* info, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, XrVector2f* buffer); +typedef XrResult (XRAPI_PTR *PFN_xrGetSpatialBufferVector3fEXT)(XrSpatialSnapshotEXT snapshot, const XrSpatialBufferGetInfoEXT* info, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, XrVector3f* buffer); #ifndef XR_NO_PROTOTYPES #ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSpatialCapabilitiesEXT(XrInstance instance, XrSystemId systemId, - uint32_t capabilityCapacityInput, - uint32_t* capabilityCountOutput, - XrSpatialCapabilityEXT* capabilities); +XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSpatialCapabilitiesEXT( + XrInstance instance, + XrSystemId systemId, + uint32_t capabilityCapacityInput, + uint32_t* capabilityCountOutput, + XrSpatialCapabilityEXT* capabilities); XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSpatialCapabilityComponentTypesEXT( - XrInstance instance, XrSystemId systemId, XrSpatialCapabilityEXT capability, - XrSpatialCapabilityComponentTypesEXT* capabilityComponents); + XrInstance instance, + XrSystemId systemId, + XrSpatialCapabilityEXT capability, + XrSpatialCapabilityComponentTypesEXT* capabilityComponents); -XRAPI_ATTR XrResult XRAPI_CALL -xrEnumerateSpatialCapabilityFeaturesEXT(XrInstance instance, XrSystemId systemId, XrSpatialCapabilityEXT capability, - uint32_t capabilityFeatureCapacityInput, uint32_t* capabilityFeatureCountOutput, - XrSpatialCapabilityFeatureEXT* capabilityFeatures); +XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSpatialCapabilityFeaturesEXT( + XrInstance instance, + XrSystemId systemId, + XrSpatialCapabilityEXT capability, + uint32_t capabilityFeatureCapacityInput, + uint32_t* capabilityFeatureCountOutput, + XrSpatialCapabilityFeatureEXT* capabilityFeatures); -XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialContextAsyncEXT(XrSession session, - const XrSpatialContextCreateInfoEXT* createInfo, - XrFutureEXT* future); +XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialContextAsyncEXT( + XrSession session, + const XrSpatialContextCreateInfoEXT* createInfo, + XrFutureEXT* future); -XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialContextCompleteEXT(XrSession session, XrFutureEXT future, - XrCreateSpatialContextCompletionEXT* completion); +XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialContextCompleteEXT( + XrSession session, + XrFutureEXT future, + XrCreateSpatialContextCompletionEXT* completion); -XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpatialContextEXT(XrSpatialContextEXT spatialContext); +XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpatialContextEXT( + XrSpatialContextEXT spatialContext); XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialDiscoverySnapshotAsyncEXT( - XrSpatialContextEXT spatialContext, const XrSpatialDiscoverySnapshotCreateInfoEXT* createInfo, XrFutureEXT* future); + XrSpatialContextEXT spatialContext, + const XrSpatialDiscoverySnapshotCreateInfoEXT* createInfo, + XrFutureEXT* future); XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialDiscoverySnapshotCompleteEXT( - XrSpatialContextEXT spatialContext, + XrSpatialContextEXT spatialContext, const XrCreateSpatialDiscoverySnapshotCompletionInfoEXT* createSnapshotCompletionInfo, XrCreateSpatialDiscoverySnapshotCompletionEXT* completion); XRAPI_ATTR XrResult XRAPI_CALL xrQuerySpatialComponentDataEXT( - XrSpatialSnapshotEXT snapshot, const XrSpatialComponentDataQueryConditionEXT* queryCondition, - XrSpatialComponentDataQueryResultEXT* queryResult); - -XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpatialSnapshotEXT(XrSpatialSnapshotEXT snapshot); - -XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialEntityFromIdEXT(XrSpatialContextEXT spatialContext, - const XrSpatialEntityFromIdCreateInfoEXT* createInfo, - XrSpatialEntityEXT* spatialEntity); - -XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpatialEntityEXT(XrSpatialEntityEXT spatialEntity); - -XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialUpdateSnapshotEXT(XrSpatialContextEXT spatialContext, - const XrSpatialUpdateSnapshotCreateInfoEXT* createInfo, - XrSpatialSnapshotEXT* snapshot); - -XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialBufferStringEXT(XrSpatialSnapshotEXT snapshot, - const XrSpatialBufferGetInfoEXT* info, - uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, - char* buffer); - -XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialBufferUint8EXT(XrSpatialSnapshotEXT snapshot, - const XrSpatialBufferGetInfoEXT* info, - uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, - uint8_t* buffer); - -XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialBufferUint16EXT(XrSpatialSnapshotEXT snapshot, - const XrSpatialBufferGetInfoEXT* info, - uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, - uint16_t* buffer); - -XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialBufferUint32EXT(XrSpatialSnapshotEXT snapshot, - const XrSpatialBufferGetInfoEXT* info, - uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, - uint32_t* buffer); - -XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialBufferFloatEXT(XrSpatialSnapshotEXT snapshot, - const XrSpatialBufferGetInfoEXT* info, - uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, - float* buffer); - -XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialBufferVector2fEXT(XrSpatialSnapshotEXT snapshot, - const XrSpatialBufferGetInfoEXT* info, - uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, - XrVector2f* buffer); - -XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialBufferVector3fEXT(XrSpatialSnapshotEXT snapshot, - const XrSpatialBufferGetInfoEXT* info, - uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, - XrVector3f* buffer); + XrSpatialSnapshotEXT snapshot, + const XrSpatialComponentDataQueryConditionEXT* queryCondition, + XrSpatialComponentDataQueryResultEXT* queryResult); + +XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpatialSnapshotEXT( + XrSpatialSnapshotEXT snapshot); + +XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialEntityFromIdEXT( + XrSpatialContextEXT spatialContext, + const XrSpatialEntityFromIdCreateInfoEXT* createInfo, + XrSpatialEntityEXT* spatialEntity); + +XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpatialEntityEXT( + XrSpatialEntityEXT spatialEntity); + +XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialUpdateSnapshotEXT( + XrSpatialContextEXT spatialContext, + const XrSpatialUpdateSnapshotCreateInfoEXT* createInfo, + XrSpatialSnapshotEXT* snapshot); + +XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialBufferStringEXT( + XrSpatialSnapshotEXT snapshot, + const XrSpatialBufferGetInfoEXT* info, + uint32_t bufferCapacityInput, + uint32_t* bufferCountOutput, + char* buffer); + +XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialBufferUint8EXT( + XrSpatialSnapshotEXT snapshot, + const XrSpatialBufferGetInfoEXT* info, + uint32_t bufferCapacityInput, + uint32_t* bufferCountOutput, + uint8_t* buffer); + +XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialBufferUint16EXT( + XrSpatialSnapshotEXT snapshot, + const XrSpatialBufferGetInfoEXT* info, + uint32_t bufferCapacityInput, + uint32_t* bufferCountOutput, + uint16_t* buffer); + +XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialBufferUint32EXT( + XrSpatialSnapshotEXT snapshot, + const XrSpatialBufferGetInfoEXT* info, + uint32_t bufferCapacityInput, + uint32_t* bufferCountOutput, + uint32_t* buffer); + +XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialBufferFloatEXT( + XrSpatialSnapshotEXT snapshot, + const XrSpatialBufferGetInfoEXT* info, + uint32_t bufferCapacityInput, + uint32_t* bufferCountOutput, + float* buffer); + +XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialBufferVector2fEXT( + XrSpatialSnapshotEXT snapshot, + const XrSpatialBufferGetInfoEXT* info, + uint32_t bufferCapacityInput, + uint32_t* bufferCountOutput, + XrVector2f* buffer); + +XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialBufferVector3fEXT( + XrSpatialSnapshotEXT snapshot, + const XrSpatialBufferGetInfoEXT* info, + uint32_t bufferCapacityInput, + uint32_t* bufferCountOutput, + XrVector3f* buffer); #endif /* XR_EXTENSION_PROTOTYPES */ #endif /* !XR_NO_PROTOTYPES */ + // XR_EXT_spatial_plane_tracking is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_spatial_plane_tracking 1 #define XR_EXT_spatial_plane_tracking_SPEC_VERSION 1 #define XR_EXT_SPATIAL_PLANE_TRACKING_EXTENSION_NAME "XR_EXT_spatial_plane_tracking" typedef enum XrSpatialPlaneAlignmentEXT { - XR_SPATIAL_PLANE_ALIGNMENT_HORIZONTAL_UPWARD_EXT = 0, - XR_SPATIAL_PLANE_ALIGNMENT_HORIZONTAL_DOWNWARD_EXT = 1, - XR_SPATIAL_PLANE_ALIGNMENT_VERTICAL_EXT = 2, - XR_SPATIAL_PLANE_ALIGNMENT_ARBITRARY_EXT = 3, - XR_SPATIAL_PLANE_ALIGNMENT_MAX_ENUM_EXT = 0x7FFFFFFF + XR_SPATIAL_PLANE_ALIGNMENT_HORIZONTAL_UPWARD_EXT = 0, + XR_SPATIAL_PLANE_ALIGNMENT_HORIZONTAL_DOWNWARD_EXT = 1, + XR_SPATIAL_PLANE_ALIGNMENT_VERTICAL_EXT = 2, + XR_SPATIAL_PLANE_ALIGNMENT_ARBITRARY_EXT = 3, + XR_SPATIAL_PLANE_ALIGNMENT_MAX_ENUM_EXT = 0x7FFFFFFF } XrSpatialPlaneAlignmentEXT; typedef enum XrSpatialPlaneSemanticLabelEXT { - XR_SPATIAL_PLANE_SEMANTIC_LABEL_UNCATEGORIZED_EXT = 1, - XR_SPATIAL_PLANE_SEMANTIC_LABEL_FLOOR_EXT = 2, - XR_SPATIAL_PLANE_SEMANTIC_LABEL_WALL_EXT = 3, - XR_SPATIAL_PLANE_SEMANTIC_LABEL_CEILING_EXT = 4, - XR_SPATIAL_PLANE_SEMANTIC_LABEL_TABLE_EXT = 5, - XR_SPATIAL_PLANE_SEMANTIC_LABEL_MAX_ENUM_EXT = 0x7FFFFFFF + XR_SPATIAL_PLANE_SEMANTIC_LABEL_UNCATEGORIZED_EXT = 1, + XR_SPATIAL_PLANE_SEMANTIC_LABEL_FLOOR_EXT = 2, + XR_SPATIAL_PLANE_SEMANTIC_LABEL_WALL_EXT = 3, + XR_SPATIAL_PLANE_SEMANTIC_LABEL_CEILING_EXT = 4, + XR_SPATIAL_PLANE_SEMANTIC_LABEL_TABLE_EXT = 5, + XR_SPATIAL_PLANE_SEMANTIC_LABEL_MAX_ENUM_EXT = 0x7FFFFFFF } XrSpatialPlaneSemanticLabelEXT; typedef struct XrSpatialCapabilityConfigurationPlaneTrackingEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpatialCapabilityEXT capability; - uint32_t enabledComponentCount; - const XrSpatialComponentTypeEXT* enabledComponents; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpatialCapabilityEXT capability; + uint32_t enabledComponentCount; + const XrSpatialComponentTypeEXT* enabledComponents; } XrSpatialCapabilityConfigurationPlaneTrackingEXT; // XrSpatialComponentPlaneAlignmentListEXT extends XrSpatialComponentDataQueryResultEXT typedef struct XrSpatialComponentPlaneAlignmentListEXT { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t planeAlignmentCount; - XrSpatialPlaneAlignmentEXT* planeAlignments; + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t planeAlignmentCount; + XrSpatialPlaneAlignmentEXT* planeAlignments; } XrSpatialComponentPlaneAlignmentListEXT; // XrSpatialComponentMesh2DListEXT extends XrSpatialComponentDataQueryResultEXT typedef struct XrSpatialComponentMesh2DListEXT { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t meshCount; - XrSpatialMeshDataEXT* meshes; + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t meshCount; + XrSpatialMeshDataEXT* meshes; } XrSpatialComponentMesh2DListEXT; typedef struct XrSpatialPolygon2DDataEXT { - XrPosef origin; - XrSpatialBufferEXT vertexBuffer; + XrPosef origin; + XrSpatialBufferEXT vertexBuffer; } XrSpatialPolygon2DDataEXT; // XrSpatialComponentPolygon2DListEXT extends XrSpatialComponentDataQueryResultEXT typedef struct XrSpatialComponentPolygon2DListEXT { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t polygonCount; - XrSpatialPolygon2DDataEXT* polygons; + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t polygonCount; + XrSpatialPolygon2DDataEXT* polygons; } XrSpatialComponentPolygon2DListEXT; // XrSpatialComponentPlaneSemanticLabelListEXT extends XrSpatialComponentDataQueryResultEXT typedef struct XrSpatialComponentPlaneSemanticLabelListEXT { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t semanticLabelCount; - XrSpatialPlaneSemanticLabelEXT* semanticLabels; + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t semanticLabelCount; + XrSpatialPlaneSemanticLabelEXT* semanticLabels; } XrSpatialComponentPlaneSemanticLabelListEXT; + + // XR_EXT_spatial_marker_tracking is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_spatial_marker_tracking 1 #define XR_EXT_spatial_marker_tracking_SPEC_VERSION 1 #define XR_EXT_SPATIAL_MARKER_TRACKING_EXTENSION_NAME "XR_EXT_spatial_marker_tracking" typedef enum XrSpatialMarkerArucoDictEXT { - XR_SPATIAL_MARKER_ARUCO_DICT_4X4_50_EXT = 1, - XR_SPATIAL_MARKER_ARUCO_DICT_4X4_100_EXT = 2, - XR_SPATIAL_MARKER_ARUCO_DICT_4X4_250_EXT = 3, - XR_SPATIAL_MARKER_ARUCO_DICT_4X4_1000_EXT = 4, - XR_SPATIAL_MARKER_ARUCO_DICT_5X5_50_EXT = 5, - XR_SPATIAL_MARKER_ARUCO_DICT_5X5_100_EXT = 6, - XR_SPATIAL_MARKER_ARUCO_DICT_5X5_250_EXT = 7, - XR_SPATIAL_MARKER_ARUCO_DICT_5X5_1000_EXT = 8, - XR_SPATIAL_MARKER_ARUCO_DICT_6X6_50_EXT = 9, - XR_SPATIAL_MARKER_ARUCO_DICT_6X6_100_EXT = 10, - XR_SPATIAL_MARKER_ARUCO_DICT_6X6_250_EXT = 11, - XR_SPATIAL_MARKER_ARUCO_DICT_6X6_1000_EXT = 12, - XR_SPATIAL_MARKER_ARUCO_DICT_7X7_50_EXT = 13, - XR_SPATIAL_MARKER_ARUCO_DICT_7X7_100_EXT = 14, - XR_SPATIAL_MARKER_ARUCO_DICT_7X7_250_EXT = 15, - XR_SPATIAL_MARKER_ARUCO_DICT_7X7_1000_EXT = 16, - XR_SPATIAL_MARKER_ARUCO_DICT_MAX_ENUM_EXT = 0x7FFFFFFF + XR_SPATIAL_MARKER_ARUCO_DICT_4X4_50_EXT = 1, + XR_SPATIAL_MARKER_ARUCO_DICT_4X4_100_EXT = 2, + XR_SPATIAL_MARKER_ARUCO_DICT_4X4_250_EXT = 3, + XR_SPATIAL_MARKER_ARUCO_DICT_4X4_1000_EXT = 4, + XR_SPATIAL_MARKER_ARUCO_DICT_5X5_50_EXT = 5, + XR_SPATIAL_MARKER_ARUCO_DICT_5X5_100_EXT = 6, + XR_SPATIAL_MARKER_ARUCO_DICT_5X5_250_EXT = 7, + XR_SPATIAL_MARKER_ARUCO_DICT_5X5_1000_EXT = 8, + XR_SPATIAL_MARKER_ARUCO_DICT_6X6_50_EXT = 9, + XR_SPATIAL_MARKER_ARUCO_DICT_6X6_100_EXT = 10, + XR_SPATIAL_MARKER_ARUCO_DICT_6X6_250_EXT = 11, + XR_SPATIAL_MARKER_ARUCO_DICT_6X6_1000_EXT = 12, + XR_SPATIAL_MARKER_ARUCO_DICT_7X7_50_EXT = 13, + XR_SPATIAL_MARKER_ARUCO_DICT_7X7_100_EXT = 14, + XR_SPATIAL_MARKER_ARUCO_DICT_7X7_250_EXT = 15, + XR_SPATIAL_MARKER_ARUCO_DICT_7X7_1000_EXT = 16, + XR_SPATIAL_MARKER_ARUCO_DICT_MAX_ENUM_EXT = 0x7FFFFFFF } XrSpatialMarkerArucoDictEXT; typedef enum XrSpatialMarkerAprilTagDictEXT { - XR_SPATIAL_MARKER_APRIL_TAG_DICT_16H5_EXT = 1, - XR_SPATIAL_MARKER_APRIL_TAG_DICT_25H9_EXT = 2, - XR_SPATIAL_MARKER_APRIL_TAG_DICT_36H10_EXT = 3, - XR_SPATIAL_MARKER_APRIL_TAG_DICT_36H11_EXT = 4, - XR_SPATIAL_MARKER_APRIL_TAG_DICT_MAX_ENUM_EXT = 0x7FFFFFFF + XR_SPATIAL_MARKER_APRIL_TAG_DICT_16H5_EXT = 1, + XR_SPATIAL_MARKER_APRIL_TAG_DICT_25H9_EXT = 2, + XR_SPATIAL_MARKER_APRIL_TAG_DICT_36H10_EXT = 3, + XR_SPATIAL_MARKER_APRIL_TAG_DICT_36H11_EXT = 4, + XR_SPATIAL_MARKER_APRIL_TAG_DICT_MAX_ENUM_EXT = 0x7FFFFFFF } XrSpatialMarkerAprilTagDictEXT; typedef struct XrSpatialCapabilityConfigurationQrCodeEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpatialCapabilityEXT capability; - uint32_t enabledComponentCount; - const XrSpatialComponentTypeEXT* enabledComponents; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpatialCapabilityEXT capability; + uint32_t enabledComponentCount; + const XrSpatialComponentTypeEXT* enabledComponents; } XrSpatialCapabilityConfigurationQrCodeEXT; typedef struct XrSpatialCapabilityConfigurationMicroQrCodeEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpatialCapabilityEXT capability; - uint32_t enabledComponentCount; - const XrSpatialComponentTypeEXT* enabledComponents; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpatialCapabilityEXT capability; + uint32_t enabledComponentCount; + const XrSpatialComponentTypeEXT* enabledComponents; } XrSpatialCapabilityConfigurationMicroQrCodeEXT; typedef struct XrSpatialCapabilityConfigurationArucoMarkerEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpatialCapabilityEXT capability; - uint32_t enabledComponentCount; - const XrSpatialComponentTypeEXT* enabledComponents; - XrSpatialMarkerArucoDictEXT arUcoDict; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpatialCapabilityEXT capability; + uint32_t enabledComponentCount; + const XrSpatialComponentTypeEXT* enabledComponents; + XrSpatialMarkerArucoDictEXT arUcoDict; } XrSpatialCapabilityConfigurationArucoMarkerEXT; typedef struct XrSpatialCapabilityConfigurationAprilTagEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpatialCapabilityEXT capability; - uint32_t enabledComponentCount; - const XrSpatialComponentTypeEXT* enabledComponents; - XrSpatialMarkerAprilTagDictEXT aprilDict; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpatialCapabilityEXT capability; + uint32_t enabledComponentCount; + const XrSpatialComponentTypeEXT* enabledComponents; + XrSpatialMarkerAprilTagDictEXT aprilDict; } XrSpatialCapabilityConfigurationAprilTagEXT; -// XrSpatialMarkerSizeEXT extends -// XrSpatialCapabilityConfigurationArucoMarkerEXT,XrSpatialCapabilityConfigurationAprilTagEXT,XrSpatialCapabilityConfigurationQrCodeEXT,XrSpatialCapabilityConfigurationMicroQrCodeEXT +// XrSpatialMarkerSizeEXT extends XrSpatialCapabilityConfigurationArucoMarkerEXT,XrSpatialCapabilityConfigurationAprilTagEXT,XrSpatialCapabilityConfigurationQrCodeEXT,XrSpatialCapabilityConfigurationMicroQrCodeEXT typedef struct XrSpatialMarkerSizeEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - float markerSideLength; + XrStructureType type; + const void* XR_MAY_ALIAS next; + float markerSideLength; } XrSpatialMarkerSizeEXT; -// XrSpatialMarkerStaticOptimizationEXT extends -// XrSpatialCapabilityConfigurationArucoMarkerEXT,XrSpatialCapabilityConfigurationAprilTagEXT,XrSpatialCapabilityConfigurationQrCodeEXT,XrSpatialCapabilityConfigurationMicroQrCodeEXT +// XrSpatialMarkerStaticOptimizationEXT extends XrSpatialCapabilityConfigurationArucoMarkerEXT,XrSpatialCapabilityConfigurationAprilTagEXT,XrSpatialCapabilityConfigurationQrCodeEXT,XrSpatialCapabilityConfigurationMicroQrCodeEXT typedef struct XrSpatialMarkerStaticOptimizationEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrBool32 optimizeForStaticMarker; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrBool32 optimizeForStaticMarker; } XrSpatialMarkerStaticOptimizationEXT; typedef struct XrSpatialMarkerDataEXT { - XrSpatialCapabilityEXT capability; - uint32_t markerId; - XrSpatialBufferEXT data; + XrSpatialCapabilityEXT capability; + uint32_t markerId; + XrSpatialBufferEXT data; } XrSpatialMarkerDataEXT; // XrSpatialComponentMarkerListEXT extends XrSpatialComponentDataQueryResultEXT typedef struct XrSpatialComponentMarkerListEXT { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t markerCount; - XrSpatialMarkerDataEXT* markers; + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t markerCount; + XrSpatialMarkerDataEXT* markers; } XrSpatialComponentMarkerListEXT; + + // XR_LOGITECH_mx_ink_stylus_interaction is a preprocessor guard. Do not pass it to API calls. #define XR_LOGITECH_mx_ink_stylus_interaction 1 #define XR_LOGITECH_mx_ink_stylus_interaction_SPEC_VERSION 1 #define XR_LOGITECH_MX_INK_STYLUS_INTERACTION_EXTENSION_NAME "XR_LOGITECH_mx_ink_stylus_interaction" + // XR_EXT_spatial_anchor is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_spatial_anchor 1 #define XR_EXT_spatial_anchor_SPEC_VERSION 1 #define XR_EXT_SPATIAL_ANCHOR_EXTENSION_NAME "XR_EXT_spatial_anchor" typedef struct XrSpatialCapabilityConfigurationAnchorEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpatialCapabilityEXT capability; - uint32_t enabledComponentCount; - const XrSpatialComponentTypeEXT* enabledComponents; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpatialCapabilityEXT capability; + uint32_t enabledComponentCount; + const XrSpatialComponentTypeEXT* enabledComponents; } XrSpatialCapabilityConfigurationAnchorEXT; // XrSpatialComponentAnchorListEXT extends XrSpatialComponentDataQueryResultEXT typedef struct XrSpatialComponentAnchorListEXT { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t locationCount; - XrPosef* locations; + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t locationCount; + XrPosef* locations; } XrSpatialComponentAnchorListEXT; typedef struct XrSpatialAnchorCreateInfoEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpace baseSpace; - XrTime time; - XrPosef pose; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpace baseSpace; + XrTime time; + XrPosef pose; } XrSpatialAnchorCreateInfoEXT; -typedef XrResult(XRAPI_PTR* PFN_xrCreateSpatialAnchorEXT)(XrSpatialContextEXT spatialContext, - const XrSpatialAnchorCreateInfoEXT* createInfo, - XrSpatialEntityIdEXT* anchorEntityId, - XrSpatialEntityEXT* anchorEntity); +typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialAnchorEXT)(XrSpatialContextEXT spatialContext, const XrSpatialAnchorCreateInfoEXT* createInfo, XrSpatialEntityIdEXT* anchorEntityId, XrSpatialEntityEXT* anchorEntity); #ifndef XR_NO_PROTOTYPES #ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorEXT(XrSpatialContextEXT spatialContext, - const XrSpatialAnchorCreateInfoEXT* createInfo, - XrSpatialEntityIdEXT* anchorEntityId, - XrSpatialEntityEXT* anchorEntity); +XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorEXT( + XrSpatialContextEXT spatialContext, + const XrSpatialAnchorCreateInfoEXT* createInfo, + XrSpatialEntityIdEXT* anchorEntityId, + XrSpatialEntityEXT* anchorEntity); #endif /* XR_EXTENSION_PROTOTYPES */ #endif /* !XR_NO_PROTOTYPES */ + // XR_EXT_spatial_persistence is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_spatial_persistence 1 XR_DEFINE_HANDLE(XrSpatialPersistenceContextEXT) @@ -10349,167 +11459,185 @@ XR_DEFINE_HANDLE(XrSpatialPersistenceContextEXT) #define XR_EXT_SPATIAL_PERSISTENCE_EXTENSION_NAME "XR_EXT_spatial_persistence" typedef enum XrSpatialPersistenceScopeEXT { - XR_SPATIAL_PERSISTENCE_SCOPE_SYSTEM_MANAGED_EXT = 1, - XR_SPATIAL_PERSISTENCE_SCOPE_LOCAL_ANCHORS_EXT = 1000781000, - XR_SPATIAL_PERSISTENCE_SCOPE_MAX_ENUM_EXT = 0x7FFFFFFF + XR_SPATIAL_PERSISTENCE_SCOPE_SYSTEM_MANAGED_EXT = 1, + XR_SPATIAL_PERSISTENCE_SCOPE_LOCAL_ANCHORS_EXT = 1000781000, + XR_SPATIAL_PERSISTENCE_SCOPE_MAX_ENUM_EXT = 0x7FFFFFFF } XrSpatialPersistenceScopeEXT; typedef enum XrSpatialPersistenceContextResultEXT { - XR_SPATIAL_PERSISTENCE_CONTEXT_RESULT_SUCCESS_EXT = 0, - XR_SPATIAL_PERSISTENCE_CONTEXT_RESULT_ENTITY_NOT_TRACKING_EXT = -1000781001, - XR_SPATIAL_PERSISTENCE_CONTEXT_RESULT_PERSIST_UUID_NOT_FOUND_EXT = -1000781002, - XR_SPATIAL_PERSISTENCE_CONTEXT_RESULT_MAX_ENUM_EXT = 0x7FFFFFFF + XR_SPATIAL_PERSISTENCE_CONTEXT_RESULT_SUCCESS_EXT = 0, + XR_SPATIAL_PERSISTENCE_CONTEXT_RESULT_ENTITY_NOT_TRACKING_EXT = -1000781001, + XR_SPATIAL_PERSISTENCE_CONTEXT_RESULT_PERSIST_UUID_NOT_FOUND_EXT = -1000781002, + XR_SPATIAL_PERSISTENCE_CONTEXT_RESULT_MAX_ENUM_EXT = 0x7FFFFFFF } XrSpatialPersistenceContextResultEXT; typedef enum XrSpatialPersistenceStateEXT { - XR_SPATIAL_PERSISTENCE_STATE_LOADED_EXT = 1, - XR_SPATIAL_PERSISTENCE_STATE_NOT_FOUND_EXT = 2, - XR_SPATIAL_PERSISTENCE_STATE_MAX_ENUM_EXT = 0x7FFFFFFF + XR_SPATIAL_PERSISTENCE_STATE_LOADED_EXT = 1, + XR_SPATIAL_PERSISTENCE_STATE_NOT_FOUND_EXT = 2, + XR_SPATIAL_PERSISTENCE_STATE_MAX_ENUM_EXT = 0x7FFFFFFF } XrSpatialPersistenceStateEXT; typedef struct XrSpatialPersistenceContextCreateInfoEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpatialPersistenceScopeEXT scope; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpatialPersistenceScopeEXT scope; } XrSpatialPersistenceContextCreateInfoEXT; typedef struct XrCreateSpatialPersistenceContextCompletionEXT { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrResult futureResult; - XrSpatialPersistenceContextResultEXT createResult; - XrSpatialPersistenceContextEXT persistenceContext; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrResult futureResult; + XrSpatialPersistenceContextResultEXT createResult; + XrSpatialPersistenceContextEXT persistenceContext; } XrCreateSpatialPersistenceContextCompletionEXT; // XrSpatialContextPersistenceConfigEXT extends XrSpatialContextCreateInfoEXT typedef struct XrSpatialContextPersistenceConfigEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - uint32_t persistenceContextCount; - const XrSpatialPersistenceContextEXT* persistenceContexts; + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t persistenceContextCount; + const XrSpatialPersistenceContextEXT* persistenceContexts; } XrSpatialContextPersistenceConfigEXT; -// XrSpatialDiscoveryPersistenceUuidFilterEXT extends -// XrSpatialDiscoverySnapshotCreateInfoEXT,XrSpatialComponentDataQueryConditionEXT +// XrSpatialDiscoveryPersistenceUuidFilterEXT extends XrSpatialDiscoverySnapshotCreateInfoEXT,XrSpatialComponentDataQueryConditionEXT typedef struct XrSpatialDiscoveryPersistenceUuidFilterEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - uint32_t persistedUuidCount; - const XrUuid* persistedUuids; + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t persistedUuidCount; + const XrUuid* persistedUuids; } XrSpatialDiscoveryPersistenceUuidFilterEXT; typedef struct XrSpatialPersistenceDataEXT { - XrUuid persistUuid; - XrSpatialPersistenceStateEXT persistState; + XrUuid persistUuid; + XrSpatialPersistenceStateEXT persistState; } XrSpatialPersistenceDataEXT; // XrSpatialComponentPersistenceListEXT extends XrSpatialComponentDataQueryResultEXT typedef struct XrSpatialComponentPersistenceListEXT { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t persistDataCount; - XrSpatialPersistenceDataEXT* persistData; + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t persistDataCount; + XrSpatialPersistenceDataEXT* persistData; } XrSpatialComponentPersistenceListEXT; -typedef XrResult(XRAPI_PTR* PFN_xrEnumerateSpatialPersistenceScopesEXT)( - XrInstance instance, XrSystemId systemId, uint32_t persistenceScopeCapacityInput, - uint32_t* persistenceScopeCountOutput, XrSpatialPersistenceScopeEXT* persistenceScopes); -typedef XrResult(XRAPI_PTR* PFN_xrCreateSpatialPersistenceContextAsyncEXT)( - XrSession session, const XrSpatialPersistenceContextCreateInfoEXT* createInfo, XrFutureEXT* future); -typedef XrResult(XRAPI_PTR* PFN_xrCreateSpatialPersistenceContextCompleteEXT)( - XrSession session, XrFutureEXT future, XrCreateSpatialPersistenceContextCompletionEXT* completion); -typedef XrResult(XRAPI_PTR* PFN_xrDestroySpatialPersistenceContextEXT)( - XrSpatialPersistenceContextEXT persistenceContext); +typedef XrResult (XRAPI_PTR *PFN_xrEnumerateSpatialPersistenceScopesEXT)(XrInstance instance, XrSystemId systemId, uint32_t persistenceScopeCapacityInput, uint32_t* persistenceScopeCountOutput, XrSpatialPersistenceScopeEXT* persistenceScopes); +typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialPersistenceContextAsyncEXT)(XrSession session, const XrSpatialPersistenceContextCreateInfoEXT* createInfo, XrFutureEXT* future); +typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialPersistenceContextCompleteEXT)(XrSession session, XrFutureEXT future, XrCreateSpatialPersistenceContextCompletionEXT* completion); +typedef XrResult (XRAPI_PTR *PFN_xrDestroySpatialPersistenceContextEXT)(XrSpatialPersistenceContextEXT persistenceContext); #ifndef XR_NO_PROTOTYPES #ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSpatialPersistenceScopesEXT(XrInstance instance, XrSystemId systemId, - uint32_t persistenceScopeCapacityInput, - uint32_t* persistenceScopeCountOutput, - XrSpatialPersistenceScopeEXT* persistenceScopes); +XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSpatialPersistenceScopesEXT( + XrInstance instance, + XrSystemId systemId, + uint32_t persistenceScopeCapacityInput, + uint32_t* persistenceScopeCountOutput, + XrSpatialPersistenceScopeEXT* persistenceScopes); XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialPersistenceContextAsyncEXT( - XrSession session, const XrSpatialPersistenceContextCreateInfoEXT* createInfo, XrFutureEXT* future); + XrSession session, + const XrSpatialPersistenceContextCreateInfoEXT* createInfo, + XrFutureEXT* future); XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialPersistenceContextCompleteEXT( - XrSession session, XrFutureEXT future, XrCreateSpatialPersistenceContextCompletionEXT* completion); + XrSession session, + XrFutureEXT future, + XrCreateSpatialPersistenceContextCompletionEXT* completion); -XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpatialPersistenceContextEXT(XrSpatialPersistenceContextEXT persistenceContext); +XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpatialPersistenceContextEXT( + XrSpatialPersistenceContextEXT persistenceContext); #endif /* XR_EXTENSION_PROTOTYPES */ #endif /* !XR_NO_PROTOTYPES */ + // XR_EXT_spatial_persistence_operations is a preprocessor guard. Do not pass it to API calls. #define XR_EXT_spatial_persistence_operations 1 #define XR_EXT_spatial_persistence_operations_SPEC_VERSION 1 #define XR_EXT_SPATIAL_PERSISTENCE_OPERATIONS_EXTENSION_NAME "XR_EXT_spatial_persistence_operations" typedef struct XrSpatialEntityPersistInfoEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpatialContextEXT spatialContext; - XrSpatialEntityIdEXT spatialEntityId; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpatialContextEXT spatialContext; + XrSpatialEntityIdEXT spatialEntityId; } XrSpatialEntityPersistInfoEXT; typedef struct XrPersistSpatialEntityCompletionEXT { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrResult futureResult; - XrSpatialPersistenceContextResultEXT persistResult; - XrUuid persistUuid; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrResult futureResult; + XrSpatialPersistenceContextResultEXT persistResult; + XrUuid persistUuid; } XrPersistSpatialEntityCompletionEXT; typedef struct XrSpatialEntityUnpersistInfoEXT { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrUuid persistUuid; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrUuid persistUuid; } XrSpatialEntityUnpersistInfoEXT; typedef struct XrUnpersistSpatialEntityCompletionEXT { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrResult futureResult; - XrSpatialPersistenceContextResultEXT unpersistResult; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrResult futureResult; + XrSpatialPersistenceContextResultEXT unpersistResult; } XrUnpersistSpatialEntityCompletionEXT; -typedef XrResult(XRAPI_PTR* PFN_xrPersistSpatialEntityAsyncEXT)(XrSpatialPersistenceContextEXT persistenceContext, - const XrSpatialEntityPersistInfoEXT* persistInfo, - XrFutureEXT* future); -typedef XrResult(XRAPI_PTR* PFN_xrPersistSpatialEntityCompleteEXT)(XrSpatialPersistenceContextEXT persistenceContext, - XrFutureEXT future, - XrPersistSpatialEntityCompletionEXT* completion); -typedef XrResult(XRAPI_PTR* PFN_xrUnpersistSpatialEntityAsyncEXT)(XrSpatialPersistenceContextEXT persistenceContext, - const XrSpatialEntityUnpersistInfoEXT* unpersistInfo, - XrFutureEXT* future); -typedef XrResult(XRAPI_PTR* PFN_xrUnpersistSpatialEntityCompleteEXT)(XrSpatialPersistenceContextEXT persistenceContext, - XrFutureEXT future, - XrUnpersistSpatialEntityCompletionEXT* completion); +typedef XrResult (XRAPI_PTR *PFN_xrPersistSpatialEntityAsyncEXT)(XrSpatialPersistenceContextEXT persistenceContext, const XrSpatialEntityPersistInfoEXT* persistInfo, XrFutureEXT* future); +typedef XrResult (XRAPI_PTR *PFN_xrPersistSpatialEntityCompleteEXT)(XrSpatialPersistenceContextEXT persistenceContext, XrFutureEXT future, XrPersistSpatialEntityCompletionEXT* completion); +typedef XrResult (XRAPI_PTR *PFN_xrUnpersistSpatialEntityAsyncEXT)(XrSpatialPersistenceContextEXT persistenceContext, const XrSpatialEntityUnpersistInfoEXT* unpersistInfo, XrFutureEXT* future); +typedef XrResult (XRAPI_PTR *PFN_xrUnpersistSpatialEntityCompleteEXT)(XrSpatialPersistenceContextEXT persistenceContext, XrFutureEXT future, XrUnpersistSpatialEntityCompletionEXT* completion); #ifndef XR_NO_PROTOTYPES #ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrPersistSpatialEntityAsyncEXT(XrSpatialPersistenceContextEXT persistenceContext, - const XrSpatialEntityPersistInfoEXT* persistInfo, - XrFutureEXT* future); +XRAPI_ATTR XrResult XRAPI_CALL xrPersistSpatialEntityAsyncEXT( + XrSpatialPersistenceContextEXT persistenceContext, + const XrSpatialEntityPersistInfoEXT* persistInfo, + XrFutureEXT* future); -XRAPI_ATTR XrResult XRAPI_CALL xrPersistSpatialEntityCompleteEXT(XrSpatialPersistenceContextEXT persistenceContext, - XrFutureEXT future, - XrPersistSpatialEntityCompletionEXT* completion); +XRAPI_ATTR XrResult XRAPI_CALL xrPersistSpatialEntityCompleteEXT( + XrSpatialPersistenceContextEXT persistenceContext, + XrFutureEXT future, + XrPersistSpatialEntityCompletionEXT* completion); -XRAPI_ATTR XrResult XRAPI_CALL xrUnpersistSpatialEntityAsyncEXT(XrSpatialPersistenceContextEXT persistenceContext, - const XrSpatialEntityUnpersistInfoEXT* unpersistInfo, - XrFutureEXT* future); +XRAPI_ATTR XrResult XRAPI_CALL xrUnpersistSpatialEntityAsyncEXT( + XrSpatialPersistenceContextEXT persistenceContext, + const XrSpatialEntityUnpersistInfoEXT* unpersistInfo, + XrFutureEXT* future); -XRAPI_ATTR XrResult XRAPI_CALL xrUnpersistSpatialEntityCompleteEXT(XrSpatialPersistenceContextEXT persistenceContext, - XrFutureEXT future, - XrUnpersistSpatialEntityCompletionEXT* completion); +XRAPI_ATTR XrResult XRAPI_CALL xrUnpersistSpatialEntityCompleteEXT( + XrSpatialPersistenceContextEXT persistenceContext, + XrFutureEXT future, + XrUnpersistSpatialEntityCompletionEXT* completion); #endif /* XR_EXTENSION_PROTOTYPES */ #endif /* !XR_NO_PROTOTYPES */ + +// XR_EXT_loader_init_properties is a preprocessor guard. Do not pass it to API calls. +#define XR_EXT_loader_init_properties 1 +#define XR_EXT_loader_init_properties_SPEC_VERSION 1 +#define XR_EXT_LOADER_INIT_PROPERTIES_EXTENSION_NAME "XR_EXT_loader_init_properties" +typedef struct XrLoaderInitPropertyValueEXT { + const char* name; + const char* value; +} XrLoaderInitPropertyValueEXT; + +// XrLoaderInitInfoPropertiesEXT extends XrLoaderInitInfoBaseHeaderKHR +typedef struct XrLoaderInitInfoPropertiesEXT { + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t propertyValueCount; + const XrLoaderInitPropertyValueEXT* propertyValues; +} XrLoaderInitInfoPropertiesEXT; + + + // XR_PICO_external_camera is a preprocessor guard. Do not pass it to API calls. #define XR_PICO_external_camera 1 #define XR_PICO_external_camera_SPEC_VERSION 1 #define XR_PICO_EXTERNAL_CAMERA_EXTENSION_NAME "XR_PICO_external_camera" typedef struct XrEventDataMrcStatusChangedPICO { - XrStructureType type; - void* XR_MAY_ALIAS next; - int32_t mrcStatus; + XrStructureType type; + const void* XR_MAY_ALIAS next; + int32_t mrcStatus; } XrEventDataMrcStatusChangedPICO; typedef struct XrMrcSpaceCreateInfoPICO { @@ -10801,42 +11929,63 @@ typedef XrFlags64 XrEyeTrackerFlagsPICO; static const XrEyeTrackerFlagsPICO XR_EYE_TRACKER_LEFT_BIT_PICO = 0x00000001; static const XrEyeTrackerFlagsPICO XR_EYE_TRACKER_RIGHT_BIT_PICO = 0x00000002; -typedef XrFlags64 XrEyeTrackerTrackingStateFlagsPICO; - -// Flag bits for XrEyeTrackerTrackingStateFlagsPICO -static const XrEyeTrackerTrackingStateFlagsPICO XR_EYE_TRACKER_TRACKING_STATE_LEFT_EYE_BIT_PICO = 0x00000001; -static const XrEyeTrackerTrackingStateFlagsPICO XR_EYE_TRACKER_TRACKING_STATE_RIGHT_EYE_BIT_PICO = 0x00000002; - typedef struct XrEyeTrackerCreateInfoPICO { XrStructureType type; const void* XR_MAY_ALIAS next; } XrEyeTrackerCreateInfoPICO; +typedef struct XrEyeTrackerGazeInfoPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrEyeTrackerFlagsPICO flags; + XrTime time; + XrSpace baseSpace; +} XrEyeTrackerGazeInfoPICO; + typedef struct XrEyeTrackerDataInfoPICO { XrStructureType type; const void* XR_MAY_ALIAS next; - XrEyeTrackerFlagsPICO eyeTrackingFlags; } XrEyeTrackerDataInfoPICO; typedef struct XrEyeDataPICO { XrStructureType type; void* XR_MAY_ALIAS next; float openness; - float pupilDilation; - XrVector2f middleCanthusUv; } XrEyeDataPICO; typedef struct XrEyeTrackerDataPICO { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrEyeTrackerTrackingStateFlagsPICO trackingState; - XrEyeDataPICO leftEyeData; - XrEyeDataPICO rightEyeData; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrEyeDataPICO leftEyeData; + XrEyeDataPICO rightEyeData; } XrEyeTrackerDataPICO; +typedef struct XrEyeGazePICO { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 isValid; + XrPosef pose; +} XrEyeGazePICO; + +typedef struct XrEyeTrackerGazePICO { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrEyeGazePICO leftEyeGaze; + XrEyeGazePICO rightEyeGaze; +} XrEyeTrackerGazePICO; + +typedef struct XrEyeTrackerGazeDepthPICO { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 isValid; + float depthConfidence; + float gazeDepth; +} XrEyeTrackerGazeDepthPICO; + typedef XrResult (XRAPI_PTR *PFN_xrCreateEyeTrackerPICO)(XrSession session, const XrEyeTrackerCreateInfoPICO* createInfo, XrEyeTrackerPICO* tracker); typedef XrResult (XRAPI_PTR *PFN_xrDestroyEyeTrackerPICO)(XrEyeTrackerPICO eyeTracker); typedef XrResult (XRAPI_PTR *PFN_xrGetEyeDataPICO)(XrEyeTrackerPICO tracker, const XrEyeTrackerDataInfoPICO* info, XrEyeTrackerDataPICO* eyeTrackerData); +typedef XrResult (XRAPI_PTR *PFN_xrGetEyeGazePICO)(XrEyeTrackerPICO tracker, const XrEyeTrackerGazeInfoPICO* info, XrEyeTrackerGazePICO* gaze); #ifndef XR_NO_PROTOTYPES #ifdef XR_EXTENSION_PROTOTYPES @@ -10852,6 +12001,11 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetEyeDataPICO( XrEyeTrackerPICO tracker, const XrEyeTrackerDataInfoPICO* info, XrEyeTrackerDataPICO* eyeTrackerData); + +XRAPI_ATTR XrResult XRAPI_CALL xrGetEyeGazePICO( + XrEyeTrackerPICO tracker, + const XrEyeTrackerGazeInfoPICO* info, + XrEyeTrackerGazePICO* gaze); #endif /* XR_EXTENSION_PROTOTYPES */ #endif /* !XR_NO_PROTOTYPES */ @@ -10869,7 +12023,7 @@ XR_DEFINE_HANDLE(XrSecureMrFrameworkPICO) XR_DEFINE_HANDLE(XrSecureMrPipelinePICO) #define XR_MAX_OPERATOR_NODE_NAME_PICO 512 #define XR_MAX_ARITHMETIC_COMPOSE_OPERATOR_CONFIG_LENGTH_PICO 2048 -#define XR_PICO_secure_mixed_reality_SPEC_VERSION 1 +#define XR_PICO_secure_mixed_reality_SPEC_VERSION 2 #define XR_PICO_SECURE_MIXED_REALITY_EXTENSION_NAME "XR_PICO_secure_mixed_reality" typedef enum XrSecureMrOperatorTypePICO { @@ -10904,10 +12058,11 @@ typedef enum XrSecureMrOperatorTypePICO { XR_SECURE_MR_OPERATOR_TYPE_UPDATE_GLTF_PICO = 31, XR_SECURE_MR_OPERATOR_TYPE_RENDER_TEXT_PICO = 32, XR_SECURE_MR_OPERATOR_TYPE_LOAD_TEXTURE_PICO = 33, - XR_SECURE_MR_OPERATOR_TYPE_SVD_PICO = 34, - XR_SECURE_MR_OPERATOR_TYPE_NORM_PICO = 35, - XR_SECURE_MR_OPERATOR_TYPE_SWAP_HWC_CHW_PICO = 36, - XR_SECURE_MR_OPERATOR_TYPE_MAX_ENUM_PICO = 0x7FFFFFFF + XR_SECURE_MR_OPERATOR_TYPE_SVD_PICO = 34, + XR_SECURE_MR_OPERATOR_TYPE_NORM_PICO = 35, + XR_SECURE_MR_OPERATOR_TYPE_SWAP_HWC_CHW_PICO = 36, + XR_SECURE_MR_OPERATOR_TYPE_JAVASCRIPT_PICO = 39, + XR_SECURE_MR_OPERATOR_TYPE_MAX_ENUM_PICO = 0x7FFFFFFF } XrSecureMrOperatorTypePICO; typedef enum XrSecureMrModelEncodingPICO { @@ -10968,6 +12123,8 @@ typedef enum XrSecureMrTensorDataTypePICO { XR_SECURE_MR_TENSOR_DATA_TYPE_INT32_PICO = 5, XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO = 6, XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT64_PICO = 7, + XR_SECURE_MR_TENSOR_DATA_TYPE_DYNAMIC_TEXTURE_UINT8_PICO = 8, + XR_SECURE_MR_TENSOR_DATA_TYPE_DYNAMIC_TEXTURE_FLOAT32_PICO = 9, XR_SECURE_MR_TENSOR_DATA_TYPE_MAX_ENUM_PICO = 0x7FFFFFFF } XrSecureMrTensorDataTypePICO; @@ -10979,6 +12136,7 @@ typedef enum XrSecureMrTensorTypePICO { XR_SECURE_MR_TENSOR_TYPE_TIMESTAMP_PICO = 5, XR_SECURE_MR_TENSOR_TYPE_MAT_PICO = 6, XR_SECURE_MR_TENSOR_TYPE_GLTF_PICO = 7, + XR_SECURE_MR_TENSOR_TYPE_MAT_DYNAMIC_TEXTURE_PICO = 8, XR_SECURE_MR_TENSOR_TYPE_MAX_ENUM_PICO = 0x7FFFFFFF } XrSecureMrTensorTypePICO; @@ -11151,6 +12309,13 @@ typedef struct XrSecureMrOperatorColorConvertPICO { int32_t convert; } XrSecureMrOperatorColorConvertPICO; +typedef struct XrSecureMrOperatorJavascriptPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + char* configText; + int32_t configLength; +} XrSecureMrOperatorJavascriptPICO; + typedef XrResult (XRAPI_PTR *PFN_xrCreateSecureMrFrameworkPICO)(XrSession session, const XrSecureMrFrameworkCreateInfoPICO* createInfo, XrSecureMrFrameworkPICO* framework); typedef XrResult (XRAPI_PTR *PFN_xrDestroySecureMrFrameworkPICO)(XrSecureMrFrameworkPICO framework); typedef XrResult (XRAPI_PTR *PFN_xrCreateSecureMrPipelinePICO)(XrSecureMrFrameworkPICO framework, const XrSecureMrPipelineCreateInfoPICO* createInfo, XrSecureMrPipelinePICO* pipeline); @@ -11294,21 +12459,21 @@ typedef struct XrExpandDeviceBatteryStatePICO { typedef struct XrEventDataExpandDeviceConnectionStateChangedPICO { XrStructureType type; - void* XR_MAY_ALIAS next; + const void* XR_MAY_ALIAS next; XrExpandDeviceIdPICO deviceId; XrExpandDeviceConnectionStatePICO connectionState; } XrEventDataExpandDeviceConnectionStateChangedPICO; typedef struct XrEventDataExpandDeviceBatteryStateChangedPICO { XrStructureType type; - void* XR_MAY_ALIAS next; + const void* XR_MAY_ALIAS next; XrExpandDeviceIdPICO deviceId; XrExpandDeviceBatteryStatePICO batteryState; } XrEventDataExpandDeviceBatteryStateChangedPICO; typedef struct XrEventDataExpandDeviceCustomDataStateChangedPICO { XrStructureType type; - void* XR_MAY_ALIAS next; + const void* XR_MAY_ALIAS next; XrExpandDeviceCustomDataStatePICO dataState; } XrEventDataExpandDeviceCustomDataStateChangedPICO; @@ -11496,6 +12661,8 @@ typedef struct XrSymmetricFovReprojectionViewConfigurationPICO { uint32_t recommendedNotRenderingImageRectWidth; } XrSymmetricFovReprojectionViewConfigurationPICO; + + // XR_PICO_dynamic_object_tracking is a preprocessor guard. Do not pass it to API calls. #define XR_PICO_dynamic_object_tracking 1 XR_DEFINE_ATOM(XrSpatialEntityIdPICO) @@ -11503,87 +12670,94 @@ XR_DEFINE_ATOM(XrSpatialEntityIdPICO) #define XR_PICO_DYNAMIC_OBJECT_TRACKING_EXTENSION_NAME "XR_PICO_dynamic_object_tracking" typedef enum XrDynamicObjectTypePICO { - XR_DYNAMIC_OBJECT_TYPE_UNKNOWN_PICO = 0, - XR_DYNAMIC_OBJECT_TYPE_KEYBOARD_PICO = 1010018000, - XR_DYNAMIC_OBJECT_TYPE_MOUSE_PICO = 1010019000, - XR_DYNAMIC_OBJECT_TYPE_MAX_ENUM_PICO = 0x7FFFFFFF + XR_DYNAMIC_OBJECT_TYPE_UNKNOWN_PICO = 0, + XR_DYNAMIC_OBJECT_TYPE_KEYBOARD_PICO = 1010018000, + XR_DYNAMIC_OBJECT_TYPE_MOUSE_PICO = 1010019000, + XR_DYNAMIC_OBJECT_TYPE_MAX_ENUM_PICO = 0x7FFFFFFF } XrDynamicObjectTypePICO; typedef enum XrSpatialEntityComponentTypePICO { - XR_SPATIAL_ENTITY_COMPONENT_TYPE_LOCATION_PICO = 0, - XR_SPATIAL_ENTITY_COMPONENT_TYPE_SEMANTIC_PICO = 1, - XR_SPATIAL_ENTITY_COMPONENT_TYPE_BOUNDING_BOX_2D_PICO = 2, - XR_SPATIAL_ENTITY_COMPONENT_TYPE_POLYGON_PICO = 3, - XR_SPATIAL_ENTITY_COMPONENT_TYPE_BOUNDING_BOX_3D_PICO = 4, - XR_SPATIAL_ENTITY_COMPONENT_TYPE_TRIANGLE_MESH_PICO = 5, - XR_SPATIAL_ENTITY_COMPONENT_TYPE_DYNAMIC_OBJECT_PICO = 1010017000, - XR_SPATIAL_ENTITY_COMPONENT_TYPE_MAX_ENUM_PICO = 0x7FFFFFFF + XR_SPATIAL_ENTITY_COMPONENT_TYPE_LOCATION_PICO = 0, + XR_SPATIAL_ENTITY_COMPONENT_TYPE_SEMANTIC_PICO = 1, + XR_SPATIAL_ENTITY_COMPONENT_TYPE_BOUNDING_BOX_2D_PICO = 2, + XR_SPATIAL_ENTITY_COMPONENT_TYPE_POLYGON_PICO = 3, + XR_SPATIAL_ENTITY_COMPONENT_TYPE_BOUNDING_BOX_3D_PICO = 4, + XR_SPATIAL_ENTITY_COMPONENT_TYPE_TRIANGLE_MESH_PICO = 5, + XR_SPATIAL_ENTITY_COMPONENT_TYPE_SPHERE_PICO = 6, + XR_SPATIAL_ENTITY_COMPONENT_TYPE_DYNAMIC_OBJECT_PICO = 1010017000, + XR_SPATIAL_ENTITY_COMPONENT_TYPE_MAX_ENUM_PICO = 0x7FFFFFFF } XrSpatialEntityComponentTypePICO; // XrSystemDynamicObjectTrackingPropertiesPICO extends XrSystemProperties typedef struct XrSystemDynamicObjectTrackingPropertiesPICO { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrBool32 supportsDynamicObjectTracking; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 supportsDynamicObjectTracking; } XrSystemDynamicObjectTrackingPropertiesPICO; // XrSenseDataProviderCreateInfoDynamicObjectPICO extends XrSenseDataProviderCreateInfoBD typedef struct XrSenseDataProviderCreateInfoDynamicObjectPICO { - XrStructureType type; - const void* XR_MAY_ALIAS next; - uint32_t trackingTypeCount; - const XrDynamicObjectTypePICO* trackingTypes; + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t trackingTypeCount; + const XrDynamicObjectTypePICO* trackingTypes; } XrSenseDataProviderCreateInfoDynamicObjectPICO; // XrSpatialEntityDynamicObjectGetInfoPICO extends XrSpatialEntityComponentGetInfoBaseHeaderPICO typedef struct XrSpatialEntityDynamicObjectGetInfoPICO { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpatialEntityIdPICO entity; - XrSpatialEntityComponentTypePICO componentType; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpatialEntityIdPICO entity; + XrSpatialEntityComponentTypePICO componentType; } XrSpatialEntityDynamicObjectGetInfoPICO; typedef struct XrDynamicObjectDataPICO { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrDynamicObjectTypePICO objectType; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrDynamicObjectTypePICO objectType; } XrDynamicObjectDataPICO; // XrSpatialEntityComponentDataDynamicObjectPICO extends XrSpatialEntityComponentDataBaseHeaderPICO typedef struct XrSpatialEntityComponentDataDynamicObjectPICO { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrDynamicObjectDataPICO data; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrDynamicObjectDataPICO data; } XrSpatialEntityComponentDataDynamicObjectPICO; typedef struct XrSenseDataFilterDynamicObjectTypePICO { - XrStructureType type; - const void* XR_MAY_ALIAS next; - uint32_t typeCount; - const XrDynamicObjectTypePICO* types; + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t typeCount; + const XrDynamicObjectTypePICO* types; } XrSenseDataFilterDynamicObjectTypePICO; + + // XR_PICO_dynamic_object_keyboard is a preprocessor guard. Do not pass it to API calls. #define XR_PICO_dynamic_object_keyboard 1 #define XR_PICO_dynamic_object_keyboard_SPEC_VERSION 1 #define XR_PICO_DYNAMIC_OBJECT_KEYBOARD_EXTENSION_NAME "XR_PICO_dynamic_object_keyboard" // XrSystemDynamicObjectKeyboardPropertiesPICO extends XrSystemProperties typedef struct XrSystemDynamicObjectKeyboardPropertiesPICO { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrBool32 supportsDynamicObjectKeyboard; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 supportsDynamicObjectKeyboard; } XrSystemDynamicObjectKeyboardPropertiesPICO; + + // XR_PICO_dynamic_object_mouse is a preprocessor guard. Do not pass it to API calls. #define XR_PICO_dynamic_object_mouse 1 #define XR_PICO_dynamic_object_mouse_SPEC_VERSION 1 #define XR_PICO_DYNAMIC_OBJECT_MOUSE_EXTENSION_NAME "XR_PICO_dynamic_object_mouse" // XrSystemDynamicObjectMousePropertiesPICO extends XrSystemProperties typedef struct XrSystemDynamicObjectMousePropertiesPICO { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrBool32 supportsDynamicObjectMouse; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 supportsDynamicObjectMouse; } XrSystemDynamicObjectMousePropertiesPICO; + + // XR_PICO_layer_color_matrix is a preprocessor guard. Do not pass it to API calls. #define XR_PICO_layer_color_matrix 1 #define XR_PICO_layer_color_matrix_SPEC_VERSION 1 @@ -11598,602 +12772,1079 @@ typedef struct XrLayerColorMatrixPICO { XrColorMatrix3x3fPICO matrix; } XrLayerColorMatrixPICO; -// XR_PICO_spatial_sensing is a preprocessor guard. Do not pass it to API calls. -#define XR_PICO_spatial_sensing 1 -XR_DEFINE_HANDLE(XrSenseDataProviderPICO) -XR_DEFINE_HANDLE(XrSenseDataSnapshotPICO) -XR_DEFINE_HANDLE(XrAnchorPICO) -#define XR_PICO_spatial_sensing_SPEC_VERSION 2 -#define XR_PICO_SPATIAL_SENSING_EXTENSION_NAME "XR_PICO_spatial_sensing" -typedef enum XrSemanticLabelPICO { - XR_SEMANTIC_LABEL_UNKNOWN_PICO = 0, - XR_SEMANTIC_LABEL_FLOOR_PICO = 1, - XR_SEMANTIC_LABEL_CEILING_PICO = 2, - XR_SEMANTIC_LABEL_WALL_PICO = 3, - XR_SEMANTIC_LABEL_DOOR_PICO = 4, - XR_SEMANTIC_LABEL_WINDOW_PICO = 5, - XR_SEMANTIC_LABEL_OPENING_PICO = 6, - XR_SEMANTIC_LABEL_TABLE_PICO = 7, - XR_SEMANTIC_LABEL_SOFA_PICO = 8, - XR_SEMANTIC_LABEL_CHAIR_PICO = 9, - XR_SEMANTIC_LABEL_HUMAN_PICO = 10, - XR_SEMANTIC_LABEL_BEAM_PICO = 11, - XR_SEMANTIC_LABEL_COLUMN_PICO = 12, - XR_SEMANTIC_LABEL_CURTAIN_PICO = 13, - XR_SEMANTIC_LABEL_CABINET_PICO = 14, - XR_SEMANTIC_LABEL_BED_PICO = 15, - XR_SEMANTIC_LABEL_PLANT_PICO = 16, - XR_SEMANTIC_LABEL_SCREEN_PICO = 17, - XR_SEMANTIC_LABEL_VIRTUAL_WALL_PICO = 18, - XR_SEMANTIC_LABEL_REFRIGERATOR_PICO = 19, - XR_SEMANTIC_LABEL_WASHING_MACHINE_PICO = 20, - XR_SEMANTIC_LABEL_AIR_CONDITIONER_PICO = 21, - XR_SEMANTIC_LABEL_LAMP_PICO = 22, - XR_SEMANTIC_LABEL_WALL_ART_PICO = 23, - XR_SEMANTIC_LABEL_STAIRWAY_PICO = 24, - XR_SEMANTIC_LABEL_MAX_ENUM_PICO = 0x7FFFFFFF -} XrSemanticLabelPICO; -typedef enum XrSenseDataProviderStatePICO { - XR_SENSE_DATA_PROVIDER_STATE_INITIALIZED_PICO = 0, - XR_SENSE_DATA_PROVIDER_STATE_RUNNING_PICO = 1, - XR_SENSE_DATA_PROVIDER_STATE_STOPPED_PICO = 2, - XR_SENSE_DATA_PROVIDER_STATE_MAX_ENUM_PICO = 0x7FFFFFFF -} XrSenseDataProviderStatePICO; -// XrSystemSpatialSensingPropertiesPICO extends XrSystemProperties -typedef struct XrSystemSpatialSensingPropertiesPICO { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrBool32 supportsSpatialSensing; -} XrSystemSpatialSensingPropertiesPICO; +// XR_PICO_readback_tensor is a preprocessor guard. Do not pass it to API calls. +#define XR_PICO_readback_tensor 1 +XR_DEFINE_HANDLE(XrReadbackTexturePICO) +#define XR_PICO_readback_tensor_SPEC_VERSION 2 +#define XR_PICO_READBACK_TENSOR_EXTENSION_NAME "XR_PICO_readback_tensor" +typedef struct XrReadbackTensorBufferPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t bufferCapacityInput; + uint32_t bufferCountOutput; + void* XR_MAY_ALIAS buffer; +} XrReadbackTensorBufferPICO; -typedef struct XrSpatialEntityComponentGetInfoBaseHeaderPICO { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpatialEntityIdPICO entity; - XrSpatialEntityComponentTypePICO componentType; -} XrSpatialEntityComponentGetInfoBaseHeaderPICO; +typedef struct XrCreateBufferFromGlobalTensorCompletionPICO { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrResult futureResult; + XrReadbackTensorBufferPICO* tensorBuffer; +} XrCreateBufferFromGlobalTensorCompletionPICO; -typedef struct XrSpatialEntityComponentDataBaseHeaderPICO { - XrStructureType type; - void* XR_MAY_ALIAS next; -} XrSpatialEntityComponentDataBaseHeaderPICO; +typedef struct XrCreateTextureFromGlobalTensorCompletionPICO { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrResult futureResult; + XrReadbackTexturePICO texture; +} XrCreateTextureFromGlobalTensorCompletionPICO; -// XrSpatialEntityLocationGetInfoPICO extends XrSpatialEntityComponentGetInfoBaseHeaderPICO -typedef struct XrSpatialEntityLocationGetInfoPICO { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpatialEntityIdPICO entity; - XrSpatialEntityComponentTypePICO componentType; - XrSpace baseSpace; - XrTime time; -} XrSpatialEntityLocationGetInfoPICO; +typedef struct XR_MAY_ALIAS XrReadbackTextureImageBaseHeaderPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; +} XrReadbackTextureImageBaseHeaderPICO; -// XrSpatialEntityLocationDataPICO extends XrSpatialEntityComponentDataBaseHeaderPICO -typedef struct XrSpatialEntityLocationDataPICO { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrSpaceLocationData location; -} XrSpatialEntityLocationDataPICO; +typedef XrResult (XRAPI_PTR *PFN_xrCreateBufferFromGlobalTensorAsyncPICO)(XrSecureMrTensorPICO tensor, XrFutureEXT* future); +typedef XrResult (XRAPI_PTR *PFN_xrCreateBufferFromGlobalTensorCompletePICO)(XrSecureMrTensorPICO tensor, XrFutureEXT future, XrCreateBufferFromGlobalTensorCompletionPICO* completion); +typedef XrResult (XRAPI_PTR *PFN_xrCreateTextureFromGlobalTensorAsyncPICO)(XrSecureMrTensorPICO tensor, XrFutureEXT* future); +typedef XrResult (XRAPI_PTR *PFN_xrCreateTextureFromGlobalTensorCompletePICO)(XrSecureMrTensorPICO tensor, XrFutureEXT future, XrCreateTextureFromGlobalTensorCompletionPICO* completion); +typedef XrResult (XRAPI_PTR *PFN_xrGetReadbackTextureImagePICO)(XrReadbackTexturePICO readbackTexture, XrReadbackTextureImageBaseHeaderPICO* img); +typedef XrResult (XRAPI_PTR *PFN_xrReleaseReadbackTexturePICO)(XrReadbackTexturePICO readbackTexture); -// XrSpatialEntitySemanticGetInfoPICO extends XrSpatialEntityComponentGetInfoBaseHeaderPICO -typedef struct XrSpatialEntitySemanticGetInfoPICO { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpatialEntityIdPICO entity; - XrSpatialEntityComponentTypePICO componentType; -} XrSpatialEntitySemanticGetInfoPICO; +#ifndef XR_NO_PROTOTYPES +#ifdef XR_EXTENSION_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrCreateBufferFromGlobalTensorAsyncPICO( + XrSecureMrTensorPICO tensor, + XrFutureEXT* future); -// XrSpatialEntitySemanticDataPICO extends XrSpatialEntityComponentDataBaseHeaderPICO -typedef struct XrSpatialEntitySemanticDataPICO { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t semanticCapacityInput; - uint32_t semanticCountOutput; - XrSemanticLabelPICO* semanticLabels; -} XrSpatialEntitySemanticDataPICO; +XRAPI_ATTR XrResult XRAPI_CALL xrCreateBufferFromGlobalTensorCompletePICO( + XrSecureMrTensorPICO tensor, + XrFutureEXT future, + XrCreateBufferFromGlobalTensorCompletionPICO* completion); -// XrSpatialEntityBoundingBox2DGetInfoPICO extends XrSpatialEntityComponentGetInfoBaseHeaderPICO -typedef struct XrSpatialEntityBoundingBox2DGetInfoPICO { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpatialEntityIdPICO entity; - XrSpatialEntityComponentTypePICO componentType; -} XrSpatialEntityBoundingBox2DGetInfoPICO; +XRAPI_ATTR XrResult XRAPI_CALL xrCreateTextureFromGlobalTensorAsyncPICO( + XrSecureMrTensorPICO tensor, + XrFutureEXT* future); -// XrSpatialEntityBoundingBox2DDataPICO extends XrSpatialEntityComponentDataBaseHeaderPICO -typedef struct XrSpatialEntityBoundingBox2DDataPICO { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrRect2Df boundingBox2D; -} XrSpatialEntityBoundingBox2DDataPICO; +XRAPI_ATTR XrResult XRAPI_CALL xrCreateTextureFromGlobalTensorCompletePICO( + XrSecureMrTensorPICO tensor, + XrFutureEXT future, + XrCreateTextureFromGlobalTensorCompletionPICO* completion); -// XrSpatialEntityPolygonGetInfoPICO extends XrSpatialEntityComponentGetInfoBaseHeaderPICO -typedef struct XrSpatialEntityPolygonGetInfoPICO { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpatialEntityIdPICO entity; - XrSpatialEntityComponentTypePICO componentType; +XRAPI_ATTR XrResult XRAPI_CALL xrGetReadbackTextureImagePICO( + XrReadbackTexturePICO readbackTexture, + XrReadbackTextureImageBaseHeaderPICO* img); + +XRAPI_ATTR XrResult XRAPI_CALL xrReleaseReadbackTexturePICO( + XrReadbackTexturePICO readbackTexture); +#endif /* XR_EXTENSION_PROTOTYPES */ +#endif /* !XR_NO_PROTOTYPES */ + + +// XR_PICO_camera_image is a preprocessor guard. Do not pass it to API calls. +#define XR_PICO_camera_image 1 +XR_DEFINE_ATOM(XrCameraIdPICO) +XR_DEFINE_ATOM(XrCameraImageIdPICO) +XR_DEFINE_HANDLE(XrCameraDevicePICO) +XR_DEFINE_HANDLE(XrCameraCaptureSessionPICO) +#define XR_PICO_camera_image_SPEC_VERSION 1 +#define XR_PICO_CAMERA_IMAGE_EXTENSION_NAME "XR_PICO_camera_image" + +typedef enum XrCameraPropertyTypePICO { + XR_CAMERA_PROPERTY_TYPE_FACING_PICO = 1, + XR_CAMERA_PROPERTY_TYPE_POSITION_PICO = 2, + XR_CAMERA_PROPERTY_TYPE_CAMERA_TYPE_PICO = 3, + XR_CAMERA_PROPERTY_TYPE_MAX_ENUM_PICO = 0x7FFFFFFF +} XrCameraPropertyTypePICO; + +typedef enum XrCameraFacingPICO { + XR_CAMERA_FACING_WORLD_PICO = 1, + XR_CAMERA_FACING_MAX_ENUM_PICO = 0x7FFFFFFF +} XrCameraFacingPICO; + +typedef enum XrCameraPositionPICO { + XR_CAMERA_POSITION_UNSPECIFIED_PICO = 1, + XR_CAMERA_POSITION_LEFT_PICO = 2, + XR_CAMERA_POSITION_RIGHT_PICO = 3, + XR_CAMERA_POSITION_MAX_ENUM_PICO = 0x7FFFFFFF +} XrCameraPositionPICO; + +typedef enum XrCameraTypePICO { + XR_CAMERA_TYPE_PASSTHROUGH_COLOR_PICO = 1, + XR_CAMERA_TYPE_MAX_ENUM_PICO = 0x7FFFFFFF +} XrCameraTypePICO; + +typedef enum XrCameraCapabilityTypePICO { + XR_CAMERA_CAPABILITY_TYPE_IMAGE_RESOLUTION_PICO = 1, + XR_CAMERA_CAPABILITY_TYPE_IMAGE_FORMAT_PICO = 2, + XR_CAMERA_CAPABILITY_TYPE_DATA_TRANSFER_TYPE_PICO = 3, + XR_CAMERA_CAPABILITY_TYPE_CAMERA_MODEL_PICO = 4, + XR_CAMERA_CAPABILITY_TYPE_IMAGE_FPS_PICO = 5, + XR_CAMERA_CAPABILITY_TYPE_MAX_ENUM_PICO = 0x7FFFFFFF +} XrCameraCapabilityTypePICO; + +typedef enum XrCameraDataTransferTypePICO { + XR_CAMERA_DATA_TRANSFER_TYPE_RAW_BUFFER_PICO = 1, + XR_CAMERA_DATA_TRANSFER_TYPE_MAX_ENUM_PICO = 0x7FFFFFFF +} XrCameraDataTransferTypePICO; + +typedef enum XrCameraImageFormatPICO { + XR_CAMERA_IMAGE_FORMAT_RGBA_8888_PICO = 1, + XR_CAMERA_IMAGE_FORMAT_MAX_ENUM_PICO = 0x7FFFFFFF +} XrCameraImageFormatPICO; + +typedef enum XrCameraModelPICO { + XR_CAMERA_MODEL_PINHOLE_PICO = 1, + XR_CAMERA_MODEL_MAX_ENUM_PICO = 0x7FFFFFFF +} XrCameraModelPICO; + +typedef enum XrCameraImageFpsPICO { + XR_CAMERA_IMAGE_FPS_30_PICO = 1, + XR_CAMERA_IMAGE_FPS_60_PICO = 2, + XR_CAMERA_IMAGE_FPS_MAX_ENUM_PICO = 0x7FFFFFFF +} XrCameraImageFpsPICO; +typedef struct XR_MAY_ALIAS XrCameraPropertyBaseHeaderPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; +} XrCameraPropertyBaseHeaderPICO; + +typedef struct XrCameraPropertiesPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t propertyCount; + XrCameraPropertyBaseHeaderPICO** properties; +} XrCameraPropertiesPICO; + +typedef struct XR_MAY_ALIAS XrCameraCapabilityBaseHeaderPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; +} XrCameraCapabilityBaseHeaderPICO; + +typedef struct XrCameraCapabilitiesPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t capabilityCount; + XrCameraCapabilityBaseHeaderPICO** capabilities; +} XrCameraCapabilitiesPICO; + +typedef struct XrAvailableCamerasEnumerateInfoPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + const XrCameraPropertiesPICO* properties; + const XrCameraCapabilitiesPICO* capabilities; +} XrAvailableCamerasEnumerateInfoPICO; + +typedef struct XrCameraPropertiesGetInfoPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrCameraIdPICO cameraId; +} XrCameraPropertiesGetInfoPICO; + +typedef struct XrCameraPropertyFacingPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrCameraFacingPICO facing; +} XrCameraPropertyFacingPICO; + +typedef struct XrCameraPropertyPositionPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrCameraPositionPICO position; +} XrCameraPropertyPositionPICO; + +typedef struct XrCameraPropertyCameraTypePICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrCameraTypePICO cameraType; +} XrCameraPropertyCameraTypePICO; + +typedef struct XrCameraSupportedCapabilitiesGetInfoPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrCameraIdPICO id; +} XrCameraSupportedCapabilitiesGetInfoPICO; + +typedef struct XR_MAY_ALIAS XrCameraSupportedCapabilityBaseHeaderPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; +} XrCameraSupportedCapabilityBaseHeaderPICO; + +typedef struct XrCameraSupportedCapabilitiesPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t capabilityCount; + XrCameraSupportedCapabilityBaseHeaderPICO** capabilities; +} XrCameraSupportedCapabilitiesPICO; + +typedef struct XrCameraSupportedCapabilityImageResolutionPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t resolutionCapacityInput; + uint32_t resolutionCountOutput; + XrExtent2Di* resolutions; +} XrCameraSupportedCapabilityImageResolutionPICO; + +typedef struct XrCameraCapabilityImageResolutionPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrExtent2Di resolution; +} XrCameraCapabilityImageResolutionPICO; + +typedef struct XrCameraSupportedCapabilityDataTransferTypePICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t typeCapacityInput; + uint32_t typeCountOutput; + XrCameraDataTransferTypePICO* types; +} XrCameraSupportedCapabilityDataTransferTypePICO; + +typedef struct XrCameraCapabilityDataTransferTypePICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrCameraDataTransferTypePICO transferType; +} XrCameraCapabilityDataTransferTypePICO; + +typedef struct XrCameraSupportedCapabilityImageFormatPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t formatCapacityInput; + uint32_t formatCountOutput; + XrCameraImageFormatPICO* formats; +} XrCameraSupportedCapabilityImageFormatPICO; + +typedef struct XrCameraCapabilityImageFormatPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrCameraImageFormatPICO format; +} XrCameraCapabilityImageFormatPICO; + +typedef struct XrCameraSupportedCapabilityCameraModelPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t modelCapacityInput; + uint32_t modelCountOutput; + XrCameraModelPICO* models; +} XrCameraSupportedCapabilityCameraModelPICO; + +typedef struct XrCameraCapabilityCameraModelPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrCameraModelPICO model; +} XrCameraCapabilityCameraModelPICO; + +typedef struct XrCameraSupportedCapabilityImageFpsPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t fpsCapacityInput; + uint32_t fpsCountOutput; + XrCameraImageFpsPICO* fps; +} XrCameraSupportedCapabilityImageFpsPICO; + +typedef struct XrCameraCapabilityImageFpsPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrCameraImageFpsPICO fps; +} XrCameraCapabilityImageFpsPICO; + +typedef struct XrCameraDeviceCreateInfoPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrCameraIdPICO cameraId; +} XrCameraDeviceCreateInfoPICO; + +typedef struct XrCreateCameraDeviceCompletionPICO { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrResult futureResult; + XrCameraDevicePICO device; +} XrCreateCameraDeviceCompletionPICO; + +typedef struct XrCameraCaptureSessionCreateInfoPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrCameraDevicePICO camera; + uint32_t configCount; + const XrCameraCapabilityBaseHeaderPICO* const* configs; +} XrCameraCaptureSessionCreateInfoPICO; + +typedef struct XrCreateCameraCaptureSessionCompletionPICO { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrResult futureResult; + XrCameraCaptureSessionPICO captureSession; +} XrCreateCameraCaptureSessionCompletionPICO; + +typedef struct XrCameraIntrinsicsPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrVector2f focalLength; + XrVector2f principalPoint; + XrVector2f fov; +} XrCameraIntrinsicsPICO; + +typedef struct XrCameraExtrinsicsPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrPosef pose; +} XrCameraExtrinsicsPICO; + +typedef struct XrCameraCaptureBeginInfoPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; +} XrCameraCaptureBeginInfoPICO; + +typedef struct XrCameraImageAcquireInfoPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrTime lastCaptureTime; +} XrCameraImageAcquireInfoPICO; + +typedef struct XrCameraImagePICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrTime captureTime; + XrCameraImageIdPICO imageId; +} XrCameraImagePICO; + +typedef struct XR_MAY_ALIAS XrCameraImageDataBaseHeaderPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; +} XrCameraImageDataBaseHeaderPICO; + +typedef struct XrCameraImageDataRawBufferPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t width; + uint32_t height; + uint32_t stride; + uint32_t bytesPerPixel; + uint32_t pixelStride; + uint32_t bufferSize; + uint8_t* buffer; +} XrCameraImageDataRawBufferPICO; + +typedef XrResult (XRAPI_PTR *PFN_xrEnumerateAvailableCamerasPICO)(XrInstance instance, const XrAvailableCamerasEnumerateInfoPICO* enumerateInfo, uint32_t cameraIdCapacityInput, uint32_t* cameraIdCountOutput, XrCameraIdPICO* cameraIds); +typedef XrResult (XRAPI_PTR *PFN_xrEnumerateCameraPropertyTypesPICO)(XrInstance instance, XrCameraIdPICO cameraId, uint32_t typeCapacityInput, uint32_t* typeCountOutput, XrCameraPropertyTypePICO* types); +typedef XrResult (XRAPI_PTR *PFN_xrGetCameraPropertiesPICO)(XrInstance instance, const XrCameraPropertiesGetInfoPICO* getInfo, XrCameraPropertiesPICO* properties); +typedef XrResult (XRAPI_PTR *PFN_xrEnumerateCameraCapabilityTypesPICO)(XrInstance instance, XrCameraIdPICO cameraId, uint32_t typeCapacityInput, uint32_t* typeCountOutput, XrCameraCapabilityTypePICO* types); +typedef XrResult (XRAPI_PTR *PFN_xrGetCameraSupportedCapabilitiesPICO)(XrInstance instance, const XrCameraSupportedCapabilitiesGetInfoPICO* getInfo, XrCameraSupportedCapabilitiesPICO* capabilities); +typedef XrResult (XRAPI_PTR *PFN_xrCreateCameraDeviceAsyncPICO)(XrInstance instance, const XrCameraDeviceCreateInfoPICO* createInfo, XrFutureEXT* future); +typedef XrResult (XRAPI_PTR *PFN_xrCreateCameraDeviceCompletePICO)(XrInstance instance, XrFutureEXT future, XrCreateCameraDeviceCompletionPICO* completion); +typedef XrResult (XRAPI_PTR *PFN_xrDestroyCameraDevicePICO)(XrCameraDevicePICO device); +typedef XrResult (XRAPI_PTR *PFN_xrCreateCameraCaptureSessionAsyncPICO)(XrSession session, const XrCameraCaptureSessionCreateInfoPICO* createInfo, XrFutureEXT* future); +typedef XrResult (XRAPI_PTR *PFN_xrCreateCameraCaptureSessionCompletePICO)(XrSession session, XrFutureEXT future, XrCreateCameraCaptureSessionCompletionPICO* completion); +typedef XrResult (XRAPI_PTR *PFN_xrDestroyCameraCaptureSessionPICO)(XrCameraCaptureSessionPICO captureSession); +typedef XrResult (XRAPI_PTR *PFN_xrGetCameraIntrinsicsPICO)(XrCameraCaptureSessionPICO session, XrCameraIntrinsicsPICO* intrinsics); +typedef XrResult (XRAPI_PTR *PFN_xrGetCameraExtrinsicsPICO)(XrCameraCaptureSessionPICO session, XrCameraExtrinsicsPICO* extrinsics); +typedef XrResult (XRAPI_PTR *PFN_xrBeginCameraCapturePICO)(XrCameraCaptureSessionPICO session, XrCameraCaptureBeginInfoPICO* beginInfo); +typedef XrResult (XRAPI_PTR *PFN_xrEndCameraCapturePICO)(XrCameraCaptureSessionPICO session); +typedef XrResult (XRAPI_PTR *PFN_xrAcquireCameraImagePICO)(XrCameraCaptureSessionPICO session, const XrCameraImageAcquireInfoPICO* acquireInfo, XrCameraImagePICO* image); +typedef XrResult (XRAPI_PTR *PFN_xrGetCameraImageDataPICO)(XrCameraCaptureSessionPICO session, XrCameraImageIdPICO imageId, XrCameraImageDataBaseHeaderPICO* imageData); +typedef XrResult (XRAPI_PTR *PFN_xrReleaseCameraImagePICO)(XrCameraCaptureSessionPICO session, XrCameraImageIdPICO imageId); + +#ifndef XR_NO_PROTOTYPES +#ifdef XR_EXTENSION_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateAvailableCamerasPICO( + XrInstance instance, + const XrAvailableCamerasEnumerateInfoPICO* enumerateInfo, + uint32_t cameraIdCapacityInput, + uint32_t* cameraIdCountOutput, + XrCameraIdPICO* cameraIds); + +XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateCameraPropertyTypesPICO( + XrInstance instance, + XrCameraIdPICO cameraId, + uint32_t typeCapacityInput, + uint32_t* typeCountOutput, + XrCameraPropertyTypePICO* types); + +XRAPI_ATTR XrResult XRAPI_CALL xrGetCameraPropertiesPICO( + XrInstance instance, + const XrCameraPropertiesGetInfoPICO* getInfo, + XrCameraPropertiesPICO* properties); + +XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateCameraCapabilityTypesPICO( + XrInstance instance, + XrCameraIdPICO cameraId, + uint32_t typeCapacityInput, + uint32_t* typeCountOutput, + XrCameraCapabilityTypePICO* types); + +XRAPI_ATTR XrResult XRAPI_CALL xrGetCameraSupportedCapabilitiesPICO( + XrInstance instance, + const XrCameraSupportedCapabilitiesGetInfoPICO* getInfo, + XrCameraSupportedCapabilitiesPICO* capabilities); + +XRAPI_ATTR XrResult XRAPI_CALL xrCreateCameraDeviceAsyncPICO( + XrInstance instance, + const XrCameraDeviceCreateInfoPICO* createInfo, + XrFutureEXT* future); + +XRAPI_ATTR XrResult XRAPI_CALL xrCreateCameraDeviceCompletePICO( + XrInstance instance, + XrFutureEXT future, + XrCreateCameraDeviceCompletionPICO* completion); + +XRAPI_ATTR XrResult XRAPI_CALL xrDestroyCameraDevicePICO( + XrCameraDevicePICO device); + +XRAPI_ATTR XrResult XRAPI_CALL xrCreateCameraCaptureSessionAsyncPICO( + XrSession session, + const XrCameraCaptureSessionCreateInfoPICO* createInfo, + XrFutureEXT* future); + +XRAPI_ATTR XrResult XRAPI_CALL xrCreateCameraCaptureSessionCompletePICO( + XrSession session, + XrFutureEXT future, + XrCreateCameraCaptureSessionCompletionPICO* completion); + +XRAPI_ATTR XrResult XRAPI_CALL xrDestroyCameraCaptureSessionPICO( + XrCameraCaptureSessionPICO captureSession); + +XRAPI_ATTR XrResult XRAPI_CALL xrGetCameraIntrinsicsPICO( + XrCameraCaptureSessionPICO session, + XrCameraIntrinsicsPICO* intrinsics); + +XRAPI_ATTR XrResult XRAPI_CALL xrGetCameraExtrinsicsPICO( + XrCameraCaptureSessionPICO session, + XrCameraExtrinsicsPICO* extrinsics); + +XRAPI_ATTR XrResult XRAPI_CALL xrBeginCameraCapturePICO( + XrCameraCaptureSessionPICO session, + XrCameraCaptureBeginInfoPICO* beginInfo); + +XRAPI_ATTR XrResult XRAPI_CALL xrEndCameraCapturePICO( + XrCameraCaptureSessionPICO session); + +XRAPI_ATTR XrResult XRAPI_CALL xrAcquireCameraImagePICO( + XrCameraCaptureSessionPICO session, + const XrCameraImageAcquireInfoPICO* acquireInfo, + XrCameraImagePICO* image); + +XRAPI_ATTR XrResult XRAPI_CALL xrGetCameraImageDataPICO( + XrCameraCaptureSessionPICO session, + XrCameraImageIdPICO imageId, + XrCameraImageDataBaseHeaderPICO* imageData); + +XRAPI_ATTR XrResult XRAPI_CALL xrReleaseCameraImagePICO( + XrCameraCaptureSessionPICO session, + XrCameraImageIdPICO imageId); +#endif /* XR_EXTENSION_PROTOTYPES */ +#endif /* !XR_NO_PROTOTYPES */ + + +// XR_PICO_spatial_sensing is a preprocessor guard. Do not pass it to API calls. +#define XR_PICO_spatial_sensing 1 +XR_DEFINE_HANDLE(XrSenseDataProviderPICO) +XR_DEFINE_HANDLE(XrSenseDataSnapshotPICO) +XR_DEFINE_HANDLE(XrAnchorPICO) +#define XR_PICO_spatial_sensing_SPEC_VERSION 2 +#define XR_PICO_SPATIAL_SENSING_EXTENSION_NAME "XR_PICO_spatial_sensing" + +typedef enum XrSemanticLabelPICO { + XR_SEMANTIC_LABEL_UNKNOWN_PICO = 0, + XR_SEMANTIC_LABEL_FLOOR_PICO = 1, + XR_SEMANTIC_LABEL_CEILING_PICO = 2, + XR_SEMANTIC_LABEL_WALL_PICO = 3, + XR_SEMANTIC_LABEL_DOOR_PICO = 4, + XR_SEMANTIC_LABEL_WINDOW_PICO = 5, + XR_SEMANTIC_LABEL_OPENING_PICO = 6, + XR_SEMANTIC_LABEL_TABLE_PICO = 7, + XR_SEMANTIC_LABEL_SOFA_PICO = 8, + XR_SEMANTIC_LABEL_CHAIR_PICO = 9, + XR_SEMANTIC_LABEL_HUMAN_PICO = 10, + XR_SEMANTIC_LABEL_BEAM_PICO = 11, + XR_SEMANTIC_LABEL_COLUMN_PICO = 12, + XR_SEMANTIC_LABEL_CURTAIN_PICO = 13, + XR_SEMANTIC_LABEL_CABINET_PICO = 14, + XR_SEMANTIC_LABEL_BED_PICO = 15, + XR_SEMANTIC_LABEL_PLANT_PICO = 16, + XR_SEMANTIC_LABEL_SCREEN_PICO = 17, + XR_SEMANTIC_LABEL_VIRTUAL_WALL_PICO = 18, + XR_SEMANTIC_LABEL_REFRIGERATOR_PICO = 19, + XR_SEMANTIC_LABEL_WASHING_MACHINE_PICO = 20, + XR_SEMANTIC_LABEL_AIR_CONDITIONER_PICO = 21, + XR_SEMANTIC_LABEL_LAMP_PICO = 22, + XR_SEMANTIC_LABEL_WALL_ART_PICO = 23, + XR_SEMANTIC_LABEL_STAIRWAY_PICO = 24, + XR_SEMANTIC_LABEL_MAX_ENUM_PICO = 0x7FFFFFFF +} XrSemanticLabelPICO; + +typedef enum XrSenseDataProviderStatePICO { + XR_SENSE_DATA_PROVIDER_STATE_INITIALIZED_PICO = 0, + XR_SENSE_DATA_PROVIDER_STATE_RUNNING_PICO = 1, + XR_SENSE_DATA_PROVIDER_STATE_STOPPED_PICO = 2, + XR_SENSE_DATA_PROVIDER_STATE_MAX_ENUM_PICO = 0x7FFFFFFF +} XrSenseDataProviderStatePICO; +// XrSystemSpatialSensingPropertiesPICO extends XrSystemProperties +typedef struct XrSystemSpatialSensingPropertiesPICO { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 supportsSpatialSensing; +} XrSystemSpatialSensingPropertiesPICO; + +typedef struct XrSpatialEntityComponentGetInfoBaseHeaderPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpatialEntityIdPICO entity; + XrSpatialEntityComponentTypePICO componentType; +} XrSpatialEntityComponentGetInfoBaseHeaderPICO; + +typedef struct XrSpatialEntityComponentDataBaseHeaderPICO { + XrStructureType type; + void* XR_MAY_ALIAS next; +} XrSpatialEntityComponentDataBaseHeaderPICO; + +// XrSpatialEntityLocationGetInfoPICO extends XrSpatialEntityComponentGetInfoBaseHeaderPICO +typedef struct XrSpatialEntityLocationGetInfoPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpatialEntityIdPICO entity; + XrSpatialEntityComponentTypePICO componentType; + XrSpace baseSpace; + XrTime time; +} XrSpatialEntityLocationGetInfoPICO; + +// XrSpatialEntityLocationDataPICO extends XrSpatialEntityComponentDataBaseHeaderPICO +typedef struct XrSpatialEntityLocationDataPICO { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrSpaceLocationData* location; +} XrSpatialEntityLocationDataPICO; + +// XrSpatialEntitySemanticGetInfoPICO extends XrSpatialEntityComponentGetInfoBaseHeaderPICO +typedef struct XrSpatialEntitySemanticGetInfoPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpatialEntityIdPICO entity; + XrSpatialEntityComponentTypePICO componentType; +} XrSpatialEntitySemanticGetInfoPICO; + +// XrSpatialEntitySemanticDataPICO extends XrSpatialEntityComponentDataBaseHeaderPICO +typedef struct XrSpatialEntitySemanticDataPICO { + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t semanticCapacityInput; + uint32_t semanticCountOutput; + XrSemanticLabelPICO* semanticLabels; +} XrSpatialEntitySemanticDataPICO; + +// XrSpatialEntityBoundingBox2DGetInfoPICO extends XrSpatialEntityComponentGetInfoBaseHeaderPICO +typedef struct XrSpatialEntityBoundingBox2DGetInfoPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpatialEntityIdPICO entity; + XrSpatialEntityComponentTypePICO componentType; +} XrSpatialEntityBoundingBox2DGetInfoPICO; + +// XrSpatialEntityBoundingBox2DDataPICO extends XrSpatialEntityComponentDataBaseHeaderPICO +typedef struct XrSpatialEntityBoundingBox2DDataPICO { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrRect2Df boundingBox2D; +} XrSpatialEntityBoundingBox2DDataPICO; + +// XrSpatialEntityPolygonGetInfoPICO extends XrSpatialEntityComponentGetInfoBaseHeaderPICO +typedef struct XrSpatialEntityPolygonGetInfoPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpatialEntityIdPICO entity; + XrSpatialEntityComponentTypePICO componentType; } XrSpatialEntityPolygonGetInfoPICO; // XrSpatialEntityPolygonDataPICO extends XrSpatialEntityComponentDataBaseHeaderPICO typedef struct XrSpatialEntityPolygonDataPICO { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t polygonCapacityInput; - uint32_t polygonCountOutput; - XrVector2f* polygonVertices; + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t polygonCapacityInput; + uint32_t polygonCountOutput; + XrVector2f* polygonVertices; } XrSpatialEntityPolygonDataPICO; // XrSpatialEntityBoundingBox3DGetInfoPICO extends XrSpatialEntityComponentGetInfoBaseHeaderPICO typedef struct XrSpatialEntityBoundingBox3DGetInfoPICO { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpatialEntityIdPICO entity; - XrSpatialEntityComponentTypePICO componentType; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpatialEntityIdPICO entity; + XrSpatialEntityComponentTypePICO componentType; } XrSpatialEntityBoundingBox3DGetInfoPICO; // XrSpatialEntityBoundingBox3DDataPICO extends XrSpatialEntityComponentDataBaseHeaderPICO typedef struct XrSpatialEntityBoundingBox3DDataPICO { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrBoxf boundingBox3D; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBoxf boundingBox3D; } XrSpatialEntityBoundingBox3DDataPICO; // XrSpatialEntityTriangleMeshGetInfoPICO extends XrSpatialEntityComponentGetInfoBaseHeaderPICO typedef struct XrSpatialEntityTriangleMeshGetInfoPICO { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpatialEntityIdPICO entity; - XrSpatialEntityComponentTypePICO componentType; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpatialEntityIdPICO entity; + XrSpatialEntityComponentTypePICO componentType; } XrSpatialEntityTriangleMeshGetInfoPICO; // XrSpatialEntityTriangleMeshDataPICO extends XrSpatialEntityComponentDataBaseHeaderPICO typedef struct XrSpatialEntityTriangleMeshDataPICO { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t vertexCapacityInput; - uint32_t vertexCountOutput; - XrVector3f* vertices; - uint32_t indexCapacityInput; - uint32_t indexCountOutput; - uint16_t* indices; + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t vertexCapacityInput; + uint32_t vertexCountOutput; + XrVector3f* vertices; + uint32_t indexCapacityInput; + uint32_t indexCountOutput; + uint16_t* indices; } XrSpatialEntityTriangleMeshDataPICO; // XrSpatialEntitySphereGetInfoPICO extends XrSpatialEntityComponentGetInfoBaseHeaderPICO typedef struct XrSpatialEntitySphereGetInfoPICO { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpatialEntityIdPICO entity; - XrSpatialEntityComponentTypePICO componentType; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpatialEntityIdPICO entity; + XrSpatialEntityComponentTypePICO componentType; } XrSpatialEntitySphereGetInfoPICO; // XrSpatialEntityComponentDataSpherePICO extends XrSpatialEntityComponentDataBaseHeaderPICO typedef struct XrSpatialEntityComponentDataSpherePICO { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrSpheref sphere; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrSpheref sphere; } XrSpatialEntityComponentDataSpherePICO; typedef struct XR_MAY_ALIAS XrSenseDataProviderCreateInfoBaseHeaderPICO { - XrStructureType type; - const void* XR_MAY_ALIAS next; + XrStructureType type; + const void* XR_MAY_ALIAS next; } XrSenseDataProviderCreateInfoBaseHeaderPICO; typedef struct XrSenseDataProviderStartInfoPICO { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSenseDataProviderPICO provider; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSenseDataProviderPICO provider; } XrSenseDataProviderStartInfoPICO; typedef struct XrSenseDataProviderStartCompletionPICO { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrResult futureResult; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrResult futureResult; } XrSenseDataProviderStartCompletionPICO; typedef struct XrEventDataSenseDataProviderStateChangedPICO { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSenseDataProviderPICO provider; - XrSenseDataProviderStatePICO newState; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSenseDataProviderPICO provider; + XrSenseDataProviderStatePICO newState; } XrEventDataSenseDataProviderStateChangedPICO; typedef struct XrEventDataSenseDataUpdatedPICO { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSenseDataProviderPICO provider; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSenseDataProviderPICO provider; } XrEventDataSenseDataUpdatedPICO; typedef struct XR_MAY_ALIAS XrSenseDataFilterBaseHeaderPICO { - XrStructureType type; - const void* XR_MAY_ALIAS next; + XrStructureType type; + const void* XR_MAY_ALIAS next; } XrSenseDataFilterBaseHeaderPICO; typedef struct XrSenseDataQueryInfoPICO { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSenseDataFilterBaseHeaderPICO* filter; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSenseDataFilterBaseHeaderPICO* filter; } XrSenseDataQueryInfoPICO; typedef struct XrSenseDataQueryCompletionPICO { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrResult futureResult; - XrSenseDataSnapshotPICO snapshot; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrResult futureResult; + XrSenseDataSnapshotPICO snapshot; } XrSenseDataQueryCompletionPICO; typedef struct XrQueriedSenseDataGetInfoPICO { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSenseDataSnapshotPICO snapshot; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSenseDataSnapshotPICO snapshot; } XrQueriedSenseDataGetInfoPICO; typedef struct XrSpatialEntityStatePICO { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrSpatialEntityIdPICO entity; - XrTime lastUpdateTime; - XrUuidEXT uuid; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrSpatialEntityIdPICO entity; + XrTime lastUpdateTime; + XrUuidEXT uuid; } XrSpatialEntityStatePICO; typedef struct XrQueriedSenseDataPICO { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t spatialEntityCapacityInput; - uint32_t spatialEntityCountOutput; - XrSpatialEntityStatePICO* spatialEntities; + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t spatialEntityCapacityInput; + uint32_t spatialEntityCountOutput; + XrSpatialEntityStatePICO* spatialEntities; } XrQueriedSenseDataPICO; typedef struct XrSenseDataFilterUuidPICO { - XrStructureType type; - const void* XR_MAY_ALIAS next; - uint32_t uuidCount; - XrUuidEXT* uuids; + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t uuidCount; + XrUuidEXT* uuids; } XrSenseDataFilterUuidPICO; typedef struct XrSenseDataFilterSemanticPICO { - XrStructureType type; - const void* XR_MAY_ALIAS next; - uint32_t semanticCount; - XrSemanticLabelPICO* semantics; + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t semanticCount; + XrSemanticLabelPICO* semantics; } XrSenseDataFilterSemanticPICO; typedef struct XrSpatialEntityAnchorRetrieveInfoPICO { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpatialEntityIdPICO entity; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpatialEntityIdPICO entity; } XrSpatialEntityAnchorRetrieveInfoPICO; typedef struct XrAnchorLocateInfoPICO { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpace baseSpace; - XrTime time; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpace baseSpace; + XrTime time; } XrAnchorLocateInfoPICO; typedef struct XrFuturePollResultProgressPICO { - XrStructureType type; - const void* XR_MAY_ALIAS next; - int32_t progress; + XrStructureType type; + const void* XR_MAY_ALIAS next; + int32_t progress; } XrFuturePollResultProgressPICO; -typedef XrResult(XRAPI_PTR* PFN_xrEnumerateSpatialEntityComponentTypesPICO)( - XrSenseDataSnapshotPICO snapshot, XrSpatialEntityIdPICO entity, uint32_t componentTypeCapacityInput, - uint32_t* componentTypeCountOutput, XrSpatialEntityComponentTypePICO* componentTypes); -typedef XrResult(XRAPI_PTR* PFN_xrGetSpatialEntityUuidPICO)(XrSenseDataSnapshotPICO snapshot, - XrSpatialEntityIdPICO entity, XrUuidEXT* uuid); -typedef XrResult(XRAPI_PTR* PFN_xrGetSpatialEntityComponentDataPICO)( - XrSenseDataSnapshotPICO snapshot, XrSpatialEntityComponentGetInfoBaseHeaderPICO* getInfo, - XrSpatialEntityComponentDataBaseHeaderPICO* componentData); -typedef XrResult(XRAPI_PTR* PFN_xrCreateSenseDataProviderPICO)(XrSession session, - XrSenseDataProviderCreateInfoBaseHeaderPICO* createInfo, - XrSenseDataProviderPICO* provider); -typedef XrResult(XRAPI_PTR* PFN_xrStartSenseDataProviderAsyncPICO)(XrSession session, - XrSenseDataProviderStartInfoPICO* startInfo, - XrFutureEXT* future); -typedef XrResult(XRAPI_PTR* PFN_xrStartSenseDataProviderCompletePICO)( - XrSession session, XrFutureEXT future, XrSenseDataProviderStartCompletionPICO* completion); -typedef XrResult(XRAPI_PTR* PFN_xrGetSenseDataProviderStatePICO)(XrSenseDataProviderPICO provider, - XrSenseDataProviderStatePICO* state); -typedef XrResult(XRAPI_PTR* PFN_xrQuerySenseDataAsyncPICO)(XrSenseDataProviderPICO provider, - XrSenseDataQueryInfoPICO* queryInfo, XrFutureEXT* future); -typedef XrResult(XRAPI_PTR* PFN_xrQuerySenseDataCompletePICO)(XrSenseDataProviderPICO provider, XrFutureEXT future, - XrSenseDataQueryCompletionPICO* completion); -typedef XrResult(XRAPI_PTR* PFN_xrDestroySenseDataSnapshotPICO)(XrSenseDataSnapshotPICO snapshot); -typedef XrResult(XRAPI_PTR* PFN_xrGetQueriedSenseDataPICO)(XrSenseDataProviderPICO provider, - XrQueriedSenseDataGetInfoPICO* getInfo, - XrQueriedSenseDataPICO* queriedSenseData); -typedef XrResult(XRAPI_PTR* PFN_xrStopSenseDataProviderPICO)(XrSenseDataProviderPICO provider); -typedef XrResult(XRAPI_PTR* PFN_xrDestroySenseDataProviderPICO)(XrSenseDataProviderPICO provider); -typedef XrResult(XRAPI_PTR* PFN_xrRetrieveSpatialEntityAnchorPICO)(XrSenseDataSnapshotPICO snapshot, - XrSpatialEntityAnchorRetrieveInfoPICO* retrieveInfo, - XrAnchorPICO* anchor); -typedef XrResult(XRAPI_PTR* PFN_xrDestroyAnchorPICO)(XrAnchorPICO anchor); -typedef XrResult(XRAPI_PTR* PFN_xrGetAnchorUuidPICO)(XrAnchorPICO anchor, XrUuidEXT* uuid); -typedef XrResult(XRAPI_PTR* PFN_xrLocateAnchorPICO)(XrAnchorPICO anchor, XrAnchorLocateInfoPICO* locateInfo, - XrSpaceLocation* location); +typedef XrResult (XRAPI_PTR *PFN_xrEnumerateSpatialEntityComponentTypesPICO)(XrSenseDataSnapshotPICO snapshot, XrSpatialEntityIdPICO entity, uint32_t componentTypeCapacityInput, uint32_t* componentTypeCountOutput, XrSpatialEntityComponentTypePICO* componentTypes); +typedef XrResult (XRAPI_PTR *PFN_xrGetSpatialEntityUuidPICO)(XrSenseDataSnapshotPICO snapshot, XrSpatialEntityIdPICO entity, XrUuidEXT* uuid); +typedef XrResult (XRAPI_PTR *PFN_xrGetSpatialEntityComponentDataPICO)(XrSenseDataSnapshotPICO snapshot, XrSpatialEntityComponentGetInfoBaseHeaderPICO* getInfo, XrSpatialEntityComponentDataBaseHeaderPICO* componentData); +typedef XrResult (XRAPI_PTR *PFN_xrCreateSenseDataProviderPICO)(XrSession session, XrSenseDataProviderCreateInfoBaseHeaderPICO* createInfo, XrSenseDataProviderPICO* provider); +typedef XrResult (XRAPI_PTR *PFN_xrStartSenseDataProviderAsyncPICO)(XrSession session, XrSenseDataProviderStartInfoPICO* startInfo, XrFutureEXT* future); +typedef XrResult (XRAPI_PTR *PFN_xrStartSenseDataProviderCompletePICO)(XrSession session, XrFutureEXT future, XrSenseDataProviderStartCompletionPICO* completion); +typedef XrResult (XRAPI_PTR *PFN_xrGetSenseDataProviderStatePICO)(XrSenseDataProviderPICO provider, XrSenseDataProviderStatePICO* state); +typedef XrResult (XRAPI_PTR *PFN_xrQuerySenseDataAsyncPICO)(XrSenseDataProviderPICO provider, XrSenseDataQueryInfoPICO* queryInfo, XrFutureEXT* future); +typedef XrResult (XRAPI_PTR *PFN_xrQuerySenseDataCompletePICO)(XrSenseDataProviderPICO provider, XrFutureEXT future, XrSenseDataQueryCompletionPICO* completion); +typedef XrResult (XRAPI_PTR *PFN_xrDestroySenseDataSnapshotPICO)(XrSenseDataSnapshotPICO snapshot); +typedef XrResult (XRAPI_PTR *PFN_xrGetQueriedSenseDataPICO)(XrSenseDataProviderPICO provider, XrQueriedSenseDataGetInfoPICO* getInfo, XrQueriedSenseDataPICO* queriedSenseData); +typedef XrResult (XRAPI_PTR *PFN_xrStopSenseDataProviderPICO)(XrSenseDataProviderPICO provider); +typedef XrResult (XRAPI_PTR *PFN_xrDestroySenseDataProviderPICO)(XrSenseDataProviderPICO provider); +typedef XrResult (XRAPI_PTR *PFN_xrRetrieveSpatialEntityAnchorPICO)(XrSenseDataSnapshotPICO snapshot, XrSpatialEntityAnchorRetrieveInfoPICO* retrieveInfo, XrAnchorPICO* anchor); +typedef XrResult (XRAPI_PTR *PFN_xrDestroyAnchorPICO)(XrAnchorPICO anchor); +typedef XrResult (XRAPI_PTR *PFN_xrGetAnchorUuidPICO)(XrAnchorPICO anchor, XrUuidEXT* uuid); +typedef XrResult (XRAPI_PTR *PFN_xrLocateAnchorPICO)(XrAnchorPICO anchor, XrAnchorLocateInfoPICO* locateInfo, XrSpaceLocation* location); #ifndef XR_NO_PROTOTYPES #ifdef XR_EXTENSION_PROTOTYPES XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSpatialEntityComponentTypesPICO( - XrSenseDataSnapshotPICO snapshot, XrSpatialEntityIdPICO entity, uint32_t componentTypeCapacityInput, - uint32_t* componentTypeCountOutput, XrSpatialEntityComponentTypePICO* componentTypes); + XrSenseDataSnapshotPICO snapshot, + XrSpatialEntityIdPICO entity, + uint32_t componentTypeCapacityInput, + uint32_t* componentTypeCountOutput, + XrSpatialEntityComponentTypePICO* componentTypes); -XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialEntityUuidPICO(XrSenseDataSnapshotPICO snapshot, - XrSpatialEntityIdPICO entity, XrUuidEXT* uuid); +XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialEntityUuidPICO( + XrSenseDataSnapshotPICO snapshot, + XrSpatialEntityIdPICO entity, + XrUuidEXT* uuid); XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialEntityComponentDataPICO( - XrSenseDataSnapshotPICO snapshot, XrSpatialEntityComponentGetInfoBaseHeaderPICO* getInfo, + XrSenseDataSnapshotPICO snapshot, + XrSpatialEntityComponentGetInfoBaseHeaderPICO* getInfo, XrSpatialEntityComponentDataBaseHeaderPICO* componentData); -XRAPI_ATTR XrResult XRAPI_CALL xrCreateSenseDataProviderPICO(XrSession session, - XrSenseDataProviderCreateInfoBaseHeaderPICO* createInfo, - XrSenseDataProviderPICO* provider); +XRAPI_ATTR XrResult XRAPI_CALL xrCreateSenseDataProviderPICO( + XrSession session, + XrSenseDataProviderCreateInfoBaseHeaderPICO* createInfo, + XrSenseDataProviderPICO* provider); -XRAPI_ATTR XrResult XRAPI_CALL xrStartSenseDataProviderAsyncPICO(XrSession session, - XrSenseDataProviderStartInfoPICO* startInfo, - XrFutureEXT* future); +XRAPI_ATTR XrResult XRAPI_CALL xrStartSenseDataProviderAsyncPICO( + XrSession session, + XrSenseDataProviderStartInfoPICO* startInfo, + XrFutureEXT* future); -XRAPI_ATTR XrResult XRAPI_CALL xrStartSenseDataProviderCompletePICO(XrSession session, XrFutureEXT future, - XrSenseDataProviderStartCompletionPICO* completion); +XRAPI_ATTR XrResult XRAPI_CALL xrStartSenseDataProviderCompletePICO( + XrSession session, + XrFutureEXT future, + XrSenseDataProviderStartCompletionPICO* completion); -XRAPI_ATTR XrResult XRAPI_CALL xrGetSenseDataProviderStatePICO(XrSenseDataProviderPICO provider, - XrSenseDataProviderStatePICO* state); +XRAPI_ATTR XrResult XRAPI_CALL xrGetSenseDataProviderStatePICO( + XrSenseDataProviderPICO provider, + XrSenseDataProviderStatePICO* state); -XRAPI_ATTR XrResult XRAPI_CALL xrQuerySenseDataAsyncPICO(XrSenseDataProviderPICO provider, - XrSenseDataQueryInfoPICO* queryInfo, XrFutureEXT* future); +XRAPI_ATTR XrResult XRAPI_CALL xrQuerySenseDataAsyncPICO( + XrSenseDataProviderPICO provider, + XrSenseDataQueryInfoPICO* queryInfo, + XrFutureEXT* future); -XRAPI_ATTR XrResult XRAPI_CALL xrQuerySenseDataCompletePICO(XrSenseDataProviderPICO provider, XrFutureEXT future, - XrSenseDataQueryCompletionPICO* completion); +XRAPI_ATTR XrResult XRAPI_CALL xrQuerySenseDataCompletePICO( + XrSenseDataProviderPICO provider, + XrFutureEXT future, + XrSenseDataQueryCompletionPICO* completion); -XRAPI_ATTR XrResult XRAPI_CALL xrDestroySenseDataSnapshotPICO(XrSenseDataSnapshotPICO snapshot); +XRAPI_ATTR XrResult XRAPI_CALL xrDestroySenseDataSnapshotPICO( + XrSenseDataSnapshotPICO snapshot); -XRAPI_ATTR XrResult XRAPI_CALL xrGetQueriedSenseDataPICO(XrSenseDataProviderPICO provider, - XrQueriedSenseDataGetInfoPICO* getInfo, - XrQueriedSenseDataPICO* queriedSenseData); +XRAPI_ATTR XrResult XRAPI_CALL xrGetQueriedSenseDataPICO( + XrSenseDataProviderPICO provider, + XrQueriedSenseDataGetInfoPICO* getInfo, + XrQueriedSenseDataPICO* queriedSenseData); -XRAPI_ATTR XrResult XRAPI_CALL xrStopSenseDataProviderPICO(XrSenseDataProviderPICO provider); +XRAPI_ATTR XrResult XRAPI_CALL xrStopSenseDataProviderPICO( + XrSenseDataProviderPICO provider); -XRAPI_ATTR XrResult XRAPI_CALL xrDestroySenseDataProviderPICO(XrSenseDataProviderPICO provider); +XRAPI_ATTR XrResult XRAPI_CALL xrDestroySenseDataProviderPICO( + XrSenseDataProviderPICO provider); -XRAPI_ATTR XrResult XRAPI_CALL xrRetrieveSpatialEntityAnchorPICO(XrSenseDataSnapshotPICO snapshot, - XrSpatialEntityAnchorRetrieveInfoPICO* retrieveInfo, - XrAnchorPICO* anchor); +XRAPI_ATTR XrResult XRAPI_CALL xrRetrieveSpatialEntityAnchorPICO( + XrSenseDataSnapshotPICO snapshot, + XrSpatialEntityAnchorRetrieveInfoPICO* retrieveInfo, + XrAnchorPICO* anchor); -XRAPI_ATTR XrResult XRAPI_CALL xrDestroyAnchorPICO(XrAnchorPICO anchor); +XRAPI_ATTR XrResult XRAPI_CALL xrDestroyAnchorPICO( + XrAnchorPICO anchor); -XRAPI_ATTR XrResult XRAPI_CALL xrGetAnchorUuidPICO(XrAnchorPICO anchor, XrUuidEXT* uuid); +XRAPI_ATTR XrResult XRAPI_CALL xrGetAnchorUuidPICO( + XrAnchorPICO anchor, + XrUuidEXT* uuid); -XRAPI_ATTR XrResult XRAPI_CALL xrLocateAnchorPICO(XrAnchorPICO anchor, XrAnchorLocateInfoPICO* locateInfo, - XrSpaceLocation* location); +XRAPI_ATTR XrResult XRAPI_CALL xrLocateAnchorPICO( + XrAnchorPICO anchor, + XrAnchorLocateInfoPICO* locateInfo, + XrSpaceLocation* location); #endif /* XR_EXTENSION_PROTOTYPES */ #endif /* !XR_NO_PROTOTYPES */ + // XR_PICO_spatial_anchor is a preprocessor guard. Do not pass it to API calls. #define XR_PICO_spatial_anchor 1 #define XR_PICO_spatial_anchor_SPEC_VERSION 1 #define XR_PICO_SPATIAL_ANCHOR_EXTENSION_NAME "XR_PICO_spatial_anchor" typedef enum XrPersistenceLocationPICO { - XR_PERSISTENCE_LOCATION_LOCAL_PICO = 0, - XR_PERSISTENCE_LOCATION_MAX_ENUM_PICO = 0x7FFFFFFF + XR_PERSISTENCE_LOCATION_LOCAL_PICO = 0, + XR_PERSISTENCE_LOCATION_MAX_ENUM_PICO = 0x7FFFFFFF } XrPersistenceLocationPICO; // XrSystemSpatialAnchorPropertiesPICO extends XrSystemProperties typedef struct XrSystemSpatialAnchorPropertiesPICO { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrBool32 supportsSpatialAnchor; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 supportsSpatialAnchor; } XrSystemSpatialAnchorPropertiesPICO; typedef struct XrSenseDataProviderCreateInfoSpatialAnchorPICO { - XrStructureType type; - const void* XR_MAY_ALIAS next; + XrStructureType type; + const void* XR_MAY_ALIAS next; } XrSenseDataProviderCreateInfoSpatialAnchorPICO; typedef struct XrSpatialAnchorCreateInfoPICO { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpace space; - XrPosef pose; - XrTime time; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpace space; + XrPosef pose; + XrTime time; } XrSpatialAnchorCreateInfoPICO; typedef struct XrSpatialAnchorCreateCompletionPICO { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrResult futureResult; - XrAnchorPICO anchor; - XrUuidEXT uuid; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrResult futureResult; + XrAnchorPICO anchor; + XrUuidEXT uuid; } XrSpatialAnchorCreateCompletionPICO; typedef struct XrSpatialAnchorPersistInfoPICO { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrPersistenceLocationPICO location; - XrAnchorPICO anchor; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrPersistenceLocationPICO location; + XrAnchorPICO anchor; } XrSpatialAnchorPersistInfoPICO; typedef struct XrSpatialAnchorPersistCompletionPICO { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrResult futureResult; - XrAnchorPICO anchor; - XrUuidEXT uuid; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrResult futureResult; + XrAnchorPICO anchor; + XrUuidEXT uuid; } XrSpatialAnchorPersistCompletionPICO; typedef struct XrSpatialAnchorUnpersistInfoPICO { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrPersistenceLocationPICO location; - XrAnchorPICO anchor; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrPersistenceLocationPICO location; + XrAnchorPICO anchor; } XrSpatialAnchorUnpersistInfoPICO; typedef struct XrSpatialAnchorUnpersistCompletionPICO { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrResult futureResult; - XrAnchorPICO anchor; - XrUuidEXT uuid; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrResult futureResult; + XrAnchorPICO anchor; + XrUuidEXT uuid; } XrSpatialAnchorUnpersistCompletionPICO; -typedef XrResult(XRAPI_PTR* PFN_xrCreateSpatialAnchorAsyncPICO)(XrSenseDataProviderPICO provider, - XrSpatialAnchorCreateInfoPICO* info, - XrFutureEXT* future); -typedef XrResult(XRAPI_PTR* PFN_xrCreateSpatialAnchorCompletePICO)(XrSenseDataProviderPICO provider, XrFutureEXT future, - XrSpatialAnchorCreateCompletionPICO* completion); -typedef XrResult(XRAPI_PTR* PFN_xrPersistSpatialAnchorAsyncPICO)(XrSenseDataProviderPICO provider, - XrSpatialAnchorPersistInfoPICO* info, - XrFutureEXT* future); -typedef XrResult(XRAPI_PTR* PFN_xrPersistSpatialAnchorCompletePICO)(XrSenseDataProviderPICO provider, - XrFutureEXT future, - XrSpatialAnchorPersistCompletionPICO* completion); -typedef XrResult(XRAPI_PTR* PFN_xrUnpersistSpatialAnchorAsyncPICO)(XrSenseDataProviderPICO provider, - XrSpatialAnchorUnpersistInfoPICO* info, - XrFutureEXT* future); -typedef XrResult(XRAPI_PTR* PFN_xrUnpersistSpatialAnchorCompletePICO)( - XrSenseDataProviderPICO provider, XrFutureEXT future, XrSpatialAnchorUnpersistCompletionPICO* completion); +typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialAnchorAsyncPICO)(XrSenseDataProviderPICO provider, XrSpatialAnchorCreateInfoPICO* info, XrFutureEXT* future); +typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialAnchorCompletePICO)(XrSenseDataProviderPICO provider, XrFutureEXT future, XrSpatialAnchorCreateCompletionPICO* completion); +typedef XrResult (XRAPI_PTR *PFN_xrPersistSpatialAnchorAsyncPICO)(XrSenseDataProviderPICO provider, XrSpatialAnchorPersistInfoPICO* info, XrFutureEXT* future); +typedef XrResult (XRAPI_PTR *PFN_xrPersistSpatialAnchorCompletePICO)(XrSenseDataProviderPICO provider, XrFutureEXT future, XrSpatialAnchorPersistCompletionPICO* completion); +typedef XrResult (XRAPI_PTR *PFN_xrUnpersistSpatialAnchorAsyncPICO)(XrSenseDataProviderPICO provider, XrSpatialAnchorUnpersistInfoPICO* info, XrFutureEXT* future); +typedef XrResult (XRAPI_PTR *PFN_xrUnpersistSpatialAnchorCompletePICO)(XrSenseDataProviderPICO provider, XrFutureEXT future, XrSpatialAnchorUnpersistCompletionPICO* completion); #ifndef XR_NO_PROTOTYPES #ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorAsyncPICO(XrSenseDataProviderPICO provider, - XrSpatialAnchorCreateInfoPICO* info, XrFutureEXT* future); +XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorAsyncPICO( + XrSenseDataProviderPICO provider, + XrSpatialAnchorCreateInfoPICO* info, + XrFutureEXT* future); -XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorCompletePICO(XrSenseDataProviderPICO provider, XrFutureEXT future, - XrSpatialAnchorCreateCompletionPICO* completion); +XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorCompletePICO( + XrSenseDataProviderPICO provider, + XrFutureEXT future, + XrSpatialAnchorCreateCompletionPICO* completion); -XRAPI_ATTR XrResult XRAPI_CALL xrPersistSpatialAnchorAsyncPICO(XrSenseDataProviderPICO provider, - XrSpatialAnchorPersistInfoPICO* info, - XrFutureEXT* future); +XRAPI_ATTR XrResult XRAPI_CALL xrPersistSpatialAnchorAsyncPICO( + XrSenseDataProviderPICO provider, + XrSpatialAnchorPersistInfoPICO* info, + XrFutureEXT* future); -XRAPI_ATTR XrResult XRAPI_CALL xrPersistSpatialAnchorCompletePICO(XrSenseDataProviderPICO provider, XrFutureEXT future, - XrSpatialAnchorPersistCompletionPICO* completion); +XRAPI_ATTR XrResult XRAPI_CALL xrPersistSpatialAnchorCompletePICO( + XrSenseDataProviderPICO provider, + XrFutureEXT future, + XrSpatialAnchorPersistCompletionPICO* completion); -XRAPI_ATTR XrResult XRAPI_CALL xrUnpersistSpatialAnchorAsyncPICO(XrSenseDataProviderPICO provider, - XrSpatialAnchorUnpersistInfoPICO* info, - XrFutureEXT* future); +XRAPI_ATTR XrResult XRAPI_CALL xrUnpersistSpatialAnchorAsyncPICO( + XrSenseDataProviderPICO provider, + XrSpatialAnchorUnpersistInfoPICO* info, + XrFutureEXT* future); -XRAPI_ATTR XrResult XRAPI_CALL xrUnpersistSpatialAnchorCompletePICO(XrSenseDataProviderPICO provider, - XrFutureEXT future, - XrSpatialAnchorUnpersistCompletionPICO* completion); +XRAPI_ATTR XrResult XRAPI_CALL xrUnpersistSpatialAnchorCompletePICO( + XrSenseDataProviderPICO provider, + XrFutureEXT future, + XrSpatialAnchorUnpersistCompletionPICO* completion); #endif /* XR_EXTENSION_PROTOTYPES */ #endif /* !XR_NO_PROTOTYPES */ + // XR_PICO_spatial_anchor_sharing is a preprocessor guard. Do not pass it to API calls. #define XR_PICO_spatial_anchor_sharing 1 #define XR_PICO_spatial_anchor_sharing_SPEC_VERSION 1 #define XR_PICO_SPATIAL_ANCHOR_SHARING_EXTENSION_NAME "XR_PICO_spatial_anchor_sharing" // XrSystemSpatialAnchorSharingPropertiesPICO extends XrSystemProperties typedef struct XrSystemSpatialAnchorSharingPropertiesPICO { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrBool32 supportsSpatialAnchorSharing; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 supportsSpatialAnchorSharing; } XrSystemSpatialAnchorSharingPropertiesPICO; typedef struct XrSpatialAnchorShareInfoPICO { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrAnchorPICO anchor; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrAnchorPICO anchor; } XrSpatialAnchorShareInfoPICO; typedef struct XrSpatialAnchorShareCompletionPICO { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrResult futureResult; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrResult futureResult; } XrSpatialAnchorShareCompletionPICO; typedef struct XrSharedSpatialAnchorDownloadInfoPICO { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrUuidEXT uuid; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrUuidEXT uuid; } XrSharedSpatialAnchorDownloadInfoPICO; typedef struct XrSharedSpatialAnchorDownloadCompletionPICO { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrResult futureResult; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrResult futureResult; } XrSharedSpatialAnchorDownloadCompletionPICO; -typedef XrResult(XRAPI_PTR* PFN_xrShareSpatialAnchorAsyncPICO)(XrSenseDataProviderPICO provider, - XrSpatialAnchorShareInfoPICO* info, XrFutureEXT* future); -typedef XrResult(XRAPI_PTR* PFN_xrShareSpatialAnchorCompletePICO)(XrSenseDataProviderPICO provider, XrFutureEXT future, - XrSpatialAnchorShareCompletionPICO* completion); -typedef XrResult(XRAPI_PTR* PFN_xrDownloadSharedSpatialAnchorAsyncPICO)(XrSenseDataProviderPICO provider, - XrSharedSpatialAnchorDownloadInfoPICO* info, - XrFutureEXT* future); -typedef XrResult(XRAPI_PTR* PFN_xrDownloadSharedSpatialAnchorCompletePICO)( - XrSenseDataProviderPICO provider, XrFutureEXT future, XrSharedSpatialAnchorDownloadCompletionPICO* completion); +typedef XrResult (XRAPI_PTR *PFN_xrShareSpatialAnchorAsyncPICO)(XrSenseDataProviderPICO provider, XrSpatialAnchorShareInfoPICO* info, XrFutureEXT* future); +typedef XrResult (XRAPI_PTR *PFN_xrShareSpatialAnchorCompletePICO)(XrSenseDataProviderPICO provider, XrFutureEXT future, XrSpatialAnchorShareCompletionPICO* completion); +typedef XrResult (XRAPI_PTR *PFN_xrDownloadSharedSpatialAnchorAsyncPICO)(XrSenseDataProviderPICO provider, XrSharedSpatialAnchorDownloadInfoPICO* info, XrFutureEXT* future); +typedef XrResult (XRAPI_PTR *PFN_xrDownloadSharedSpatialAnchorCompletePICO)(XrSenseDataProviderPICO provider, XrFutureEXT future, XrSharedSpatialAnchorDownloadCompletionPICO* completion); #ifndef XR_NO_PROTOTYPES #ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrShareSpatialAnchorAsyncPICO(XrSenseDataProviderPICO provider, - XrSpatialAnchorShareInfoPICO* info, XrFutureEXT* future); +XRAPI_ATTR XrResult XRAPI_CALL xrShareSpatialAnchorAsyncPICO( + XrSenseDataProviderPICO provider, + XrSpatialAnchorShareInfoPICO* info, + XrFutureEXT* future); -XRAPI_ATTR XrResult XRAPI_CALL xrShareSpatialAnchorCompletePICO(XrSenseDataProviderPICO provider, XrFutureEXT future, - XrSpatialAnchorShareCompletionPICO* completion); +XRAPI_ATTR XrResult XRAPI_CALL xrShareSpatialAnchorCompletePICO( + XrSenseDataProviderPICO provider, + XrFutureEXT future, + XrSpatialAnchorShareCompletionPICO* completion); -XRAPI_ATTR XrResult XRAPI_CALL xrDownloadSharedSpatialAnchorAsyncPICO(XrSenseDataProviderPICO provider, - XrSharedSpatialAnchorDownloadInfoPICO* info, - XrFutureEXT* future); +XRAPI_ATTR XrResult XRAPI_CALL xrDownloadSharedSpatialAnchorAsyncPICO( + XrSenseDataProviderPICO provider, + XrSharedSpatialAnchorDownloadInfoPICO* info, + XrFutureEXT* future); XRAPI_ATTR XrResult XRAPI_CALL xrDownloadSharedSpatialAnchorCompletePICO( - XrSenseDataProviderPICO provider, XrFutureEXT future, XrSharedSpatialAnchorDownloadCompletionPICO* completion); + XrSenseDataProviderPICO provider, + XrFutureEXT future, + XrSharedSpatialAnchorDownloadCompletionPICO* completion); #endif /* XR_EXTENSION_PROTOTYPES */ #endif /* !XR_NO_PROTOTYPES */ + // XR_PICO_scene_capture is a preprocessor guard. Do not pass it to API calls. #define XR_PICO_scene_capture 1 #define XR_PICO_scene_capture_SPEC_VERSION 1 #define XR_PICO_SCENE_CAPTURE_EXTENSION_NAME "XR_PICO_scene_capture" // XrSystemSceneCapturePropertiesPICO extends XrSystemProperties typedef struct XrSystemSceneCapturePropertiesPICO { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrBool32 supportsSceneCapture; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 supportsSceneCapture; } XrSystemSceneCapturePropertiesPICO; typedef struct XrSenseDataProviderCreateInfoSceneCapturePICO { - XrStructureType type; - const void* XR_MAY_ALIAS next; + XrStructureType type; + const void* XR_MAY_ALIAS next; } XrSenseDataProviderCreateInfoSceneCapturePICO; typedef struct XrSceneCaptureStartInfoPICO { - XrStructureType type; - const void* XR_MAY_ALIAS next; + XrStructureType type; + const void* XR_MAY_ALIAS next; } XrSceneCaptureStartInfoPICO; typedef struct XrSceneCaptureStartCompletionPICO { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrResult futureResult; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrResult futureResult; } XrSceneCaptureStartCompletionPICO; -typedef XrResult(XRAPI_PTR* PFN_xrStartSceneCaptureAsyncPICO)(XrSenseDataProviderPICO provider, - XrSceneCaptureStartInfoPICO* info, XrFutureEXT* future); -typedef XrResult(XRAPI_PTR* PFN_xrStartSceneCaptureCompletePICO)(XrSenseDataProviderPICO provider, XrFutureEXT future, - XrSceneCaptureStartCompletionPICO* completion); +typedef XrResult (XRAPI_PTR *PFN_xrStartSceneCaptureAsyncPICO)(XrSenseDataProviderPICO provider, XrSceneCaptureStartInfoPICO* info, XrFutureEXT* future); +typedef XrResult (XRAPI_PTR *PFN_xrStartSceneCaptureCompletePICO)(XrSenseDataProviderPICO provider, XrFutureEXT future, XrSceneCaptureStartCompletionPICO* completion); #ifndef XR_NO_PROTOTYPES #ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrStartSceneCaptureAsyncPICO(XrSenseDataProviderPICO provider, - XrSceneCaptureStartInfoPICO* info, XrFutureEXT* future); +XRAPI_ATTR XrResult XRAPI_CALL xrStartSceneCaptureAsyncPICO( + XrSenseDataProviderPICO provider, + XrSceneCaptureStartInfoPICO* info, + XrFutureEXT* future); -XRAPI_ATTR XrResult XRAPI_CALL xrStartSceneCaptureCompletePICO(XrSenseDataProviderPICO provider, XrFutureEXT future, - XrSceneCaptureStartCompletionPICO* completion); +XRAPI_ATTR XrResult XRAPI_CALL xrStartSceneCaptureCompletePICO( + XrSenseDataProviderPICO provider, + XrFutureEXT future, + XrSceneCaptureStartCompletionPICO* completion); #endif /* XR_EXTENSION_PROTOTYPES */ #endif /* !XR_NO_PROTOTYPES */ + // XR_PICO_spatial_mesh is a preprocessor guard. Do not pass it to API calls. #define XR_PICO_spatial_mesh 1 #define XR_PICO_spatial_mesh_SPEC_VERSION 1 #define XR_PICO_SPATIAL_MESH_EXTENSION_NAME "XR_PICO_spatial_mesh" typedef enum XrSpatialMeshLodPICO { - XR_SPATIAL_MESH_LOD_COARSE_PICO = 0, - XR_SPATIAL_MESH_LOD_MEDIUM_PICO = 1, - XR_SPATIAL_MESH_LOD_FINE_PICO = 2, - XR_SPATIAL_MESH_LOD_MAX_ENUM_PICO = 0x7FFFFFFF + XR_SPATIAL_MESH_LOD_COARSE_PICO = 0, + XR_SPATIAL_MESH_LOD_MEDIUM_PICO = 1, + XR_SPATIAL_MESH_LOD_FINE_PICO = 2, + XR_SPATIAL_MESH_LOD_MAX_ENUM_PICO = 0x7FFFFFFF } XrSpatialMeshLodPICO; typedef XrFlags64 XrSpatialMeshConfigFlagsPICO; @@ -12203,18 +13854,19 @@ static const XrSpatialMeshConfigFlagsPICO XR_SPATIAL_MESH_CONFIG_ALIGN_SEMANTIC_ // XrSystemSpatialMeshPropertiesPICO extends XrSystemProperties typedef struct XrSystemSpatialMeshPropertiesPICO { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrBool32 supportsSpatialMesh; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 supportsSpatialMesh; } XrSystemSpatialMeshPropertiesPICO; typedef struct XrSenseDataProviderCreateInfoSpatialMeshPICO { - XrStructureType type; - const void* XR_MAY_ALIAS next; - XrSpatialMeshConfigFlagsPICO configFlags; - XrSpatialMeshLodPICO lod; + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpatialMeshConfigFlagsPICO configFlags; + XrSpatialMeshLodPICO lod; } XrSenseDataProviderCreateInfoSpatialMeshPICO; + #ifdef __cplusplus } #endif diff --git a/external/openxr/include/openxr/openxr_pico.h b/external/openxr/include/openxr/openxr_pico.h new file mode 100644 index 0000000..b3696fc --- /dev/null +++ b/external/openxr/include/openxr/openxr_pico.h @@ -0,0 +1,23 @@ +/** + * @file openxr_pico_ext.h + * @brief this header lists the openxr extensions defined by Pico + * which haven't join in the openxr registery yet. + * @version 0.1 + * + * @copyright Copyright (c) 2022 + * + */ + +#ifndef OPENXR_PICO_H_ +#define OPENXR_PICO_H_ 1 + +#if defined(__cplusplus) +extern "C" { +#endif + + +#ifdef __cplusplus +} +#endif + +#endif // OPENXR_PICO_H_ \ No newline at end of file diff --git a/external/openxr/include/openxr/openxr_platform.h b/external/openxr/include/openxr/openxr_platform.h index f363b00..40a1b06 100644 --- a/external/openxr/include/openxr/openxr_platform.h +++ b/external/openxr/include/openxr/openxr_platform.h @@ -104,69 +104,70 @@ typedef struct XrVulkanSwapchainFormatListCreateInfoKHR { #ifdef XR_USE_PLATFORM_WIN32 // XrGraphicsBindingOpenGLWin32KHR extends XrSessionCreateInfo typedef struct XrGraphicsBindingOpenGLWin32KHR { - XrStructureType type; - const void* XR_MAY_ALIAS next; - HDC hDC; - HGLRC hGLRC; + XrStructureType type; + const void* XR_MAY_ALIAS next; + HDC hDC; + HGLRC hGLRC; } XrGraphicsBindingOpenGLWin32KHR; -#endif // XR_USE_PLATFORM_WIN32 +#endif // XR_USE_PLATFORM_WIN32 #ifdef XR_USE_PLATFORM_XLIB // XrGraphicsBindingOpenGLXlibKHR extends XrSessionCreateInfo typedef struct XrGraphicsBindingOpenGLXlibKHR { - XrStructureType type; - const void* XR_MAY_ALIAS next; - Display* xDisplay; - uint32_t visualid; - GLXFBConfig glxFBConfig; - GLXDrawable glxDrawable; - GLXContext glxContext; + XrStructureType type; + const void* XR_MAY_ALIAS next; + Display* xDisplay; + uint32_t visualid; + GLXFBConfig glxFBConfig; + GLXDrawable glxDrawable; + GLXContext glxContext; } XrGraphicsBindingOpenGLXlibKHR; -#endif // XR_USE_PLATFORM_XLIB +#endif // XR_USE_PLATFORM_XLIB #ifdef XR_USE_PLATFORM_XCB // XrGraphicsBindingOpenGLXcbKHR extends XrSessionCreateInfo typedef struct XrGraphicsBindingOpenGLXcbKHR { - XrStructureType type; - const void* XR_MAY_ALIAS next; - xcb_connection_t* connection; - uint32_t screenNumber; - xcb_glx_fbconfig_t fbconfigid; - xcb_visualid_t visualid; - xcb_glx_drawable_t glxDrawable; - xcb_glx_context_t glxContext; + XrStructureType type; + const void* XR_MAY_ALIAS next; + xcb_connection_t* connection; + uint32_t screenNumber; + xcb_glx_fbconfig_t fbconfigid; + xcb_visualid_t visualid; + xcb_glx_drawable_t glxDrawable; + xcb_glx_context_t glxContext; } XrGraphicsBindingOpenGLXcbKHR; -#endif // XR_USE_PLATFORM_XCB +#endif // XR_USE_PLATFORM_XCB #ifdef XR_USE_PLATFORM_WAYLAND // XrGraphicsBindingOpenGLWaylandKHR extends XrSessionCreateInfo typedef struct XrGraphicsBindingOpenGLWaylandKHR { - XrStructureType type; - const void* XR_MAY_ALIAS next; - struct wl_display* display; + XrStructureType type; + const void* XR_MAY_ALIAS next; + struct wl_display* display; } XrGraphicsBindingOpenGLWaylandKHR; -#endif // XR_USE_PLATFORM_WAYLAND +#endif // XR_USE_PLATFORM_WAYLAND typedef struct XrSwapchainImageOpenGLKHR { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t image; + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t image; } XrSwapchainImageOpenGLKHR; typedef struct XrGraphicsRequirementsOpenGLKHR { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrVersion minApiVersionSupported; - XrVersion maxApiVersionSupported; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrVersion minApiVersionSupported; + XrVersion maxApiVersionSupported; } XrGraphicsRequirementsOpenGLKHR; -typedef XrResult(XRAPI_PTR* PFN_xrGetOpenGLGraphicsRequirementsKHR)( - XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsOpenGLKHR* graphicsRequirements); +typedef XrResult (XRAPI_PTR *PFN_xrGetOpenGLGraphicsRequirementsKHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsOpenGLKHR* graphicsRequirements); #ifndef XR_NO_PROTOTYPES #ifdef XR_EXTENSION_PROTOTYPES XRAPI_ATTR XrResult XRAPI_CALL xrGetOpenGLGraphicsRequirementsKHR( - XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsOpenGLKHR* graphicsRequirements); + XrInstance instance, + XrSystemId systemId, + XrGraphicsRequirementsOpenGLKHR* graphicsRequirements); #endif /* XR_EXTENSION_PROTOTYPES */ #endif /* !XR_NO_PROTOTYPES */ #endif /* XR_USE_GRAPHICS_API_OPENGL */ @@ -180,34 +181,35 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetOpenGLGraphicsRequirementsKHR( #ifdef XR_USE_PLATFORM_ANDROID // XrGraphicsBindingOpenGLESAndroidKHR extends XrSessionCreateInfo typedef struct XrGraphicsBindingOpenGLESAndroidKHR { - XrStructureType type; - const void* XR_MAY_ALIAS next; - EGLDisplay display; - EGLConfig config; - EGLContext context; + XrStructureType type; + const void* XR_MAY_ALIAS next; + EGLDisplay display; + EGLConfig config; + EGLContext context; } XrGraphicsBindingOpenGLESAndroidKHR; -#endif // XR_USE_PLATFORM_ANDROID +#endif // XR_USE_PLATFORM_ANDROID typedef struct XrSwapchainImageOpenGLESKHR { - XrStructureType type; - void* XR_MAY_ALIAS next; - uint32_t image; + XrStructureType type; + void* XR_MAY_ALIAS next; + uint32_t image; } XrSwapchainImageOpenGLESKHR; typedef struct XrGraphicsRequirementsOpenGLESKHR { - XrStructureType type; - void* XR_MAY_ALIAS next; - XrVersion minApiVersionSupported; - XrVersion maxApiVersionSupported; + XrStructureType type; + void* XR_MAY_ALIAS next; + XrVersion minApiVersionSupported; + XrVersion maxApiVersionSupported; } XrGraphicsRequirementsOpenGLESKHR; -typedef XrResult(XRAPI_PTR* PFN_xrGetOpenGLESGraphicsRequirementsKHR)( - XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsOpenGLESKHR* graphicsRequirements); +typedef XrResult (XRAPI_PTR *PFN_xrGetOpenGLESGraphicsRequirementsKHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsOpenGLESKHR* graphicsRequirements); #ifndef XR_NO_PROTOTYPES #ifdef XR_EXTENSION_PROTOTYPES XRAPI_ATTR XrResult XRAPI_CALL xrGetOpenGLESGraphicsRequirementsKHR( - XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsOpenGLESKHR* graphicsRequirements); + XrInstance instance, + XrSystemId systemId, + XrGraphicsRequirementsOpenGLESKHR* graphicsRequirements); #endif /* XR_EXTENSION_PROTOTYPES */ #endif /* !XR_NO_PROTOTYPES */ #endif /* XR_USE_GRAPHICS_API_OPENGL_ES */ @@ -281,35 +283,36 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanGraphicsRequirementsKHR( // XR_KHR_D3D11_enable is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_D3D11_enable 1 -#define XR_KHR_D3D11_enable_SPEC_VERSION 10 +#define XR_KHR_D3D11_enable_SPEC_VERSION 10 #define XR_KHR_D3D11_ENABLE_EXTENSION_NAME "XR_KHR_D3D11_enable" // XrGraphicsBindingD3D11KHR extends XrSessionCreateInfo typedef struct XrGraphicsBindingD3D11KHR { - XrStructureType type; - const void* XR_MAY_ALIAS next; - ID3D11Device* device; + XrStructureType type; + const void* XR_MAY_ALIAS next; + ID3D11Device* device; } XrGraphicsBindingD3D11KHR; typedef struct XrSwapchainImageD3D11KHR { - XrStructureType type; - void* XR_MAY_ALIAS next; - ID3D11Texture2D* texture; + XrStructureType type; + void* XR_MAY_ALIAS next; + ID3D11Texture2D* texture; } XrSwapchainImageD3D11KHR; typedef struct XrGraphicsRequirementsD3D11KHR { - XrStructureType type; - void* XR_MAY_ALIAS next; - LUID adapterLuid; - D3D_FEATURE_LEVEL minFeatureLevel; + XrStructureType type; + void* XR_MAY_ALIAS next; + LUID adapterLuid; + D3D_FEATURE_LEVEL minFeatureLevel; } XrGraphicsRequirementsD3D11KHR; -typedef XrResult(XRAPI_PTR* PFN_xrGetD3D11GraphicsRequirementsKHR)( - XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsD3D11KHR* graphicsRequirements); +typedef XrResult (XRAPI_PTR *PFN_xrGetD3D11GraphicsRequirementsKHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsD3D11KHR* graphicsRequirements); #ifndef XR_NO_PROTOTYPES #ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrGetD3D11GraphicsRequirementsKHR(XrInstance instance, XrSystemId systemId, - XrGraphicsRequirementsD3D11KHR* graphicsRequirements); +XRAPI_ATTR XrResult XRAPI_CALL xrGetD3D11GraphicsRequirementsKHR( + XrInstance instance, + XrSystemId systemId, + XrGraphicsRequirementsD3D11KHR* graphicsRequirements); #endif /* XR_EXTENSION_PROTOTYPES */ #endif /* !XR_NO_PROTOTYPES */ #endif /* XR_USE_GRAPHICS_API_D3D11 */ @@ -318,36 +321,37 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetD3D11GraphicsRequirementsKHR(XrInstance inst // XR_KHR_D3D12_enable is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_D3D12_enable 1 -#define XR_KHR_D3D12_enable_SPEC_VERSION 10 +#define XR_KHR_D3D12_enable_SPEC_VERSION 10 #define XR_KHR_D3D12_ENABLE_EXTENSION_NAME "XR_KHR_D3D12_enable" // XrGraphicsBindingD3D12KHR extends XrSessionCreateInfo typedef struct XrGraphicsBindingD3D12KHR { - XrStructureType type; - const void* XR_MAY_ALIAS next; - ID3D12Device* device; - ID3D12CommandQueue* queue; + XrStructureType type; + const void* XR_MAY_ALIAS next; + ID3D12Device* device; + ID3D12CommandQueue* queue; } XrGraphicsBindingD3D12KHR; typedef struct XrSwapchainImageD3D12KHR { - XrStructureType type; - void* XR_MAY_ALIAS next; - ID3D12Resource* texture; + XrStructureType type; + void* XR_MAY_ALIAS next; + ID3D12Resource* texture; } XrSwapchainImageD3D12KHR; typedef struct XrGraphicsRequirementsD3D12KHR { - XrStructureType type; - void* XR_MAY_ALIAS next; - LUID adapterLuid; - D3D_FEATURE_LEVEL minFeatureLevel; + XrStructureType type; + void* XR_MAY_ALIAS next; + LUID adapterLuid; + D3D_FEATURE_LEVEL minFeatureLevel; } XrGraphicsRequirementsD3D12KHR; -typedef XrResult(XRAPI_PTR* PFN_xrGetD3D12GraphicsRequirementsKHR)( - XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsD3D12KHR* graphicsRequirements); +typedef XrResult (XRAPI_PTR *PFN_xrGetD3D12GraphicsRequirementsKHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsD3D12KHR* graphicsRequirements); #ifndef XR_NO_PROTOTYPES #ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrGetD3D12GraphicsRequirementsKHR(XrInstance instance, XrSystemId systemId, - XrGraphicsRequirementsD3D12KHR* graphicsRequirements); +XRAPI_ATTR XrResult XRAPI_CALL xrGetD3D12GraphicsRequirementsKHR( + XrInstance instance, + XrSystemId systemId, + XrGraphicsRequirementsD3D12KHR* graphicsRequirements); #endif /* XR_EXTENSION_PROTOTYPES */ #endif /* !XR_NO_PROTOTYPES */ #endif /* XR_USE_GRAPHICS_API_D3D12 */ @@ -356,34 +360,35 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetD3D12GraphicsRequirementsKHR(XrInstance inst // XR_KHR_metal_enable is a preprocessor guard. Do not pass it to API calls. #define XR_KHR_metal_enable 1 -#define XR_KHR_metal_enable_SPEC_VERSION 2 +#define XR_KHR_metal_enable_SPEC_VERSION 2 #define XR_KHR_METAL_ENABLE_EXTENSION_NAME "XR_KHR_metal_enable" // XrGraphicsBindingMetalKHR extends XrSessionCreateInfo typedef struct XrGraphicsBindingMetalKHR { - XrStructureType type; - const void* XR_MAY_ALIAS next; - void* XR_MAY_ALIAS commandQueue; + XrStructureType type; + const void* XR_MAY_ALIAS next; + void* XR_MAY_ALIAS commandQueue; } XrGraphicsBindingMetalKHR; typedef struct XrSwapchainImageMetalKHR { - XrStructureType type; - const void* XR_MAY_ALIAS next; - void* XR_MAY_ALIAS texture; + XrStructureType type; + const void* XR_MAY_ALIAS next; + void* XR_MAY_ALIAS texture; } XrSwapchainImageMetalKHR; typedef struct XrGraphicsRequirementsMetalKHR { - XrStructureType type; - void* XR_MAY_ALIAS next; - void* XR_MAY_ALIAS metalDevice; + XrStructureType type; + void* XR_MAY_ALIAS next; + void* XR_MAY_ALIAS metalDevice; } XrGraphicsRequirementsMetalKHR; -typedef XrResult(XRAPI_PTR* PFN_xrGetMetalGraphicsRequirementsKHR)( - XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsMetalKHR* graphicsRequirements); +typedef XrResult (XRAPI_PTR *PFN_xrGetMetalGraphicsRequirementsKHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsMetalKHR* graphicsRequirements); #ifndef XR_NO_PROTOTYPES #ifdef XR_EXTENSION_PROTOTYPES -XRAPI_ATTR XrResult XRAPI_CALL xrGetMetalGraphicsRequirementsKHR(XrInstance instance, XrSystemId systemId, - XrGraphicsRequirementsMetalKHR* graphicsRequirements); +XRAPI_ATTR XrResult XRAPI_CALL xrGetMetalGraphicsRequirementsKHR( + XrInstance instance, + XrSystemId systemId, + XrGraphicsRequirementsMetalKHR* graphicsRequirements); #endif /* XR_EXTENSION_PROTOTYPES */ #endif /* !XR_NO_PROTOTYPES */ #endif /* XR_USE_GRAPHICS_API_METAL */ @@ -764,6 +769,76 @@ typedef struct XrVulkanSwapchainCreateInfoMETA { #endif /* XR_USE_GRAPHICS_API_VULKAN */ +#ifdef XR_USE_PLATFORM_ANDROID + +// XR_ANDROID_anchor_sharing_export is a preprocessor guard. Do not pass it to API calls. +#define XR_ANDROID_anchor_sharing_export 1 +#define XR_ANDROID_anchor_sharing_export_SPEC_VERSION 1 +#define XR_ANDROID_ANCHOR_SHARING_EXPORT_EXTENSION_NAME "XR_ANDROID_anchor_sharing_export" +typedef struct XrAnchorSharingInfoANDROID { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpace anchor; +} XrAnchorSharingInfoANDROID; + +typedef struct XrAnchorSharingTokenANDROID { + XrStructureType type; + void* XR_MAY_ALIAS next; + struct AIBinder* token; +} XrAnchorSharingTokenANDROID; + +// XrSystemAnchorSharingExportPropertiesANDROID extends XrSystemProperties +typedef struct XrSystemAnchorSharingExportPropertiesANDROID { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 supportsAnchorSharingExport; +} XrSystemAnchorSharingExportPropertiesANDROID; + +typedef XrResult (XRAPI_PTR *PFN_xrShareAnchorANDROID)(XrSession session, const XrAnchorSharingInfoANDROID* sharingInfo, XrAnchorSharingTokenANDROID* anchorToken); +typedef XrResult (XRAPI_PTR *PFN_xrUnshareAnchorANDROID)(XrSession session, XrSpace anchor); + +#ifndef XR_NO_PROTOTYPES +#ifdef XR_EXTENSION_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrShareAnchorANDROID( + XrSession session, + const XrAnchorSharingInfoANDROID* sharingInfo, + XrAnchorSharingTokenANDROID* anchorToken); + +XRAPI_ATTR XrResult XRAPI_CALL xrUnshareAnchorANDROID( + XrSession session, + XrSpace anchor); +#endif /* XR_EXTENSION_PROTOTYPES */ +#endif /* !XR_NO_PROTOTYPES */ +#endif /* XR_USE_PLATFORM_ANDROID */ + +#ifdef XR_USE_GRAPHICS_API_VULKAN + +// XR_PICO_readback_tensor_vulkan is a preprocessor guard. Do not pass it to API calls. +#define XR_PICO_readback_tensor_vulkan 1 +#define XR_PICO_readback_tensor_vulkan_SPEC_VERSION 1 +#define XR_PICO_READBACK_TENSOR_VULKAN_EXTENSION_NAME "XR_PICO_readback_tensor_vulkan" +typedef struct XrReadbackTextureImageVulkanPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + VkImage image; +} XrReadbackTextureImageVulkanPICO; + +#endif /* XR_USE_GRAPHICS_API_VULKAN */ + +#ifdef XR_USE_GRAPHICS_API_OPENGL_ES + +// XR_PICO_readback_tensor_opengles is a preprocessor guard. Do not pass it to API calls. +#define XR_PICO_readback_tensor_opengles 1 +#define XR_PICO_readback_tensor_opengles_SPEC_VERSION 1 +#define XR_PICO_READBACK_TENSOR_OPENGLES_EXTENSION_NAME "XR_PICO_readback_tensor_opengles" +typedef struct XrReadbackTextureImageOpenGLPICO { + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t texId; +} XrReadbackTextureImageOpenGLPICO; + +#endif /* XR_USE_GRAPHICS_API_OPENGL_ES */ + #ifdef __cplusplus } #endif diff --git a/external/openxr/include/openxr/openxr_reflection.h b/external/openxr/include/openxr/openxr_reflection.h index e3ddbb7..24457fe 100644 --- a/external/openxr/include/openxr/openxr_reflection.h +++ b/external/openxr/include/openxr/openxr_reflection.h @@ -141,26 +141,40 @@ XR_ENUM_STR(XrResult); _(XR_ERROR_SPACE_NETWORK_TIMEOUT_FB, -1000169002) \ _(XR_ERROR_SPACE_NETWORK_REQUEST_FAILED_FB, -1000169003) \ _(XR_ERROR_SPACE_CLOUD_STORAGE_DISABLED_FB, -1000169004) \ + _(XR_ERROR_SPACE_INSUFFICIENT_RESOURCES_META, -1000259000) \ + _(XR_ERROR_SPACE_STORAGE_AT_CAPACITY_META, -1000259001) \ + _(XR_ERROR_SPACE_INSUFFICIENT_VIEW_META, -1000259002) \ + _(XR_ERROR_SPACE_PERMISSION_INSUFFICIENT_META, -1000259003) \ + _(XR_ERROR_SPACE_RATE_LIMITED_META, -1000259004) \ + _(XR_ERROR_SPACE_TOO_DARK_META, -1000259005) \ + _(XR_ERROR_SPACE_TOO_BRIGHT_META, -1000259006) \ _(XR_ERROR_PASSTHROUGH_COLOR_LUT_BUFFER_SIZE_MISMATCH_META, -1000266000) \ - _(XR_ENVIRONMENT_DEPTH_NOT_AVAILABLE_META, 1000291000) \ - _(XR_ERROR_RENDER_MODEL_ID_INVALID_EXT, -1000300000) \ - _(XR_ERROR_RENDER_MODEL_ASSET_UNAVAILABLE_EXT, -1000300001) \ - _(XR_ERROR_RENDER_MODEL_GLTF_EXTENSION_REQUIRED_EXT, -1000300002) \ - _(XR_ERROR_NOT_INTERACTION_RENDER_MODEL_EXT, -1000301000) \ - _(XR_ERROR_HINT_ALREADY_SET_QCOM, -1000306000) \ - _(XR_ERROR_NOT_AN_ANCHOR_HTC, -1000319000) \ - _(XR_ERROR_SPATIAL_ENTITY_ID_INVALID_BD, -1000389000) \ - _(XR_ERROR_SPATIAL_SENSING_SERVICE_UNAVAILABLE_BD, -1000389001) \ - _(XR_ERROR_ANCHOR_NOT_SUPPORTED_FOR_ENTITY_BD, -1000389002) \ - _(XR_ERROR_SPATIAL_ANCHOR_NOT_FOUND_BD, -1000390000) \ - _(XR_ERROR_SPATIAL_ANCHOR_SHARING_NETWORK_TIMEOUT_BD, -1000391000) \ - _(XR_ERROR_SPATIAL_ANCHOR_SHARING_AUTHENTICATION_FAILURE_BD, -1000391001) \ - _(XR_ERROR_SPATIAL_ANCHOR_SHARING_NETWORK_FAILURE_BD, -1000391002) \ - _(XR_ERROR_SPATIAL_ANCHOR_SHARING_LOCALIZATION_FAIL_BD, -1000391003) \ - _(XR_ERROR_SPATIAL_ANCHOR_SHARING_MAP_INSUFFICIENT_BD, -1000391004) \ - _(XR_ERROR_SCENE_CAPTURE_FAILURE_BD, -1000392000) \ - _(XR_ERROR_SPACE_NOT_LOCATABLE_EXT, -1000429000) \ + _(XR_ENVIRONMENT_DEPTH_NOT_AVAILABLE_META, 1000291000) \ + _(XR_ERROR_RENDER_MODEL_ID_INVALID_EXT, -1000300000) \ + _(XR_ERROR_RENDER_MODEL_ASSET_UNAVAILABLE_EXT, -1000300001) \ + _(XR_ERROR_RENDER_MODEL_GLTF_EXTENSION_REQUIRED_EXT, -1000300002) \ + _(XR_ERROR_NOT_INTERACTION_RENDER_MODEL_EXT, -1000301000) \ + _(XR_ERROR_HINT_ALREADY_SET_QCOM, -1000306000) \ + _(XR_ERROR_NOT_AN_ANCHOR_HTC, -1000319000) \ + _(XR_ERROR_SPATIAL_ENTITY_ID_INVALID_BD, -1000389000) \ + _(XR_ERROR_SPATIAL_SENSING_SERVICE_UNAVAILABLE_BD, -1000389001) \ + _(XR_ERROR_ANCHOR_NOT_SUPPORTED_FOR_ENTITY_BD, -1000389002) \ + _(XR_ERROR_SPATIAL_ANCHOR_NOT_FOUND_BD, -1000390000) \ + _(XR_ERROR_SPATIAL_ANCHOR_SHARING_NETWORK_TIMEOUT_BD, -1000391000) \ + _(XR_ERROR_SPATIAL_ANCHOR_SHARING_AUTHENTICATION_FAILURE_BD, -1000391001) \ + _(XR_ERROR_SPATIAL_ANCHOR_SHARING_NETWORK_FAILURE_BD, -1000391002) \ + _(XR_ERROR_SPATIAL_ANCHOR_SHARING_LOCALIZATION_FAIL_BD, -1000391003) \ + _(XR_ERROR_SPATIAL_ANCHOR_SHARING_MAP_INSUFFICIENT_BD, -1000391004) \ + _(XR_ERROR_SCENE_CAPTURE_FAILURE_BD, -1000392000) \ + _(XR_ERROR_SPACE_NOT_LOCATABLE_EXT, -1000429000) \ _(XR_ERROR_PLANE_DETECTION_PERMISSION_DENIED_EXT, -1000429001) \ + _(XR_ERROR_MISMATCHING_TRACKABLE_TYPE_ANDROID, -1000455000) \ + _(XR_ERROR_TRACKABLE_TYPE_NOT_SUPPORTED_ANDROID, -1000455001) \ + _(XR_ERROR_ANCHOR_ID_NOT_FOUND_ANDROID, -1000457000) \ + _(XR_ERROR_ANCHOR_ALREADY_PERSISTED_ANDROID, -1000457001) \ + _(XR_ERROR_ANCHOR_NOT_TRACKING_ANDROID, -1000457002) \ + _(XR_ERROR_PERSISTED_DATA_NOT_READY_ANDROID, -1000457003) \ + _(XR_ERROR_SERVICE_NOT_READY_ANDROID, -1000458000) \ _(XR_ERROR_FUTURE_PENDING_EXT, -1000469001) \ _(XR_ERROR_FUTURE_INVALID_EXT, -1000469002) \ _(XR_ERROR_SYSTEM_NOTIFICATION_PERMISSION_DENIED_ML, -1000473000) \ @@ -172,14 +186,15 @@ XR_ENUM_STR(XrResult); _(XR_ERROR_COLOCATION_DISCOVERY_NO_DISCOVERY_METHOD_META, -1000571002) \ _(XR_COLOCATION_DISCOVERY_ALREADY_ADVERTISING_META, 1000571003) \ _(XR_COLOCATION_DISCOVERY_ALREADY_DISCOVERING_META, 1000571004) \ - _(XR_ERROR_SPACE_GROUP_NOT_FOUND_META, -1000572002) \ - _(XR_ERROR_SPATIAL_CAPABILITY_UNSUPPORTED_EXT, -1000740001) \ - _(XR_ERROR_SPATIAL_ENTITY_ID_INVALID_EXT, -1000740002) \ - _(XR_ERROR_SPATIAL_BUFFER_ID_INVALID_EXT, -1000740003) \ - _(XR_ERROR_SPATIAL_COMPONENT_UNSUPPORTED_FOR_CAPABILITY_EXT, -1000740004) \ - _(XR_ERROR_SPATIAL_CAPABILITY_CONFIGURATION_INVALID_EXT, -1000740005) \ - _(XR_ERROR_SPATIAL_COMPONENT_NOT_ENABLED_EXT, -1000740006) \ - _(XR_ERROR_VIRTUAL_BOUNDARY_TRIGGER_NODE_TYPE_UNSUPPORTED_PICO, -1010001000) \ + _(XR_ERROR_SPACE_GROUP_NOT_FOUND_META, -1000572002) \ + _(XR_ERROR_ANCHOR_NOT_OWNED_BY_CALLER_ANDROID, -1000701000) \ + _(XR_ERROR_SPATIAL_CAPABILITY_UNSUPPORTED_EXT, -1000740001) \ + _(XR_ERROR_SPATIAL_ENTITY_ID_INVALID_EXT, -1000740002) \ + _(XR_ERROR_SPATIAL_BUFFER_ID_INVALID_EXT, -1000740003) \ + _(XR_ERROR_SPATIAL_COMPONENT_UNSUPPORTED_FOR_CAPABILITY_EXT, -1000740004) \ + _(XR_ERROR_SPATIAL_CAPABILITY_CONFIGURATION_INVALID_EXT, -1000740005) \ + _(XR_ERROR_SPATIAL_COMPONENT_NOT_ENABLED_EXT, -1000740006) \ + _(XR_ERROR_VIRTUAL_BOUNDARY_TRIGGER_NODE_TYPE_UNSUPPORTED_PICO, -1010001000) \ _(XR_ERROR_MOTION_TRACKER_TYPE_MISMATCH_PICO, -1010002000) \ _(XR_ERROR_MOTION_TRACKER_COUNT_EXCEEDED_PICO, -1010002001) \ _(XR_ERROR_MOTION_TRACKING_MODE_MISMATCH_PICO, -1010002002) \ @@ -189,16 +204,25 @@ XR_ENUM_STR(XrResult); _(XR_ERROR_SECURE_MR_PIPELINE_LOCKED_PICO, -1010007002) \ _(XR_ERROR_SECURE_MR_USAGE_BEFORE_INIT_PICO, -1010007003) \ _(XR_ERROR_SECURE_MR_OTHER_INTERNAL_ERROR_PICO, -1010007004) \ - _(XR_ERROR_EXPAND_TRACKER_ID_INVALID_PICO, -1010008000) \ - _(XR_ERROR_SPATIAL_SENSING_SERVICE_UNAVAILABLE_PICO, -1200389027) \ - _(XR_ERROR_SPATIAL_ANCHOR_SHARING_NETWORK_TIMEOUT_PICO, -1200391101) \ - _(XR_ERROR_SPATIAL_ANCHOR_SHARING_AUTHENTICATION_FAILURE_PICO, -1200391102) \ - _(XR_ERROR_SPATIAL_ANCHOR_SHARING_NETWORK_FAILURE_PICO, -1200391103) \ - _(XR_ERROR_SPATIAL_ANCHOR_SHARING_LOCALIZATION_FAIL_PICO, -1200391104) \ - _(XR_ERROR_SPATIAL_ANCHOR_SHARING_MAP_INSUFFICIENT_PICO, -1200391105) \ - _(XR_ERROR_SPATIAL_PERSISTENCE_SCOPE_UNSUPPORTED_EXT, -1000763001) \ - _(XR_ERROR_SPATIAL_PERSISTENCE_SCOPE_INCOMPATIBLE_EXT, -1000781001) \ - _(XR_RESULT_MAX_ENUM, 0x7FFFFFFF) + _(XR_ERROR_EXPAND_TRACKER_ID_INVALID_PICO, -1010008000) \ + _(XR_ERROR_CAMERA_UNAVAILABLE_PICO, -1010033000) \ + _(XR_ERROR_CAMERA_OCCUPIED_PICO, -1010033001) \ + _(XR_ERROR_CAMERA_CAPTURE_SESSION_CAPTURING_PICO, -1010033002) \ + _(XR_ERROR_CAMERA_CAPTURE_SESSION_NOT_CAPTURING_PICO, -1010033003) \ + _(XR_ERROR_CAMERA_ID_INVALID_PICO, -1010033004) \ + _(XR_ERROR_CAMERA_IMAGE_ID_INVALID_PICO, -1010033005) \ + _(XR_ERROR_CAMERA_PROPERTY_TYPE_INVALID_PICO, -1010033006) \ + _(XR_ERROR_CAMERA_CAPABILITY_TYPE_INVALID_PICO, -1010033007) \ + _(XR_CAMERA_IMAGE_NO_UPDATE_PICO, 1010033000) \ + _(XR_ERROR_SPATIAL_SENSING_SERVICE_UNAVAILABLE_PICO, -1200389027) \ + _(XR_ERROR_SPATIAL_ANCHOR_SHARING_NETWORK_TIMEOUT_PICO, -1200391101) \ + _(XR_ERROR_SPATIAL_ANCHOR_SHARING_AUTHENTICATION_FAILURE_PICO, -1200391102) \ + _(XR_ERROR_SPATIAL_ANCHOR_SHARING_NETWORK_FAILURE_PICO, -1200391103) \ + _(XR_ERROR_SPATIAL_ANCHOR_SHARING_LOCALIZATION_FAIL_PICO, -1200391104) \ + _(XR_ERROR_SPATIAL_ANCHOR_SHARING_MAP_INSUFFICIENT_PICO, -1200391105) \ + _(XR_ERROR_SPATIAL_PERSISTENCE_SCOPE_UNSUPPORTED_EXT, -1000763001) \ + _(XR_ERROR_SPATIAL_PERSISTENCE_SCOPE_INCOMPATIBLE_EXT, -1000781001) \ + _(XR_RESULT_MAX_ENUM, 0x7FFFFFFF) #define XR_LIST_ENUM_XrStructureType(_) \ _(XR_TYPE_UNKNOWN, 0) \ @@ -527,17 +551,33 @@ XR_ENUM_STR(XrResult); _(XR_TYPE_EVENT_DATA_SPACE_LIST_SAVE_COMPLETE_FB, 1000238001) \ _(XR_TYPE_SPACE_USER_CREATE_INFO_FB, 1000241001) \ _(XR_TYPE_SYSTEM_HEADSET_ID_PROPERTIES_META, 1000245000) \ + _(XR_TYPE_SYSTEM_SPACE_DISCOVERY_PROPERTIES_META, 1000247000) \ + _(XR_TYPE_SPACE_DISCOVERY_INFO_META, 1000247001) \ + _(XR_TYPE_SPACE_FILTER_UUID_META, 1000247003) \ + _(XR_TYPE_SPACE_FILTER_COMPONENT_META, 1000247004) \ + _(XR_TYPE_SPACE_DISCOVERY_RESULT_META, 1000247005) \ + _(XR_TYPE_SPACE_DISCOVERY_RESULTS_META, 1000247006) \ + _(XR_TYPE_EVENT_DATA_SPACE_DISCOVERY_RESULTS_AVAILABLE_META, 1000247007) \ + _(XR_TYPE_EVENT_DATA_SPACE_DISCOVERY_COMPLETE_META, 1000247008) \ _(XR_TYPE_RECOMMENDED_LAYER_RESOLUTION_META, 1000254000) \ _(XR_TYPE_RECOMMENDED_LAYER_RESOLUTION_GET_INFO_META, 1000254001) \ + _(XR_TYPE_SYSTEM_SPACE_PERSISTENCE_PROPERTIES_META, 1000259000) \ + _(XR_TYPE_SPACES_SAVE_INFO_META, 1000259001) \ + _(XR_TYPE_EVENT_DATA_SPACES_SAVE_RESULT_META, 1000259002) \ + _(XR_TYPE_SPACES_ERASE_INFO_META, 1000259003) \ + _(XR_TYPE_EVENT_DATA_SPACES_ERASE_RESULT_META, 1000259004) \ _(XR_TYPE_SYSTEM_PASSTHROUGH_COLOR_LUT_PROPERTIES_META, 1000266000) \ _(XR_TYPE_PASSTHROUGH_COLOR_LUT_CREATE_INFO_META, 1000266001) \ _(XR_TYPE_PASSTHROUGH_COLOR_LUT_UPDATE_INFO_META, 1000266002) \ _(XR_TYPE_PASSTHROUGH_COLOR_MAP_LUT_META, 1000266100) \ _(XR_TYPE_PASSTHROUGH_COLOR_MAP_INTERPOLATED_LUT_META, 1000266101) \ _(XR_TYPE_SPACE_TRIANGLE_MESH_GET_INFO_META, 1000269001) \ - _(XR_TYPE_SPACE_TRIANGLE_MESH_META, 1000269002) \ - _(XR_TYPE_SYSTEM_PROPERTIES_BODY_TRACKING_FULL_BODY_META, 1000274000) \ - _(XR_TYPE_EVENT_DATA_PASSTHROUGH_LAYER_RESUMED_META, 1000282000) \ + _(XR_TYPE_SPACE_TRIANGLE_MESH_META, 1000269002) \ + _(XR_TYPE_SYSTEM_PROPERTIES_BODY_TRACKING_FULL_BODY_META, 1000274000) \ + _(XR_TYPE_EVENT_DATA_PASSTHROUGH_LAYER_RESUMED_META, 1000282000) \ + _(XR_TYPE_BODY_TRACKING_CALIBRATION_INFO_META, 1000283002) \ + _(XR_TYPE_BODY_TRACKING_CALIBRATION_STATUS_META, 1000283003) \ + _(XR_TYPE_SYSTEM_PROPERTIES_BODY_TRACKING_CALIBRATION_META, 1000283004) \ _(XR_TYPE_SYSTEM_FACE_TRACKING_PROPERTIES2_FB, 1000287013) \ _(XR_TYPE_FACE_TRACKER_CREATE_INFO2_FB, 1000287014) \ _(XR_TYPE_FACE_EXPRESSION_INFO2_FB, 1000287015) \ @@ -552,23 +592,23 @@ XR_ENUM_STR(XrResult); _(XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_VIEW_META, 1000291004) \ _(XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_META, 1000291005) \ _(XR_TYPE_ENVIRONMENT_DEPTH_HAND_REMOVAL_SET_INFO_META, 1000291006) \ - _(XR_TYPE_SYSTEM_ENVIRONMENT_DEPTH_PROPERTIES_META, 1000291007) \ - _(XR_TYPE_RENDER_MODEL_CREATE_INFO_EXT, 1000300000) \ - _(XR_TYPE_RENDER_MODEL_PROPERTIES_GET_INFO_EXT, 1000300001) \ - _(XR_TYPE_RENDER_MODEL_PROPERTIES_EXT, 1000300002) \ - _(XR_TYPE_RENDER_MODEL_SPACE_CREATE_INFO_EXT, 1000300003) \ - _(XR_TYPE_RENDER_MODEL_STATE_GET_INFO_EXT, 1000300004) \ - _(XR_TYPE_RENDER_MODEL_STATE_EXT, 1000300005) \ - _(XR_TYPE_RENDER_MODEL_ASSET_CREATE_INFO_EXT, 1000300006) \ - _(XR_TYPE_RENDER_MODEL_ASSET_DATA_GET_INFO_EXT, 1000300007) \ - _(XR_TYPE_RENDER_MODEL_ASSET_DATA_EXT, 1000300008) \ - _(XR_TYPE_RENDER_MODEL_ASSET_PROPERTIES_GET_INFO_EXT, 1000300009) \ - _(XR_TYPE_RENDER_MODEL_ASSET_PROPERTIES_EXT, 1000300010) \ - _(XR_TYPE_INTERACTION_RENDER_MODEL_IDS_ENUMERATE_INFO_EXT, 1000301000) \ - _(XR_TYPE_INTERACTION_RENDER_MODEL_SUBACTION_PATH_INFO_EXT, 1000301001) \ - _(XR_TYPE_EVENT_DATA_INTERACTION_RENDER_MODELS_CHANGED_EXT, 1000301002) \ - _(XR_TYPE_INTERACTION_RENDER_MODEL_TOP_LEVEL_USER_PATH_GET_INFO_EXT, 1000301003) \ - _(XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC, 1000317001) \ + _(XR_TYPE_SYSTEM_ENVIRONMENT_DEPTH_PROPERTIES_META, 1000291007) \ + _(XR_TYPE_RENDER_MODEL_CREATE_INFO_EXT, 1000300000) \ + _(XR_TYPE_RENDER_MODEL_PROPERTIES_GET_INFO_EXT, 1000300001) \ + _(XR_TYPE_RENDER_MODEL_PROPERTIES_EXT, 1000300002) \ + _(XR_TYPE_RENDER_MODEL_SPACE_CREATE_INFO_EXT, 1000300003) \ + _(XR_TYPE_RENDER_MODEL_STATE_GET_INFO_EXT, 1000300004) \ + _(XR_TYPE_RENDER_MODEL_STATE_EXT, 1000300005) \ + _(XR_TYPE_RENDER_MODEL_ASSET_CREATE_INFO_EXT, 1000300006) \ + _(XR_TYPE_RENDER_MODEL_ASSET_DATA_GET_INFO_EXT, 1000300007) \ + _(XR_TYPE_RENDER_MODEL_ASSET_DATA_EXT, 1000300008) \ + _(XR_TYPE_RENDER_MODEL_ASSET_PROPERTIES_GET_INFO_EXT, 1000300009) \ + _(XR_TYPE_RENDER_MODEL_ASSET_PROPERTIES_EXT, 1000300010) \ + _(XR_TYPE_INTERACTION_RENDER_MODEL_IDS_ENUMERATE_INFO_EXT, 1000301000) \ + _(XR_TYPE_INTERACTION_RENDER_MODEL_SUBACTION_PATH_INFO_EXT, 1000301001) \ + _(XR_TYPE_EVENT_DATA_INTERACTION_RENDER_MODELS_CHANGED_EXT, 1000301002) \ + _(XR_TYPE_INTERACTION_RENDER_MODEL_TOP_LEVEL_USER_PATH_GET_INFO_EXT, 1000301003) \ + _(XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC, 1000317001) \ _(XR_TYPE_PASSTHROUGH_COLOR_HTC, 1000317002) \ _(XR_TYPE_PASSTHROUGH_MESH_TRANSFORM_INFO_HTC, 1000317003) \ _(XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_HTC, 1000317004) \ @@ -588,46 +628,51 @@ XR_ENUM_STR(XrResult); _(XR_TYPE_BODY_TRACKER_CREATE_INFO_BD, 1000385001) \ _(XR_TYPE_BODY_JOINTS_LOCATE_INFO_BD, 1000385002) \ _(XR_TYPE_BODY_JOINT_LOCATIONS_BD, 1000385003) \ - _(XR_TYPE_SYSTEM_BODY_TRACKING_PROPERTIES_BD, 1000385004) \ - _(XR_TYPE_SYSTEM_SPATIAL_SENSING_PROPERTIES_BD, 1000389000) \ - _(XR_TYPE_SPATIAL_ENTITY_COMPONENT_GET_INFO_BD, 1000389001) \ - _(XR_TYPE_SPATIAL_ENTITY_LOCATION_GET_INFO_BD, 1000389002) \ - _(XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_LOCATION_BD, 1000389003) \ - _(XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_SEMANTIC_BD, 1000389004) \ - _(XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_BOUNDING_BOX_2D_BD, 1000389005) \ - _(XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_POLYGON_BD, 1000389006) \ - _(XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_BOUNDING_BOX_3D_BD, 1000389007) \ - _(XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_TRIANGLE_MESH_BD, 1000389008) \ - _(XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_BD, 1000389009) \ - _(XR_TYPE_SENSE_DATA_PROVIDER_START_INFO_BD, 1000389010) \ - _(XR_TYPE_EVENT_DATA_SENSE_DATA_PROVIDER_STATE_CHANGED_BD, 1000389011) \ - _(XR_TYPE_EVENT_DATA_SENSE_DATA_UPDATED_BD, 1000389012) \ - _(XR_TYPE_SENSE_DATA_QUERY_INFO_BD, 1000389013) \ - _(XR_TYPE_SENSE_DATA_QUERY_COMPLETION_BD, 1000389014) \ - _(XR_TYPE_SENSE_DATA_FILTER_UUID_BD, 1000389015) \ - _(XR_TYPE_SENSE_DATA_FILTER_SEMANTIC_BD, 1000389016) \ - _(XR_TYPE_QUERIED_SENSE_DATA_GET_INFO_BD, 1000389017) \ - _(XR_TYPE_QUERIED_SENSE_DATA_BD, 1000389018) \ - _(XR_TYPE_SPATIAL_ENTITY_STATE_BD, 1000389019) \ - _(XR_TYPE_SPATIAL_ENTITY_ANCHOR_CREATE_INFO_BD, 1000389020) \ - _(XR_TYPE_ANCHOR_SPACE_CREATE_INFO_BD, 1000389021) \ - _(XR_TYPE_SYSTEM_SPATIAL_ANCHOR_PROPERTIES_BD, 1000390000) \ - _(XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_BD, 1000390001) \ - _(XR_TYPE_SPATIAL_ANCHOR_CREATE_COMPLETION_BD, 1000390002) \ - _(XR_TYPE_SPATIAL_ANCHOR_PERSIST_INFO_BD, 1000390003) \ - _(XR_TYPE_SPATIAL_ANCHOR_UNPERSIST_INFO_BD, 1000390004) \ - _(XR_TYPE_SYSTEM_SPATIAL_ANCHOR_SHARING_PROPERTIES_BD, 1000391000) \ - _(XR_TYPE_SPATIAL_ANCHOR_SHARE_INFO_BD, 1000391001) \ - _(XR_TYPE_SHARED_SPATIAL_ANCHOR_DOWNLOAD_INFO_BD, 1000391002) \ - _(XR_TYPE_SYSTEM_SPATIAL_SCENE_PROPERTIES_BD, 1000392000) \ - _(XR_TYPE_SCENE_CAPTURE_INFO_BD, 1000392001) \ - _(XR_TYPE_SYSTEM_SPATIAL_MESH_PROPERTIES_BD, 1000393000) \ - _(XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SPATIAL_MESH_BD, 1000393001) \ - _(XR_TYPE_FUTURE_POLL_RESULT_PROGRESS_BD, 1000394001) \ - _(XR_TYPE_SYSTEM_SPATIAL_PLANE_PROPERTIES_BD, 1000396000) \ - _(XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_PLANE_ORIENTATION_BD, 1000396001) \ - _(XR_TYPE_SENSE_DATA_FILTER_PLANE_ORIENTATION_BD, 1000396002) \ - _(XR_TYPE_HAND_TRACKING_DATA_SOURCE_INFO_EXT, 1000428000) \ + _(XR_TYPE_SYSTEM_BODY_TRACKING_PROPERTIES_BD, 1000385004) \ + _(XR_TYPE_SYSTEM_FACIAL_SIMULATION_PROPERTIES_BD, 1000386001) \ + _(XR_TYPE_FACE_TRACKER_CREATE_INFO_BD, 1000386002) \ + _(XR_TYPE_FACIAL_SIMULATION_DATA_GET_INFO_BD, 1000386003) \ + _(XR_TYPE_FACIAL_SIMULATION_DATA_BD, 1000386004) \ + _(XR_TYPE_LIP_EXPRESSION_DATA_BD, 1000386005) \ + _(XR_TYPE_SYSTEM_SPATIAL_SENSING_PROPERTIES_BD, 1000389000) \ + _(XR_TYPE_SPATIAL_ENTITY_COMPONENT_GET_INFO_BD, 1000389001) \ + _(XR_TYPE_SPATIAL_ENTITY_LOCATION_GET_INFO_BD, 1000389002) \ + _(XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_LOCATION_BD, 1000389003) \ + _(XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_SEMANTIC_BD, 1000389004) \ + _(XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_BOUNDING_BOX_2D_BD, 1000389005) \ + _(XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_POLYGON_BD, 1000389006) \ + _(XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_BOUNDING_BOX_3D_BD, 1000389007) \ + _(XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_TRIANGLE_MESH_BD, 1000389008) \ + _(XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_BD, 1000389009) \ + _(XR_TYPE_SENSE_DATA_PROVIDER_START_INFO_BD, 1000389010) \ + _(XR_TYPE_EVENT_DATA_SENSE_DATA_PROVIDER_STATE_CHANGED_BD, 1000389011) \ + _(XR_TYPE_EVENT_DATA_SENSE_DATA_UPDATED_BD, 1000389012) \ + _(XR_TYPE_SENSE_DATA_QUERY_INFO_BD, 1000389013) \ + _(XR_TYPE_SENSE_DATA_QUERY_COMPLETION_BD, 1000389014) \ + _(XR_TYPE_SENSE_DATA_FILTER_UUID_BD, 1000389015) \ + _(XR_TYPE_SENSE_DATA_FILTER_SEMANTIC_BD, 1000389016) \ + _(XR_TYPE_QUERIED_SENSE_DATA_GET_INFO_BD, 1000389017) \ + _(XR_TYPE_QUERIED_SENSE_DATA_BD, 1000389018) \ + _(XR_TYPE_SPATIAL_ENTITY_STATE_BD, 1000389019) \ + _(XR_TYPE_SPATIAL_ENTITY_ANCHOR_CREATE_INFO_BD, 1000389020) \ + _(XR_TYPE_ANCHOR_SPACE_CREATE_INFO_BD, 1000389021) \ + _(XR_TYPE_SYSTEM_SPATIAL_ANCHOR_PROPERTIES_BD, 1000390000) \ + _(XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_BD, 1000390001) \ + _(XR_TYPE_SPATIAL_ANCHOR_CREATE_COMPLETION_BD, 1000390002) \ + _(XR_TYPE_SPATIAL_ANCHOR_PERSIST_INFO_BD, 1000390003) \ + _(XR_TYPE_SPATIAL_ANCHOR_UNPERSIST_INFO_BD, 1000390004) \ + _(XR_TYPE_SYSTEM_SPATIAL_ANCHOR_SHARING_PROPERTIES_BD, 1000391000) \ + _(XR_TYPE_SPATIAL_ANCHOR_SHARE_INFO_BD, 1000391001) \ + _(XR_TYPE_SHARED_SPATIAL_ANCHOR_DOWNLOAD_INFO_BD, 1000391002) \ + _(XR_TYPE_SYSTEM_SPATIAL_SCENE_PROPERTIES_BD, 1000392000) \ + _(XR_TYPE_SCENE_CAPTURE_INFO_BD, 1000392001) \ + _(XR_TYPE_SYSTEM_SPATIAL_MESH_PROPERTIES_BD, 1000393000) \ + _(XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SPATIAL_MESH_BD, 1000393001) \ + _(XR_TYPE_FUTURE_POLL_RESULT_PROGRESS_BD, 1000394001) \ + _(XR_TYPE_SYSTEM_SPATIAL_PLANE_PROPERTIES_BD, 1000396000) \ + _(XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_PLANE_ORIENTATION_BD, 1000396001) \ + _(XR_TYPE_SENSE_DATA_FILTER_PLANE_ORIENTATION_BD, 1000396002) \ + _(XR_TYPE_HAND_TRACKING_DATA_SOURCE_INFO_EXT, 1000428000) \ _(XR_TYPE_HAND_TRACKING_DATA_SOURCE_STATE_EXT, 1000428001) \ _(XR_TYPE_PLANE_DETECTOR_CREATE_INFO_EXT, 1000429001) \ _(XR_TYPE_PLANE_DETECTOR_BEGIN_INFO_EXT, 1000429002) \ @@ -636,6 +681,25 @@ XR_ENUM_STR(XrResult); _(XR_TYPE_PLANE_DETECTOR_LOCATION_EXT, 1000429005) \ _(XR_TYPE_PLANE_DETECTOR_POLYGON_BUFFER_EXT, 1000429006) \ _(XR_TYPE_SYSTEM_PLANE_DETECTION_PROPERTIES_EXT, 1000429007) \ + _(XR_TYPE_TRACKABLE_GET_INFO_ANDROID, 1000455000) \ + _(XR_TYPE_ANCHOR_SPACE_CREATE_INFO_ANDROID, 1000455001) \ + _(XR_TYPE_TRACKABLE_PLANE_ANDROID, 1000455003) \ + _(XR_TYPE_TRACKABLE_TRACKER_CREATE_INFO_ANDROID, 1000455004) \ + _(XR_TYPE_SYSTEM_TRACKABLES_PROPERTIES_ANDROID, 1000455005) \ + _(XR_TYPE_PERSISTED_ANCHOR_SPACE_CREATE_INFO_ANDROID, 1000457001) \ + _(XR_TYPE_PERSISTED_ANCHOR_SPACE_INFO_ANDROID, 1000457002) \ + _(XR_TYPE_DEVICE_ANCHOR_PERSISTENCE_CREATE_INFO_ANDROID, 1000457003) \ + _(XR_TYPE_SYSTEM_DEVICE_ANCHOR_PERSISTENCE_PROPERTIES_ANDROID, 1000457004) \ + _(XR_TYPE_FACE_TRACKER_CREATE_INFO_ANDROID, 1000458000) \ + _(XR_TYPE_FACE_STATE_GET_INFO_ANDROID, 1000458001) \ + _(XR_TYPE_FACE_STATE_ANDROID, 1000458002) \ + _(XR_TYPE_SYSTEM_FACE_TRACKING_PROPERTIES_ANDROID, 1000458003) \ + _(XR_TYPE_PASSTHROUGH_CAMERA_STATE_GET_INFO_ANDROID, 1000460000) \ + _(XR_TYPE_SYSTEM_PASSTHROUGH_CAMERA_STATE_PROPERTIES_ANDROID, 1000460001) \ + _(XR_TYPE_RAYCAST_INFO_ANDROID, 1000463000) \ + _(XR_TYPE_RAYCAST_HIT_RESULTS_ANDROID, 1000463001) \ + _(XR_TYPE_TRACKABLE_OBJECT_ANDROID, 1000466000) \ + _(XR_TYPE_TRACKABLE_OBJECT_CONFIGURATION_ANDROID, 1000466001) \ _(XR_TYPE_FUTURE_CANCEL_INFO_EXT, 1000469000) \ _(XR_TYPE_FUTURE_POLL_INFO_EXT, 1000469001) \ _(XR_TYPE_FUTURE_COMPLETION_EXT, 1000469002) \ @@ -658,11 +722,11 @@ XR_ENUM_STR(XrResult); _(XR_TYPE_SYSTEM_FACIAL_EXPRESSION_PROPERTIES_ML, 1000482004) \ _(XR_TYPE_FACIAL_EXPRESSION_CLIENT_CREATE_INFO_ML, 1000482005) \ _(XR_TYPE_FACIAL_EXPRESSION_BLEND_SHAPE_GET_INFO_ML, 1000482006) \ - _(XR_TYPE_FACIAL_EXPRESSION_BLEND_SHAPE_PROPERTIES_ML, 1000482007) \ - _(XR_TYPE_SYSTEM_SIMULTANEOUS_HANDS_AND_CONTROLLERS_PROPERTIES_META, 1000532001) \ - _(XR_TYPE_SIMULTANEOUS_HANDS_AND_CONTROLLERS_TRACKING_RESUME_INFO_META, 1000532002) \ - _(XR_TYPE_SIMULTANEOUS_HANDS_AND_CONTROLLERS_TRACKING_PAUSE_INFO_META, 1000532003) \ - _(XR_TYPE_COLOCATION_DISCOVERY_START_INFO_META, 1000571010) \ + _(XR_TYPE_FACIAL_EXPRESSION_BLEND_SHAPE_PROPERTIES_ML, 1000482007) \ + _(XR_TYPE_SYSTEM_SIMULTANEOUS_HANDS_AND_CONTROLLERS_PROPERTIES_META, 1000532001) \ + _(XR_TYPE_SIMULTANEOUS_HANDS_AND_CONTROLLERS_TRACKING_RESUME_INFO_META, 1000532002) \ + _(XR_TYPE_SIMULTANEOUS_HANDS_AND_CONTROLLERS_TRACKING_PAUSE_INFO_META, 1000532003) \ + _(XR_TYPE_COLOCATION_DISCOVERY_START_INFO_META, 1000571010) \ _(XR_TYPE_COLOCATION_DISCOVERY_STOP_INFO_META, 1000571011) \ _(XR_TYPE_COLOCATION_ADVERTISEMENT_START_INFO_META, 1000571012) \ _(XR_TYPE_COLOCATION_ADVERTISEMENT_STOP_INFO_META, 1000571013) \ @@ -676,37 +740,43 @@ XR_ENUM_STR(XrResult); _(XR_TYPE_SYSTEM_COLOCATION_DISCOVERY_PROPERTIES_META, 1000571030) \ _(XR_TYPE_SHARE_SPACES_RECIPIENT_GROUPS_META, 1000572000) \ _(XR_TYPE_SPACE_GROUP_UUID_FILTER_INFO_META, 1000572001) \ - _(XR_TYPE_SYSTEM_SPATIAL_ENTITY_GROUP_SHARING_PROPERTIES_META, 1000572100) \ - _(XR_TYPE_SPATIAL_CAPABILITY_COMPONENT_TYPES_EXT, 1000740000) \ - _(XR_TYPE_SPATIAL_CONTEXT_CREATE_INFO_EXT, 1000740001) \ - _(XR_TYPE_CREATE_SPATIAL_CONTEXT_COMPLETION_EXT, 1000740002) \ - _(XR_TYPE_SPATIAL_DISCOVERY_SNAPSHOT_CREATE_INFO_EXT, 1000740003) \ - _(XR_TYPE_CREATE_SPATIAL_DISCOVERY_SNAPSHOT_COMPLETION_INFO_EXT, 1000740004) \ - _(XR_TYPE_CREATE_SPATIAL_DISCOVERY_SNAPSHOT_COMPLETION_EXT, 1000740005) \ - _(XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_CONDITION_EXT, 1000740006) \ - _(XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_RESULT_EXT, 1000740007) \ - _(XR_TYPE_SPATIAL_BUFFER_GET_INFO_EXT, 1000740008) \ - _(XR_TYPE_SPATIAL_COMPONENT_BOUNDED_2D_LIST_EXT, 1000740009) \ - _(XR_TYPE_SPATIAL_COMPONENT_BOUNDED_3D_LIST_EXT, 1000740010) \ - _(XR_TYPE_SPATIAL_COMPONENT_PARENT_LIST_EXT, 1000740011) \ - _(XR_TYPE_SPATIAL_COMPONENT_MESH_3D_LIST_EXT, 1000740012) \ - _(XR_TYPE_SPATIAL_ENTITY_FROM_ID_CREATE_INFO_EXT, 1000740013) \ - _(XR_TYPE_SPATIAL_UPDATE_SNAPSHOT_CREATE_INFO_EXT, 1000740014) \ - _(XR_TYPE_EVENT_DATA_SPATIAL_DISCOVERY_RECOMMENDED_EXT, 1000740015) \ - _(XR_TYPE_SPATIAL_FILTER_TRACKING_STATE_EXT, 1000740016) \ - _(XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_PLANE_TRACKING_EXT, 1000741000) \ - _(XR_TYPE_SPATIAL_COMPONENT_PLANE_ALIGNMENT_LIST_EXT, 1000741001) \ - _(XR_TYPE_SPATIAL_COMPONENT_MESH_2D_LIST_EXT, 1000741002) \ - _(XR_TYPE_SPATIAL_COMPONENT_POLYGON_2D_LIST_EXT, 1000741003) \ - _(XR_TYPE_SPATIAL_COMPONENT_PLANE_SEMANTIC_LABEL_LIST_EXT, 1000741004) \ - _(XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_QR_CODE_EXT, 1000743000) \ - _(XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_MICRO_QR_CODE_EXT, 1000743001) \ - _(XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_ARUCO_MARKER_EXT, 1000743002) \ - _(XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_APRIL_TAG_EXT, 1000743003) \ - _(XR_TYPE_SPATIAL_MARKER_SIZE_EXT, 1000743004) \ - _(XR_TYPE_SPATIAL_MARKER_STATIC_OPTIMIZATION_EXT, 1000743005) \ - _(XR_TYPE_SPATIAL_COMPONENT_MARKER_LIST_EXT, 1000743006) \ - _(XR_TYPE_EVENT_DATA_MRC_STATUS_CHANGED_PICO, 1010000000) \ + _(XR_TYPE_SYSTEM_SPATIAL_ENTITY_GROUP_SHARING_PROPERTIES_META, 1000572100) \ + _(XR_TYPE_ANCHOR_SHARING_INFO_ANDROID, 1000701000) \ + _(XR_TYPE_ANCHOR_SHARING_TOKEN_ANDROID, 1000701001) \ + _(XR_TYPE_SYSTEM_ANCHOR_SHARING_EXPORT_PROPERTIES_ANDROID, 1000701002) \ + _(XR_TYPE_SYSTEM_MARKER_TRACKING_PROPERTIES_ANDROID, 1000707000) \ + _(XR_TYPE_TRACKABLE_MARKER_CONFIGURATION_ANDROID, 1000707001) \ + _(XR_TYPE_TRACKABLE_MARKER_ANDROID, 1000707002) \ + _(XR_TYPE_SPATIAL_CAPABILITY_COMPONENT_TYPES_EXT, 1000740000) \ + _(XR_TYPE_SPATIAL_CONTEXT_CREATE_INFO_EXT, 1000740001) \ + _(XR_TYPE_CREATE_SPATIAL_CONTEXT_COMPLETION_EXT, 1000740002) \ + _(XR_TYPE_SPATIAL_DISCOVERY_SNAPSHOT_CREATE_INFO_EXT, 1000740003) \ + _(XR_TYPE_CREATE_SPATIAL_DISCOVERY_SNAPSHOT_COMPLETION_INFO_EXT, 1000740004) \ + _(XR_TYPE_CREATE_SPATIAL_DISCOVERY_SNAPSHOT_COMPLETION_EXT, 1000740005) \ + _(XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_CONDITION_EXT, 1000740006) \ + _(XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_RESULT_EXT, 1000740007) \ + _(XR_TYPE_SPATIAL_BUFFER_GET_INFO_EXT, 1000740008) \ + _(XR_TYPE_SPATIAL_COMPONENT_BOUNDED_2D_LIST_EXT, 1000740009) \ + _(XR_TYPE_SPATIAL_COMPONENT_BOUNDED_3D_LIST_EXT, 1000740010) \ + _(XR_TYPE_SPATIAL_COMPONENT_PARENT_LIST_EXT, 1000740011) \ + _(XR_TYPE_SPATIAL_COMPONENT_MESH_3D_LIST_EXT, 1000740012) \ + _(XR_TYPE_SPATIAL_ENTITY_FROM_ID_CREATE_INFO_EXT, 1000740013) \ + _(XR_TYPE_SPATIAL_UPDATE_SNAPSHOT_CREATE_INFO_EXT, 1000740014) \ + _(XR_TYPE_EVENT_DATA_SPATIAL_DISCOVERY_RECOMMENDED_EXT, 1000740015) \ + _(XR_TYPE_SPATIAL_FILTER_TRACKING_STATE_EXT, 1000740016) \ + _(XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_PLANE_TRACKING_EXT, 1000741000) \ + _(XR_TYPE_SPATIAL_COMPONENT_PLANE_ALIGNMENT_LIST_EXT, 1000741001) \ + _(XR_TYPE_SPATIAL_COMPONENT_MESH_2D_LIST_EXT, 1000741002) \ + _(XR_TYPE_SPATIAL_COMPONENT_POLYGON_2D_LIST_EXT, 1000741003) \ + _(XR_TYPE_SPATIAL_COMPONENT_PLANE_SEMANTIC_LABEL_LIST_EXT, 1000741004) \ + _(XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_QR_CODE_EXT, 1000743000) \ + _(XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_MICRO_QR_CODE_EXT, 1000743001) \ + _(XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_ARUCO_MARKER_EXT, 1000743002) \ + _(XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_APRIL_TAG_EXT, 1000743003) \ + _(XR_TYPE_SPATIAL_MARKER_SIZE_EXT, 1000743004) \ + _(XR_TYPE_SPATIAL_MARKER_STATIC_OPTIMIZATION_EXT, 1000743005) \ + _(XR_TYPE_SPATIAL_COMPONENT_MARKER_LIST_EXT, 1000743006) \ + _(XR_TYPE_EVENT_DATA_MRC_STATUS_CHANGED_PICO, 1010000000) \ _(XR_TYPE_MRC_SPACE_CREATE_INFO_PICO, 1010000001) \ _(XR_TYPE_EXTERNAL_CAMERA_PARAMETER_PICO, 1010000002) \ _(XR_TYPE_VIRTUAL_BOUNDARY_INFO_PICO, 1010001001) \ @@ -723,10 +793,14 @@ XR_ENUM_STR(XrResult); _(XR_TYPE_EVENT_DATA_MOTION_TRACKER_POWER_KEY_EVENT_PICO, 1010002006) \ _(XR_TYPE_COMPOSITION_LAYER_FISHEYE_PICO, 1010003000) \ _(XR_TYPE_LAYER_SETTINGS_PICO, 1010004000) \ - _(XR_TYPE_EYE_TRACKER_CREATE_INFO_PICO, 1010006000) \ - _(XR_TYPE_EYE_TRACKER_DATA_INFO_PICO, 1010006001) \ - _(XR_TYPE_EYE_TRACKER_DATA_PICO, 1010006002) \ - _(XR_TYPE_EYE_DATA_PICO, 1010006003) \ + _(XR_TYPE_EYE_TRACKER_CREATE_INFO_PICO, 1010006001) \ + _(XR_TYPE_EYE_TRACKER_DATA_INFO_PICO, 1010006002) \ + _(XR_TYPE_EYE_TRACKER_DATA_PICO, 1010006003) \ + _(XR_TYPE_EYE_DATA_PICO, 1010006004) \ + _(XR_TYPE_EYE_TRACKER_GAZE_INFO_PICO, 1010006005) \ + _(XR_TYPE_EYE_TRACKER_GAZE_PICO, 1010006006) \ + _(XR_TYPE_EYE_GAZE_PICO, 1010006007) \ + _(XR_TYPE_EYE_TRACKER_GAZE_DEPTH_PICO, 1010006008) \ _(XR_TYPE_SECURE_MR_FRAMEWORK_CREATE_INFO_PICO, 1010007000) \ _(XR_TYPE_SECURE_MR_PIPELINE_CREATE_INFO_PICO, 1010007001) \ _(XR_TYPE_SECURE_MR_OPERATOR_BASE_HEADER_PICO, 1010007002) \ @@ -748,6 +822,7 @@ XR_ENUM_STR(XrResult); _(XR_TYPE_SECURE_MR_OPERATOR_IO_MAP_PICO, 1010007018) \ _(XR_TYPE_SECURE_MR_OPERATOR_SORT_MATRIX_PICO, 1010007019) \ _(XR_TYPE_SECURE_MR_OPERATOR_COLOR_CONVERT_PICO, 1010007020) \ + _(XR_TYPE_SECURE_MR_OPERATOR_JAVASCRIPT_PICO, 1010007021) \ _(XR_TYPE_EXPAND_DEVICE_MOTOR_VIBRATE_PICO, 1010008000) \ _(XR_TYPE_EXPAND_DEVICE_CUSTOM_DATA_PICO, 1010008001) \ _(XR_TYPE_EXPAND_DEVICE_BATTERY_STATE_PICO, 1010008002) \ @@ -759,77 +834,112 @@ XR_ENUM_STR(XrResult); _(XR_TYPE_BODY_JOINT_VELOCITIES_PICO, 1010009003) \ _(XR_TYPE_BODY_JOINT_ACCELERATIONS_PICO, 1010009004) \ _(XR_TYPE_BODY_TRACKING_STATE_PICO, 1010009005) \ - _(XR_TYPE_SYMMETRIC_FOV_REPROJECTION_VIEW_CONFIGURATION_PICO, 1010016000) \ - _(XR_TYPE_SYSTEM_DYNAMIC_OBJECT_TRACKING_PROPERTIES_PICO, 1010017000) \ - _(XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_DYNAMIC_OBJECT_PICO, 1010017001) \ - _(XR_TYPE_SPATIAL_ENTITY_DYNAMIC_OBJECT_GET_INFO_PICO, 1010017002) \ - _(XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_DYNAMIC_OBJECT_PICO, 1010017003) \ - _(XR_TYPE_DYNAMIC_OBJECT_DATA_PICO, 1010017004) \ - _(XR_TYPE_SENSE_DATA_FILTER_DYNAMIC_OBJECT_TYPE_PICO, 1010017005) \ - _(XR_TYPE_SYSTEM_DYNAMIC_OBJECT_KEYBOARD_PROPERTIES_PICO, 1010018000) \ - _(XR_TYPE_SYSTEM_DYNAMIC_OBJECT_MOUSE_PROPERTIES_PICO, 1010019000) \ - _(XR_TYPE_LAYER_COLOR_MATRIX_PICO, 1010026000) \ - _(XR_TYPE_SYSTEM_SPATIAL_SENSING_PROPERTIES_PICO, 1200389000) \ - _(XR_TYPE_SPATIAL_ENTITY_LOCATION_GET_INFO_PICO, 1200389002) \ - _(XR_TYPE_SPATIAL_ENTITY_LOCATION_DATA_PICO, 1200389003) \ - _(XR_TYPE_SPATIAL_ENTITY_SEMANTIC_GET_INFO_PICO, 1200389004) \ - _(XR_TYPE_SPATIAL_ENTITY_SEMANTIC_DATA_PICO, 1200389005) \ - _(XR_TYPE_SPATIAL_ENTITY_BOUNDING_BOX_2D_GET_INFO_PICO, 1200389006) \ - _(XR_TYPE_SPATIAL_ENTITY_BOUNDING_BOX_2D_DATA_PICO, 1200389007) \ - _(XR_TYPE_SPATIAL_ENTITY_POLYGON_GET_INFO_PICO, 1200389008) \ - _(XR_TYPE_SPATIAL_ENTITY_POLYGON_DATA_PICO, 1200389009) \ - _(XR_TYPE_SPATIAL_ENTITY_BOUNDING_BOX_3D_GET_INFO_PICO, 1200389010) \ - _(XR_TYPE_SPATIAL_ENTITY_BOUNDING_BOX_3D_DATA_PICO, 1200389011) \ - _(XR_TYPE_SPATIAL_ENTITY_TRIANGLE_MESH_GET_INFO_PICO, 1200389012) \ - _(XR_TYPE_SPATIAL_ENTITY_TRIANGLE_MESH_DATA_PICO, 1200389013) \ - _(XR_TYPE_SENSE_DATA_PROVIDER_START_COMPLETION_PICO, 1200389014) \ - _(XR_TYPE_EVENT_DATA_SENSE_DATA_PROVIDER_STATE_CHANGED_PICO, 1200389015) \ - _(XR_TYPE_SENSE_DATA_FILTER_UUID_PICO, 1200389016) \ - _(XR_TYPE_SENSE_DATA_FILTER_SEMANTIC_PICO, 1200389017) \ - _(XR_TYPE_SENSE_DATA_QUERY_INFO_PICO, 1200389018) \ - _(XR_TYPE_SENSE_DATA_QUERY_COMPLETION_PICO, 1200389019) \ - _(XR_TYPE_QUERIED_SENSE_DATA_GET_INFO_PICO, 1200389020) \ - _(XR_TYPE_SPATIAL_ENTITY_STATE_PICO, 1200389021) \ - _(XR_TYPE_QUERIED_SENSE_DATA_PICO, 1200389022) \ - _(XR_TYPE_EVENT_DATA_SENSE_DATA_UPDATED_PICO, 1200389023) \ - _(XR_TYPE_SENSE_DATA_PROVIDER_START_INFO_PICO, 1200389024) \ - _(XR_TYPE_SPATIAL_ENTITY_ANCHOR_RETRIEVE_INFO_PICO, 1200389025) \ - _(XR_TYPE_ANCHOR_LOCATE_INFO_PICO, 1200389026) \ - _(XR_TYPE_FUTURE_POLL_RESULT_PROGRESS_PICO, 1200389028) \ - _(XR_TYPE_SPATIAL_ENTITY_SPHERE_GET_INFO_PICO, 1200389029) \ - _(XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_SPHERE_PICO, 1200389030) \ - _(XR_TYPE_SYSTEM_SPATIAL_ANCHOR_PROPERTIES_PICO, 1200390000) \ - _(XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SPATIAL_ANCHOR_PICO, 1200390001) \ - _(XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_PICO, 1200390002) \ - _(XR_TYPE_SPATIAL_ANCHOR_CREATE_COMPLETION_PICO, 1200390003) \ - _(XR_TYPE_SPATIAL_ANCHOR_PERSIST_INFO_PICO, 1200390004) \ - _(XR_TYPE_SPATIAL_ANCHOR_PERSIST_COMPLETION_PICO, 1200390005) \ - _(XR_TYPE_SPATIAL_ANCHOR_UNPERSIST_INFO_PICO, 1200390006) \ - _(XR_TYPE_SPATIAL_ANCHOR_UNPERSIST_COMPLETION_PICO, 1200390007) \ - _(XR_TYPE_SYSTEM_SPATIAL_ANCHOR_SHARING_PROPERTIES_PICO, 1200391000) \ - _(XR_TYPE_SPATIAL_ANCHOR_SHARE_INFO_PICO, 1200391001) \ - _(XR_TYPE_SPATIAL_ANCHOR_SHARE_COMPLETION_PICO, 1200391002) \ - _(XR_TYPE_SPATIAL_ANCHOR_DOWNLOAD_INFO_PICO, 1200391003) \ - _(XR_TYPE_SPATIAL_ANCHOR_DOWNLOAD_COMPLETION_PICO, 1200391004) \ - _(XR_TYPE_SYSTEM_SCENE_CAPTURE_PROPERTIES_PICO, 1200392000) \ - _(XR_TYPE_SCENE_CAPTURE_START_INFO_PICO, 1200392001) \ - _(XR_TYPE_SCENE_CAPTURE_START_COMPLETION_PICO, 1200392002) \ - _(XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SCENE_CAPTURE_PICO, 1200392003) \ - _(XR_TYPE_SYSTEM_SPATIAL_MESH_PROPERTIES_PICO, 1200393000) \ - _(XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SPATIAL_MESH_PICO, 1200393001) \ - _(XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_ANCHOR_EXT, 1000762000) \ - _(XR_TYPE_SPATIAL_COMPONENT_ANCHOR_LIST_EXT, 1000762001) \ - _(XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_EXT, 1000762002) \ - _(XR_TYPE_SPATIAL_PERSISTENCE_CONTEXT_CREATE_INFO_EXT, 1000763000) \ - _(XR_TYPE_CREATE_SPATIAL_PERSISTENCE_CONTEXT_COMPLETION_EXT, 1000763001) \ - _(XR_TYPE_SPATIAL_CONTEXT_PERSISTENCE_CONFIG_EXT, 1000763002) \ - _(XR_TYPE_SPATIAL_DISCOVERY_PERSISTENCE_UUID_FILTER_EXT, 1000763003) \ - _(XR_TYPE_SPATIAL_COMPONENT_PERSISTENCE_LIST_EXT, 1000763004) \ - _(XR_TYPE_SPATIAL_ENTITY_PERSIST_INFO_EXT, 1000781000) \ - _(XR_TYPE_PERSIST_SPATIAL_ENTITY_COMPLETION_EXT, 1000781001) \ - _(XR_TYPE_SPATIAL_ENTITY_UNPERSIST_INFO_EXT, 1000781002) \ - _(XR_TYPE_UNPERSIST_SPATIAL_ENTITY_COMPLETION_EXT, 1000781003) \ - _(XR_STRUCTURE_TYPE_MAX_ENUM, 0x7FFFFFFF) + _(XR_TYPE_SYMMETRIC_FOV_REPROJECTION_VIEW_CONFIGURATION_PICO, 1010016000) \ + _(XR_TYPE_SYSTEM_DYNAMIC_OBJECT_TRACKING_PROPERTIES_PICO, 1010017000) \ + _(XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_DYNAMIC_OBJECT_PICO, 1010017001) \ + _(XR_TYPE_SPATIAL_ENTITY_DYNAMIC_OBJECT_GET_INFO_PICO, 1010017002) \ + _(XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_DYNAMIC_OBJECT_PICO, 1010017003) \ + _(XR_TYPE_DYNAMIC_OBJECT_DATA_PICO, 1010017004) \ + _(XR_TYPE_SENSE_DATA_FILTER_DYNAMIC_OBJECT_TYPE_PICO, 1010017005) \ + _(XR_TYPE_SYSTEM_DYNAMIC_OBJECT_KEYBOARD_PROPERTIES_PICO, 1010018000) \ + _(XR_TYPE_SYSTEM_DYNAMIC_OBJECT_MOUSE_PROPERTIES_PICO, 1010019000) \ + _(XR_TYPE_LAYER_COLOR_MATRIX_PICO, 1010026000) \ + _(XR_TYPE_AVAILABLE_CAMERAS_ENUMERATE_INFO_PICO, 1010033000) \ + _(XR_TYPE_CAMERA_PROPERTIES_GET_INFO_PICO, 1010033001) \ + _(XR_TYPE_CAMERA_PROPERTIES_PICO, 1010033002) \ + _(XR_TYPE_CAMERA_PROPERTY_FACING_PICO, 1010033003) \ + _(XR_TYPE_CAMERA_PROPERTY_POSITION_PICO, 1010033004) \ + _(XR_TYPE_CAMERA_PROPERTY_CAMERA_TYPE_PICO, 1010033005) \ + _(XR_TYPE_CAMERA_SUPPORTED_CAPABILITIES_GET_INFO_PICO, 1010033006) \ + _(XR_TYPE_CAMERA_SUPPORTED_CAPABILITIES_PICO, 1010033007) \ + _(XR_TYPE_CAMERA_SUPPORTED_CAPABILITY_IMAGE_RESOLUTION_PICO, 1010033008) \ + _(XR_TYPE_CAMERA_CAPABILITY_IMAGE_RESOLUTION_PICO, 1010033009) \ + _(XR_TYPE_CAMERA_SUPPORTED_CAPABILITY_DATA_TRANSFER_TYPE_PICO, 1010033010) \ + _(XR_TYPE_CAMERA_CAPABILITY_DATA_TRANSFER_TYPE_PICO, 1010033011) \ + _(XR_TYPE_CAMERA_SUPPORTED_CAPABILITY_IMAGE_FORMAT_PICO, 1010033012) \ + _(XR_TYPE_CAMERA_CAPABILITY_IMAGE_FORMAT_PICO, 1010033013) \ + _(XR_TYPE_CAMERA_SUPPORTED_CAPABILITY_CAMERA_MODEL_PICO, 1010033014) \ + _(XR_TYPE_CAMERA_CAPABILITY_CAMERA_MODEL_PICO, 1010033015) \ + _(XR_TYPE_CAMERA_SUPPORTED_CAPABILITY_IMAGE_FPS_PICO, 1010033016) \ + _(XR_TYPE_CAMERA_CAPABILITY_IMAGE_FPS_PICO, 1010033017) \ + _(XR_TYPE_CAMERA_DEVICE_CREATE_INFO_PICO, 1010033018) \ + _(XR_TYPE_CREATE_CAMERA_DEVICE_COMPLETION_PICO, 1010033019) \ + _(XR_TYPE_CAMERA_CAPTURE_SESSION_CREATE_INFO_PICO, 1010033020) \ + _(XR_TYPE_CREATE_CAMERA_CAPTURE_SESSION_COMPLETION_PICO, 1010033021) \ + _(XR_TYPE_CAMERA_INTRINSICS_PICO, 1010033022) \ + _(XR_TYPE_CAMERA_EXTRINSICS_PICO, 1010033023) \ + _(XR_TYPE_CAMERA_CAPTURE_BEGIN_INFO_PICO, 1010033024) \ + _(XR_TYPE_CAMERA_IMAGE_ACQUIRE_INFO_PICO, 1010033025) \ + _(XR_TYPE_CAMERA_IMAGE_PICO, 1010033026) \ + _(XR_TYPE_CAMERA_IMAGE_DATA_RAW_BUFFER_PICO, 1010033027) \ + _(XR_TYPE_CAMERA_CAPABILITIES_PICO, 1010033028) \ + _(XR_TYPE_READBACK_TENSOR_BUFFER_PICO, 1010027000) \ + _(XR_TYPE_CREATE_BUFFER_FROM_GLOBAL_TENSOR_COMPLETION_PICO, 1010027001) \ + _(XR_TYPE_CREATE_TEXTURE_FROM_GLOBAL_TENSOR_COMPLETION_PICO, 1010027002) \ + _(XR_TYPE_READBACK_TEXTURE_IMAGE_VULKAN_PICO, 1010028000) \ + _(XR_TYPE_READBACK_TEXTURE_IMAGE_OPENGL_PICO, 1010029000) \ + _(XR_TYPE_SYSTEM_SPATIAL_SENSING_PROPERTIES_PICO, 1200389000) \ + _(XR_TYPE_SPATIAL_ENTITY_LOCATION_GET_INFO_PICO, 1200389002) \ + _(XR_TYPE_SPATIAL_ENTITY_LOCATION_DATA_PICO, 1200389003) \ + _(XR_TYPE_SPATIAL_ENTITY_SEMANTIC_GET_INFO_PICO, 1200389004) \ + _(XR_TYPE_SPATIAL_ENTITY_SEMANTIC_DATA_PICO, 1200389005) \ + _(XR_TYPE_SPATIAL_ENTITY_BOUNDING_BOX_2D_GET_INFO_PICO, 1200389006) \ + _(XR_TYPE_SPATIAL_ENTITY_BOUNDING_BOX_2D_DATA_PICO, 1200389007) \ + _(XR_TYPE_SPATIAL_ENTITY_POLYGON_GET_INFO_PICO, 1200389008) \ + _(XR_TYPE_SPATIAL_ENTITY_POLYGON_DATA_PICO, 1200389009) \ + _(XR_TYPE_SPATIAL_ENTITY_BOUNDING_BOX_3D_GET_INFO_PICO, 1200389010) \ + _(XR_TYPE_SPATIAL_ENTITY_BOUNDING_BOX_3D_DATA_PICO, 1200389011) \ + _(XR_TYPE_SPATIAL_ENTITY_TRIANGLE_MESH_GET_INFO_PICO, 1200389012) \ + _(XR_TYPE_SPATIAL_ENTITY_TRIANGLE_MESH_DATA_PICO, 1200389013) \ + _(XR_TYPE_SENSE_DATA_PROVIDER_START_COMPLETION_PICO, 1200389014) \ + _(XR_TYPE_EVENT_DATA_SENSE_DATA_PROVIDER_STATE_CHANGED_PICO, 1200389015) \ + _(XR_TYPE_SENSE_DATA_FILTER_UUID_PICO, 1200389016) \ + _(XR_TYPE_SENSE_DATA_FILTER_SEMANTIC_PICO, 1200389017) \ + _(XR_TYPE_SENSE_DATA_QUERY_INFO_PICO, 1200389018) \ + _(XR_TYPE_SENSE_DATA_QUERY_COMPLETION_PICO, 1200389019) \ + _(XR_TYPE_QUERIED_SENSE_DATA_GET_INFO_PICO, 1200389020) \ + _(XR_TYPE_SPATIAL_ENTITY_STATE_PICO, 1200389021) \ + _(XR_TYPE_QUERIED_SENSE_DATA_PICO, 1200389022) \ + _(XR_TYPE_EVENT_DATA_SENSE_DATA_UPDATED_PICO, 1200389023) \ + _(XR_TYPE_SENSE_DATA_PROVIDER_START_INFO_PICO, 1200389024) \ + _(XR_TYPE_SPATIAL_ENTITY_ANCHOR_RETRIEVE_INFO_PICO, 1200389025) \ + _(XR_TYPE_ANCHOR_LOCATE_INFO_PICO, 1200389026) \ + _(XR_TYPE_FUTURE_POLL_RESULT_PROGRESS_PICO, 1200389028) \ + _(XR_TYPE_SPATIAL_ENTITY_SPHERE_GET_INFO_PICO, 1200389029) \ + _(XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_SPHERE_PICO, 1200389030) \ + _(XR_TYPE_SYSTEM_SPATIAL_ANCHOR_PROPERTIES_PICO, 1200390000) \ + _(XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SPATIAL_ANCHOR_PICO, 1200390001) \ + _(XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_PICO, 1200390002) \ + _(XR_TYPE_SPATIAL_ANCHOR_CREATE_COMPLETION_PICO, 1200390003) \ + _(XR_TYPE_SPATIAL_ANCHOR_PERSIST_INFO_PICO, 1200390004) \ + _(XR_TYPE_SPATIAL_ANCHOR_PERSIST_COMPLETION_PICO, 1200390005) \ + _(XR_TYPE_SPATIAL_ANCHOR_UNPERSIST_INFO_PICO, 1200390006) \ + _(XR_TYPE_SPATIAL_ANCHOR_UNPERSIST_COMPLETION_PICO, 1200390007) \ + _(XR_TYPE_SYSTEM_SPATIAL_ANCHOR_SHARING_PROPERTIES_PICO, 1200391000) \ + _(XR_TYPE_SPATIAL_ANCHOR_SHARE_INFO_PICO, 1200391001) \ + _(XR_TYPE_SPATIAL_ANCHOR_SHARE_COMPLETION_PICO, 1200391002) \ + _(XR_TYPE_SHARED_SPATIAL_ANCHOR_DOWNLOAD_INFO_PICO, 1200391003) \ + _(XR_TYPE_SHARED_SPATIAL_ANCHOR_DOWNLOAD_COMPLETION_PICO, 1200391004) \ + _(XR_TYPE_SYSTEM_SCENE_CAPTURE_PROPERTIES_PICO, 1200392000) \ + _(XR_TYPE_SCENE_CAPTURE_START_INFO_PICO, 1200392001) \ + _(XR_TYPE_SCENE_CAPTURE_START_COMPLETION_PICO, 1200392002) \ + _(XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SCENE_CAPTURE_PICO, 1200392003) \ + _(XR_TYPE_SYSTEM_SPATIAL_MESH_PROPERTIES_PICO, 1200393000) \ + _(XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SPATIAL_MESH_PICO, 1200393001) \ + _(XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_ANCHOR_EXT, 1000762000) \ + _(XR_TYPE_SPATIAL_COMPONENT_ANCHOR_LIST_EXT, 1000762001) \ + _(XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_EXT, 1000762002) \ + _(XR_TYPE_SPATIAL_PERSISTENCE_CONTEXT_CREATE_INFO_EXT, 1000763000) \ + _(XR_TYPE_CREATE_SPATIAL_PERSISTENCE_CONTEXT_COMPLETION_EXT, 1000763001) \ + _(XR_TYPE_SPATIAL_CONTEXT_PERSISTENCE_CONFIG_EXT, 1000763002) \ + _(XR_TYPE_SPATIAL_DISCOVERY_PERSISTENCE_UUID_FILTER_EXT, 1000763003) \ + _(XR_TYPE_SPATIAL_COMPONENT_PERSISTENCE_LIST_EXT, 1000763004) \ + _(XR_TYPE_SPATIAL_ENTITY_PERSIST_INFO_EXT, 1000781000) \ + _(XR_TYPE_PERSIST_SPATIAL_ENTITY_COMPLETION_EXT, 1000781001) \ + _(XR_TYPE_SPATIAL_ENTITY_UNPERSIST_INFO_EXT, 1000781002) \ + _(XR_TYPE_UNPERSIST_SPATIAL_ENTITY_COMPLETION_EXT, 1000781003) \ + _(XR_TYPE_LOADER_INIT_INFO_PROPERTIES_EXT, 1000838000) \ + _(XR_STRUCTURE_TYPE_MAX_ENUM, 0x7FFFFFFF) #define XR_LIST_ENUM_XrFormFactor(_) \ _(XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY, 1) \ @@ -919,30 +1029,37 @@ XR_ENUM_STR(XrResult); _(XR_OBJECT_TYPE_PASSTHROUGH_COLOR_LUT_META, 1000266000) \ _(XR_OBJECT_TYPE_FACE_TRACKER2_FB, 1000287012) \ _(XR_OBJECT_TYPE_ENVIRONMENT_DEPTH_PROVIDER_META, 1000291000) \ - _(XR_OBJECT_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_META, 1000291001) \ - _(XR_OBJECT_TYPE_RENDER_MODEL_EXT, 1000300000) \ - _(XR_OBJECT_TYPE_RENDER_MODEL_ASSET_EXT, 1000300001) \ - _(XR_OBJECT_TYPE_PASSTHROUGH_HTC, 1000317000) \ + _(XR_OBJECT_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_META, 1000291001) \ + _(XR_OBJECT_TYPE_RENDER_MODEL_EXT, 1000300000) \ + _(XR_OBJECT_TYPE_RENDER_MODEL_ASSET_EXT, 1000300001) \ + _(XR_OBJECT_TYPE_PASSTHROUGH_HTC, 1000317000) \ _(XR_OBJECT_TYPE_BODY_TRACKER_HTC, 1000320000) \ - _(XR_OBJECT_TYPE_BODY_TRACKER_BD, 1000385000) \ - _(XR_OBJECT_TYPE_SENSE_DATA_PROVIDER_BD, 1000389000) \ - _(XR_OBJECT_TYPE_SENSE_DATA_SNAPSHOT_BD, 1000389001) \ - _(XR_OBJECT_TYPE_ANCHOR_BD, 1000389002) \ - _(XR_OBJECT_TYPE_PLANE_DETECTOR_EXT, 1000429000) \ + _(XR_OBJECT_TYPE_BODY_TRACKER_BD, 1000385000) \ + _(XR_OBJECT_TYPE_FACE_TRACKER_BD, 1000386000) \ + _(XR_OBJECT_TYPE_SENSE_DATA_PROVIDER_BD, 1000389000) \ + _(XR_OBJECT_TYPE_SENSE_DATA_SNAPSHOT_BD, 1000389001) \ + _(XR_OBJECT_TYPE_ANCHOR_BD, 1000389002) \ + _(XR_OBJECT_TYPE_PLANE_DETECTOR_EXT, 1000429000) \ + _(XR_OBJECT_TYPE_TRACKABLE_TRACKER_ANDROID, 1000455001) \ + _(XR_OBJECT_TYPE_DEVICE_ANCHOR_PERSISTENCE_ANDROID, 1000457000) \ + _(XR_OBJECT_TYPE_FACE_TRACKER_ANDROID, 1000458000) \ _(XR_OBJECT_TYPE_WORLD_MESH_DETECTOR_ML, 1000474000) \ - _(XR_OBJECT_TYPE_FACIAL_EXPRESSION_CLIENT_ML, 1000482000) \ - _(XR_OBJECT_TYPE_SPATIAL_ENTITY_EXT, 1000740000) \ - _(XR_OBJECT_TYPE_SPATIAL_CONTEXT_EXT, 1000740001) \ - _(XR_OBJECT_TYPE_SPATIAL_SNAPSHOT_EXT, 1000740002) \ - _(XR_OBJECT_TYPE_EYE_TRACKER_PICO, 1010006000) \ + _(XR_OBJECT_TYPE_FACIAL_EXPRESSION_CLIENT_ML, 1000482000) \ + _(XR_OBJECT_TYPE_SPATIAL_ENTITY_EXT, 1000740000) \ + _(XR_OBJECT_TYPE_SPATIAL_CONTEXT_EXT, 1000740001) \ + _(XR_OBJECT_TYPE_SPATIAL_SNAPSHOT_EXT, 1000740002) \ + _(XR_OBJECT_TYPE_EYE_TRACKER_PICO, 1010006000) \ _(XR_OBJECT_TYPE_SECURE_MR_FRAMEWORK_PICO, 1010007000) \ _(XR_OBJECT_TYPE_SECURE_MR_PIPELINE_PICO, 1010007001) \ - _(XR_OBJECT_TYPE_SECURE_MR_TENSOR_PICO, 1010007002) \ - _(XR_OBJECT_TYPE_SENSE_DATA_PROVIDER_PICO, 1200389000) \ - _(XR_OBJECT_TYPE_SENSE_DATA_SNAPSHOT_PICO, 1200389001) \ - _(XR_OBJECT_TYPE_ANCHOR_PICO, 1200389002) \ - _(XR_OBJECT_TYPE_SPATIAL_PERSISTENCE_CONTEXT_EXT, 1000763000) \ - _(XR_OBJECT_TYPE_MAX_ENUM, 0x7FFFFFFF) + _(XR_OBJECT_TYPE_SECURE_MR_TENSOR_PICO, 1010007002) \ + _(XR_OBJECT_TYPE_CAMERA_DEVICE_PICO, 1010033000) \ + _(XR_OBJECT_TYPE_CAMERA_CAPTURE_SESSION_PICO, 1010033001) \ + _(XR_OBJECT_TYPE_READBACK_TEXTURE_PICO, 1010027000) \ + _(XR_OBJECT_TYPE_SENSE_DATA_PROVIDER_PICO, 1200389000) \ + _(XR_OBJECT_TYPE_SENSE_DATA_SNAPSHOT_PICO, 1200389001) \ + _(XR_OBJECT_TYPE_ANCHOR_PICO, 1200389002) \ + _(XR_OBJECT_TYPE_SPATIAL_PERSISTENCE_CONTEXT_EXT, 1000763000) \ + _(XR_OBJECT_TYPE_MAX_ENUM, 0x7FFFFFFF) #define XR_LIST_ENUM_XrLoaderInterfaceStructs(_) \ _(XR_LOADER_INTERFACE_STRUCT_UNINTIALIZED, 0) \ @@ -1131,9 +1248,9 @@ XR_ENUM_STR(XrResult); _(XR_BODY_JOINT_MAX_ENUM_FB, 0x7FFFFFFF) #define XR_LIST_ENUM_XrBodyJointSetFB(_) \ - _(XR_BODY_JOINT_SET_DEFAULT_FB, 0) \ - _(XR_BODY_JOINT_SET_FULL_BODY_META, 1000274000) \ - _(XR_BODY_JOINT_SET_MAX_ENUM_FB, 0x7FFFFFFF) + _(XR_BODY_JOINT_SET_DEFAULT_FB, 0) \ + _(XR_BODY_JOINT_SET_FULL_BODY_META, 1000274000) \ + _(XR_BODY_JOINT_SET_MAX_ENUM_FB, 0x7FFFFFFF) #define XR_LIST_ENUM_XrHandJointsMotionRangeEXT(_) \ _(XR_HAND_JOINTS_MOTION_RANGE_UNOBSTRUCTED_EXT, 1) \ @@ -1602,94 +1719,100 @@ XR_ENUM_STR(XrResult); _(XR_PASSTHROUGH_COLOR_LUT_CHANNELS_RGBA_META, 2) \ _(XR_PASSTHROUGH_COLOR_LUT_CHANNELS_MAX_ENUM_META, 0x7FFFFFFF) -#define XR_LIST_ENUM_XrFullBodyJointMETA(_) \ - _(XR_FULL_BODY_JOINT_ROOT_META, 0) \ - _(XR_FULL_BODY_JOINT_HIPS_META, 1) \ - _(XR_FULL_BODY_JOINT_SPINE_LOWER_META, 2) \ - _(XR_FULL_BODY_JOINT_SPINE_MIDDLE_META, 3) \ - _(XR_FULL_BODY_JOINT_SPINE_UPPER_META, 4) \ - _(XR_FULL_BODY_JOINT_CHEST_META, 5) \ - _(XR_FULL_BODY_JOINT_NECK_META, 6) \ - _(XR_FULL_BODY_JOINT_HEAD_META, 7) \ - _(XR_FULL_BODY_JOINT_LEFT_SHOULDER_META, 8) \ - _(XR_FULL_BODY_JOINT_LEFT_SCAPULA_META, 9) \ - _(XR_FULL_BODY_JOINT_LEFT_ARM_UPPER_META, 10) \ - _(XR_FULL_BODY_JOINT_LEFT_ARM_LOWER_META, 11) \ - _(XR_FULL_BODY_JOINT_LEFT_HAND_WRIST_TWIST_META, 12) \ - _(XR_FULL_BODY_JOINT_RIGHT_SHOULDER_META, 13) \ - _(XR_FULL_BODY_JOINT_RIGHT_SCAPULA_META, 14) \ - _(XR_FULL_BODY_JOINT_RIGHT_ARM_UPPER_META, 15) \ - _(XR_FULL_BODY_JOINT_RIGHT_ARM_LOWER_META, 16) \ - _(XR_FULL_BODY_JOINT_RIGHT_HAND_WRIST_TWIST_META, 17) \ - _(XR_FULL_BODY_JOINT_LEFT_HAND_PALM_META, 18) \ - _(XR_FULL_BODY_JOINT_LEFT_HAND_WRIST_META, 19) \ - _(XR_FULL_BODY_JOINT_LEFT_HAND_THUMB_METACARPAL_META, 20) \ - _(XR_FULL_BODY_JOINT_LEFT_HAND_THUMB_PROXIMAL_META, 21) \ - _(XR_FULL_BODY_JOINT_LEFT_HAND_THUMB_DISTAL_META, 22) \ - _(XR_FULL_BODY_JOINT_LEFT_HAND_THUMB_TIP_META, 23) \ - _(XR_FULL_BODY_JOINT_LEFT_HAND_INDEX_METACARPAL_META, 24) \ - _(XR_FULL_BODY_JOINT_LEFT_HAND_INDEX_PROXIMAL_META, 25) \ - _(XR_FULL_BODY_JOINT_LEFT_HAND_INDEX_INTERMEDIATE_META, 26) \ - _(XR_FULL_BODY_JOINT_LEFT_HAND_INDEX_DISTAL_META, 27) \ - _(XR_FULL_BODY_JOINT_LEFT_HAND_INDEX_TIP_META, 28) \ - _(XR_FULL_BODY_JOINT_LEFT_HAND_MIDDLE_METACARPAL_META, 29) \ - _(XR_FULL_BODY_JOINT_LEFT_HAND_MIDDLE_PROXIMAL_META, 30) \ - _(XR_FULL_BODY_JOINT_LEFT_HAND_MIDDLE_INTERMEDIATE_META, 31) \ - _(XR_FULL_BODY_JOINT_LEFT_HAND_MIDDLE_DISTAL_META, 32) \ - _(XR_FULL_BODY_JOINT_LEFT_HAND_MIDDLE_TIP_META, 33) \ - _(XR_FULL_BODY_JOINT_LEFT_HAND_RING_METACARPAL_META, 34) \ - _(XR_FULL_BODY_JOINT_LEFT_HAND_RING_PROXIMAL_META, 35) \ - _(XR_FULL_BODY_JOINT_LEFT_HAND_RING_INTERMEDIATE_META, 36) \ - _(XR_FULL_BODY_JOINT_LEFT_HAND_RING_DISTAL_META, 37) \ - _(XR_FULL_BODY_JOINT_LEFT_HAND_RING_TIP_META, 38) \ - _(XR_FULL_BODY_JOINT_LEFT_HAND_LITTLE_METACARPAL_META, 39) \ - _(XR_FULL_BODY_JOINT_LEFT_HAND_LITTLE_PROXIMAL_META, 40) \ - _(XR_FULL_BODY_JOINT_LEFT_HAND_LITTLE_INTERMEDIATE_META, 41) \ - _(XR_FULL_BODY_JOINT_LEFT_HAND_LITTLE_DISTAL_META, 42) \ - _(XR_FULL_BODY_JOINT_LEFT_HAND_LITTLE_TIP_META, 43) \ - _(XR_FULL_BODY_JOINT_RIGHT_HAND_PALM_META, 44) \ - _(XR_FULL_BODY_JOINT_RIGHT_HAND_WRIST_META, 45) \ - _(XR_FULL_BODY_JOINT_RIGHT_HAND_THUMB_METACARPAL_META, 46) \ - _(XR_FULL_BODY_JOINT_RIGHT_HAND_THUMB_PROXIMAL_META, 47) \ - _(XR_FULL_BODY_JOINT_RIGHT_HAND_THUMB_DISTAL_META, 48) \ - _(XR_FULL_BODY_JOINT_RIGHT_HAND_THUMB_TIP_META, 49) \ - _(XR_FULL_BODY_JOINT_RIGHT_HAND_INDEX_METACARPAL_META, 50) \ - _(XR_FULL_BODY_JOINT_RIGHT_HAND_INDEX_PROXIMAL_META, 51) \ - _(XR_FULL_BODY_JOINT_RIGHT_HAND_INDEX_INTERMEDIATE_META, 52) \ - _(XR_FULL_BODY_JOINT_RIGHT_HAND_INDEX_DISTAL_META, 53) \ - _(XR_FULL_BODY_JOINT_RIGHT_HAND_INDEX_TIP_META, 54) \ - _(XR_FULL_BODY_JOINT_RIGHT_HAND_MIDDLE_METACARPAL_META, 55) \ - _(XR_FULL_BODY_JOINT_RIGHT_HAND_MIDDLE_PROXIMAL_META, 56) \ - _(XR_FULL_BODY_JOINT_RIGHT_HAND_MIDDLE_INTERMEDIATE_META, 57) \ - _(XR_FULL_BODY_JOINT_RIGHT_HAND_MIDDLE_DISTAL_META, 58) \ - _(XR_FULL_BODY_JOINT_RIGHT_HAND_MIDDLE_TIP_META, 59) \ - _(XR_FULL_BODY_JOINT_RIGHT_HAND_RING_METACARPAL_META, 60) \ - _(XR_FULL_BODY_JOINT_RIGHT_HAND_RING_PROXIMAL_META, 61) \ - _(XR_FULL_BODY_JOINT_RIGHT_HAND_RING_INTERMEDIATE_META, 62) \ - _(XR_FULL_BODY_JOINT_RIGHT_HAND_RING_DISTAL_META, 63) \ - _(XR_FULL_BODY_JOINT_RIGHT_HAND_RING_TIP_META, 64) \ - _(XR_FULL_BODY_JOINT_RIGHT_HAND_LITTLE_METACARPAL_META, 65) \ - _(XR_FULL_BODY_JOINT_RIGHT_HAND_LITTLE_PROXIMAL_META, 66) \ - _(XR_FULL_BODY_JOINT_RIGHT_HAND_LITTLE_INTERMEDIATE_META, 67) \ - _(XR_FULL_BODY_JOINT_RIGHT_HAND_LITTLE_DISTAL_META, 68) \ - _(XR_FULL_BODY_JOINT_RIGHT_HAND_LITTLE_TIP_META, 69) \ - _(XR_FULL_BODY_JOINT_LEFT_UPPER_LEG_META, 70) \ - _(XR_FULL_BODY_JOINT_LEFT_LOWER_LEG_META, 71) \ - _(XR_FULL_BODY_JOINT_LEFT_FOOT_ANKLE_TWIST_META, 72) \ - _(XR_FULL_BODY_JOINT_LEFT_FOOT_ANKLE_META, 73) \ - _(XR_FULL_BODY_JOINT_LEFT_FOOT_SUBTALAR_META, 74) \ - _(XR_FULL_BODY_JOINT_LEFT_FOOT_TRANSVERSE_META, 75) \ - _(XR_FULL_BODY_JOINT_LEFT_FOOT_BALL_META, 76) \ - _(XR_FULL_BODY_JOINT_RIGHT_UPPER_LEG_META, 77) \ - _(XR_FULL_BODY_JOINT_RIGHT_LOWER_LEG_META, 78) \ - _(XR_FULL_BODY_JOINT_RIGHT_FOOT_ANKLE_TWIST_META, 79) \ - _(XR_FULL_BODY_JOINT_RIGHT_FOOT_ANKLE_META, 80) \ - _(XR_FULL_BODY_JOINT_RIGHT_FOOT_SUBTALAR_META, 81) \ - _(XR_FULL_BODY_JOINT_RIGHT_FOOT_TRANSVERSE_META, 82) \ - _(XR_FULL_BODY_JOINT_RIGHT_FOOT_BALL_META, 83) \ - _(XR_FULL_BODY_JOINT_COUNT_META, 84) \ - _(XR_FULL_BODY_JOINT_NONE_META, 85) \ - _(XR_FULL_BODY_JOINT_MAX_ENUM_META, 0x7FFFFFFF) +#define XR_LIST_ENUM_XrFullBodyJointMETA(_) \ + _(XR_FULL_BODY_JOINT_ROOT_META, 0) \ + _(XR_FULL_BODY_JOINT_HIPS_META, 1) \ + _(XR_FULL_BODY_JOINT_SPINE_LOWER_META, 2) \ + _(XR_FULL_BODY_JOINT_SPINE_MIDDLE_META, 3) \ + _(XR_FULL_BODY_JOINT_SPINE_UPPER_META, 4) \ + _(XR_FULL_BODY_JOINT_CHEST_META, 5) \ + _(XR_FULL_BODY_JOINT_NECK_META, 6) \ + _(XR_FULL_BODY_JOINT_HEAD_META, 7) \ + _(XR_FULL_BODY_JOINT_LEFT_SHOULDER_META, 8) \ + _(XR_FULL_BODY_JOINT_LEFT_SCAPULA_META, 9) \ + _(XR_FULL_BODY_JOINT_LEFT_ARM_UPPER_META, 10) \ + _(XR_FULL_BODY_JOINT_LEFT_ARM_LOWER_META, 11) \ + _(XR_FULL_BODY_JOINT_LEFT_HAND_WRIST_TWIST_META, 12) \ + _(XR_FULL_BODY_JOINT_RIGHT_SHOULDER_META, 13) \ + _(XR_FULL_BODY_JOINT_RIGHT_SCAPULA_META, 14) \ + _(XR_FULL_BODY_JOINT_RIGHT_ARM_UPPER_META, 15) \ + _(XR_FULL_BODY_JOINT_RIGHT_ARM_LOWER_META, 16) \ + _(XR_FULL_BODY_JOINT_RIGHT_HAND_WRIST_TWIST_META, 17) \ + _(XR_FULL_BODY_JOINT_LEFT_HAND_PALM_META, 18) \ + _(XR_FULL_BODY_JOINT_LEFT_HAND_WRIST_META, 19) \ + _(XR_FULL_BODY_JOINT_LEFT_HAND_THUMB_METACARPAL_META, 20) \ + _(XR_FULL_BODY_JOINT_LEFT_HAND_THUMB_PROXIMAL_META, 21) \ + _(XR_FULL_BODY_JOINT_LEFT_HAND_THUMB_DISTAL_META, 22) \ + _(XR_FULL_BODY_JOINT_LEFT_HAND_THUMB_TIP_META, 23) \ + _(XR_FULL_BODY_JOINT_LEFT_HAND_INDEX_METACARPAL_META, 24) \ + _(XR_FULL_BODY_JOINT_LEFT_HAND_INDEX_PROXIMAL_META, 25) \ + _(XR_FULL_BODY_JOINT_LEFT_HAND_INDEX_INTERMEDIATE_META, 26) \ + _(XR_FULL_BODY_JOINT_LEFT_HAND_INDEX_DISTAL_META, 27) \ + _(XR_FULL_BODY_JOINT_LEFT_HAND_INDEX_TIP_META, 28) \ + _(XR_FULL_BODY_JOINT_LEFT_HAND_MIDDLE_METACARPAL_META, 29) \ + _(XR_FULL_BODY_JOINT_LEFT_HAND_MIDDLE_PROXIMAL_META, 30) \ + _(XR_FULL_BODY_JOINT_LEFT_HAND_MIDDLE_INTERMEDIATE_META, 31) \ + _(XR_FULL_BODY_JOINT_LEFT_HAND_MIDDLE_DISTAL_META, 32) \ + _(XR_FULL_BODY_JOINT_LEFT_HAND_MIDDLE_TIP_META, 33) \ + _(XR_FULL_BODY_JOINT_LEFT_HAND_RING_METACARPAL_META, 34) \ + _(XR_FULL_BODY_JOINT_LEFT_HAND_RING_PROXIMAL_META, 35) \ + _(XR_FULL_BODY_JOINT_LEFT_HAND_RING_INTERMEDIATE_META, 36) \ + _(XR_FULL_BODY_JOINT_LEFT_HAND_RING_DISTAL_META, 37) \ + _(XR_FULL_BODY_JOINT_LEFT_HAND_RING_TIP_META, 38) \ + _(XR_FULL_BODY_JOINT_LEFT_HAND_LITTLE_METACARPAL_META, 39) \ + _(XR_FULL_BODY_JOINT_LEFT_HAND_LITTLE_PROXIMAL_META, 40) \ + _(XR_FULL_BODY_JOINT_LEFT_HAND_LITTLE_INTERMEDIATE_META, 41) \ + _(XR_FULL_BODY_JOINT_LEFT_HAND_LITTLE_DISTAL_META, 42) \ + _(XR_FULL_BODY_JOINT_LEFT_HAND_LITTLE_TIP_META, 43) \ + _(XR_FULL_BODY_JOINT_RIGHT_HAND_PALM_META, 44) \ + _(XR_FULL_BODY_JOINT_RIGHT_HAND_WRIST_META, 45) \ + _(XR_FULL_BODY_JOINT_RIGHT_HAND_THUMB_METACARPAL_META, 46) \ + _(XR_FULL_BODY_JOINT_RIGHT_HAND_THUMB_PROXIMAL_META, 47) \ + _(XR_FULL_BODY_JOINT_RIGHT_HAND_THUMB_DISTAL_META, 48) \ + _(XR_FULL_BODY_JOINT_RIGHT_HAND_THUMB_TIP_META, 49) \ + _(XR_FULL_BODY_JOINT_RIGHT_HAND_INDEX_METACARPAL_META, 50) \ + _(XR_FULL_BODY_JOINT_RIGHT_HAND_INDEX_PROXIMAL_META, 51) \ + _(XR_FULL_BODY_JOINT_RIGHT_HAND_INDEX_INTERMEDIATE_META, 52) \ + _(XR_FULL_BODY_JOINT_RIGHT_HAND_INDEX_DISTAL_META, 53) \ + _(XR_FULL_BODY_JOINT_RIGHT_HAND_INDEX_TIP_META, 54) \ + _(XR_FULL_BODY_JOINT_RIGHT_HAND_MIDDLE_METACARPAL_META, 55) \ + _(XR_FULL_BODY_JOINT_RIGHT_HAND_MIDDLE_PROXIMAL_META, 56) \ + _(XR_FULL_BODY_JOINT_RIGHT_HAND_MIDDLE_INTERMEDIATE_META, 57) \ + _(XR_FULL_BODY_JOINT_RIGHT_HAND_MIDDLE_DISTAL_META, 58) \ + _(XR_FULL_BODY_JOINT_RIGHT_HAND_MIDDLE_TIP_META, 59) \ + _(XR_FULL_BODY_JOINT_RIGHT_HAND_RING_METACARPAL_META, 60) \ + _(XR_FULL_BODY_JOINT_RIGHT_HAND_RING_PROXIMAL_META, 61) \ + _(XR_FULL_BODY_JOINT_RIGHT_HAND_RING_INTERMEDIATE_META, 62) \ + _(XR_FULL_BODY_JOINT_RIGHT_HAND_RING_DISTAL_META, 63) \ + _(XR_FULL_BODY_JOINT_RIGHT_HAND_RING_TIP_META, 64) \ + _(XR_FULL_BODY_JOINT_RIGHT_HAND_LITTLE_METACARPAL_META, 65) \ + _(XR_FULL_BODY_JOINT_RIGHT_HAND_LITTLE_PROXIMAL_META, 66) \ + _(XR_FULL_BODY_JOINT_RIGHT_HAND_LITTLE_INTERMEDIATE_META, 67) \ + _(XR_FULL_BODY_JOINT_RIGHT_HAND_LITTLE_DISTAL_META, 68) \ + _(XR_FULL_BODY_JOINT_RIGHT_HAND_LITTLE_TIP_META, 69) \ + _(XR_FULL_BODY_JOINT_LEFT_UPPER_LEG_META, 70) \ + _(XR_FULL_BODY_JOINT_LEFT_LOWER_LEG_META, 71) \ + _(XR_FULL_BODY_JOINT_LEFT_FOOT_ANKLE_TWIST_META, 72) \ + _(XR_FULL_BODY_JOINT_LEFT_FOOT_ANKLE_META, 73) \ + _(XR_FULL_BODY_JOINT_LEFT_FOOT_SUBTALAR_META, 74) \ + _(XR_FULL_BODY_JOINT_LEFT_FOOT_TRANSVERSE_META, 75) \ + _(XR_FULL_BODY_JOINT_LEFT_FOOT_BALL_META, 76) \ + _(XR_FULL_BODY_JOINT_RIGHT_UPPER_LEG_META, 77) \ + _(XR_FULL_BODY_JOINT_RIGHT_LOWER_LEG_META, 78) \ + _(XR_FULL_BODY_JOINT_RIGHT_FOOT_ANKLE_TWIST_META, 79) \ + _(XR_FULL_BODY_JOINT_RIGHT_FOOT_ANKLE_META, 80) \ + _(XR_FULL_BODY_JOINT_RIGHT_FOOT_SUBTALAR_META, 81) \ + _(XR_FULL_BODY_JOINT_RIGHT_FOOT_TRANSVERSE_META, 82) \ + _(XR_FULL_BODY_JOINT_RIGHT_FOOT_BALL_META, 83) \ + _(XR_FULL_BODY_JOINT_COUNT_META, 84) \ + _(XR_FULL_BODY_JOINT_NONE_META, 85) \ + _(XR_FULL_BODY_JOINT_MAX_ENUM_META, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrBodyTrackingCalibrationStateMETA(_) \ + _(XR_BODY_TRACKING_CALIBRATION_STATE_VALID_META, 1) \ + _(XR_BODY_TRACKING_CALIBRATION_STATE_CALIBRATING_META, 2) \ + _(XR_BODY_TRACKING_CALIBRATION_STATE_INVALID_META, 3) \ + _(XR_BODY_TRACKING_CALIBRATION_STATE_MAX_ENUM_META, 0x7FFFFFFF) #define XR_LIST_ENUM_XrFaceExpression2FB(_) \ _(XR_FACE_EXPRESSION2_BROW_LOWERER_L_FB, 0) \ @@ -1890,73 +2013,158 @@ XR_ENUM_STR(XrResult); _(XR_BODY_JOINT_SET_FULL_BODY_JOINTS_BD, 2) \ _(XR_BODY_JOINT_SET_MAX_ENUM_BD, 0x7FFFFFFF) -#define XR_LIST_ENUM_XrSpatialEntityComponentTypeBD(_) \ - _(XR_SPATIAL_ENTITY_COMPONENT_TYPE_LOCATION_BD, 0) \ - _(XR_SPATIAL_ENTITY_COMPONENT_TYPE_SEMANTIC_BD, 1) \ - _(XR_SPATIAL_ENTITY_COMPONENT_TYPE_BOUNDING_BOX_2D_BD, 2) \ - _(XR_SPATIAL_ENTITY_COMPONENT_TYPE_POLYGON_BD, 3) \ - _(XR_SPATIAL_ENTITY_COMPONENT_TYPE_BOUNDING_BOX_3D_BD, 4) \ - _(XR_SPATIAL_ENTITY_COMPONENT_TYPE_TRIANGLE_MESH_BD, 5) \ - _(XR_SPATIAL_ENTITY_COMPONENT_TYPE_PLANE_ORIENTATION_BD, 1000396000) \ - _(XR_SPATIAL_ENTITY_COMPONENT_TYPE_MAX_ENUM_BD, 0x7FFFFFFF) - -#define XR_LIST_ENUM_XrSemanticLabelBD(_) \ - _(XR_SEMANTIC_LABEL_UNKNOWN_BD, 0) \ - _(XR_SEMANTIC_LABEL_FLOOR_BD, 1) \ - _(XR_SEMANTIC_LABEL_CEILING_BD, 2) \ - _(XR_SEMANTIC_LABEL_WALL_BD, 3) \ - _(XR_SEMANTIC_LABEL_DOOR_BD, 4) \ - _(XR_SEMANTIC_LABEL_WINDOW_BD, 5) \ - _(XR_SEMANTIC_LABEL_OPENING_BD, 6) \ - _(XR_SEMANTIC_LABEL_TABLE_BD, 7) \ - _(XR_SEMANTIC_LABEL_SOFA_BD, 8) \ - _(XR_SEMANTIC_LABEL_CHAIR_BD, 9) \ - _(XR_SEMANTIC_LABEL_HUMAN_BD, 10) \ - _(XR_SEMANTIC_LABEL_BEAM_BD, 11) \ - _(XR_SEMANTIC_LABEL_COLUMN_BD, 12) \ - _(XR_SEMANTIC_LABEL_CURTAIN_BD, 13) \ - _(XR_SEMANTIC_LABEL_CABINET_BD, 14) \ - _(XR_SEMANTIC_LABEL_BED_BD, 15) \ - _(XR_SEMANTIC_LABEL_PLANT_BD, 16) \ - _(XR_SEMANTIC_LABEL_SCREEN_BD, 17) \ - _(XR_SEMANTIC_LABEL_VIRTUAL_WALL_BD, 18) \ - _(XR_SEMANTIC_LABEL_REFRIGERATOR_BD, 19) \ - _(XR_SEMANTIC_LABEL_WASHING_MACHINE_BD, 20) \ - _(XR_SEMANTIC_LABEL_AIR_CONDITIONER_BD, 21) \ - _(XR_SEMANTIC_LABEL_LAMP_BD, 22) \ - _(XR_SEMANTIC_LABEL_WALL_ART_BD, 23) \ - _(XR_SEMANTIC_LABEL_STAIRWAY_BD, 24) \ - _(XR_SEMANTIC_LABEL_MAX_ENUM_BD, 0x7FFFFFFF) - -#define XR_LIST_ENUM_XrSenseDataProviderTypeBD(_) \ - _(XR_SENSE_DATA_PROVIDER_TYPE_ANCHOR_BD, 1000390000) \ - _(XR_SENSE_DATA_PROVIDER_TYPE_SCENE_BD, 1000392000) \ - _(XR_SENSE_DATA_PROVIDER_TYPE_MESH_BD, 1000393000) \ - _(XR_SENSE_DATA_PROVIDER_TYPE_PLANE_BD, 1000396000) \ - _(XR_SENSE_DATA_PROVIDER_TYPE_MAX_ENUM_BD, 0x7FFFFFFF) - -#define XR_LIST_ENUM_XrSenseDataProviderStateBD(_) \ - _(XR_SENSE_DATA_PROVIDER_STATE_INITIALIZED_BD, 0) \ - _(XR_SENSE_DATA_PROVIDER_STATE_RUNNING_BD, 1) \ - _(XR_SENSE_DATA_PROVIDER_STATE_STOPPED_BD, 2) \ - _(XR_SENSE_DATA_PROVIDER_STATE_MAX_ENUM_BD, 0x7FFFFFFF) +#define XR_LIST_ENUM_XrFacialSimulationModeBD(_) \ + _(XR_FACIAL_SIMULATION_MODE_DEFAULT_BD, 0) \ + _(XR_FACIAL_SIMULATION_MODE_COMBINED_AUDIO_BD, 1) \ + _(XR_FACIAL_SIMULATION_MODE_COMBINED_AUDIO_WITH_LIP_BD, 2) \ + _(XR_FACIAL_SIMULATION_MODE_ONLY_AUDIO_WITH_LIP_BD, 3) \ + _(XR_FACIAL_SIMULATION_MODE_MAX_ENUM_BD, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrFaceExpressionBD(_) \ + _(XR_FACE_EXPRESSION_BROW_DROP_L_BD, 0) \ + _(XR_FACE_EXPRESSION_BROW_DROP_R_BD, 1) \ + _(XR_FACE_EXPRESSION_BROW_INNER_UPWARDS_BD, 2) \ + _(XR_FACE_EXPRESSION_BROW_OUTER_UPWARDS_L_BD, 3) \ + _(XR_FACE_EXPRESSION_BROW_OUTER_UPWARDS_R_BD, 4) \ + _(XR_FACE_EXPRESSION_EYE_BLINK_L_BD, 5) \ + _(XR_FACE_EXPRESSION_EYE_LOOK_DROP_L_BD, 6) \ + _(XR_FACE_EXPRESSION_EYE_LOOK_IN_L_BD, 7) \ + _(XR_FACE_EXPRESSION_EYE_LOOK_OUT_L_BD, 8) \ + _(XR_FACE_EXPRESSION_EYE_LOOK_UPWARDS_L_BD, 9) \ + _(XR_FACE_EXPRESSION_EYE_LOOK_SQUINT_L_BD, 10) \ + _(XR_FACE_EXPRESSION_EYE_LOOK_WIDE_L_BD, 11) \ + _(XR_FACE_EXPRESSION_EYE_BLINK_R_BD, 12) \ + _(XR_FACE_EXPRESSION_EYE_LOOK_DROP_R_BD, 13) \ + _(XR_FACE_EXPRESSION_EYE_LOOK_IN_R_BD, 14) \ + _(XR_FACE_EXPRESSION_EYE_LOOK_OUT_R_BD, 15) \ + _(XR_FACE_EXPRESSION_EYE_LOOK_UPWARDS_R_BD, 16) \ + _(XR_FACE_EXPRESSION_EYE_LOOK_SQUINT_R_BD, 17) \ + _(XR_FACE_EXPRESSION_EYE_LOOK_WIDE_R_BD, 18) \ + _(XR_FACE_EXPRESSION_NOSE_SNEER_L_BD, 19) \ + _(XR_FACE_EXPRESSION_NOSE_SNEER_R_BD, 20) \ + _(XR_FACE_EXPRESSION_CHEEK_PUFF_BD, 21) \ + _(XR_FACE_EXPRESSION_CHEEK_SQUINT_L_BD, 22) \ + _(XR_FACE_EXPRESSION_CHEEK_SQUINT_R_BD, 23) \ + _(XR_FACE_EXPRESSION_MOUTH_CLOSE_BD, 24) \ + _(XR_FACE_EXPRESSION_MOUTH_FUNNEL_BD, 25) \ + _(XR_FACE_EXPRESSION_MOUTH_PUCKER_BD, 26) \ + _(XR_FACE_EXPRESSION_MOUTH_L_BD, 27) \ + _(XR_FACE_EXPRESSION_MOUTH_R_BD, 28) \ + _(XR_FACE_EXPRESSION_MOUTH_SMILE_L_BD, 29) \ + _(XR_FACE_EXPRESSION_MOUTH_SMILE_R_BD, 30) \ + _(XR_FACE_EXPRESSION_MOUTH_FROWN_L_BD, 31) \ + _(XR_FACE_EXPRESSION_MOUTH_FROWN_R_BD, 32) \ + _(XR_FACE_EXPRESSION_MOUTH_DIMPLE_L_BD, 33) \ + _(XR_FACE_EXPRESSION_MOUTH_DIMPLE_R_BD, 34) \ + _(XR_FACE_EXPRESSION_MOUTH_STRETCH_L_BD, 35) \ + _(XR_FACE_EXPRESSION_MOUTH_STRETCH_R_BD, 36) \ + _(XR_FACE_EXPRESSION_MOUTH_ROLL_LOWER_BD, 37) \ + _(XR_FACE_EXPRESSION_MOUTH_ROLL_UPPER_BD, 38) \ + _(XR_FACE_EXPRESSION_MOUTH_SHRUG_LOWER_BD, 39) \ + _(XR_FACE_EXPRESSION_MOUTH_SHRUG_UPPER_BD, 40) \ + _(XR_FACE_EXPRESSION_MOUTH_PRESS_L_BD, 41) \ + _(XR_FACE_EXPRESSION_MOUTH_PRESS_R_BD, 42) \ + _(XR_FACE_EXPRESSION_MOUTH_LOWER_DROP_L_BD, 43) \ + _(XR_FACE_EXPRESSION_MOUTH_LOWER_DROP_R_BD, 44) \ + _(XR_FACE_EXPRESSION_MOUTH_UPPER_UPWARDS_L_BD, 45) \ + _(XR_FACE_EXPRESSION_MOUTH_UPPER_UPWARDS_R_BD, 46) \ + _(XR_FACE_EXPRESSION_JAW_FORWARD_BD, 47) \ + _(XR_FACE_EXPRESSION_JAW_L_BD, 48) \ + _(XR_FACE_EXPRESSION_JAW_R_BD, 49) \ + _(XR_FACE_EXPRESSION_JAW_OPEN_BD, 50) \ + _(XR_FACE_EXPRESSION_TONGUE_OUT_BD, 51) \ + _(XR_FACE_EXPRESSION_MAX_ENUM_BD, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrLipExpressionBD(_) \ + _(XR_LIP_EXPRESSION_PP_BD, 0) \ + _(XR_LIP_EXPRESSION_CH_BD, 1) \ + _(XR_LIP_EXPRESSION_LO_BD, 2) \ + _(XR_LIP_EXPRESSION_O_BD, 3) \ + _(XR_LIP_EXPRESSION_I_BD, 4) \ + _(XR_LIP_EXPRESSION_LU_BD, 5) \ + _(XR_LIP_EXPRESSION_RR_BD, 6) \ + _(XR_LIP_EXPRESSION_XX_BD, 7) \ + _(XR_LIP_EXPRESSION_LAA_BD, 8) \ + _(XR_LIP_EXPRESSION_LI_BD, 9) \ + _(XR_LIP_EXPRESSION_FF_BD, 10) \ + _(XR_LIP_EXPRESSION_U_BD, 11) \ + _(XR_LIP_EXPRESSION_TH_BD, 12) \ + _(XR_LIP_EXPRESSION_LKK_BD, 13) \ + _(XR_LIP_EXPRESSION_SS_BD, 14) \ + _(XR_LIP_EXPRESSION_LE_BD, 15) \ + _(XR_LIP_EXPRESSION_DD_BD, 16) \ + _(XR_LIP_EXPRESSION_E_BD, 17) \ + _(XR_LIP_EXPRESSION_LNN_BD, 18) \ + _(XR_LIP_EXPRESSION_SIL_BD, 19) \ + _(XR_LIP_EXPRESSION_MAX_ENUM_BD, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrSpatialEntityComponentTypeBD(_) \ + _(XR_SPATIAL_ENTITY_COMPONENT_TYPE_LOCATION_BD, 0) \ + _(XR_SPATIAL_ENTITY_COMPONENT_TYPE_SEMANTIC_BD, 1) \ + _(XR_SPATIAL_ENTITY_COMPONENT_TYPE_BOUNDING_BOX_2D_BD, 2) \ + _(XR_SPATIAL_ENTITY_COMPONENT_TYPE_POLYGON_BD, 3) \ + _(XR_SPATIAL_ENTITY_COMPONENT_TYPE_BOUNDING_BOX_3D_BD, 4) \ + _(XR_SPATIAL_ENTITY_COMPONENT_TYPE_TRIANGLE_MESH_BD, 5) \ + _(XR_SPATIAL_ENTITY_COMPONENT_TYPE_PLANE_ORIENTATION_BD, 1000396000) \ + _(XR_SPATIAL_ENTITY_COMPONENT_TYPE_MAX_ENUM_BD, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrSemanticLabelBD(_) \ + _(XR_SEMANTIC_LABEL_UNKNOWN_BD, 0) \ + _(XR_SEMANTIC_LABEL_FLOOR_BD, 1) \ + _(XR_SEMANTIC_LABEL_CEILING_BD, 2) \ + _(XR_SEMANTIC_LABEL_WALL_BD, 3) \ + _(XR_SEMANTIC_LABEL_DOOR_BD, 4) \ + _(XR_SEMANTIC_LABEL_WINDOW_BD, 5) \ + _(XR_SEMANTIC_LABEL_OPENING_BD, 6) \ + _(XR_SEMANTIC_LABEL_TABLE_BD, 7) \ + _(XR_SEMANTIC_LABEL_SOFA_BD, 8) \ + _(XR_SEMANTIC_LABEL_CHAIR_BD, 9) \ + _(XR_SEMANTIC_LABEL_HUMAN_BD, 10) \ + _(XR_SEMANTIC_LABEL_BEAM_BD, 11) \ + _(XR_SEMANTIC_LABEL_COLUMN_BD, 12) \ + _(XR_SEMANTIC_LABEL_CURTAIN_BD, 13) \ + _(XR_SEMANTIC_LABEL_CABINET_BD, 14) \ + _(XR_SEMANTIC_LABEL_BED_BD, 15) \ + _(XR_SEMANTIC_LABEL_PLANT_BD, 16) \ + _(XR_SEMANTIC_LABEL_SCREEN_BD, 17) \ + _(XR_SEMANTIC_LABEL_VIRTUAL_WALL_BD, 18) \ + _(XR_SEMANTIC_LABEL_REFRIGERATOR_BD, 19) \ + _(XR_SEMANTIC_LABEL_WASHING_MACHINE_BD, 20) \ + _(XR_SEMANTIC_LABEL_AIR_CONDITIONER_BD, 21) \ + _(XR_SEMANTIC_LABEL_LAMP_BD, 22) \ + _(XR_SEMANTIC_LABEL_WALL_ART_BD, 23) \ + _(XR_SEMANTIC_LABEL_STAIRWAY_BD, 24) \ + _(XR_SEMANTIC_LABEL_MAX_ENUM_BD, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrSenseDataProviderTypeBD(_) \ + _(XR_SENSE_DATA_PROVIDER_TYPE_ANCHOR_BD, 1000390000) \ + _(XR_SENSE_DATA_PROVIDER_TYPE_SCENE_BD, 1000392000) \ + _(XR_SENSE_DATA_PROVIDER_TYPE_MESH_BD, 1000393000) \ + _(XR_SENSE_DATA_PROVIDER_TYPE_PLANE_BD, 1000396000) \ + _(XR_SENSE_DATA_PROVIDER_TYPE_MAX_ENUM_BD, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrSenseDataProviderStateBD(_) \ + _(XR_SENSE_DATA_PROVIDER_STATE_INITIALIZED_BD, 0) \ + _(XR_SENSE_DATA_PROVIDER_STATE_RUNNING_BD, 1) \ + _(XR_SENSE_DATA_PROVIDER_STATE_STOPPED_BD, 2) \ + _(XR_SENSE_DATA_PROVIDER_STATE_MAX_ENUM_BD, 0x7FFFFFFF) #define XR_LIST_ENUM_XrPersistenceLocationBD(_) \ - _(XR_PERSISTENCE_LOCATION_LOCAL_BD, 0) \ - _(XR_PERSISTENCE_LOCATION_MAX_ENUM_BD, 0x7FFFFFFF) + _(XR_PERSISTENCE_LOCATION_LOCAL_BD, 0) \ + _(XR_PERSISTENCE_LOCATION_MAX_ENUM_BD, 0x7FFFFFFF) #define XR_LIST_ENUM_XrSpatialMeshLodBD(_) \ - _(XR_SPATIAL_MESH_LOD_COARSE_BD, 0) \ - _(XR_SPATIAL_MESH_LOD_MEDIUM_BD, 1) \ - _(XR_SPATIAL_MESH_LOD_FINE_BD, 2) \ - _(XR_SPATIAL_MESH_LOD_MAX_ENUM_BD, 0x7FFFFFFF) - -#define XR_LIST_ENUM_XrPlaneOrientationBD(_) \ - _(XR_PLANE_ORIENTATION_HORIZONTAL_UPWARD_BD, 0) \ - _(XR_PLANE_ORIENTATION_HORIZONTAL_DOWNWARD_BD, 1) \ - _(XR_PLANE_ORIENTATION_VERTICAL_BD, 2) \ - _(XR_PLANE_ORIENTATION_ARBITRARY_BD, 3) \ - _(XR_PLANE_ORIENTATION_MAX_ENUM_BD, 0x7FFFFFFF) + _(XR_SPATIAL_MESH_LOD_COARSE_BD, 0) \ + _(XR_SPATIAL_MESH_LOD_MEDIUM_BD, 1) \ + _(XR_SPATIAL_MESH_LOD_FINE_BD, 2) \ + _(XR_SPATIAL_MESH_LOD_MAX_ENUM_BD, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrPlaneOrientationBD(_) \ + _(XR_PLANE_ORIENTATION_HORIZONTAL_UPWARD_BD, 0) \ + _(XR_PLANE_ORIENTATION_HORIZONTAL_DOWNWARD_BD, 1) \ + _(XR_PLANE_ORIENTATION_VERTICAL_BD, 2) \ + _(XR_PLANE_ORIENTATION_ARBITRARY_BD, 3) \ + _(XR_PLANE_ORIENTATION_MAX_ENUM_BD, 0x7FFFFFFF) #define XR_LIST_ENUM_XrHandTrackingDataSourceEXT(_) \ _(XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT, 1) \ @@ -1986,6 +2194,138 @@ XR_ENUM_STR(XrResult); _(XR_PLANE_DETECTION_STATE_FATAL_EXT, 4) \ _(XR_PLANE_DETECTION_STATE_MAX_ENUM_EXT, 0x7FFFFFFF) +#define XR_LIST_ENUM_XrTrackingStateANDROID(_) \ + _(XR_TRACKING_STATE_PAUSED_ANDROID, 0) \ + _(XR_TRACKING_STATE_STOPPED_ANDROID, 1) \ + _(XR_TRACKING_STATE_TRACKING_ANDROID, 2) \ + _(XR_TRACKING_STATE_MAX_ENUM_ANDROID, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrTrackableTypeANDROID(_) \ + _(XR_TRACKABLE_TYPE_NOT_VALID_ANDROID, 0) \ + _(XR_TRACKABLE_TYPE_PLANE_ANDROID, 1) \ + _(XR_TRACKABLE_TYPE_DEPTH_ANDROID, 1000463000) \ + _(XR_TRACKABLE_TYPE_OBJECT_ANDROID, 1000466000) \ + _(XR_TRACKABLE_TYPE_MARKER_ANDROID, 1000707000) \ + _(XR_TRACKABLE_TYPE_MAX_ENUM_ANDROID, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrPlaneTypeANDROID(_) \ + _(XR_PLANE_TYPE_HORIZONTAL_DOWNWARD_FACING_ANDROID, 0) \ + _(XR_PLANE_TYPE_HORIZONTAL_UPWARD_FACING_ANDROID, 1) \ + _(XR_PLANE_TYPE_VERTICAL_ANDROID, 2) \ + _(XR_PLANE_TYPE_ARBITRARY_ANDROID, 3) \ + _(XR_PLANE_TYPE_MAX_ENUM_ANDROID, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrPlaneLabelANDROID(_) \ + _(XR_PLANE_LABEL_UNKNOWN_ANDROID, 0) \ + _(XR_PLANE_LABEL_WALL_ANDROID, 1) \ + _(XR_PLANE_LABEL_FLOOR_ANDROID, 2) \ + _(XR_PLANE_LABEL_CEILING_ANDROID, 3) \ + _(XR_PLANE_LABEL_TABLE_ANDROID, 4) \ + _(XR_PLANE_LABEL_MAX_ENUM_ANDROID, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrAnchorPersistStateANDROID(_) \ + _(XR_ANCHOR_PERSIST_STATE_PERSIST_NOT_REQUESTED_ANDROID, 0) \ + _(XR_ANCHOR_PERSIST_STATE_PERSIST_PENDING_ANDROID, 1) \ + _(XR_ANCHOR_PERSIST_STATE_PERSISTED_ANDROID, 2) \ + _(XR_ANCHOR_PERSIST_STATE_MAX_ENUM_ANDROID, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrFaceParameterIndicesANDROID(_) \ + _(XR_FACE_PARAMETER_INDICES_BROW_LOWERER_L_ANDROID, 0) \ + _(XR_FACE_PARAMETER_INDICES_BROW_LOWERER_R_ANDROID, 1) \ + _(XR_FACE_PARAMETER_INDICES_CHEEK_PUFF_L_ANDROID, 2) \ + _(XR_FACE_PARAMETER_INDICES_CHEEK_PUFF_R_ANDROID, 3) \ + _(XR_FACE_PARAMETER_INDICES_CHEEK_RAISER_L_ANDROID, 4) \ + _(XR_FACE_PARAMETER_INDICES_CHEEK_RAISER_R_ANDROID, 5) \ + _(XR_FACE_PARAMETER_INDICES_CHEEK_SUCK_L_ANDROID, 6) \ + _(XR_FACE_PARAMETER_INDICES_CHEEK_SUCK_R_ANDROID, 7) \ + _(XR_FACE_PARAMETER_INDICES_CHIN_RAISER_B_ANDROID, 8) \ + _(XR_FACE_PARAMETER_INDICES_CHIN_RAISER_T_ANDROID, 9) \ + _(XR_FACE_PARAMETER_INDICES_DIMPLER_L_ANDROID, 10) \ + _(XR_FACE_PARAMETER_INDICES_DIMPLER_R_ANDROID, 11) \ + _(XR_FACE_PARAMETER_INDICES_EYES_CLOSED_L_ANDROID, 12) \ + _(XR_FACE_PARAMETER_INDICES_EYES_CLOSED_R_ANDROID, 13) \ + _(XR_FACE_PARAMETER_INDICES_EYES_LOOK_DOWN_L_ANDROID, 14) \ + _(XR_FACE_PARAMETER_INDICES_EYES_LOOK_DOWN_R_ANDROID, 15) \ + _(XR_FACE_PARAMETER_INDICES_EYES_LOOK_LEFT_L_ANDROID, 16) \ + _(XR_FACE_PARAMETER_INDICES_EYES_LOOK_LEFT_R_ANDROID, 17) \ + _(XR_FACE_PARAMETER_INDICES_EYES_LOOK_RIGHT_L_ANDROID, 18) \ + _(XR_FACE_PARAMETER_INDICES_EYES_LOOK_RIGHT_R_ANDROID, 19) \ + _(XR_FACE_PARAMETER_INDICES_EYES_LOOK_UP_L_ANDROID, 20) \ + _(XR_FACE_PARAMETER_INDICES_EYES_LOOK_UP_R_ANDROID, 21) \ + _(XR_FACE_PARAMETER_INDICES_INNER_BROW_RAISER_L_ANDROID, 22) \ + _(XR_FACE_PARAMETER_INDICES_INNER_BROW_RAISER_R_ANDROID, 23) \ + _(XR_FACE_PARAMETER_INDICES_JAW_DROP_ANDROID, 24) \ + _(XR_FACE_PARAMETER_INDICES_JAW_SIDEWAYS_LEFT_ANDROID, 25) \ + _(XR_FACE_PARAMETER_INDICES_JAW_SIDEWAYS_RIGHT_ANDROID, 26) \ + _(XR_FACE_PARAMETER_INDICES_JAW_THRUST_ANDROID, 27) \ + _(XR_FACE_PARAMETER_INDICES_LID_TIGHTENER_L_ANDROID, 28) \ + _(XR_FACE_PARAMETER_INDICES_LID_TIGHTENER_R_ANDROID, 29) \ + _(XR_FACE_PARAMETER_INDICES_LIP_CORNER_DEPRESSOR_L_ANDROID, 30) \ + _(XR_FACE_PARAMETER_INDICES_LIP_CORNER_DEPRESSOR_R_ANDROID, 31) \ + _(XR_FACE_PARAMETER_INDICES_LIP_CORNER_PULLER_L_ANDROID, 32) \ + _(XR_FACE_PARAMETER_INDICES_LIP_CORNER_PULLER_R_ANDROID, 33) \ + _(XR_FACE_PARAMETER_INDICES_LIP_FUNNELER_LB_ANDROID, 34) \ + _(XR_FACE_PARAMETER_INDICES_LIP_FUNNELER_LT_ANDROID, 35) \ + _(XR_FACE_PARAMETER_INDICES_LIP_FUNNELER_RB_ANDROID, 36) \ + _(XR_FACE_PARAMETER_INDICES_LIP_FUNNELER_RT_ANDROID, 37) \ + _(XR_FACE_PARAMETER_INDICES_LIP_PRESSOR_L_ANDROID, 38) \ + _(XR_FACE_PARAMETER_INDICES_LIP_PRESSOR_R_ANDROID, 39) \ + _(XR_FACE_PARAMETER_INDICES_LIP_PUCKER_L_ANDROID, 40) \ + _(XR_FACE_PARAMETER_INDICES_LIP_PUCKER_R_ANDROID, 41) \ + _(XR_FACE_PARAMETER_INDICES_LIP_STRETCHER_L_ANDROID, 42) \ + _(XR_FACE_PARAMETER_INDICES_LIP_STRETCHER_R_ANDROID, 43) \ + _(XR_FACE_PARAMETER_INDICES_LIP_SUCK_LB_ANDROID, 44) \ + _(XR_FACE_PARAMETER_INDICES_LIP_SUCK_LT_ANDROID, 45) \ + _(XR_FACE_PARAMETER_INDICES_LIP_SUCK_RB_ANDROID, 46) \ + _(XR_FACE_PARAMETER_INDICES_LIP_SUCK_RT_ANDROID, 47) \ + _(XR_FACE_PARAMETER_INDICES_LIP_TIGHTENER_L_ANDROID, 48) \ + _(XR_FACE_PARAMETER_INDICES_LIP_TIGHTENER_R_ANDROID, 49) \ + _(XR_FACE_PARAMETER_INDICES_LIPS_TOWARD_ANDROID, 50) \ + _(XR_FACE_PARAMETER_INDICES_LOWER_LIP_DEPRESSOR_L_ANDROID, 51) \ + _(XR_FACE_PARAMETER_INDICES_LOWER_LIP_DEPRESSOR_R_ANDROID, 52) \ + _(XR_FACE_PARAMETER_INDICES_MOUTH_LEFT_ANDROID, 53) \ + _(XR_FACE_PARAMETER_INDICES_MOUTH_RIGHT_ANDROID, 54) \ + _(XR_FACE_PARAMETER_INDICES_NOSE_WRINKLER_L_ANDROID, 55) \ + _(XR_FACE_PARAMETER_INDICES_NOSE_WRINKLER_R_ANDROID, 56) \ + _(XR_FACE_PARAMETER_INDICES_OUTER_BROW_RAISER_L_ANDROID, 57) \ + _(XR_FACE_PARAMETER_INDICES_OUTER_BROW_RAISER_R_ANDROID, 58) \ + _(XR_FACE_PARAMETER_INDICES_UPPER_LID_RAISER_L_ANDROID, 59) \ + _(XR_FACE_PARAMETER_INDICES_UPPER_LID_RAISER_R_ANDROID, 60) \ + _(XR_FACE_PARAMETER_INDICES_UPPER_LIP_RAISER_L_ANDROID, 61) \ + _(XR_FACE_PARAMETER_INDICES_UPPER_LIP_RAISER_R_ANDROID, 62) \ + _(XR_FACE_PARAMETER_INDICES_TONGUE_OUT_ANDROID, 63) \ + _(XR_FACE_PARAMETER_INDICES_TONGUE_LEFT_ANDROID, 64) \ + _(XR_FACE_PARAMETER_INDICES_TONGUE_RIGHT_ANDROID, 65) \ + _(XR_FACE_PARAMETER_INDICES_TONGUE_UP_ANDROID, 66) \ + _(XR_FACE_PARAMETER_INDICES_TONGUE_DOWN_ANDROID, 67) \ + _(XR_FACE_PARAMETER_INDICES_MAX_ENUM_ANDROID, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrFaceTrackingStateANDROID(_) \ + _(XR_FACE_TRACKING_STATE_PAUSED_ANDROID, 0) \ + _(XR_FACE_TRACKING_STATE_STOPPED_ANDROID, 1) \ + _(XR_FACE_TRACKING_STATE_TRACKING_ANDROID, 2) \ + _(XR_FACE_TRACKING_STATE_MAX_ENUM_ANDROID, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrFaceConfidenceRegionsANDROID(_) \ + _(XR_FACE_CONFIDENCE_REGIONS_LOWER_ANDROID, 0) \ + _(XR_FACE_CONFIDENCE_REGIONS_LEFT_UPPER_ANDROID, 1) \ + _(XR_FACE_CONFIDENCE_REGIONS_RIGHT_UPPER_ANDROID, 2) \ + _(XR_FACE_CONFIDENCE_REGIONS_MAX_ENUM_ANDROID, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrPassthroughCameraStateANDROID(_) \ + _(XR_PASSTHROUGH_CAMERA_STATE_DISABLED_ANDROID, 0) \ + _(XR_PASSTHROUGH_CAMERA_STATE_INITIALIZING_ANDROID, 1) \ + _(XR_PASSTHROUGH_CAMERA_STATE_READY_ANDROID, 2) \ + _(XR_PASSTHROUGH_CAMERA_STATE_ERROR_ANDROID, 3) \ + _(XR_PASSTHROUGH_CAMERA_STATE_MAX_ENUM_ANDROID, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrObjectLabelANDROID(_) \ + _(XR_OBJECT_LABEL_UNKNOWN_ANDROID, 0) \ + _(XR_OBJECT_LABEL_KEYBOARD_ANDROID, 1) \ + _(XR_OBJECT_LABEL_MOUSE_ANDROID, 2) \ + _(XR_OBJECT_LABEL_LAPTOP_ANDROID, 3) \ + _(XR_OBJECT_LABEL_MAX_ENUM_ANDROID, 0x7FFFFFFF) + #define XR_LIST_ENUM_XrFutureStateEXT(_) \ _(XR_FUTURE_STATE_PENDING_EXT, 1) \ _(XR_FUTURE_STATE_READY_EXT, 2) \ @@ -2074,107 +2414,135 @@ XR_ENUM_STR(XrResult); _(XR_FACIAL_BLEND_SHAPE_TONGUE_OUT_ML, 45) \ _(XR_FACIAL_BLEND_SHAPE_MAX_ENUM_ML, 0x7FFFFFFF) -#define XR_LIST_ENUM_XrSpatialCapabilityEXT(_) \ - _(XR_SPATIAL_CAPABILITY_PLANE_TRACKING_EXT, 1000741000) \ - _(XR_SPATIAL_CAPABILITY_MARKER_TRACKING_QR_CODE_EXT, 1000743000) \ - _(XR_SPATIAL_CAPABILITY_MARKER_TRACKING_MICRO_QR_CODE_EXT, 1000743001) \ - _(XR_SPATIAL_CAPABILITY_MARKER_TRACKING_ARUCO_MARKER_EXT, 1000743002) \ - _(XR_SPATIAL_CAPABILITY_MARKER_TRACKING_APRIL_TAG_EXT, 1000743003) \ - _(XR_SPATIAL_CAPABILITY_ANCHOR_EXT, 1000762000) \ - _(XR_SPATIAL_CAPABILITY_MAX_ENUM_EXT, 0x7FFFFFFF) - -#define XR_LIST_ENUM_XrSpatialCapabilityFeatureEXT(_) \ - _(XR_SPATIAL_CAPABILITY_FEATURE_MARKER_TRACKING_FIXED_SIZE_MARKERS_EXT, 1000743000) \ - _(XR_SPATIAL_CAPABILITY_FEATURE_MARKER_TRACKING_STATIC_MARKERS_EXT, 1000743001) \ - _(XR_SPATIAL_CAPABILITY_FEATURE_MAX_ENUM_EXT, 0x7FFFFFFF) - -#define XR_LIST_ENUM_XrSpatialComponentTypeEXT(_) \ - _(XR_SPATIAL_COMPONENT_TYPE_BOUNDED_2D_EXT, 1) \ - _(XR_SPATIAL_COMPONENT_TYPE_BOUNDED_3D_EXT, 2) \ - _(XR_SPATIAL_COMPONENT_TYPE_PARENT_EXT, 3) \ - _(XR_SPATIAL_COMPONENT_TYPE_MESH_3D_EXT, 4) \ - _(XR_SPATIAL_COMPONENT_TYPE_PLANE_ALIGNMENT_EXT, 1000741000) \ - _(XR_SPATIAL_COMPONENT_TYPE_MESH_2D_EXT, 1000741001) \ - _(XR_SPATIAL_COMPONENT_TYPE_POLYGON_2D_EXT, 1000741002) \ - _(XR_SPATIAL_COMPONENT_TYPE_PLANE_SEMANTIC_LABEL_EXT, 1000741003) \ - _(XR_SPATIAL_COMPONENT_TYPE_MARKER_EXT, 1000743000) \ - _(XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT, 1000762000) \ - _(XR_SPATIAL_COMPONENT_TYPE_PERSISTENCE_EXT, 1000763000) \ - _(XR_SPATIAL_COMPONENT_TYPE_MAX_ENUM_EXT, 0x7FFFFFFF) +#define XR_LIST_ENUM_XrTrackableMarkerTrackingModeANDROID(_) \ + _(XR_TRACKABLE_MARKER_TRACKING_MODE_DYNAMIC_ANDROID, 0) \ + _(XR_TRACKABLE_MARKER_TRACKING_MODE_STATIC_ANDROID, 1) \ + _(XR_TRACKABLE_MARKER_TRACKING_MODE_MAX_ENUM_ANDROID, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrTrackableMarkerDictionaryANDROID(_) \ + _(XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_4X4_50_ANDROID, 0) \ + _(XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_4X4_100_ANDROID, 1) \ + _(XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_4X4_250_ANDROID, 2) \ + _(XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_4X4_1000_ANDROID, 3) \ + _(XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_5X5_50_ANDROID, 4) \ + _(XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_5X5_100_ANDROID, 5) \ + _(XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_5X5_250_ANDROID, 6) \ + _(XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_5X5_1000_ANDROID, 7) \ + _(XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_6X6_50_ANDROID, 8) \ + _(XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_6X6_100_ANDROID, 9) \ + _(XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_6X6_250_ANDROID, 10) \ + _(XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_6X6_1000_ANDROID, 11) \ + _(XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_7X7_50_ANDROID, 12) \ + _(XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_7X7_100_ANDROID, 13) \ + _(XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_7X7_250_ANDROID, 14) \ + _(XR_TRACKABLE_MARKER_DICTIONARY_ARUCO_7X7_1000_ANDROID, 15) \ + _(XR_TRACKABLE_MARKER_DICTIONARY_APRILTAG_16H5_ANDROID, 16) \ + _(XR_TRACKABLE_MARKER_DICTIONARY_APRILTAG_25H9_ANDROID, 17) \ + _(XR_TRACKABLE_MARKER_DICTIONARY_APRILTAG_36H10_ANDROID, 18) \ + _(XR_TRACKABLE_MARKER_DICTIONARY_APRILTAG_36H11_ANDROID, 19) \ + _(XR_TRACKABLE_MARKER_DICTIONARY_MAX_ENUM_ANDROID, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrSpatialCapabilityEXT(_) \ + _(XR_SPATIAL_CAPABILITY_PLANE_TRACKING_EXT, 1000741000) \ + _(XR_SPATIAL_CAPABILITY_MARKER_TRACKING_QR_CODE_EXT, 1000743000) \ + _(XR_SPATIAL_CAPABILITY_MARKER_TRACKING_MICRO_QR_CODE_EXT, 1000743001) \ + _(XR_SPATIAL_CAPABILITY_MARKER_TRACKING_ARUCO_MARKER_EXT, 1000743002) \ + _(XR_SPATIAL_CAPABILITY_MARKER_TRACKING_APRIL_TAG_EXT, 1000743003) \ + _(XR_SPATIAL_CAPABILITY_ANCHOR_EXT, 1000762000) \ + _(XR_SPATIAL_CAPABILITY_MAX_ENUM_EXT, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrSpatialCapabilityFeatureEXT(_) \ + _(XR_SPATIAL_CAPABILITY_FEATURE_MARKER_TRACKING_FIXED_SIZE_MARKERS_EXT, 1000743000) \ + _(XR_SPATIAL_CAPABILITY_FEATURE_MARKER_TRACKING_STATIC_MARKERS_EXT, 1000743001) \ + _(XR_SPATIAL_CAPABILITY_FEATURE_MAX_ENUM_EXT, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrSpatialComponentTypeEXT(_) \ + _(XR_SPATIAL_COMPONENT_TYPE_BOUNDED_2D_EXT, 1) \ + _(XR_SPATIAL_COMPONENT_TYPE_BOUNDED_3D_EXT, 2) \ + _(XR_SPATIAL_COMPONENT_TYPE_PARENT_EXT, 3) \ + _(XR_SPATIAL_COMPONENT_TYPE_MESH_3D_EXT, 4) \ + _(XR_SPATIAL_COMPONENT_TYPE_PLANE_ALIGNMENT_EXT, 1000741000) \ + _(XR_SPATIAL_COMPONENT_TYPE_MESH_2D_EXT, 1000741001) \ + _(XR_SPATIAL_COMPONENT_TYPE_POLYGON_2D_EXT, 1000741002) \ + _(XR_SPATIAL_COMPONENT_TYPE_PLANE_SEMANTIC_LABEL_EXT, 1000741003) \ + _(XR_SPATIAL_COMPONENT_TYPE_MARKER_EXT, 1000743000) \ + _(XR_SPATIAL_COMPONENT_TYPE_ANCHOR_EXT, 1000762000) \ + _(XR_SPATIAL_COMPONENT_TYPE_PERSISTENCE_EXT, 1000763000) \ + _(XR_SPATIAL_COMPONENT_TYPE_MAX_ENUM_EXT, 0x7FFFFFFF) #define XR_LIST_ENUM_XrSpatialEntityTrackingStateEXT(_) \ - _(XR_SPATIAL_ENTITY_TRACKING_STATE_STOPPED_EXT, 1) \ - _(XR_SPATIAL_ENTITY_TRACKING_STATE_PAUSED_EXT, 2) \ - _(XR_SPATIAL_ENTITY_TRACKING_STATE_TRACKING_EXT, 3) \ - _(XR_SPATIAL_ENTITY_TRACKING_STATE_MAX_ENUM_EXT, 0x7FFFFFFF) + _(XR_SPATIAL_ENTITY_TRACKING_STATE_STOPPED_EXT, 1) \ + _(XR_SPATIAL_ENTITY_TRACKING_STATE_PAUSED_EXT, 2) \ + _(XR_SPATIAL_ENTITY_TRACKING_STATE_TRACKING_EXT, 3) \ + _(XR_SPATIAL_ENTITY_TRACKING_STATE_MAX_ENUM_EXT, 0x7FFFFFFF) #define XR_LIST_ENUM_XrSpatialBufferTypeEXT(_) \ - _(XR_SPATIAL_BUFFER_TYPE_UNKNOWN_EXT, 0) \ - _(XR_SPATIAL_BUFFER_TYPE_STRING_EXT, 1) \ - _(XR_SPATIAL_BUFFER_TYPE_UINT8_EXT, 2) \ - _(XR_SPATIAL_BUFFER_TYPE_UINT16_EXT, 3) \ - _(XR_SPATIAL_BUFFER_TYPE_UINT32_EXT, 4) \ - _(XR_SPATIAL_BUFFER_TYPE_FLOAT_EXT, 5) \ - _(XR_SPATIAL_BUFFER_TYPE_VECTOR2F_EXT, 6) \ - _(XR_SPATIAL_BUFFER_TYPE_VECTOR3F_EXT, 7) \ - _(XR_SPATIAL_BUFFER_TYPE_MAX_ENUM_EXT, 0x7FFFFFFF) - -#define XR_LIST_ENUM_XrSpatialPlaneAlignmentEXT(_) \ - _(XR_SPATIAL_PLANE_ALIGNMENT_HORIZONTAL_UPWARD_EXT, 0) \ - _(XR_SPATIAL_PLANE_ALIGNMENT_HORIZONTAL_DOWNWARD_EXT, 1) \ - _(XR_SPATIAL_PLANE_ALIGNMENT_VERTICAL_EXT, 2) \ - _(XR_SPATIAL_PLANE_ALIGNMENT_ARBITRARY_EXT, 3) \ - _(XR_SPATIAL_PLANE_ALIGNMENT_MAX_ENUM_EXT, 0x7FFFFFFF) - -#define XR_LIST_ENUM_XrSpatialPlaneSemanticLabelEXT(_) \ - _(XR_SPATIAL_PLANE_SEMANTIC_LABEL_UNCATEGORIZED_EXT, 1) \ - _(XR_SPATIAL_PLANE_SEMANTIC_LABEL_FLOOR_EXT, 2) \ - _(XR_SPATIAL_PLANE_SEMANTIC_LABEL_WALL_EXT, 3) \ - _(XR_SPATIAL_PLANE_SEMANTIC_LABEL_CEILING_EXT, 4) \ - _(XR_SPATIAL_PLANE_SEMANTIC_LABEL_TABLE_EXT, 5) \ - _(XR_SPATIAL_PLANE_SEMANTIC_LABEL_MAX_ENUM_EXT, 0x7FFFFFFF) + _(XR_SPATIAL_BUFFER_TYPE_UNKNOWN_EXT, 0) \ + _(XR_SPATIAL_BUFFER_TYPE_STRING_EXT, 1) \ + _(XR_SPATIAL_BUFFER_TYPE_UINT8_EXT, 2) \ + _(XR_SPATIAL_BUFFER_TYPE_UINT16_EXT, 3) \ + _(XR_SPATIAL_BUFFER_TYPE_UINT32_EXT, 4) \ + _(XR_SPATIAL_BUFFER_TYPE_FLOAT_EXT, 5) \ + _(XR_SPATIAL_BUFFER_TYPE_VECTOR2F_EXT, 6) \ + _(XR_SPATIAL_BUFFER_TYPE_VECTOR3F_EXT, 7) \ + _(XR_SPATIAL_BUFFER_TYPE_MAX_ENUM_EXT, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrSpatialPlaneAlignmentEXT(_) \ + _(XR_SPATIAL_PLANE_ALIGNMENT_HORIZONTAL_UPWARD_EXT, 0) \ + _(XR_SPATIAL_PLANE_ALIGNMENT_HORIZONTAL_DOWNWARD_EXT, 1) \ + _(XR_SPATIAL_PLANE_ALIGNMENT_VERTICAL_EXT, 2) \ + _(XR_SPATIAL_PLANE_ALIGNMENT_ARBITRARY_EXT, 3) \ + _(XR_SPATIAL_PLANE_ALIGNMENT_MAX_ENUM_EXT, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrSpatialPlaneSemanticLabelEXT(_) \ + _(XR_SPATIAL_PLANE_SEMANTIC_LABEL_UNCATEGORIZED_EXT, 1) \ + _(XR_SPATIAL_PLANE_SEMANTIC_LABEL_FLOOR_EXT, 2) \ + _(XR_SPATIAL_PLANE_SEMANTIC_LABEL_WALL_EXT, 3) \ + _(XR_SPATIAL_PLANE_SEMANTIC_LABEL_CEILING_EXT, 4) \ + _(XR_SPATIAL_PLANE_SEMANTIC_LABEL_TABLE_EXT, 5) \ + _(XR_SPATIAL_PLANE_SEMANTIC_LABEL_MAX_ENUM_EXT, 0x7FFFFFFF) #define XR_LIST_ENUM_XrSpatialMarkerArucoDictEXT(_) \ - _(XR_SPATIAL_MARKER_ARUCO_DICT_4X4_50_EXT, 1) \ - _(XR_SPATIAL_MARKER_ARUCO_DICT_4X4_100_EXT, 2) \ - _(XR_SPATIAL_MARKER_ARUCO_DICT_4X4_250_EXT, 3) \ - _(XR_SPATIAL_MARKER_ARUCO_DICT_4X4_1000_EXT, 4) \ - _(XR_SPATIAL_MARKER_ARUCO_DICT_5X5_50_EXT, 5) \ - _(XR_SPATIAL_MARKER_ARUCO_DICT_5X5_100_EXT, 6) \ - _(XR_SPATIAL_MARKER_ARUCO_DICT_5X5_250_EXT, 7) \ - _(XR_SPATIAL_MARKER_ARUCO_DICT_5X5_1000_EXT, 8) \ - _(XR_SPATIAL_MARKER_ARUCO_DICT_6X6_50_EXT, 9) \ - _(XR_SPATIAL_MARKER_ARUCO_DICT_6X6_100_EXT, 10) \ - _(XR_SPATIAL_MARKER_ARUCO_DICT_6X6_250_EXT, 11) \ - _(XR_SPATIAL_MARKER_ARUCO_DICT_6X6_1000_EXT, 12) \ - _(XR_SPATIAL_MARKER_ARUCO_DICT_7X7_50_EXT, 13) \ - _(XR_SPATIAL_MARKER_ARUCO_DICT_7X7_100_EXT, 14) \ - _(XR_SPATIAL_MARKER_ARUCO_DICT_7X7_250_EXT, 15) \ - _(XR_SPATIAL_MARKER_ARUCO_DICT_7X7_1000_EXT, 16) \ - _(XR_SPATIAL_MARKER_ARUCO_DICT_MAX_ENUM_EXT, 0x7FFFFFFF) + _(XR_SPATIAL_MARKER_ARUCO_DICT_4X4_50_EXT, 1) \ + _(XR_SPATIAL_MARKER_ARUCO_DICT_4X4_100_EXT, 2) \ + _(XR_SPATIAL_MARKER_ARUCO_DICT_4X4_250_EXT, 3) \ + _(XR_SPATIAL_MARKER_ARUCO_DICT_4X4_1000_EXT, 4) \ + _(XR_SPATIAL_MARKER_ARUCO_DICT_5X5_50_EXT, 5) \ + _(XR_SPATIAL_MARKER_ARUCO_DICT_5X5_100_EXT, 6) \ + _(XR_SPATIAL_MARKER_ARUCO_DICT_5X5_250_EXT, 7) \ + _(XR_SPATIAL_MARKER_ARUCO_DICT_5X5_1000_EXT, 8) \ + _(XR_SPATIAL_MARKER_ARUCO_DICT_6X6_50_EXT, 9) \ + _(XR_SPATIAL_MARKER_ARUCO_DICT_6X6_100_EXT, 10) \ + _(XR_SPATIAL_MARKER_ARUCO_DICT_6X6_250_EXT, 11) \ + _(XR_SPATIAL_MARKER_ARUCO_DICT_6X6_1000_EXT, 12) \ + _(XR_SPATIAL_MARKER_ARUCO_DICT_7X7_50_EXT, 13) \ + _(XR_SPATIAL_MARKER_ARUCO_DICT_7X7_100_EXT, 14) \ + _(XR_SPATIAL_MARKER_ARUCO_DICT_7X7_250_EXT, 15) \ + _(XR_SPATIAL_MARKER_ARUCO_DICT_7X7_1000_EXT, 16) \ + _(XR_SPATIAL_MARKER_ARUCO_DICT_MAX_ENUM_EXT, 0x7FFFFFFF) #define XR_LIST_ENUM_XrSpatialMarkerAprilTagDictEXT(_) \ - _(XR_SPATIAL_MARKER_APRIL_TAG_DICT_16H5_EXT, 1) \ - _(XR_SPATIAL_MARKER_APRIL_TAG_DICT_25H9_EXT, 2) \ - _(XR_SPATIAL_MARKER_APRIL_TAG_DICT_36H10_EXT, 3) \ - _(XR_SPATIAL_MARKER_APRIL_TAG_DICT_36H11_EXT, 4) \ - _(XR_SPATIAL_MARKER_APRIL_TAG_DICT_MAX_ENUM_EXT, 0x7FFFFFFF) - -#define XR_LIST_ENUM_XrSpatialPersistenceScopeEXT(_) \ - _(XR_SPATIAL_PERSISTENCE_SCOPE_SYSTEM_MANAGED_EXT, 1) \ - _(XR_SPATIAL_PERSISTENCE_SCOPE_LOCAL_ANCHORS_EXT, 1000781000) \ - _(XR_SPATIAL_PERSISTENCE_SCOPE_MAX_ENUM_EXT, 0x7FFFFFFF) - -#define XR_LIST_ENUM_XrSpatialPersistenceContextResultEXT(_) \ - _(XR_SPATIAL_PERSISTENCE_CONTEXT_RESULT_SUCCESS_EXT, 0) \ - _(XR_SPATIAL_PERSISTENCE_CONTEXT_RESULT_ENTITY_NOT_TRACKING_EXT, -1000781001) \ - _(XR_SPATIAL_PERSISTENCE_CONTEXT_RESULT_PERSIST_UUID_NOT_FOUND_EXT, -1000781002) \ - _(XR_SPATIAL_PERSISTENCE_CONTEXT_RESULT_MAX_ENUM_EXT, 0x7FFFFFFF) + _(XR_SPATIAL_MARKER_APRIL_TAG_DICT_16H5_EXT, 1) \ + _(XR_SPATIAL_MARKER_APRIL_TAG_DICT_25H9_EXT, 2) \ + _(XR_SPATIAL_MARKER_APRIL_TAG_DICT_36H10_EXT, 3) \ + _(XR_SPATIAL_MARKER_APRIL_TAG_DICT_36H11_EXT, 4) \ + _(XR_SPATIAL_MARKER_APRIL_TAG_DICT_MAX_ENUM_EXT, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrSpatialPersistenceScopeEXT(_) \ + _(XR_SPATIAL_PERSISTENCE_SCOPE_SYSTEM_MANAGED_EXT, 1) \ + _(XR_SPATIAL_PERSISTENCE_SCOPE_LOCAL_ANCHORS_EXT, 1000781000) \ + _(XR_SPATIAL_PERSISTENCE_SCOPE_MAX_ENUM_EXT, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrSpatialPersistenceContextResultEXT(_) \ + _(XR_SPATIAL_PERSISTENCE_CONTEXT_RESULT_SUCCESS_EXT, 0) \ + _(XR_SPATIAL_PERSISTENCE_CONTEXT_RESULT_ENTITY_NOT_TRACKING_EXT, -1000781001) \ + _(XR_SPATIAL_PERSISTENCE_CONTEXT_RESULT_PERSIST_UUID_NOT_FOUND_EXT, -1000781002) \ + _(XR_SPATIAL_PERSISTENCE_CONTEXT_RESULT_MAX_ENUM_EXT, 0x7FFFFFFF) #define XR_LIST_ENUM_XrSpatialPersistenceStateEXT(_) \ - _(XR_SPATIAL_PERSISTENCE_STATE_LOADED_EXT, 1) \ - _(XR_SPATIAL_PERSISTENCE_STATE_NOT_FOUND_EXT, 2) \ - _(XR_SPATIAL_PERSISTENCE_STATE_MAX_ENUM_EXT, 0x7FFFFFFF) + _(XR_SPATIAL_PERSISTENCE_STATE_LOADED_EXT, 1) \ + _(XR_SPATIAL_PERSISTENCE_STATE_NOT_FOUND_EXT, 2) \ + _(XR_SPATIAL_PERSISTENCE_STATE_MAX_ENUM_EXT, 0x7FFFFFFF) #define XR_LIST_ENUM_XrVirtualBoundaryModePICO(_) \ _(XR_VIRTUAL_BOUNDARY_MODE_STATIONARY_PICO, 0) \ @@ -2235,11 +2603,12 @@ XR_ENUM_STR(XrResult); _(XR_SECURE_MR_OPERATOR_TYPE_SWITCH_GLTF_RENDER_STATUS_PICO, 30) \ _(XR_SECURE_MR_OPERATOR_TYPE_UPDATE_GLTF_PICO, 31) \ _(XR_SECURE_MR_OPERATOR_TYPE_RENDER_TEXT_PICO, 32) \ - _(XR_SECURE_MR_OPERATOR_TYPE_LOAD_TEXTURE_PICO, 33) \ - _(XR_SECURE_MR_OPERATOR_TYPE_SVD_PICO, 34) \ - _(XR_SECURE_MR_OPERATOR_TYPE_NORM_PICO, 35) \ - _(XR_SECURE_MR_OPERATOR_TYPE_SWAP_HWC_CHW_PICO, 36) \ - _(XR_SECURE_MROPERATOR_TYPE_MAX_ENUM_PICO, 0x7FFFFFFF) + _(XR_SECURE_MR_OPERATOR_TYPE_LOAD_TEXTURE_PICO, 33) \ + _(XR_SECURE_MR_OPERATOR_TYPE_SVD_PICO, 34) \ + _(XR_SECURE_MR_OPERATOR_TYPE_NORM_PICO, 35) \ + _(XR_SECURE_MR_OPERATOR_TYPE_SWAP_HWC_CHW_PICO, 36) \ + _(XR_SECURE_MR_OPERATOR_TYPE_JAVASCRIPT_PICO, 39) \ + _(XR_SECURE_MROPERATOR_TYPE_MAX_ENUM_PICO, 0x7FFFFFFF) #define XR_LIST_ENUM_XrSecureMrModelEncodingPICO(_) \ _(XR_SECURE_MR_MODEL_ENCODING_FLOAT_32_PICO, 1) \ @@ -2294,6 +2663,8 @@ XR_ENUM_STR(XrResult); _(XR_SECURE_MR_TENSOR_DATA_TYPE_INT32_PICO, 5) \ _(XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO, 6) \ _(XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT64_PICO, 7) \ + _(XR_SECURE_MR_TENSOR_DATA_TYPE_DYNAMIC_TEXTURE_UINT8_PICO, 8) \ + _(XR_SECURE_MR_TENSOR_DATA_TYPE_DYNAMIC_TEXTURE_FLOAT32_PICO, 9) \ _(XR_SECURE_MRTENSOR_DATA_TYPE_MAX_ENUM_PICO, 0x7FFFFFFF) #define XR_LIST_ENUM_XrSecureMrTensorTypePICO(_) \ @@ -2304,6 +2675,7 @@ XR_ENUM_STR(XrResult); _(XR_SECURE_MR_TENSOR_TYPE_TIMESTAMP_PICO, 5) \ _(XR_SECURE_MR_TENSOR_TYPE_MAT_PICO, 6) \ _(XR_SECURE_MR_TENSOR_TYPE_GLTF_PICO, 7) \ + _(XR_SECURE_MR_TENSOR_TYPE_MAT_DYNAMIC_TEXTURE_PICO, 8) \ _(XR_SECURE_MRTENSOR_TYPE_MAX_ENUM_PICO, 0x7FFFFFFF) #define XR_LIST_ENUM_XrSecureMrComparisonPICO(_) \ @@ -2366,65 +2738,111 @@ XR_ENUM_STR(XrResult); _(XR_ADAPTIVE_RESOLUTION_SETTING_POWER_SAVING_PICO, 2) \ _(XR_ADAPTIVE_RESOLUTION_SETTING_MAX_ENUM_PICO, 0x7FFFFFFF) -#define XR_LIST_ENUM_XrDynamicObjectTypePICO(_) \ - _(XR_DYNAMIC_OBJECT_TYPE_UNKNOWN_PICO, 0) \ - _(XR_DYNAMIC_OBJECT_TYPE_KEYBOARD_PICO, 1010018000) \ - _(XR_DYNAMIC_OBJECT_TYPE_MOUSE_PICO, 1010019000) \ - _(XR_DYNAMIC_OBJECT_TYPE_MAX_ENUM_PICO, 0x7FFFFFFF) - -#define XR_LIST_ENUM_XrSpatialEntityComponentTypePICO(_) \ - _(XR_SPATIAL_ENTITY_COMPONENT_TYPE_LOCATION_PICO, 0) \ - _(XR_SPATIAL_ENTITY_COMPONENT_TYPE_SEMANTIC_PICO, 1) \ - _(XR_SPATIAL_ENTITY_COMPONENT_TYPE_BOUNDING_BOX_2D_PICO, 2) \ - _(XR_SPATIAL_ENTITY_COMPONENT_TYPE_POLYGON_PICO, 3) \ - _(XR_SPATIAL_ENTITY_COMPONENT_TYPE_BOUNDING_BOX_3D_PICO, 4) \ - _(XR_SPATIAL_ENTITY_COMPONENT_TYPE_TRIANGLE_MESH_PICO, 5) \ - _(XR_SPATIAL_ENTITY_COMPONENT_TYPE_DYNAMIC_OBJECT_PICO, 1010017000) \ - _(XR_SPATIAL_ENTITY_COMPONENT_TYPE_MAX_ENUM_PICO, 0x7FFFFFFF) - -#define XR_LIST_ENUM_XrSemanticLabelPICO(_) \ - _(XR_SEMANTIC_LABEL_UNKNOWN_PICO, 0) \ - _(XR_SEMANTIC_LABEL_FLOOR_PICO, 1) \ - _(XR_SEMANTIC_LABEL_CEILING_PICO, 2) \ - _(XR_SEMANTIC_LABEL_WALL_PICO, 3) \ - _(XR_SEMANTIC_LABEL_DOOR_PICO, 4) \ - _(XR_SEMANTIC_LABEL_WINDOW_PICO, 5) \ - _(XR_SEMANTIC_LABEL_OPENING_PICO, 6) \ - _(XR_SEMANTIC_LABEL_TABLE_PICO, 7) \ - _(XR_SEMANTIC_LABEL_SOFA_PICO, 8) \ - _(XR_SEMANTIC_LABEL_CHAIR_PICO, 9) \ - _(XR_SEMANTIC_LABEL_HUMAN_PICO, 10) \ - _(XR_SEMANTIC_LABEL_BEAM_PICO, 11) \ - _(XR_SEMANTIC_LABEL_COLUMN_PICO, 12) \ - _(XR_SEMANTIC_LABEL_CURTAIN_PICO, 13) \ - _(XR_SEMANTIC_LABEL_CABINET_PICO, 14) \ - _(XR_SEMANTIC_LABEL_BED_PICO, 15) \ - _(XR_SEMANTIC_LABEL_PLANT_PICO, 16) \ - _(XR_SEMANTIC_LABEL_SCREEN_PICO, 17) \ - _(XR_SEMANTIC_LABEL_VIRTUAL_WALL_PICO, 18) \ - _(XR_SEMANTIC_LABEL_REFRIGERATOR_PICO, 19) \ - _(XR_SEMANTIC_LABEL_WASHING_MACHINE_PICO, 20) \ - _(XR_SEMANTIC_LABEL_AIR_CONDITIONER_PICO, 21) \ - _(XR_SEMANTIC_LABEL_LAMP_PICO, 22) \ - _(XR_SEMANTIC_LABEL_WALL_ART_PICO, 23) \ - _(XR_SEMANTIC_LABEL_STAIRWAY_PICO, 24) \ - _(XR_SEMANTIC_LABEL_MAX_ENUM_PICO, 0x7FFFFFFF) - -#define XR_LIST_ENUM_XrSenseDataProviderStatePICO(_) \ - _(XR_SENSE_DATA_PROVIDER_STATE_INITIALIZED_PICO, 0) \ - _(XR_SENSE_DATA_PROVIDER_STATE_RUNNING_PICO, 1) \ - _(XR_SENSE_DATA_PROVIDER_STATE_STOPPED_PICO, 2) \ - _(XR_SENSE_DATA_PROVIDER_STATE_MAX_ENUM_PICO, 0x7FFFFFFF) +#define XR_LIST_ENUM_XrDynamicObjectTypePICO(_) \ + _(XR_DYNAMIC_OBJECT_TYPE_UNKNOWN_PICO, 0) \ + _(XR_DYNAMIC_OBJECT_TYPE_KEYBOARD_PICO, 1010018000) \ + _(XR_DYNAMIC_OBJECT_TYPE_MOUSE_PICO, 1010019000) \ + _(XR_DYNAMIC_OBJECT_TYPE_MAX_ENUM_PICO, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrSpatialEntityComponentTypePICO(_) \ + _(XR_SPATIAL_ENTITY_COMPONENT_TYPE_LOCATION_PICO, 0) \ + _(XR_SPATIAL_ENTITY_COMPONENT_TYPE_SEMANTIC_PICO, 1) \ + _(XR_SPATIAL_ENTITY_COMPONENT_TYPE_BOUNDING_BOX_2D_PICO, 2) \ + _(XR_SPATIAL_ENTITY_COMPONENT_TYPE_POLYGON_PICO, 3) \ + _(XR_SPATIAL_ENTITY_COMPONENT_TYPE_BOUNDING_BOX_3D_PICO, 4) \ + _(XR_SPATIAL_ENTITY_COMPONENT_TYPE_TRIANGLE_MESH_PICO, 5) \ + _(XR_SPATIAL_ENTITY_COMPONENT_TYPE_SPHERE_PICO, 6) \ + _(XR_SPATIAL_ENTITY_COMPONENT_TYPE_DYNAMIC_OBJECT_PICO, 1010017000) \ + _(XR_SPATIAL_ENTITY_COMPONENT_TYPE_MAX_ENUM_PICO, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrCameraPropertyTypePICO(_) \ + _(XR_CAMERA_PROPERTY_TYPE_FACING_PICO, 1) \ + _(XR_CAMERA_PROPERTY_TYPE_POSITION_PICO, 2) \ + _(XR_CAMERA_PROPERTY_TYPE_CAMERA_TYPE_PICO, 3) \ + _(XR_CAMERA_PROPERTY_TYPE_MAX_ENUM_PICO, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrCameraFacingPICO(_) \ + _(XR_CAMERA_FACING_WORLD_PICO, 1) \ + _(XR_CAMERA_FACING_MAX_ENUM_PICO, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrCameraPositionPICO(_) \ + _(XR_CAMERA_POSITION_UNSPECIFIED_PICO, 1) \ + _(XR_CAMERA_POSITION_LEFT_PICO, 2) \ + _(XR_CAMERA_POSITION_RIGHT_PICO, 3) \ + _(XR_CAMERA_POSITION_MAX_ENUM_PICO, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrCameraTypePICO(_) \ + _(XR_CAMERA_TYPE_PASSTHROUGH_COLOR_PICO, 1) \ + _(XR_CAMERA_TYPE_MAX_ENUM_PICO, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrCameraCapabilityTypePICO(_) \ + _(XR_CAMERA_CAPABILITY_TYPE_IMAGE_RESOLUTION_PICO, 1) \ + _(XR_CAMERA_CAPABILITY_TYPE_IMAGE_FORMAT_PICO, 2) \ + _(XR_CAMERA_CAPABILITY_TYPE_DATA_TRANSFER_TYPE_PICO, 3) \ + _(XR_CAMERA_CAPABILITY_TYPE_CAMERA_MODEL_PICO, 4) \ + _(XR_CAMERA_CAPABILITY_TYPE_IMAGE_FPS_PICO, 5) \ + _(XR_CAMERA_CAPABILITY_TYPE_MAX_ENUM_PICO, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrCameraDataTransferTypePICO(_) \ + _(XR_CAMERA_DATA_TRANSFER_TYPE_RAW_BUFFER_PICO, 1) \ + _(XR_CAMERA_DATA_TRANSFER_TYPE_MAX_ENUM_PICO, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrCameraImageFormatPICO(_) \ + _(XR_CAMERA_IMAGE_FORMAT_RGBA_8888_PICO, 1) \ + _(XR_CAMERA_IMAGE_FORMAT_MAX_ENUM_PICO, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrCameraModelPICO(_) \ + _(XR_CAMERA_MODEL_PINHOLE_PICO, 1) \ + _(XR_CAMERA_MODEL_MAX_ENUM_PICO, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrCameraImageFpsPICO(_) \ + _(XR_CAMERA_IMAGE_FPS_30_PICO, 1) \ + _(XR_CAMERA_IMAGE_FPS_60_PICO, 2) \ + _(XR_CAMERA_IMAGE_FPS_MAX_ENUM_PICO, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrSemanticLabelPICO(_) \ + _(XR_SEMANTIC_LABEL_UNKNOWN_PICO, 0) \ + _(XR_SEMANTIC_LABEL_FLOOR_PICO, 1) \ + _(XR_SEMANTIC_LABEL_CEILING_PICO, 2) \ + _(XR_SEMANTIC_LABEL_WALL_PICO, 3) \ + _(XR_SEMANTIC_LABEL_DOOR_PICO, 4) \ + _(XR_SEMANTIC_LABEL_WINDOW_PICO, 5) \ + _(XR_SEMANTIC_LABEL_OPENING_PICO, 6) \ + _(XR_SEMANTIC_LABEL_TABLE_PICO, 7) \ + _(XR_SEMANTIC_LABEL_SOFA_PICO, 8) \ + _(XR_SEMANTIC_LABEL_CHAIR_PICO, 9) \ + _(XR_SEMANTIC_LABEL_HUMAN_PICO, 10) \ + _(XR_SEMANTIC_LABEL_BEAM_PICO, 11) \ + _(XR_SEMANTIC_LABEL_COLUMN_PICO, 12) \ + _(XR_SEMANTIC_LABEL_CURTAIN_PICO, 13) \ + _(XR_SEMANTIC_LABEL_CABINET_PICO, 14) \ + _(XR_SEMANTIC_LABEL_BED_PICO, 15) \ + _(XR_SEMANTIC_LABEL_PLANT_PICO, 16) \ + _(XR_SEMANTIC_LABEL_SCREEN_PICO, 17) \ + _(XR_SEMANTIC_LABEL_VIRTUAL_WALL_PICO, 18) \ + _(XR_SEMANTIC_LABEL_REFRIGERATOR_PICO, 19) \ + _(XR_SEMANTIC_LABEL_WASHING_MACHINE_PICO, 20) \ + _(XR_SEMANTIC_LABEL_AIR_CONDITIONER_PICO, 21) \ + _(XR_SEMANTIC_LABEL_LAMP_PICO, 22) \ + _(XR_SEMANTIC_LABEL_WALL_ART_PICO, 23) \ + _(XR_SEMANTIC_LABEL_STAIRWAY_PICO, 24) \ + _(XR_SEMANTIC_LABEL_MAX_ENUM_PICO, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrSenseDataProviderStatePICO(_) \ + _(XR_SENSE_DATA_PROVIDER_STATE_INITIALIZED_PICO, 0) \ + _(XR_SENSE_DATA_PROVIDER_STATE_RUNNING_PICO, 1) \ + _(XR_SENSE_DATA_PROVIDER_STATE_STOPPED_PICO, 2) \ + _(XR_SENSE_DATA_PROVIDER_STATE_MAX_ENUM_PICO, 0x7FFFFFFF) #define XR_LIST_ENUM_XrPersistenceLocationPICO(_) \ - _(XR_PERSISTENCE_LOCATION_LOCAL_PICO, 0) \ - _(XR_PERSISTENCE_LOCATION_MAX_ENUM_PICO, 0x7FFFFFFF) + _(XR_PERSISTENCE_LOCATION_LOCAL_PICO, 0) \ + _(XR_PERSISTENCE_LOCATION_MAX_ENUM_PICO, 0x7FFFFFFF) #define XR_LIST_ENUM_XrSpatialMeshLodPICO(_) \ - _(XR_SPATIAL_MESH_LOD_COARSE_PICO, 0) \ - _(XR_SPATIAL_MESH_LOD_MEDIUM_PICO, 1) \ - _(XR_SPATIAL_MESH_LOD_FINE_PICO, 2) \ - _(XR_SPATIAL_MESH_LOD_MAX_ENUM_PICO, 0x7FFFFFFF) + _(XR_SPATIAL_MESH_LOD_COARSE_PICO, 0) \ + _(XR_SPATIAL_MESH_LOD_MEDIUM_PICO, 1) \ + _(XR_SPATIAL_MESH_LOD_FINE_PICO, 2) \ + _(XR_SPATIAL_MESH_LOD_MAX_ENUM_PICO, 0x7FFFFFFF) #define XR_LIST_BITS_XrInstanceCreateFlags(_) @@ -2622,11 +3040,11 @@ XR_ENUM_STR(XrResult); #define XR_LIST_BITS_XrFoveationDynamicFlagsHTC(_) \ _(XR_FOVEATION_DYNAMIC_LEVEL_ENABLED_BIT_HTC, 0x00000001) \ _(XR_FOVEATION_DYNAMIC_CLEAR_FOV_ENABLED_BIT_HTC, 0x00000002) \ - _(XR_FOVEATION_DYNAMIC_FOCAL_CENTER_OFFSET_ENABLED_BIT_HTC, 0x00000004) + _(XR_FOVEATION_DYNAMIC_FOCAL_CENTER_OFFSET_ENABLED_BIT_HTC, 0x00000004) \ -#define XR_LIST_BITS_XrSpatialMeshConfigFlagsBD(_) \ - _(XR_SPATIAL_MESH_CONFIG_SEMANTIC_BIT_BD, 0x00000001) \ - _(XR_SPATIAL_MESH_CONFIG_ALIGN_SEMANTIC_WITH_VERTEX_BIT_BD, 0x00000002) +#define XR_LIST_BITS_XrSpatialMeshConfigFlagsBD(_) \ + _(XR_SPATIAL_MESH_CONFIG_SEMANTIC_BIT_BD, 0x00000001) \ + _(XR_SPATIAL_MESH_CONFIG_ALIGN_SEMANTIC_WITH_VERTEX_BIT_BD, 0x00000002) \ #define XR_LIST_BITS_XrPlaneDetectionCapabilityFlagsEXT(_) \ _(XR_PLANE_DETECTION_CAPABILITY_PLANE_DETECTION_BIT_EXT, 0x00000001) \ @@ -2666,17 +3084,13 @@ XR_ENUM_STR(XrResult); _(XR_EYE_TRACKER_LEFT_BIT_PICO, 0x00000001) \ _(XR_EYE_TRACKER_RIGHT_BIT_PICO, 0x00000002) \ -#define XR_LIST_BITS_XrEyeTrackerTrackingStateFlagsPICO(_) \ - _(XR_EYE_TRACKER_TRACKING_STATE_LEFT_EYE_BIT_PICO, 0x00000001) \ - _(XR_EYE_TRACKER_TRACKING_STATE_RIGHT_EYE_BIT_PICO, 0x00000002) \ - #define XR_LIST_BITS_XrSpaceAccelerationFlagsPICO(_) \ _(XR_SPACE_ACCELERATION_LINEAR_VALID_BIT_PICO, 0x00000001) \ - _(XR_SPACE_ACCELERATION_ANGULAR_VALID_BIT_PICO, 0x00000002) + _(XR_SPACE_ACCELERATION_ANGULAR_VALID_BIT_PICO, 0x00000002) \ -#define XR_LIST_BITS_XrSpatialMeshConfigFlagsPICO(_) \ - _(XR_SPATIAL_MESH_CONFIG_SEMANTIC_BIT_PICO, 0x00000001) \ - _(XR_SPATIAL_MESH_CONFIG_ALIGN_SEMANTIC_WITH_VERTEX_BIT_PICO, 0x00000002) +#define XR_LIST_BITS_XrSpatialMeshConfigFlagsPICO(_) \ + _(XR_SPATIAL_MESH_CONFIG_SEMANTIC_BIT_PICO, 0x00000001) \ + _(XR_SPATIAL_MESH_CONFIG_ALIGN_SEMANTIC_WITH_VERTEX_BIT_PICO, 0x00000002) \ /// Calls your macro with the name of each member of XrApiLayerProperties, in order. #define XR_LIST_STRUCT_XrApiLayerProperties(_) \ @@ -5561,6 +5975,63 @@ XR_ENUM_STR(XrResult); _(next) \ _(id) \ +/// Calls your macro with the name of each member of XrSystemSpaceDiscoveryPropertiesMETA, in order. +#define XR_LIST_STRUCT_XrSystemSpaceDiscoveryPropertiesMETA(_) \ + _(type) \ + _(next) \ + _(supportsSpaceDiscovery) \ + +/// Calls your macro with the name of each member of XrSpaceFilterBaseHeaderMETA, in order. +#define XR_LIST_STRUCT_XrSpaceFilterBaseHeaderMETA(_) \ + _(type) \ + _(next) \ + +/// Calls your macro with the name of each member of XrSpaceDiscoveryInfoMETA, in order. +#define XR_LIST_STRUCT_XrSpaceDiscoveryInfoMETA(_) \ + _(type) \ + _(next) \ + _(filterCount) \ + _(filters) \ + +/// Calls your macro with the name of each member of XrSpaceFilterUuidMETA, in order. +#define XR_LIST_STRUCT_XrSpaceFilterUuidMETA(_) \ + _(type) \ + _(next) \ + _(uuidCount) \ + _(uuids) \ + +/// Calls your macro with the name of each member of XrSpaceFilterComponentMETA, in order. +#define XR_LIST_STRUCT_XrSpaceFilterComponentMETA(_) \ + _(type) \ + _(next) \ + _(componentType) \ + +/// Calls your macro with the name of each member of XrSpaceDiscoveryResultMETA, in order. +#define XR_LIST_STRUCT_XrSpaceDiscoveryResultMETA(_) \ + _(space) \ + _(uuid) \ + +/// Calls your macro with the name of each member of XrSpaceDiscoveryResultsMETA, in order. +#define XR_LIST_STRUCT_XrSpaceDiscoveryResultsMETA(_) \ + _(type) \ + _(next) \ + _(resultCapacityInput) \ + _(resultCountOutput) \ + _(results) \ + +/// Calls your macro with the name of each member of XrEventDataSpaceDiscoveryResultsAvailableMETA, in order. +#define XR_LIST_STRUCT_XrEventDataSpaceDiscoveryResultsAvailableMETA(_) \ + _(type) \ + _(next) \ + _(requestId) \ + +/// Calls your macro with the name of each member of XrEventDataSpaceDiscoveryCompleteMETA, in order. +#define XR_LIST_STRUCT_XrEventDataSpaceDiscoveryCompleteMETA(_) \ + _(type) \ + _(next) \ + _(requestId) \ + _(result) \ + /// Calls your macro with the name of each member of XrRecommendedLayerResolutionMETA, in order. #define XR_LIST_STRUCT_XrRecommendedLayerResolutionMETA(_) \ _(type) \ @@ -5575,6 +6046,42 @@ XR_ENUM_STR(XrResult); _(layer) \ _(predictedDisplayTime) \ +/// Calls your macro with the name of each member of XrSystemSpacePersistencePropertiesMETA, in order. +#define XR_LIST_STRUCT_XrSystemSpacePersistencePropertiesMETA(_) \ + _(type) \ + _(next) \ + _(supportsSpacePersistence) \ + +/// Calls your macro with the name of each member of XrSpacesSaveInfoMETA, in order. +#define XR_LIST_STRUCT_XrSpacesSaveInfoMETA(_) \ + _(type) \ + _(next) \ + _(spaceCount) \ + _(spaces) \ + +/// Calls your macro with the name of each member of XrEventDataSpacesSaveResultMETA, in order. +#define XR_LIST_STRUCT_XrEventDataSpacesSaveResultMETA(_) \ + _(type) \ + _(next) \ + _(requestId) \ + _(result) \ + +/// Calls your macro with the name of each member of XrSpacesEraseInfoMETA, in order. +#define XR_LIST_STRUCT_XrSpacesEraseInfoMETA(_) \ + _(type) \ + _(next) \ + _(spaceCount) \ + _(spaces) \ + _(uuidCount) \ + _(uuids) \ + +/// Calls your macro with the name of each member of XrEventDataSpacesEraseResultMETA, in order. +#define XR_LIST_STRUCT_XrEventDataSpacesEraseResultMETA(_) \ + _(type) \ + _(next) \ + _(requestId) \ + _(result) \ + /// Calls your macro with the name of each member of XrPassthroughColorLutDataMETA, in order. #define XR_LIST_STRUCT_XrPassthroughColorLutDataMETA(_) \ _(bufferSize) \ @@ -5629,13 +6136,13 @@ XR_ENUM_STR(XrResult); _(vertices) \ _(indexCapacityInput) \ _(indexCountOutput) \ - _(indices) + _(indices) \ /// Calls your macro with the name of each member of XrSystemPropertiesBodyTrackingFullBodyMETA, in order. #define XR_LIST_STRUCT_XrSystemPropertiesBodyTrackingFullBodyMETA(_) \ - _(type) \ - _(next) \ - _(supportsFullBodyTracking) + _(type) \ + _(next) \ + _(supportsFullBodyTracking) \ /// Calls your macro with the name of each member of XrEventDataPassthroughLayerResumedMETA, in order. #define XR_LIST_STRUCT_XrEventDataPassthroughLayerResumedMETA(_) \ @@ -5643,6 +6150,24 @@ XR_ENUM_STR(XrResult); _(next) \ _(layer) \ +/// Calls your macro with the name of each member of XrBodyTrackingCalibrationStatusMETA, in order. +#define XR_LIST_STRUCT_XrBodyTrackingCalibrationStatusMETA(_) \ + _(type) \ + _(next) \ + _(status) \ + +/// Calls your macro with the name of each member of XrBodyTrackingCalibrationInfoMETA, in order. +#define XR_LIST_STRUCT_XrBodyTrackingCalibrationInfoMETA(_) \ + _(type) \ + _(next) \ + _(bodyHeight) \ + +/// Calls your macro with the name of each member of XrSystemPropertiesBodyTrackingCalibrationMETA, in order. +#define XR_LIST_STRUCT_XrSystemPropertiesBodyTrackingCalibrationMETA(_) \ + _(type) \ + _(next) \ + _(supportsHeightOverride) \ + /// Calls your macro with the name of each member of XrSystemFaceTrackingProperties2FB, in order. #define XR_LIST_STRUCT_XrSystemFaceTrackingProperties2FB(_) \ _(type) \ @@ -5756,107 +6281,108 @@ XR_ENUM_STR(XrResult); _(type) \ _(next) \ _(supportsEnvironmentDepth) \ - _(supportsHandRemoval) + _(supportsHandRemoval) \ /// Calls your macro with the name of each member of XrRenderModelCreateInfoEXT, in order. #define XR_LIST_STRUCT_XrRenderModelCreateInfoEXT(_) \ - _(type) \ - _(next) \ - _(renderModelId) \ - _(gltfExtensionCount) \ - _(gltfExtensions) + _(type) \ + _(next) \ + _(renderModelId) \ + _(gltfExtensionCount) \ + _(gltfExtensions) \ /// Calls your macro with the name of each member of XrRenderModelPropertiesGetInfoEXT, in order. #define XR_LIST_STRUCT_XrRenderModelPropertiesGetInfoEXT(_) \ - _(type) \ - _(next) + _(type) \ + _(next) \ /// Calls your macro with the name of each member of XrRenderModelPropertiesEXT, in order. #define XR_LIST_STRUCT_XrRenderModelPropertiesEXT(_) \ - _(type) \ - _(next) \ - _(cacheId) \ - _(animatableNodeCount) + _(type) \ + _(next) \ + _(cacheId) \ + _(animatableNodeCount) \ /// Calls your macro with the name of each member of XrRenderModelSpaceCreateInfoEXT, in order. #define XR_LIST_STRUCT_XrRenderModelSpaceCreateInfoEXT(_) \ - _(type) \ - _(next) \ - _(renderModel) + _(type) \ + _(next) \ + _(renderModel) \ /// Calls your macro with the name of each member of XrRenderModelStateGetInfoEXT, in order. #define XR_LIST_STRUCT_XrRenderModelStateGetInfoEXT(_) \ - _(type) \ - _(next) \ - _(displayTime) + _(type) \ + _(next) \ + _(displayTime) \ /// Calls your macro with the name of each member of XrRenderModelNodeStateEXT, in order. #define XR_LIST_STRUCT_XrRenderModelNodeStateEXT(_) \ - _(nodePose) \ - _(isVisible) + _(nodePose) \ + _(isVisible) \ /// Calls your macro with the name of each member of XrRenderModelStateEXT, in order. #define XR_LIST_STRUCT_XrRenderModelStateEXT(_) \ - _(type) \ - _(next) \ - _(nodeStateCount) \ - _(nodeStates) + _(type) \ + _(next) \ + _(nodeStateCount) \ + _(nodeStates) \ /// Calls your macro with the name of each member of XrRenderModelAssetCreateInfoEXT, in order. #define XR_LIST_STRUCT_XrRenderModelAssetCreateInfoEXT(_) \ - _(type) \ - _(next) \ - _(cacheId) + _(type) \ + _(next) \ + _(cacheId) \ /// Calls your macro with the name of each member of XrRenderModelAssetDataGetInfoEXT, in order. #define XR_LIST_STRUCT_XrRenderModelAssetDataGetInfoEXT(_) \ - _(type) \ - _(next) + _(type) \ + _(next) \ /// Calls your macro with the name of each member of XrRenderModelAssetDataEXT, in order. #define XR_LIST_STRUCT_XrRenderModelAssetDataEXT(_) \ - _(type) \ - _(next) \ - _(bufferCapacityInput) \ - _(bufferCountOutput) \ - _(buffer) + _(type) \ + _(next) \ + _(bufferCapacityInput) \ + _(bufferCountOutput) \ + _(buffer) \ /// Calls your macro with the name of each member of XrRenderModelAssetPropertiesGetInfoEXT, in order. #define XR_LIST_STRUCT_XrRenderModelAssetPropertiesGetInfoEXT(_) \ - _(type) \ - _(next) + _(type) \ + _(next) \ /// Calls your macro with the name of each member of XrRenderModelAssetNodePropertiesEXT, in order. -#define XR_LIST_STRUCT_XrRenderModelAssetNodePropertiesEXT(_) _(uniqueName) +#define XR_LIST_STRUCT_XrRenderModelAssetNodePropertiesEXT(_) \ + _(uniqueName) \ /// Calls your macro with the name of each member of XrRenderModelAssetPropertiesEXT, in order. #define XR_LIST_STRUCT_XrRenderModelAssetPropertiesEXT(_) \ - _(type) \ - _(next) \ - _(nodePropertyCount) \ - _(nodeProperties) + _(type) \ + _(next) \ + _(nodePropertyCount) \ + _(nodeProperties) \ /// Calls your macro with the name of each member of XrInteractionRenderModelIdsEnumerateInfoEXT, in order. #define XR_LIST_STRUCT_XrInteractionRenderModelIdsEnumerateInfoEXT(_) \ - _(type) \ - _(next) + _(type) \ + _(next) \ /// Calls your macro with the name of each member of XrInteractionRenderModelSubactionPathInfoEXT, in order. #define XR_LIST_STRUCT_XrInteractionRenderModelSubactionPathInfoEXT(_) \ - _(type) \ - _(next) + _(type) \ + _(next) \ /// Calls your macro with the name of each member of XrInteractionRenderModelTopLevelUserPathGetInfoEXT, in order. #define XR_LIST_STRUCT_XrInteractionRenderModelTopLevelUserPathGetInfoEXT(_) \ - _(type) \ - _(next) \ - _(topLevelUserPathCount) \ - _(topLevelUserPaths) + _(type) \ + _(next) \ + _(topLevelUserPathCount) \ + _(topLevelUserPaths) \ /// Calls your macro with the name of each member of XrEventDataInteractionRenderModelsChangedEXT, in order. #define XR_LIST_STRUCT_XrEventDataInteractionRenderModelsChangedEXT(_) \ - _(type) \ - _(next) + _(type) \ + _(next) \ /// Calls your macro with the name of each member of XrPassthroughCreateInfoHTC, in order. #define XR_LIST_STRUCT_XrPassthroughCreateInfoHTC(_) \ @@ -6042,271 +6568,306 @@ XR_ENUM_STR(XrResult); _(next) \ _(allJointPosesTracked) \ _(jointLocationCount) \ - _(jointLocations) + _(jointLocations) \ + +/// Calls your macro with the name of each member of XrSystemFacialSimulationPropertiesBD, in order. +#define XR_LIST_STRUCT_XrSystemFacialSimulationPropertiesBD(_) \ + _(type) \ + _(next) \ + _(supportsFaceTracking) \ + +/// Calls your macro with the name of each member of XrFaceTrackerCreateInfoBD, in order. +#define XR_LIST_STRUCT_XrFaceTrackerCreateInfoBD(_) \ + _(type) \ + _(next) \ + _(mode) \ + +/// Calls your macro with the name of each member of XrFacialSimulationDataGetInfoBD, in order. +#define XR_LIST_STRUCT_XrFacialSimulationDataGetInfoBD(_) \ + _(type) \ + _(next) \ + _(time) \ + +/// Calls your macro with the name of each member of XrFacialSimulationDataBD, in order. +#define XR_LIST_STRUCT_XrFacialSimulationDataBD(_) \ + _(type) \ + _(next) \ + _(faceExpressionWeightCount) \ + _(faceExpressionWeights) \ + _(isUpperFaceDataValid) \ + _(isLowerFaceDataValid) \ + _(time) \ + +/// Calls your macro with the name of each member of XrLipExpressionDataBD, in order. +#define XR_LIST_STRUCT_XrLipExpressionDataBD(_) \ + _(type) \ + _(next) \ + _(lipsyncExpressionWeightCount) \ + _(lipsyncExpressionWeights) \ /// Calls your macro with the name of each member of XrSystemSpatialSensingPropertiesBD, in order. #define XR_LIST_STRUCT_XrSystemSpatialSensingPropertiesBD(_) \ - _(type) \ - _(next) \ - _(supportsSpatialSensing) + _(type) \ + _(next) \ + _(supportsSpatialSensing) \ /// Calls your macro with the name of each member of XrSpatialEntityComponentGetInfoBD, in order. #define XR_LIST_STRUCT_XrSpatialEntityComponentGetInfoBD(_) \ - _(type) \ - _(next) \ - _(entityId) \ - _(componentType) + _(type) \ + _(next) \ + _(entityId) \ + _(componentType) \ /// Calls your macro with the name of each member of XrSpatialEntityComponentDataBaseHeaderBD, in order. #define XR_LIST_STRUCT_XrSpatialEntityComponentDataBaseHeaderBD(_) \ - _(type) \ - _(next) + _(type) \ + _(next) \ /// Calls your macro with the name of each member of XrSpatialEntityLocationGetInfoBD, in order. #define XR_LIST_STRUCT_XrSpatialEntityLocationGetInfoBD(_) \ - _(type) \ - _(next) \ - _(baseSpace) + _(type) \ + _(next) \ + _(baseSpace) \ /// Calls your macro with the name of each member of XrSpatialEntityComponentDataLocationBD, in order. #define XR_LIST_STRUCT_XrSpatialEntityComponentDataLocationBD(_) \ - _(type) \ - _(next) \ - _(location) + _(type) \ + _(next) \ + _(location) \ /// Calls your macro with the name of each member of XrSpatialEntityComponentDataSemanticBD, in order. #define XR_LIST_STRUCT_XrSpatialEntityComponentDataSemanticBD(_) \ - _(type) \ - _(next) \ - _(labelCapacityInput) \ - _(labelCountOutput) \ - _(labels) + _(type) \ + _(next) \ + _(labelCapacityInput) \ + _(labelCountOutput) \ + _(labels) \ /// Calls your macro with the name of each member of XrSpatialEntityComponentDataBoundingBox2DBD, in order. #define XR_LIST_STRUCT_XrSpatialEntityComponentDataBoundingBox2DBD(_) \ - _(type) \ - _(next) \ - _(boundingBox2D) + _(type) \ + _(next) \ + _(boundingBox2D) \ /// Calls your macro with the name of each member of XrSpatialEntityComponentDataPolygonBD, in order. #define XR_LIST_STRUCT_XrSpatialEntityComponentDataPolygonBD(_) \ - _(type) \ - _(next) \ - _(vertexCapacityInput) \ - _(vertexCountOutput) \ - _(vertices) + _(type) \ + _(next) \ + _(vertexCapacityInput) \ + _(vertexCountOutput) \ + _(vertices) \ /// Calls your macro with the name of each member of XrSpatialEntityComponentDataBoundingBox3DBD, in order. #define XR_LIST_STRUCT_XrSpatialEntityComponentDataBoundingBox3DBD(_) \ - _(type) \ - _(next) \ - _(boundingBox3D) + _(type) \ + _(next) \ + _(boundingBox3D) \ /// Calls your macro with the name of each member of XrSpatialEntityComponentDataTriangleMeshBD, in order. #define XR_LIST_STRUCT_XrSpatialEntityComponentDataTriangleMeshBD(_) \ - _(type) \ - _(next) \ - _(vertexCapacityInput) \ - _(vertexCountOutput) \ - _(vertices) \ - _(indexCapacityInput) \ - _(indexCountOutput) \ - _(indices) + _(type) \ + _(next) \ + _(vertexCapacityInput) \ + _(vertexCountOutput) \ + _(vertices) \ + _(indexCapacityInput) \ + _(indexCountOutput) \ + _(indices) \ /// Calls your macro with the name of each member of XrSenseDataProviderCreateInfoBD, in order. #define XR_LIST_STRUCT_XrSenseDataProviderCreateInfoBD(_) \ - _(type) \ - _(next) \ - _(providerType) + _(type) \ + _(next) \ + _(providerType) \ /// Calls your macro with the name of each member of XrSenseDataProviderStartInfoBD, in order. #define XR_LIST_STRUCT_XrSenseDataProviderStartInfoBD(_) \ - _(type) \ - _(next) + _(type) \ + _(next) \ /// Calls your macro with the name of each member of XrEventDataSenseDataProviderStateChangedBD, in order. #define XR_LIST_STRUCT_XrEventDataSenseDataProviderStateChangedBD(_) \ - _(type) \ - _(next) \ - _(provider) \ - _(newState) + _(type) \ + _(next) \ + _(provider) \ + _(newState) \ /// Calls your macro with the name of each member of XrEventDataSenseDataUpdatedBD, in order. #define XR_LIST_STRUCT_XrEventDataSenseDataUpdatedBD(_) \ - _(type) \ - _(next) \ - _(provider) + _(type) \ + _(next) \ + _(provider) \ /// Calls your macro with the name of each member of XrSenseDataQueryInfoBD, in order. #define XR_LIST_STRUCT_XrSenseDataQueryInfoBD(_) \ - _(type) \ - _(next) + _(type) \ + _(next) \ /// Calls your macro with the name of each member of XrSenseDataQueryCompletionBD, in order. #define XR_LIST_STRUCT_XrSenseDataQueryCompletionBD(_) \ - _(type) \ - _(next) \ - _(futureResult) \ - _(snapshot) + _(type) \ + _(next) \ + _(futureResult) \ + _(snapshot) \ /// Calls your macro with the name of each member of XrQueriedSenseDataGetInfoBD, in order. #define XR_LIST_STRUCT_XrQueriedSenseDataGetInfoBD(_) \ - _(type) \ - _(next) + _(type) \ + _(next) \ /// Calls your macro with the name of each member of XrSpatialEntityStateBD, in order. #define XR_LIST_STRUCT_XrSpatialEntityStateBD(_) \ - _(type) \ - _(next) \ - _(entityId) \ - _(lastUpdateTime) \ - _(uuid) + _(type) \ + _(next) \ + _(entityId) \ + _(lastUpdateTime) \ + _(uuid) \ /// Calls your macro with the name of each member of XrQueriedSenseDataBD, in order. #define XR_LIST_STRUCT_XrQueriedSenseDataBD(_) \ - _(type) \ - _(next) \ - _(stateCapacityInput) \ - _(stateCountOutput) \ - _(states) + _(type) \ + _(next) \ + _(stateCapacityInput) \ + _(stateCountOutput) \ + _(states) \ /// Calls your macro with the name of each member of XrSenseDataFilterUuidBD, in order. #define XR_LIST_STRUCT_XrSenseDataFilterUuidBD(_) \ - _(type) \ - _(next) \ - _(uuidCount) \ - _(uuids) + _(type) \ + _(next) \ + _(uuidCount) \ + _(uuids) \ /// Calls your macro with the name of each member of XrSenseDataFilterSemanticBD, in order. #define XR_LIST_STRUCT_XrSenseDataFilterSemanticBD(_) \ - _(type) \ - _(next) \ - _(labelCount) \ - _(labels) + _(type) \ + _(next) \ + _(labelCount) \ + _(labels) \ /// Calls your macro with the name of each member of XrSpatialEntityAnchorCreateInfoBD, in order. #define XR_LIST_STRUCT_XrSpatialEntityAnchorCreateInfoBD(_) \ - _(type) \ - _(next) \ - _(snapshot) \ - _(entityId) + _(type) \ + _(next) \ + _(snapshot) \ + _(entityId) \ /// Calls your macro with the name of each member of XrAnchorSpaceCreateInfoBD, in order. #define XR_LIST_STRUCT_XrAnchorSpaceCreateInfoBD(_) \ - _(type) \ - _(next) \ - _(anchor) \ - _(poseInAnchorSpace) + _(type) \ + _(next) \ + _(anchor) \ + _(poseInAnchorSpace) \ /// Calls your macro with the name of each member of XrFutureCompletionEXT, in order. #define XR_LIST_STRUCT_XrFutureCompletionEXT(_) \ - _(type) \ - _(next) \ - _(futureResult) + _(type) \ + _(next) \ + _(futureResult) \ /// Calls your macro with the name of each member of XrSystemSpatialAnchorPropertiesBD, in order. #define XR_LIST_STRUCT_XrSystemSpatialAnchorPropertiesBD(_) \ - _(type) \ - _(next) \ - _(supportsSpatialAnchor) + _(type) \ + _(next) \ + _(supportsSpatialAnchor) \ /// Calls your macro with the name of each member of XrSpatialAnchorCreateInfoBD, in order. #define XR_LIST_STRUCT_XrSpatialAnchorCreateInfoBD(_) \ - _(type) \ - _(next) \ - _(space) \ - _(pose) \ - _(time) + _(type) \ + _(next) \ + _(space) \ + _(pose) \ + _(time) \ /// Calls your macro with the name of each member of XrSpatialAnchorCreateCompletionBD, in order. #define XR_LIST_STRUCT_XrSpatialAnchorCreateCompletionBD(_) \ - _(type) \ - _(next) \ - _(futureResult) \ - _(anchor) \ - _(uuid) + _(type) \ + _(next) \ + _(futureResult) \ + _(uuid) \ + _(anchor) \ /// Calls your macro with the name of each member of XrSpatialAnchorPersistInfoBD, in order. #define XR_LIST_STRUCT_XrSpatialAnchorPersistInfoBD(_) \ - _(type) \ - _(next) \ - _(location) \ - _(anchor) + _(type) \ + _(next) \ + _(location) \ + _(anchor) \ /// Calls your macro with the name of each member of XrSpatialAnchorUnpersistInfoBD, in order. #define XR_LIST_STRUCT_XrSpatialAnchorUnpersistInfoBD(_) \ - _(type) \ - _(next) \ - _(location) \ - _(anchor) + _(type) \ + _(next) \ + _(location) \ + _(anchor) \ /// Calls your macro with the name of each member of XrSystemSpatialAnchorSharingPropertiesBD, in order. #define XR_LIST_STRUCT_XrSystemSpatialAnchorSharingPropertiesBD(_) \ - _(type) \ - _(next) \ - _(supportsSpatialAnchorSharing) + _(type) \ + _(next) \ + _(supportsSpatialAnchorSharing) \ /// Calls your macro with the name of each member of XrSpatialAnchorShareInfoBD, in order. #define XR_LIST_STRUCT_XrSpatialAnchorShareInfoBD(_) \ - _(type) \ - _(next) \ - _(anchor) + _(type) \ + _(next) \ + _(anchor) \ /// Calls your macro with the name of each member of XrSharedSpatialAnchorDownloadInfoBD, in order. #define XR_LIST_STRUCT_XrSharedSpatialAnchorDownloadInfoBD(_) \ - _(type) \ - _(next) \ - _(uuid) + _(type) \ + _(next) \ + _(uuid) \ /// Calls your macro with the name of each member of XrSystemSpatialScenePropertiesBD, in order. #define XR_LIST_STRUCT_XrSystemSpatialScenePropertiesBD(_) \ - _(type) \ - _(next) \ - _(supportsSpatialScene) + _(type) \ + _(next) \ + _(supportsSpatialScene) \ /// Calls your macro with the name of each member of XrSceneCaptureInfoBD, in order. #define XR_LIST_STRUCT_XrSceneCaptureInfoBD(_) \ - _(type) \ - _(next) + _(type) \ + _(next) \ /// Calls your macro with the name of each member of XrSystemSpatialMeshPropertiesBD, in order. #define XR_LIST_STRUCT_XrSystemSpatialMeshPropertiesBD(_) \ - _(type) \ - _(next) \ - _(supportsSpatialMesh) + _(type) \ + _(next) \ + _(supportsSpatialMesh) \ /// Calls your macro with the name of each member of XrSenseDataProviderCreateInfoSpatialMeshBD, in order. #define XR_LIST_STRUCT_XrSenseDataProviderCreateInfoSpatialMeshBD(_) \ - _(type) \ - _(next) \ - _(configFlags) \ - _(lod) + _(type) \ + _(next) \ + _(configFlags) \ + _(lod) \ /// Calls your macro with the name of each member of XrFuturePollResultProgressBD, in order. #define XR_LIST_STRUCT_XrFuturePollResultProgressBD(_) \ - _(type) \ - _(next) \ - _(isSupported) \ - _(progressPercentage) + _(type) \ + _(next) \ + _(isSupported) \ + _(progressPercentage) \ /// Calls your macro with the name of each member of XrSystemSpatialPlanePropertiesBD, in order. #define XR_LIST_STRUCT_XrSystemSpatialPlanePropertiesBD(_) \ - _(type) \ - _(next) \ - _(supportsSpatialPlane) + _(type) \ + _(next) \ + _(supportsSpatialPlane) \ /// Calls your macro with the name of each member of XrSpatialEntityComponentDataPlaneOrientationBD, in order. #define XR_LIST_STRUCT_XrSpatialEntityComponentDataPlaneOrientationBD(_) \ - _(type) \ - _(next) \ - _(orientation) + _(type) \ + _(next) \ + _(orientation) \ /// Calls your macro with the name of each member of XrSenseDataFilterPlaneOrientationBD, in order. #define XR_LIST_STRUCT_XrSenseDataFilterPlaneOrientationBD(_) \ - _(type) \ - _(next) \ - _(orientationCount) \ - _(orientations) + _(type) \ + _(next) \ + _(orientationCount) \ + _(orientations) \ /// Calls your macro with the name of each member of XrHandTrackingDataSourceInfoEXT, in order. #define XR_LIST_STRUCT_XrHandTrackingDataSourceInfoEXT(_) \ @@ -6384,11 +6945,164 @@ XR_ENUM_STR(XrResult); _(vertexCountOutput) \ _(vertices) \ -/// Calls your macro with the name of each member of XrFutureCancelInfoEXT, in order. -#define XR_LIST_STRUCT_XrFutureCancelInfoEXT(_) \ +/// Calls your macro with the name of each member of XrTrackableTrackerCreateInfoANDROID, in order. +#define XR_LIST_STRUCT_XrTrackableTrackerCreateInfoANDROID(_) \ _(type) \ _(next) \ - _(future) \ + _(trackableType) \ + +/// Calls your macro with the name of each member of XrTrackableGetInfoANDROID, in order. +#define XR_LIST_STRUCT_XrTrackableGetInfoANDROID(_) \ + _(type) \ + _(next) \ + _(trackable) \ + _(baseSpace) \ + _(time) \ + +/// Calls your macro with the name of each member of XrTrackablePlaneANDROID, in order. +#define XR_LIST_STRUCT_XrTrackablePlaneANDROID(_) \ + _(type) \ + _(next) \ + _(trackingState) \ + _(centerPose) \ + _(extents) \ + _(planeType) \ + _(planeLabel) \ + _(subsumedByPlane) \ + _(lastUpdatedTime) \ + _(vertexCapacityInput) \ + _(vertexCountOutput) \ + _(vertices) \ + +/// Calls your macro with the name of each member of XrAnchorSpaceCreateInfoANDROID, in order. +#define XR_LIST_STRUCT_XrAnchorSpaceCreateInfoANDROID(_) \ + _(type) \ + _(next) \ + _(space) \ + _(time) \ + _(pose) \ + _(trackable) \ + +/// Calls your macro with the name of each member of XrSystemTrackablesPropertiesANDROID, in order. +#define XR_LIST_STRUCT_XrSystemTrackablesPropertiesANDROID(_) \ + _(type) \ + _(next) \ + _(supportsAnchor) \ + _(maxAnchors) \ + +/// Calls your macro with the name of each member of XrDeviceAnchorPersistenceCreateInfoANDROID, in order. +#define XR_LIST_STRUCT_XrDeviceAnchorPersistenceCreateInfoANDROID(_) \ + _(type) \ + _(next) \ + +/// Calls your macro with the name of each member of XrPersistedAnchorSpaceCreateInfoANDROID, in order. +#define XR_LIST_STRUCT_XrPersistedAnchorSpaceCreateInfoANDROID(_) \ + _(type) \ + _(next) \ + _(anchorId) \ + +/// Calls your macro with the name of each member of XrPersistedAnchorSpaceInfoANDROID, in order. +#define XR_LIST_STRUCT_XrPersistedAnchorSpaceInfoANDROID(_) \ + _(type) \ + _(next) \ + _(anchor) \ + +/// Calls your macro with the name of each member of XrSystemDeviceAnchorPersistencePropertiesANDROID, in order. +#define XR_LIST_STRUCT_XrSystemDeviceAnchorPersistencePropertiesANDROID(_) \ + _(type) \ + _(next) \ + _(supportsAnchorPersistence) \ + +/// Calls your macro with the name of each member of XrFaceTrackerCreateInfoANDROID, in order. +#define XR_LIST_STRUCT_XrFaceTrackerCreateInfoANDROID(_) \ + _(type) \ + _(next) \ + +/// Calls your macro with the name of each member of XrFaceStateGetInfoANDROID, in order. +#define XR_LIST_STRUCT_XrFaceStateGetInfoANDROID(_) \ + _(type) \ + _(next) \ + _(time) \ + +/// Calls your macro with the name of each member of XrFaceStateANDROID, in order. +#define XR_LIST_STRUCT_XrFaceStateANDROID(_) \ + _(type) \ + _(next) \ + _(parametersCapacityInput) \ + _(parametersCountOutput) \ + _(parameters) \ + _(faceTrackingState) \ + _(sampleTime) \ + _(isValid) \ + _(regionConfidencesCapacityInput) \ + _(regionConfidencesCountOutput) \ + _(regionConfidences) \ + +/// Calls your macro with the name of each member of XrSystemFaceTrackingPropertiesANDROID, in order. +#define XR_LIST_STRUCT_XrSystemFaceTrackingPropertiesANDROID(_) \ + _(type) \ + _(next) \ + _(supportsFaceTracking) \ + +/// Calls your macro with the name of each member of XrSystemPassthroughCameraStatePropertiesANDROID, in order. +#define XR_LIST_STRUCT_XrSystemPassthroughCameraStatePropertiesANDROID(_) \ + _(type) \ + _(next) \ + _(supportsPassthroughCameraState) \ + +/// Calls your macro with the name of each member of XrPassthroughCameraStateGetInfoANDROID, in order. +#define XR_LIST_STRUCT_XrPassthroughCameraStateGetInfoANDROID(_) \ + _(type) \ + _(next) \ + +/// Calls your macro with the name of each member of XrRaycastInfoANDROID, in order. +#define XR_LIST_STRUCT_XrRaycastInfoANDROID(_) \ + _(type) \ + _(next) \ + _(maxResults) \ + _(trackerCount) \ + _(trackers) \ + _(origin) \ + _(trajectory) \ + _(space) \ + _(time) \ + +/// Calls your macro with the name of each member of XrRaycastHitResultANDROID, in order. +#define XR_LIST_STRUCT_XrRaycastHitResultANDROID(_) \ + _(type) \ + _(trackable) \ + _(pose) \ + +/// Calls your macro with the name of each member of XrRaycastHitResultsANDROID, in order. +#define XR_LIST_STRUCT_XrRaycastHitResultsANDROID(_) \ + _(type) \ + _(next) \ + _(resultsCapacityInput) \ + _(resultsCountOutput) \ + _(results) \ + +/// Calls your macro with the name of each member of XrTrackableObjectANDROID, in order. +#define XR_LIST_STRUCT_XrTrackableObjectANDROID(_) \ + _(type) \ + _(next) \ + _(trackingState) \ + _(centerPose) \ + _(extents) \ + _(objectLabel) \ + _(lastUpdatedTime) \ + +/// Calls your macro with the name of each member of XrTrackableObjectConfigurationANDROID, in order. +#define XR_LIST_STRUCT_XrTrackableObjectConfigurationANDROID(_) \ + _(type) \ + _(next) \ + _(labelCount) \ + _(activeLabels) \ + +/// Calls your macro with the name of each member of XrFutureCancelInfoEXT, in order. +#define XR_LIST_STRUCT_XrFutureCancelInfoEXT(_) \ + _(type) \ + _(next) \ + _(future) \ /// Calls your macro with the name of each member of XrFuturePollInfoEXT, in order. #define XR_LIST_STRUCT_XrFuturePollInfoEXT(_) \ @@ -6573,23 +7287,23 @@ XR_ENUM_STR(XrResult); _(requestedFacialBlendShape) \ _(weight) \ _(flags) \ - _(time) + _(time) \ /// Calls your macro with the name of each member of XrSystemSimultaneousHandsAndControllersPropertiesMETA, in order. #define XR_LIST_STRUCT_XrSystemSimultaneousHandsAndControllersPropertiesMETA(_) \ - _(type) \ - _(next) \ - _(supportsSimultaneousHandsAndControllers) + _(type) \ + _(next) \ + _(supportsSimultaneousHandsAndControllers) \ /// Calls your macro with the name of each member of XrSimultaneousHandsAndControllersTrackingResumeInfoMETA, in order. #define XR_LIST_STRUCT_XrSimultaneousHandsAndControllersTrackingResumeInfoMETA(_) \ - _(type) \ - _(next) + _(type) \ + _(next) \ /// Calls your macro with the name of each member of XrSimultaneousHandsAndControllersTrackingPauseInfoMETA, in order. #define XR_LIST_STRUCT_XrSimultaneousHandsAndControllersTrackingPauseInfoMETA(_) \ - _(type) \ - _(next) + _(type) \ + _(next) \ /// Calls your macro with the name of each member of XrColocationDiscoveryStartInfoMETA, in order. #define XR_LIST_STRUCT_XrColocationDiscoveryStartInfoMETA(_) \ @@ -6688,347 +7402,415 @@ XR_ENUM_STR(XrResult); #define XR_LIST_STRUCT_XrSpaceGroupUuidFilterInfoMETA(_) \ _(type) \ _(next) \ - _(groupUuid) + _(groupUuid) \ + +/// Calls your macro with the name of each member of XrAnchorSharingInfoANDROID, in order. +#define XR_LIST_STRUCT_XrAnchorSharingInfoANDROID(_) \ + _(type) \ + _(next) \ + _(anchor) \ + +/// Calls your macro with the name of each member of XrAnchorSharingTokenANDROID, in order. +#define XR_LIST_STRUCT_XrAnchorSharingTokenANDROID(_) \ + _(type) \ + _(next) \ + _(token) \ + +/// Calls your macro with the name of each member of XrSystemAnchorSharingExportPropertiesANDROID, in order. +#define XR_LIST_STRUCT_XrSystemAnchorSharingExportPropertiesANDROID(_) \ + _(type) \ + _(next) \ + _(supportsAnchorSharingExport) \ + +/// Calls your macro with the name of each member of XrSystemMarkerTrackingPropertiesANDROID, in order. +#define XR_LIST_STRUCT_XrSystemMarkerTrackingPropertiesANDROID(_) \ + _(type) \ + _(next) \ + _(supportsMarkerTracking) \ + _(supportsMarkerSizeEstimation) \ + _(maxMarkerCount) \ + +/// Calls your macro with the name of each member of XrTrackableMarkerDatabaseEntryANDROID, in order. +#define XR_LIST_STRUCT_XrTrackableMarkerDatabaseEntryANDROID(_) \ + _(id) \ + _(edgeSize) \ + +/// Calls your macro with the name of each member of XrTrackableMarkerDatabaseANDROID, in order. +#define XR_LIST_STRUCT_XrTrackableMarkerDatabaseANDROID(_) \ + _(dictionary) \ + _(entryCount) \ + _(entries) \ + +/// Calls your macro with the name of each member of XrTrackableMarkerConfigurationANDROID, in order. +#define XR_LIST_STRUCT_XrTrackableMarkerConfigurationANDROID(_) \ + _(type) \ + _(next) \ + _(trackingMode) \ + _(databaseCount) \ + _(databases) \ + +/// Calls your macro with the name of each member of XrTrackableMarkerANDROID, in order. +#define XR_LIST_STRUCT_XrTrackableMarkerANDROID(_) \ + _(type) \ + _(next) \ + _(trackingState) \ + _(lastUpdatedTime) \ + _(dictionary) \ + _(markerId) \ + _(centerPose) \ + _(extents) \ /// Calls your macro with the name of each member of XrSpatialCapabilityComponentTypesEXT, in order. #define XR_LIST_STRUCT_XrSpatialCapabilityComponentTypesEXT(_) \ - _(type) \ - _(next) \ - _(componentTypeCapacityInput) \ - _(componentTypeCountOutput) \ - _(componentTypes) + _(type) \ + _(next) \ + _(componentTypeCapacityInput) \ + _(componentTypeCountOutput) \ + _(componentTypes) \ /// Calls your macro with the name of each member of XrSpatialCapabilityConfigurationBaseHeaderEXT, in order. #define XR_LIST_STRUCT_XrSpatialCapabilityConfigurationBaseHeaderEXT(_) \ - _(type) \ - _(next) \ - _(capability) \ - _(enabledComponentCount) \ - _(enabledComponents) + _(type) \ + _(next) \ + _(capability) \ + _(enabledComponentCount) \ + _(enabledComponents) \ /// Calls your macro with the name of each member of XrSpatialContextCreateInfoEXT, in order. #define XR_LIST_STRUCT_XrSpatialContextCreateInfoEXT(_) \ - _(type) \ - _(next) \ - _(capabilityConfigCount) \ - _(capabilityConfigs) + _(type) \ + _(next) \ + _(capabilityConfigCount) \ + _(capabilityConfigs) \ /// Calls your macro with the name of each member of XrCreateSpatialContextCompletionEXT, in order. #define XR_LIST_STRUCT_XrCreateSpatialContextCompletionEXT(_) \ - _(type) \ - _(next) \ - _(futureResult) \ - _(spatialContext) + _(type) \ + _(next) \ + _(futureResult) \ + _(spatialContext) \ /// Calls your macro with the name of each member of XrSpatialDiscoverySnapshotCreateInfoEXT, in order. #define XR_LIST_STRUCT_XrSpatialDiscoverySnapshotCreateInfoEXT(_) \ - _(type) \ - _(next) \ - _(componentTypeCount) \ - _(componentTypes) + _(type) \ + _(next) \ + _(componentTypeCount) \ + _(componentTypes) \ /// Calls your macro with the name of each member of XrCreateSpatialDiscoverySnapshotCompletionInfoEXT, in order. #define XR_LIST_STRUCT_XrCreateSpatialDiscoverySnapshotCompletionInfoEXT(_) \ - _(type) \ - _(next) \ - _(baseSpace) \ - _(time) \ - _(future) + _(type) \ + _(next) \ + _(baseSpace) \ + _(time) \ + _(future) \ /// Calls your macro with the name of each member of XrCreateSpatialDiscoverySnapshotCompletionEXT, in order. #define XR_LIST_STRUCT_XrCreateSpatialDiscoverySnapshotCompletionEXT(_) \ - _(type) \ - _(next) \ - _(futureResult) \ - _(snapshot) + _(type) \ + _(next) \ + _(futureResult) \ + _(snapshot) \ /// Calls your macro with the name of each member of XrSpatialComponentDataQueryConditionEXT, in order. #define XR_LIST_STRUCT_XrSpatialComponentDataQueryConditionEXT(_) \ - _(type) \ - _(next) \ - _(componentTypeCount) \ - _(componentTypes) + _(type) \ + _(next) \ + _(componentTypeCount) \ + _(componentTypes) \ /// Calls your macro with the name of each member of XrSpatialComponentDataQueryResultEXT, in order. #define XR_LIST_STRUCT_XrSpatialComponentDataQueryResultEXT(_) \ - _(type) \ - _(next) \ - _(entityIdCapacityInput) \ - _(entityIdCountOutput) \ - _(entityIds) \ - _(entityStateCapacityInput) \ - _(entityStateCountOutput) \ - _(entityStates) + _(type) \ + _(next) \ + _(entityIdCapacityInput) \ + _(entityIdCountOutput) \ + _(entityIds) \ + _(entityStateCapacityInput) \ + _(entityStateCountOutput) \ + _(entityStates) \ /// Calls your macro with the name of each member of XrSpatialBufferEXT, in order. #define XR_LIST_STRUCT_XrSpatialBufferEXT(_) \ - _(bufferId) \ - _(bufferType) + _(bufferId) \ + _(bufferType) \ /// Calls your macro with the name of each member of XrSpatialBufferGetInfoEXT, in order. #define XR_LIST_STRUCT_XrSpatialBufferGetInfoEXT(_) \ - _(type) \ - _(next) \ - _(bufferId) + _(type) \ + _(next) \ + _(bufferId) \ /// Calls your macro with the name of each member of XrSpatialBounded2DDataEXT, in order. #define XR_LIST_STRUCT_XrSpatialBounded2DDataEXT(_) \ - _(center) \ - _(extents) + _(center) \ + _(extents) \ /// Calls your macro with the name of each member of XrSpatialComponentBounded2DListEXT, in order. #define XR_LIST_STRUCT_XrSpatialComponentBounded2DListEXT(_) \ - _(type) \ - _(next) \ - _(boundCount) \ - _(bounds) + _(type) \ + _(next) \ + _(boundCount) \ + _(bounds) \ /// Calls your macro with the name of each member of XrSpatialComponentBounded3DListEXT, in order. #define XR_LIST_STRUCT_XrSpatialComponentBounded3DListEXT(_) \ - _(type) \ - _(next) \ - _(boundCount) \ - _(bounds) + _(type) \ + _(next) \ + _(boundCount) \ + _(bounds) \ /// Calls your macro with the name of each member of XrSpatialComponentParentListEXT, in order. #define XR_LIST_STRUCT_XrSpatialComponentParentListEXT(_) \ - _(type) \ - _(next) \ - _(parentCount) \ - _(parents) + _(type) \ + _(next) \ + _(parentCount) \ + _(parents) \ /// Calls your macro with the name of each member of XrSpatialMeshDataEXT, in order. #define XR_LIST_STRUCT_XrSpatialMeshDataEXT(_) \ - _(origin) \ - _(vertexBuffer) \ - _(indexBuffer) + _(origin) \ + _(vertexBuffer) \ + _(indexBuffer) \ /// Calls your macro with the name of each member of XrSpatialComponentMesh3DListEXT, in order. #define XR_LIST_STRUCT_XrSpatialComponentMesh3DListEXT(_) \ - _(type) \ - _(next) \ - _(meshCount) \ - _(meshes) + _(type) \ + _(next) \ + _(meshCount) \ + _(meshes) \ /// Calls your macro with the name of each member of XrSpatialEntityFromIdCreateInfoEXT, in order. #define XR_LIST_STRUCT_XrSpatialEntityFromIdCreateInfoEXT(_) \ - _(type) \ - _(next) \ - _(entityId) + _(type) \ + _(next) \ + _(entityId) \ /// Calls your macro with the name of each member of XrSpatialUpdateSnapshotCreateInfoEXT, in order. #define XR_LIST_STRUCT_XrSpatialUpdateSnapshotCreateInfoEXT(_) \ - _(type) \ - _(next) \ - _(entityCount) \ - _(entities) \ - _(componentTypeCount) \ - _(componentTypes) \ - _(baseSpace) \ - _(time) + _(type) \ + _(next) \ + _(entityCount) \ + _(entities) \ + _(componentTypeCount) \ + _(componentTypes) \ + _(baseSpace) \ + _(time) \ /// Calls your macro with the name of each member of XrEventDataSpatialDiscoveryRecommendedEXT, in order. #define XR_LIST_STRUCT_XrEventDataSpatialDiscoveryRecommendedEXT(_) \ - _(type) \ - _(next) \ - _(spatialContext) + _(type) \ + _(next) \ + _(spatialContext) \ /// Calls your macro with the name of each member of XrSpatialFilterTrackingStateEXT, in order. #define XR_LIST_STRUCT_XrSpatialFilterTrackingStateEXT(_) \ - _(type) \ - _(next) \ - _(trackingState) + _(type) \ + _(next) \ + _(trackingState) \ /// Calls your macro with the name of each member of XrSpatialCapabilityConfigurationPlaneTrackingEXT, in order. #define XR_LIST_STRUCT_XrSpatialCapabilityConfigurationPlaneTrackingEXT(_) \ - _(type) \ - _(next) \ - _(capability) \ - _(enabledComponentCount) \ - _(enabledComponents) + _(type) \ + _(next) \ + _(capability) \ + _(enabledComponentCount) \ + _(enabledComponents) \ /// Calls your macro with the name of each member of XrSpatialComponentPlaneAlignmentListEXT, in order. #define XR_LIST_STRUCT_XrSpatialComponentPlaneAlignmentListEXT(_) \ - _(type) \ - _(next) \ - _(planeAlignmentCount) \ - _(planeAlignments) + _(type) \ + _(next) \ + _(planeAlignmentCount) \ + _(planeAlignments) \ /// Calls your macro with the name of each member of XrSpatialComponentMesh2DListEXT, in order. #define XR_LIST_STRUCT_XrSpatialComponentMesh2DListEXT(_) \ - _(type) \ - _(next) \ - _(meshCount) \ - _(meshes) + _(type) \ + _(next) \ + _(meshCount) \ + _(meshes) \ /// Calls your macro with the name of each member of XrSpatialPolygon2DDataEXT, in order. #define XR_LIST_STRUCT_XrSpatialPolygon2DDataEXT(_) \ - _(origin) \ - _(vertexBuffer) + _(origin) \ + _(vertexBuffer) \ /// Calls your macro with the name of each member of XrSpatialComponentPolygon2DListEXT, in order. #define XR_LIST_STRUCT_XrSpatialComponentPolygon2DListEXT(_) \ - _(type) \ - _(next) \ - _(polygonCount) \ - _(polygons) + _(type) \ + _(next) \ + _(polygonCount) \ + _(polygons) \ /// Calls your macro with the name of each member of XrSpatialComponentPlaneSemanticLabelListEXT, in order. #define XR_LIST_STRUCT_XrSpatialComponentPlaneSemanticLabelListEXT(_) \ - _(type) \ - _(next) \ - _(semanticLabelCount) \ - _(semanticLabels) + _(type) \ + _(next) \ + _(semanticLabelCount) \ + _(semanticLabels) \ /// Calls your macro with the name of each member of XrSpatialCapabilityConfigurationQrCodeEXT, in order. #define XR_LIST_STRUCT_XrSpatialCapabilityConfigurationQrCodeEXT(_) \ - _(type) \ - _(next) \ - _(capability) \ - _(enabledComponentCount) \ - _(enabledComponents) + _(type) \ + _(next) \ + _(capability) \ + _(enabledComponentCount) \ + _(enabledComponents) \ /// Calls your macro with the name of each member of XrSpatialCapabilityConfigurationMicroQrCodeEXT, in order. #define XR_LIST_STRUCT_XrSpatialCapabilityConfigurationMicroQrCodeEXT(_) \ - _(type) \ - _(next) \ - _(capability) \ - _(enabledComponentCount) \ - _(enabledComponents) + _(type) \ + _(next) \ + _(capability) \ + _(enabledComponentCount) \ + _(enabledComponents) \ /// Calls your macro with the name of each member of XrSpatialCapabilityConfigurationArucoMarkerEXT, in order. #define XR_LIST_STRUCT_XrSpatialCapabilityConfigurationArucoMarkerEXT(_) \ - _(type) \ - _(next) \ - _(capability) \ - _(enabledComponentCount) \ - _(enabledComponents) \ - _(arUcoDict) + _(type) \ + _(next) \ + _(capability) \ + _(enabledComponentCount) \ + _(enabledComponents) \ + _(arUcoDict) \ /// Calls your macro with the name of each member of XrSpatialCapabilityConfigurationAprilTagEXT, in order. #define XR_LIST_STRUCT_XrSpatialCapabilityConfigurationAprilTagEXT(_) \ - _(type) \ - _(next) \ - _(capability) \ - _(enabledComponentCount) \ - _(enabledComponents) \ - _(aprilDict) + _(type) \ + _(next) \ + _(capability) \ + _(enabledComponentCount) \ + _(enabledComponents) \ + _(aprilDict) \ /// Calls your macro with the name of each member of XrSpatialMarkerSizeEXT, in order. #define XR_LIST_STRUCT_XrSpatialMarkerSizeEXT(_) \ - _(type) \ - _(next) \ - _(markerSideLength) + _(type) \ + _(next) \ + _(markerSideLength) \ /// Calls your macro with the name of each member of XrSpatialMarkerStaticOptimizationEXT, in order. #define XR_LIST_STRUCT_XrSpatialMarkerStaticOptimizationEXT(_) \ - _(type) \ - _(next) \ - _(optimizeForStaticMarker) + _(type) \ + _(next) \ + _(optimizeForStaticMarker) \ /// Calls your macro with the name of each member of XrSpatialMarkerDataEXT, in order. #define XR_LIST_STRUCT_XrSpatialMarkerDataEXT(_) \ - _(capability) \ - _(markerId) \ - _(data) + _(capability) \ + _(markerId) \ + _(data) \ /// Calls your macro with the name of each member of XrSpatialComponentMarkerListEXT, in order. #define XR_LIST_STRUCT_XrSpatialComponentMarkerListEXT(_) \ - _(type) \ - _(next) \ - _(markerCount) \ - _(markers) + _(type) \ + _(next) \ + _(markerCount) \ + _(markers) \ /// Calls your macro with the name of each member of XrSpatialCapabilityConfigurationAnchorEXT, in order. #define XR_LIST_STRUCT_XrSpatialCapabilityConfigurationAnchorEXT(_) \ - _(type) \ - _(next) \ - _(capability) \ - _(enabledComponentCount) \ - _(enabledComponents) + _(type) \ + _(next) \ + _(capability) \ + _(enabledComponentCount) \ + _(enabledComponents) \ /// Calls your macro with the name of each member of XrSpatialComponentAnchorListEXT, in order. #define XR_LIST_STRUCT_XrSpatialComponentAnchorListEXT(_) \ - _(type) \ - _(next) \ - _(locationCount) \ - _(locations) + _(type) \ + _(next) \ + _(locationCount) \ + _(locations) \ /// Calls your macro with the name of each member of XrSpatialAnchorCreateInfoEXT, in order. #define XR_LIST_STRUCT_XrSpatialAnchorCreateInfoEXT(_) \ - _(type) \ - _(next) \ - _(baseSpace) \ - _(time) \ - _(pose) + _(type) \ + _(next) \ + _(baseSpace) \ + _(time) \ + _(pose) \ /// Calls your macro with the name of each member of XrSpatialPersistenceContextCreateInfoEXT, in order. #define XR_LIST_STRUCT_XrSpatialPersistenceContextCreateInfoEXT(_) \ - _(type) \ - _(next) \ - _(scope) + _(type) \ + _(next) \ + _(scope) \ /// Calls your macro with the name of each member of XrCreateSpatialPersistenceContextCompletionEXT, in order. #define XR_LIST_STRUCT_XrCreateSpatialPersistenceContextCompletionEXT(_) \ - _(type) \ - _(next) \ - _(futureResult) \ - _(createResult) \ - _(persistenceContext) + _(type) \ + _(next) \ + _(futureResult) \ + _(createResult) \ + _(persistenceContext) \ /// Calls your macro with the name of each member of XrSpatialContextPersistenceConfigEXT, in order. #define XR_LIST_STRUCT_XrSpatialContextPersistenceConfigEXT(_) \ - _(type) \ - _(next) \ - _(persistenceContextCount) \ - _(persistenceContexts) + _(type) \ + _(next) \ + _(persistenceContextCount) \ + _(persistenceContexts) \ /// Calls your macro with the name of each member of XrSpatialDiscoveryPersistenceUuidFilterEXT, in order. #define XR_LIST_STRUCT_XrSpatialDiscoveryPersistenceUuidFilterEXT(_) \ - _(type) \ - _(next) \ - _(persistedUuidCount) \ - _(persistedUuids) + _(type) \ + _(next) \ + _(persistedUuidCount) \ + _(persistedUuids) \ /// Calls your macro with the name of each member of XrSpatialPersistenceDataEXT, in order. #define XR_LIST_STRUCT_XrSpatialPersistenceDataEXT(_) \ - _(persistUuid) \ - _(persistState) + _(persistUuid) \ + _(persistState) \ /// Calls your macro with the name of each member of XrSpatialComponentPersistenceListEXT, in order. #define XR_LIST_STRUCT_XrSpatialComponentPersistenceListEXT(_) \ - _(type) \ - _(next) \ - _(persistDataCount) \ - _(persistData) + _(type) \ + _(next) \ + _(persistDataCount) \ + _(persistData) \ /// Calls your macro with the name of each member of XrSpatialEntityPersistInfoEXT, in order. #define XR_LIST_STRUCT_XrSpatialEntityPersistInfoEXT(_) \ - _(type) \ - _(next) \ - _(spatialContext) \ - _(spatialEntityId) + _(type) \ + _(next) \ + _(spatialContext) \ + _(spatialEntityId) \ /// Calls your macro with the name of each member of XrPersistSpatialEntityCompletionEXT, in order. #define XR_LIST_STRUCT_XrPersistSpatialEntityCompletionEXT(_) \ - _(type) \ - _(next) \ - _(futureResult) \ - _(persistResult) \ - _(persistUuid) + _(type) \ + _(next) \ + _(futureResult) \ + _(persistResult) \ + _(persistUuid) \ /// Calls your macro with the name of each member of XrSpatialEntityUnpersistInfoEXT, in order. #define XR_LIST_STRUCT_XrSpatialEntityUnpersistInfoEXT(_) \ - _(type) \ - _(next) \ - _(persistUuid) + _(type) \ + _(next) \ + _(persistUuid) \ /// Calls your macro with the name of each member of XrUnpersistSpatialEntityCompletionEXT, in order. #define XR_LIST_STRUCT_XrUnpersistSpatialEntityCompletionEXT(_) \ - _(type) \ - _(next) \ - _(futureResult) \ - _(unpersistResult) + _(type) \ + _(next) \ + _(futureResult) \ + _(unpersistResult) \ + +/// Calls your macro with the name of each member of XrLoaderInitPropertyValueEXT, in order. +#define XR_LIST_STRUCT_XrLoaderInitPropertyValueEXT(_) \ + _(name) \ + _(value) \ + +/// Calls your macro with the name of each member of XrLoaderInitInfoPropertiesEXT, in order. +#define XR_LIST_STRUCT_XrLoaderInitInfoPropertiesEXT(_) \ + _(type) \ + _(next) \ + _(propertyValueCount) \ + _(propertyValues) \ /// Calls your macro with the name of each member of XrEventDataMrcStatusChangedPICO, in order. #define XR_LIST_STRUCT_XrEventDataMrcStatusChangedPICO(_) \ @@ -7171,28 +7953,54 @@ XR_ENUM_STR(XrResult); _(type) \ _(next) \ +/// Calls your macro with the name of each member of XrEyeTrackerGazeInfoPICO, in order. +#define XR_LIST_STRUCT_XrEyeTrackerGazeInfoPICO(_) \ + _(type) \ + _(next) \ + _(flags) \ + _(time) \ + _(baseSpace) \ + /// Calls your macro with the name of each member of XrEyeTrackerDataInfoPICO, in order. #define XR_LIST_STRUCT_XrEyeTrackerDataInfoPICO(_) \ _(type) \ _(next) \ - _(eyeTrackingFlags) \ /// Calls your macro with the name of each member of XrEyeDataPICO, in order. #define XR_LIST_STRUCT_XrEyeDataPICO(_) \ _(type) \ _(next) \ _(openness) \ - _(pupilDilation) \ - _(middleCanthusUv) \ /// Calls your macro with the name of each member of XrEyeTrackerDataPICO, in order. #define XR_LIST_STRUCT_XrEyeTrackerDataPICO(_) \ _(type) \ _(next) \ - _(trackingState) \ _(leftEyeData) \ _(rightEyeData) \ +/// Calls your macro with the name of each member of XrEyeGazePICO, in order. +#define XR_LIST_STRUCT_XrEyeGazePICO(_) \ + _(type) \ + _(next) \ + _(isValid) \ + _(pose) \ + +/// Calls your macro with the name of each member of XrEyeTrackerGazePICO, in order. +#define XR_LIST_STRUCT_XrEyeTrackerGazePICO(_) \ + _(type) \ + _(next) \ + _(leftEyeGaze) \ + _(rightEyeGaze) \ + +/// Calls your macro with the name of each member of XrEyeTrackerGazeDepthPICO, in order. +#define XR_LIST_STRUCT_XrEyeTrackerGazeDepthPICO(_) \ + _(type) \ + _(next) \ + _(isValid) \ + _(depthConfidence) \ + _(gazeDepth) \ + /// Calls your macro with the name of each member of XrSecureMrFrameworkCreateInfoPICO, in order. #define XR_LIST_STRUCT_XrSecureMrFrameworkCreateInfoPICO(_) \ _(type) \ @@ -7346,6 +8154,13 @@ XR_ENUM_STR(XrResult); _(next) \ _(convert) \ +/// Calls your macro with the name of each member of XrSecureMrOperatorJavascriptPICO, in order. +#define XR_LIST_STRUCT_XrSecureMrOperatorJavascriptPICO(_) \ + _(type) \ + _(next) \ + _(configText) \ + _(configLength) \ + /// Calls your macro with the name of each member of XrExpandDeviceMotorVibratePICO, in order. #define XR_LIST_STRUCT_XrExpandDeviceMotorVibratePICO(_) \ _(type) \ @@ -7449,58 +8264,58 @@ XR_ENUM_STR(XrResult); _(type) \ _(next) \ _(recommendedSymmetricFovImageRectWidth) \ - _(recommendedNotRenderingImageRectWidth) + _(recommendedNotRenderingImageRectWidth) \ /// Calls your macro with the name of each member of XrSystemDynamicObjectTrackingPropertiesPICO, in order. #define XR_LIST_STRUCT_XrSystemDynamicObjectTrackingPropertiesPICO(_) \ - _(type) \ - _(next) \ - _(supportsDynamicObjectTracking) + _(type) \ + _(next) \ + _(supportsDynamicObjectTracking) \ /// Calls your macro with the name of each member of XrSenseDataProviderCreateInfoDynamicObjectPICO, in order. #define XR_LIST_STRUCT_XrSenseDataProviderCreateInfoDynamicObjectPICO(_) \ - _(type) \ - _(next) \ - _(trackingTypeCount) \ - _(trackingTypes) + _(type) \ + _(next) \ + _(trackingTypeCount) \ + _(trackingTypes) \ /// Calls your macro with the name of each member of XrSpatialEntityDynamicObjectGetInfoPICO, in order. #define XR_LIST_STRUCT_XrSpatialEntityDynamicObjectGetInfoPICO(_) \ - _(type) \ - _(next) \ - _(entity) \ - _(componentType) + _(type) \ + _(next) \ + _(entity) \ + _(componentType) \ /// Calls your macro with the name of each member of XrDynamicObjectDataPICO, in order. #define XR_LIST_STRUCT_XrDynamicObjectDataPICO(_) \ - _(type) \ - _(next) \ - _(objectType) + _(type) \ + _(next) \ + _(objectType) \ /// Calls your macro with the name of each member of XrSpatialEntityComponentDataDynamicObjectPICO, in order. #define XR_LIST_STRUCT_XrSpatialEntityComponentDataDynamicObjectPICO(_) \ - _(type) \ - _(next) \ - _(data) + _(type) \ + _(next) \ + _(data) \ /// Calls your macro with the name of each member of XrSenseDataFilterDynamicObjectTypePICO, in order. #define XR_LIST_STRUCT_XrSenseDataFilterDynamicObjectTypePICO(_) \ - _(type) \ - _(next) \ - _(typeCount) \ - _(types) + _(type) \ + _(next) \ + _(typeCount) \ + _(types) \ /// Calls your macro with the name of each member of XrSystemDynamicObjectKeyboardPropertiesPICO, in order. #define XR_LIST_STRUCT_XrSystemDynamicObjectKeyboardPropertiesPICO(_) \ - _(type) \ - _(next) \ - _(supportsDynamicObjectKeyboard) + _(type) \ + _(next) \ + _(supportsDynamicObjectKeyboard) \ /// Calls your macro with the name of each member of XrSystemDynamicObjectMousePropertiesPICO, in order. #define XR_LIST_STRUCT_XrSystemDynamicObjectMousePropertiesPICO(_) \ - _(type) \ - _(next) \ - _(supportsDynamicObjectMouse) + _(type) \ + _(next) \ + _(supportsDynamicObjectMouse) \ /// Calls your macro with the name of each member of XrColorMatrix3x3fPICO, in order. #define XR_LIST_STRUCT_XrColorMatrix3x3fPICO(_) \ @@ -7510,352 +8325,613 @@ XR_ENUM_STR(XrResult); #define XR_LIST_STRUCT_XrLayerColorMatrixPICO(_) \ _(type) \ _(next) \ - _(matrix) + _(matrix) \ + +/// Calls your macro with the name of each member of XrReadbackTensorBufferPICO, in order. +#define XR_LIST_STRUCT_XrReadbackTensorBufferPICO(_) \ + _(type) \ + _(next) \ + _(bufferCapacityInput) \ + _(bufferCountOutput) \ + _(buffer) \ + +/// Calls your macro with the name of each member of XrCreateBufferFromGlobalTensorCompletionPICO, in order. +#define XR_LIST_STRUCT_XrCreateBufferFromGlobalTensorCompletionPICO(_) \ + _(type) \ + _(next) \ + _(futureResult) \ + _(tensorBuffer) \ + +/// Calls your macro with the name of each member of XrCreateTextureFromGlobalTensorCompletionPICO, in order. +#define XR_LIST_STRUCT_XrCreateTextureFromGlobalTensorCompletionPICO(_) \ + _(type) \ + _(next) \ + _(futureResult) \ + _(texture) \ + +/// Calls your macro with the name of each member of XrReadbackTextureImageBaseHeaderPICO, in order. +#define XR_LIST_STRUCT_XrReadbackTextureImageBaseHeaderPICO(_) \ + _(type) \ + _(next) \ + +/// Calls your macro with the name of each member of XrReadbackTextureImageVulkanPICO, in order. +#define XR_LIST_STRUCT_XrReadbackTextureImageVulkanPICO(_) \ + _(type) \ + _(next) \ + _(image) \ + +/// Calls your macro with the name of each member of XrReadbackTextureImageOpenGLPICO, in order. +#define XR_LIST_STRUCT_XrReadbackTextureImageOpenGLPICO(_) \ + _(type) \ + _(next) \ + _(texId) \ + +/// Calls your macro with the name of each member of XrCameraPropertyBaseHeaderPICO, in order. +#define XR_LIST_STRUCT_XrCameraPropertyBaseHeaderPICO(_) \ + _(type) \ + _(next) \ + +/// Calls your macro with the name of each member of XrCameraPropertiesPICO, in order. +#define XR_LIST_STRUCT_XrCameraPropertiesPICO(_) \ + _(type) \ + _(next) \ + _(propertyCount) \ + _(properties) \ + +/// Calls your macro with the name of each member of XrCameraCapabilityBaseHeaderPICO, in order. +#define XR_LIST_STRUCT_XrCameraCapabilityBaseHeaderPICO(_) \ + _(type) \ + _(next) \ + +/// Calls your macro with the name of each member of XrCameraCapabilitiesPICO, in order. +#define XR_LIST_STRUCT_XrCameraCapabilitiesPICO(_) \ + _(type) \ + _(next) \ + _(capabilityCount) \ + _(capabilities) \ + +/// Calls your macro with the name of each member of XrAvailableCamerasEnumerateInfoPICO, in order. +#define XR_LIST_STRUCT_XrAvailableCamerasEnumerateInfoPICO(_) \ + _(type) \ + _(next) \ + _(properties) \ + _(capabilities) \ + +/// Calls your macro with the name of each member of XrCameraPropertiesGetInfoPICO, in order. +#define XR_LIST_STRUCT_XrCameraPropertiesGetInfoPICO(_) \ + _(type) \ + _(next) \ + _(cameraId) \ + +/// Calls your macro with the name of each member of XrCameraPropertyFacingPICO, in order. +#define XR_LIST_STRUCT_XrCameraPropertyFacingPICO(_) \ + _(type) \ + _(next) \ + _(facing) \ + +/// Calls your macro with the name of each member of XrCameraPropertyPositionPICO, in order. +#define XR_LIST_STRUCT_XrCameraPropertyPositionPICO(_) \ + _(type) \ + _(next) \ + _(position) \ + +/// Calls your macro with the name of each member of XrCameraPropertyCameraTypePICO, in order. +#define XR_LIST_STRUCT_XrCameraPropertyCameraTypePICO(_) \ + _(type) \ + _(next) \ + _(cameraType) \ + +/// Calls your macro with the name of each member of XrCameraSupportedCapabilitiesGetInfoPICO, in order. +#define XR_LIST_STRUCT_XrCameraSupportedCapabilitiesGetInfoPICO(_) \ + _(type) \ + _(next) \ + _(id) \ + +/// Calls your macro with the name of each member of XrCameraSupportedCapabilityBaseHeaderPICO, in order. +#define XR_LIST_STRUCT_XrCameraSupportedCapabilityBaseHeaderPICO(_) \ + _(type) \ + _(next) \ + +/// Calls your macro with the name of each member of XrCameraSupportedCapabilitiesPICO, in order. +#define XR_LIST_STRUCT_XrCameraSupportedCapabilitiesPICO(_) \ + _(type) \ + _(next) \ + _(capabilityCount) \ + _(capabilities) \ + +/// Calls your macro with the name of each member of XrCameraSupportedCapabilityImageResolutionPICO, in order. +#define XR_LIST_STRUCT_XrCameraSupportedCapabilityImageResolutionPICO(_) \ + _(type) \ + _(next) \ + _(resolutionCapacityInput) \ + _(resolutionCountOutput) \ + _(resolutions) \ + +/// Calls your macro with the name of each member of XrCameraCapabilityImageResolutionPICO, in order. +#define XR_LIST_STRUCT_XrCameraCapabilityImageResolutionPICO(_) \ + _(type) \ + _(next) \ + _(resolution) \ + +/// Calls your macro with the name of each member of XrCameraSupportedCapabilityDataTransferTypePICO, in order. +#define XR_LIST_STRUCT_XrCameraSupportedCapabilityDataTransferTypePICO(_) \ + _(type) \ + _(next) \ + _(typeCapacityInput) \ + _(typeCountOutput) \ + _(types) \ + +/// Calls your macro with the name of each member of XrCameraCapabilityDataTransferTypePICO, in order. +#define XR_LIST_STRUCT_XrCameraCapabilityDataTransferTypePICO(_) \ + _(type) \ + _(next) \ + _(transferType) \ + +/// Calls your macro with the name of each member of XrCameraSupportedCapabilityImageFormatPICO, in order. +#define XR_LIST_STRUCT_XrCameraSupportedCapabilityImageFormatPICO(_) \ + _(type) \ + _(next) \ + _(formatCapacityInput) \ + _(formatCountOutput) \ + _(formats) \ + +/// Calls your macro with the name of each member of XrCameraCapabilityImageFormatPICO, in order. +#define XR_LIST_STRUCT_XrCameraCapabilityImageFormatPICO(_) \ + _(type) \ + _(next) \ + _(format) \ + +/// Calls your macro with the name of each member of XrCameraSupportedCapabilityCameraModelPICO, in order. +#define XR_LIST_STRUCT_XrCameraSupportedCapabilityCameraModelPICO(_) \ + _(type) \ + _(next) \ + _(modelCapacityInput) \ + _(modelCountOutput) \ + _(models) \ + +/// Calls your macro with the name of each member of XrCameraCapabilityCameraModelPICO, in order. +#define XR_LIST_STRUCT_XrCameraCapabilityCameraModelPICO(_) \ + _(type) \ + _(next) \ + _(model) \ + +/// Calls your macro with the name of each member of XrCameraSupportedCapabilityImageFpsPICO, in order. +#define XR_LIST_STRUCT_XrCameraSupportedCapabilityImageFpsPICO(_) \ + _(type) \ + _(next) \ + _(fpsCapacityInput) \ + _(fpsCountOutput) \ + _(fps) \ + +/// Calls your macro with the name of each member of XrCameraCapabilityImageFpsPICO, in order. +#define XR_LIST_STRUCT_XrCameraCapabilityImageFpsPICO(_) \ + _(type) \ + _(next) \ + _(fps) \ + +/// Calls your macro with the name of each member of XrCameraDeviceCreateInfoPICO, in order. +#define XR_LIST_STRUCT_XrCameraDeviceCreateInfoPICO(_) \ + _(type) \ + _(next) \ + _(cameraId) \ + +/// Calls your macro with the name of each member of XrCreateCameraDeviceCompletionPICO, in order. +#define XR_LIST_STRUCT_XrCreateCameraDeviceCompletionPICO(_) \ + _(type) \ + _(next) \ + _(futureResult) \ + _(device) \ + +/// Calls your macro with the name of each member of XrCameraCaptureSessionCreateInfoPICO, in order. +#define XR_LIST_STRUCT_XrCameraCaptureSessionCreateInfoPICO(_) \ + _(type) \ + _(next) \ + _(camera) \ + _(configCount) \ + _(configs) \ + +/// Calls your macro with the name of each member of XrCreateCameraCaptureSessionCompletionPICO, in order. +#define XR_LIST_STRUCT_XrCreateCameraCaptureSessionCompletionPICO(_) \ + _(type) \ + _(next) \ + _(futureResult) \ + _(captureSession) \ + +/// Calls your macro with the name of each member of XrCameraIntrinsicsPICO, in order. +#define XR_LIST_STRUCT_XrCameraIntrinsicsPICO(_) \ + _(type) \ + _(next) \ + _(focalLength) \ + _(principalPoint) \ + _(fov) \ + +/// Calls your macro with the name of each member of XrCameraExtrinsicsPICO, in order. +#define XR_LIST_STRUCT_XrCameraExtrinsicsPICO(_) \ + _(type) \ + _(next) \ + _(pose) \ + +/// Calls your macro with the name of each member of XrCameraCaptureBeginInfoPICO, in order. +#define XR_LIST_STRUCT_XrCameraCaptureBeginInfoPICO(_) \ + _(type) \ + _(next) \ + +/// Calls your macro with the name of each member of XrCameraImageAcquireInfoPICO, in order. +#define XR_LIST_STRUCT_XrCameraImageAcquireInfoPICO(_) \ + _(type) \ + _(next) \ + _(lastCaptureTime) \ + +/// Calls your macro with the name of each member of XrCameraImagePICO, in order. +#define XR_LIST_STRUCT_XrCameraImagePICO(_) \ + _(type) \ + _(next) \ + _(captureTime) \ + _(imageId) \ + +/// Calls your macro with the name of each member of XrCameraImageDataBaseHeaderPICO, in order. +#define XR_LIST_STRUCT_XrCameraImageDataBaseHeaderPICO(_) \ + _(type) \ + _(next) \ + +/// Calls your macro with the name of each member of XrCameraImageDataRawBufferPICO, in order. +#define XR_LIST_STRUCT_XrCameraImageDataRawBufferPICO(_) \ + _(type) \ + _(next) \ + _(width) \ + _(height) \ + _(stride) \ + _(bytesPerPixel) \ + _(pixelStride) \ + _(bufferSize) \ + _(buffer) \ /// Calls your macro with the name of each member of XrSystemSpatialSensingPropertiesPICO, in order. #define XR_LIST_STRUCT_XrSystemSpatialSensingPropertiesPICO(_) \ - _(type) \ - _(next) \ - _(supportsSpatialSensing) + _(type) \ + _(next) \ + _(supportsSpatialSensing) \ /// Calls your macro with the name of each member of XrSpatialEntityComponentGetInfoBaseHeaderPICO, in order. #define XR_LIST_STRUCT_XrSpatialEntityComponentGetInfoBaseHeaderPICO(_) \ - _(type) \ - _(next) \ - _(entity) \ - _(componentType) + _(type) \ + _(next) \ + _(entity) \ + _(componentType) \ /// Calls your macro with the name of each member of XrSpatialEntityComponentDataBaseHeaderPICO, in order. #define XR_LIST_STRUCT_XrSpatialEntityComponentDataBaseHeaderPICO(_) \ - _(type) \ - _(next) + _(type) \ + _(next) \ /// Calls your macro with the name of each member of XrSpatialEntityLocationGetInfoPICO, in order. #define XR_LIST_STRUCT_XrSpatialEntityLocationGetInfoPICO(_) \ - _(type) \ - _(next) \ - _(entity) \ - _(componentType) \ - _(baseSpace) \ - _(time) + _(type) \ + _(next) \ + _(entity) \ + _(componentType) \ + _(baseSpace) \ + _(time) \ /// Calls your macro with the name of each member of XrSpatialEntityLocationDataPICO, in order. #define XR_LIST_STRUCT_XrSpatialEntityLocationDataPICO(_) \ - _(type) \ - _(next) \ - _(location) + _(type) \ + _(next) \ + _(location) \ /// Calls your macro with the name of each member of XrSpatialEntitySemanticGetInfoPICO, in order. #define XR_LIST_STRUCT_XrSpatialEntitySemanticGetInfoPICO(_) \ - _(type) \ - _(next) \ - _(entity) \ - _(componentType) + _(type) \ + _(next) \ + _(entity) \ + _(componentType) \ /// Calls your macro with the name of each member of XrSpatialEntitySemanticDataPICO, in order. #define XR_LIST_STRUCT_XrSpatialEntitySemanticDataPICO(_) \ - _(type) \ - _(next) \ - _(semanticCapacityInput) \ - _(semanticCountOutput) \ - _(semanticLabels) + _(type) \ + _(next) \ + _(semanticCapacityInput) \ + _(semanticCountOutput) \ + _(semanticLabels) \ /// Calls your macro with the name of each member of XrSpatialEntityBoundingBox2DGetInfoPICO, in order. #define XR_LIST_STRUCT_XrSpatialEntityBoundingBox2DGetInfoPICO(_) \ - _(type) \ - _(next) \ - _(entity) \ - _(componentType) + _(type) \ + _(next) \ + _(entity) \ + _(componentType) \ /// Calls your macro with the name of each member of XrSpatialEntityBoundingBox2DDataPICO, in order. #define XR_LIST_STRUCT_XrSpatialEntityBoundingBox2DDataPICO(_) \ - _(type) \ - _(next) \ - _(boundingBox2D) + _(type) \ + _(next) \ + _(boundingBox2D) \ /// Calls your macro with the name of each member of XrSpatialEntityPolygonGetInfoPICO, in order. #define XR_LIST_STRUCT_XrSpatialEntityPolygonGetInfoPICO(_) \ - _(type) \ - _(next) \ - _(entity) \ - _(componentType) + _(type) \ + _(next) \ + _(entity) \ + _(componentType) \ /// Calls your macro with the name of each member of XrSpatialEntityPolygonDataPICO, in order. #define XR_LIST_STRUCT_XrSpatialEntityPolygonDataPICO(_) \ - _(type) \ - _(next) \ - _(polygonCapacityInput) \ - _(polygonCountOutput) \ - _(polygonVertices) + _(type) \ + _(next) \ + _(polygonCapacityInput) \ + _(polygonCountOutput) \ + _(polygonVertices) \ /// Calls your macro with the name of each member of XrSpatialEntityBoundingBox3DGetInfoPICO, in order. #define XR_LIST_STRUCT_XrSpatialEntityBoundingBox3DGetInfoPICO(_) \ - _(type) \ - _(next) \ - _(entity) \ - _(componentType) + _(type) \ + _(next) \ + _(entity) \ + _(componentType) \ /// Calls your macro with the name of each member of XrSpatialEntityBoundingBox3DDataPICO, in order. #define XR_LIST_STRUCT_XrSpatialEntityBoundingBox3DDataPICO(_) \ - _(type) \ - _(next) \ - _(boundingBox3D) + _(type) \ + _(next) \ + _(boundingBox3D) \ /// Calls your macro with the name of each member of XrSpatialEntityTriangleMeshGetInfoPICO, in order. #define XR_LIST_STRUCT_XrSpatialEntityTriangleMeshGetInfoPICO(_) \ - _(type) \ - _(next) \ - _(entity) \ - _(componentType) + _(type) \ + _(next) \ + _(entity) \ + _(componentType) \ /// Calls your macro with the name of each member of XrSpatialEntityTriangleMeshDataPICO, in order. #define XR_LIST_STRUCT_XrSpatialEntityTriangleMeshDataPICO(_) \ - _(type) \ - _(next) \ - _(vertexCapacityInput) \ - _(vertexCountOutput) \ - _(vertices) \ - _(indexCapacityInput) \ - _(indexCountOutput) \ - _(indices) + _(type) \ + _(next) \ + _(vertexCapacityInput) \ + _(vertexCountOutput) \ + _(vertices) \ + _(indexCapacityInput) \ + _(indexCountOutput) \ + _(indices) \ /// Calls your macro with the name of each member of XrSpatialEntitySphereGetInfoPICO, in order. #define XR_LIST_STRUCT_XrSpatialEntitySphereGetInfoPICO(_) \ - _(type) \ - _(next) \ - _(entity) \ - _(componentType) + _(type) \ + _(next) \ + _(entity) \ + _(componentType) \ /// Calls your macro with the name of each member of XrSpatialEntityComponentDataSpherePICO, in order. #define XR_LIST_STRUCT_XrSpatialEntityComponentDataSpherePICO(_) \ - _(type) \ - _(next) \ - _(sphere) + _(type) \ + _(next) \ + _(sphere) \ /// Calls your macro with the name of each member of XrSenseDataProviderCreateInfoBaseHeaderPICO, in order. #define XR_LIST_STRUCT_XrSenseDataProviderCreateInfoBaseHeaderPICO(_) \ - _(type) \ - _(next) + _(type) \ + _(next) \ /// Calls your macro with the name of each member of XrSenseDataProviderStartInfoPICO, in order. #define XR_LIST_STRUCT_XrSenseDataProviderStartInfoPICO(_) \ - _(type) \ - _(next) \ - _(provider) + _(type) \ + _(next) \ + _(provider) \ /// Calls your macro with the name of each member of XrSenseDataProviderStartCompletionPICO, in order. #define XR_LIST_STRUCT_XrSenseDataProviderStartCompletionPICO(_) \ - _(type) \ - _(next) \ - _(futureResult) + _(type) \ + _(next) \ + _(futureResult) \ /// Calls your macro with the name of each member of XrEventDataSenseDataProviderStateChangedPICO, in order. #define XR_LIST_STRUCT_XrEventDataSenseDataProviderStateChangedPICO(_) \ - _(type) \ - _(next) \ - _(provider) \ - _(newState) + _(type) \ + _(next) \ + _(provider) \ + _(newState) \ /// Calls your macro with the name of each member of XrEventDataSenseDataUpdatedPICO, in order. #define XR_LIST_STRUCT_XrEventDataSenseDataUpdatedPICO(_) \ - _(type) \ - _(next) \ - _(provider) + _(type) \ + _(next) \ + _(provider) \ /// Calls your macro with the name of each member of XrSenseDataFilterBaseHeaderPICO, in order. #define XR_LIST_STRUCT_XrSenseDataFilterBaseHeaderPICO(_) \ - _(type) \ - _(next) + _(type) \ + _(next) \ /// Calls your macro with the name of each member of XrSenseDataQueryInfoPICO, in order. #define XR_LIST_STRUCT_XrSenseDataQueryInfoPICO(_) \ - _(type) \ - _(next) \ - _(filter) + _(type) \ + _(next) \ + _(filter) \ /// Calls your macro with the name of each member of XrSenseDataQueryCompletionPICO, in order. #define XR_LIST_STRUCT_XrSenseDataQueryCompletionPICO(_) \ - _(type) \ - _(next) \ - _(futureResult) \ - _(snapshot) + _(type) \ + _(next) \ + _(futureResult) \ + _(snapshot) \ /// Calls your macro with the name of each member of XrQueriedSenseDataGetInfoPICO, in order. #define XR_LIST_STRUCT_XrQueriedSenseDataGetInfoPICO(_) \ - _(type) \ - _(next) \ - _(snapshot) + _(type) \ + _(next) \ + _(snapshot) \ /// Calls your macro with the name of each member of XrSpatialEntityStatePICO, in order. #define XR_LIST_STRUCT_XrSpatialEntityStatePICO(_) \ - _(type) \ - _(next) \ - _(entity) \ - _(lastUpdateTime) \ - _(uuid) + _(type) \ + _(next) \ + _(entity) \ + _(lastUpdateTime) \ + _(uuid) \ /// Calls your macro with the name of each member of XrQueriedSenseDataPICO, in order. #define XR_LIST_STRUCT_XrQueriedSenseDataPICO(_) \ - _(type) \ - _(next) \ - _(spatialEntityCapacityInput) \ - _(spatialEntityCountOutput) \ - _(spatialEntities) + _(type) \ + _(next) \ + _(spatialEntityCapacityInput) \ + _(spatialEntityCountOutput) \ + _(spatialEntities) \ /// Calls your macro with the name of each member of XrSenseDataFilterUuidPICO, in order. #define XR_LIST_STRUCT_XrSenseDataFilterUuidPICO(_) \ - _(type) \ - _(next) \ - _(uuidCount) \ - _(uuids) + _(type) \ + _(next) \ + _(uuidCount) \ + _(uuids) \ /// Calls your macro with the name of each member of XrSenseDataFilterSemanticPICO, in order. #define XR_LIST_STRUCT_XrSenseDataFilterSemanticPICO(_) \ - _(type) \ - _(next) \ - _(semanticCount) \ - _(semantics) + _(type) \ + _(next) \ + _(semanticCount) \ + _(semantics) \ /// Calls your macro with the name of each member of XrSpatialEntityAnchorRetrieveInfoPICO, in order. #define XR_LIST_STRUCT_XrSpatialEntityAnchorRetrieveInfoPICO(_) \ - _(type) \ - _(next) \ - _(entity) + _(type) \ + _(next) \ + _(entity) \ /// Calls your macro with the name of each member of XrAnchorLocateInfoPICO, in order. #define XR_LIST_STRUCT_XrAnchorLocateInfoPICO(_) \ - _(type) \ - _(next) \ - _(baseSpace) \ - _(time) + _(type) \ + _(next) \ + _(baseSpace) \ + _(time) \ /// Calls your macro with the name of each member of XrFuturePollResultProgressPICO, in order. #define XR_LIST_STRUCT_XrFuturePollResultProgressPICO(_) \ - _(type) \ - _(next) \ - _(progress) + _(type) \ + _(next) \ + _(progress) \ /// Calls your macro with the name of each member of XrSystemSpatialAnchorPropertiesPICO, in order. #define XR_LIST_STRUCT_XrSystemSpatialAnchorPropertiesPICO(_) \ - _(type) \ - _(next) \ - _(supportsSpatialAnchor) + _(type) \ + _(next) \ + _(supportsSpatialAnchor) \ /// Calls your macro with the name of each member of XrSenseDataProviderCreateInfoSpatialAnchorPICO, in order. #define XR_LIST_STRUCT_XrSenseDataProviderCreateInfoSpatialAnchorPICO(_) \ - _(type) \ - _(next) + _(type) \ + _(next) \ /// Calls your macro with the name of each member of XrSpatialAnchorCreateInfoPICO, in order. #define XR_LIST_STRUCT_XrSpatialAnchorCreateInfoPICO(_) \ - _(type) \ - _(next) \ - _(space) \ - _(pose) \ - _(time) + _(type) \ + _(next) \ + _(space) \ + _(pose) \ + _(time) \ /// Calls your macro with the name of each member of XrSpatialAnchorCreateCompletionPICO, in order. #define XR_LIST_STRUCT_XrSpatialAnchorCreateCompletionPICO(_) \ - _(type) \ - _(next) \ - _(futureResult) \ - _(anchor) \ - _(uuid) + _(type) \ + _(next) \ + _(futureResult) \ + _(anchor) \ + _(uuid) \ /// Calls your macro with the name of each member of XrSpatialAnchorPersistInfoPICO, in order. #define XR_LIST_STRUCT_XrSpatialAnchorPersistInfoPICO(_) \ - _(type) \ - _(next) \ - _(location) \ - _(anchor) + _(type) \ + _(next) \ + _(location) \ + _(anchor) \ /// Calls your macro with the name of each member of XrSpatialAnchorPersistCompletionPICO, in order. #define XR_LIST_STRUCT_XrSpatialAnchorPersistCompletionPICO(_) \ - _(type) \ - _(next) \ - _(futureResult) \ - _(anchor) \ - _(uuid) + _(type) \ + _(next) \ + _(futureResult) \ + _(anchor) \ + _(uuid) \ /// Calls your macro with the name of each member of XrSpatialAnchorUnpersistInfoPICO, in order. #define XR_LIST_STRUCT_XrSpatialAnchorUnpersistInfoPICO(_) \ - _(type) \ - _(next) \ - _(location) \ - _(anchor) + _(type) \ + _(next) \ + _(location) \ + _(anchor) \ /// Calls your macro with the name of each member of XrSpatialAnchorUnpersistCompletionPICO, in order. #define XR_LIST_STRUCT_XrSpatialAnchorUnpersistCompletionPICO(_) \ - _(type) \ - _(next) \ - _(futureResult) \ - _(anchor) \ - _(uuid) + _(type) \ + _(next) \ + _(futureResult) \ + _(anchor) \ + _(uuid) \ /// Calls your macro with the name of each member of XrSystemSpatialAnchorSharingPropertiesPICO, in order. #define XR_LIST_STRUCT_XrSystemSpatialAnchorSharingPropertiesPICO(_) \ - _(type) \ - _(next) \ - _(supportsSpatialAnchorSharing) + _(type) \ + _(next) \ + _(supportsSpatialAnchorSharing) \ /// Calls your macro with the name of each member of XrSpatialAnchorShareInfoPICO, in order. #define XR_LIST_STRUCT_XrSpatialAnchorShareInfoPICO(_) \ - _(type) \ - _(next) \ - _(anchor) + _(type) \ + _(next) \ + _(anchor) \ /// Calls your macro with the name of each member of XrSpatialAnchorShareCompletionPICO, in order. #define XR_LIST_STRUCT_XrSpatialAnchorShareCompletionPICO(_) \ - _(type) \ - _(next) \ - _(futureResult) + _(type) \ + _(next) \ + _(futureResult) \ /// Calls your macro with the name of each member of XrSharedSpatialAnchorDownloadInfoPICO, in order. #define XR_LIST_STRUCT_XrSharedSpatialAnchorDownloadInfoPICO(_) \ - _(type) \ - _(next) \ - _(uuid) + _(type) \ + _(next) \ + _(uuid) \ /// Calls your macro with the name of each member of XrSharedSpatialAnchorDownloadCompletionPICO, in order. #define XR_LIST_STRUCT_XrSharedSpatialAnchorDownloadCompletionPICO(_) \ - _(type) \ - _(next) \ - _(futureResult) + _(type) \ + _(next) \ + _(futureResult) \ /// Calls your macro with the name of each member of XrSystemSceneCapturePropertiesPICO, in order. #define XR_LIST_STRUCT_XrSystemSceneCapturePropertiesPICO(_) \ - _(type) \ - _(next) \ - _(supportsSceneCapture) + _(type) \ + _(next) \ + _(supportsSceneCapture) \ /// Calls your macro with the name of each member of XrSenseDataProviderCreateInfoSceneCapturePICO, in order. #define XR_LIST_STRUCT_XrSenseDataProviderCreateInfoSceneCapturePICO(_) \ - _(type) \ - _(next) + _(type) \ + _(next) \ /// Calls your macro with the name of each member of XrSceneCaptureStartInfoPICO, in order. #define XR_LIST_STRUCT_XrSceneCaptureStartInfoPICO(_) \ - _(type) \ - _(next) + _(type) \ + _(next) \ /// Calls your macro with the name of each member of XrSceneCaptureStartCompletionPICO, in order. #define XR_LIST_STRUCT_XrSceneCaptureStartCompletionPICO(_) \ - _(type) \ - _(next) \ - _(futureResult) + _(type) \ + _(next) \ + _(futureResult) \ /// Calls your macro with the name of each member of XrSystemSpatialMeshPropertiesPICO, in order. #define XR_LIST_STRUCT_XrSystemSpatialMeshPropertiesPICO(_) \ - _(type) \ - _(next) \ - _(supportsSpatialMesh) + _(type) \ + _(next) \ + _(supportsSpatialMesh) \ /// Calls your macro with the name of each member of XrSenseDataProviderCreateInfoSpatialMeshPICO, in order. #define XR_LIST_STRUCT_XrSenseDataProviderCreateInfoSpatialMeshPICO(_) \ - _(type) \ - _(next) \ - _(configFlags) \ - _(lod) + _(type) \ + _(next) \ + _(configFlags) \ + _(lod) \ + + /// Calls your macro with the structure type name and the XrStructureType constant for /// each known/available structure type, excluding those unavailable due to preprocessor definitions. @@ -8166,17 +9242,32 @@ XR_ENUM_STR(XrResult); _(XrEventDataSpaceListSaveCompleteFB, XR_TYPE_EVENT_DATA_SPACE_LIST_SAVE_COMPLETE_FB) \ _(XrSpaceUserCreateInfoFB, XR_TYPE_SPACE_USER_CREATE_INFO_FB) \ _(XrSystemHeadsetIdPropertiesMETA, XR_TYPE_SYSTEM_HEADSET_ID_PROPERTIES_META) \ + _(XrSystemSpaceDiscoveryPropertiesMETA, XR_TYPE_SYSTEM_SPACE_DISCOVERY_PROPERTIES_META) \ + _(XrSpaceDiscoveryInfoMETA, XR_TYPE_SPACE_DISCOVERY_INFO_META) \ + _(XrSpaceFilterUuidMETA, XR_TYPE_SPACE_FILTER_UUID_META) \ + _(XrSpaceFilterComponentMETA, XR_TYPE_SPACE_FILTER_COMPONENT_META) \ + _(XrSpaceDiscoveryResultsMETA, XR_TYPE_SPACE_DISCOVERY_RESULTS_META) \ + _(XrEventDataSpaceDiscoveryResultsAvailableMETA, XR_TYPE_EVENT_DATA_SPACE_DISCOVERY_RESULTS_AVAILABLE_META) \ + _(XrEventDataSpaceDiscoveryCompleteMETA, XR_TYPE_EVENT_DATA_SPACE_DISCOVERY_COMPLETE_META) \ _(XrRecommendedLayerResolutionMETA, XR_TYPE_RECOMMENDED_LAYER_RESOLUTION_META) \ _(XrRecommendedLayerResolutionGetInfoMETA, XR_TYPE_RECOMMENDED_LAYER_RESOLUTION_GET_INFO_META) \ + _(XrSystemSpacePersistencePropertiesMETA, XR_TYPE_SYSTEM_SPACE_PERSISTENCE_PROPERTIES_META) \ + _(XrSpacesSaveInfoMETA, XR_TYPE_SPACES_SAVE_INFO_META) \ + _(XrEventDataSpacesSaveResultMETA, XR_TYPE_EVENT_DATA_SPACES_SAVE_RESULT_META) \ + _(XrSpacesEraseInfoMETA, XR_TYPE_SPACES_ERASE_INFO_META) \ + _(XrEventDataSpacesEraseResultMETA, XR_TYPE_EVENT_DATA_SPACES_ERASE_RESULT_META) \ _(XrPassthroughColorLutCreateInfoMETA, XR_TYPE_PASSTHROUGH_COLOR_LUT_CREATE_INFO_META) \ _(XrPassthroughColorLutUpdateInfoMETA, XR_TYPE_PASSTHROUGH_COLOR_LUT_UPDATE_INFO_META) \ _(XrPassthroughColorMapLutMETA, XR_TYPE_PASSTHROUGH_COLOR_MAP_LUT_META) \ _(XrPassthroughColorMapInterpolatedLutMETA, XR_TYPE_PASSTHROUGH_COLOR_MAP_INTERPOLATED_LUT_META) \ _(XrSystemPassthroughColorLutPropertiesMETA, XR_TYPE_SYSTEM_PASSTHROUGH_COLOR_LUT_PROPERTIES_META) \ _(XrSpaceTriangleMeshGetInfoMETA, XR_TYPE_SPACE_TRIANGLE_MESH_GET_INFO_META) \ - _(XrSpaceTriangleMeshMETA, XR_TYPE_SPACE_TRIANGLE_MESH_META) \ - _(XrSystemPropertiesBodyTrackingFullBodyMETA, XR_TYPE_SYSTEM_PROPERTIES_BODY_TRACKING_FULL_BODY_META) \ - _(XrEventDataPassthroughLayerResumedMETA, XR_TYPE_EVENT_DATA_PASSTHROUGH_LAYER_RESUMED_META) \ + _(XrSpaceTriangleMeshMETA, XR_TYPE_SPACE_TRIANGLE_MESH_META) \ + _(XrSystemPropertiesBodyTrackingFullBodyMETA, XR_TYPE_SYSTEM_PROPERTIES_BODY_TRACKING_FULL_BODY_META) \ + _(XrEventDataPassthroughLayerResumedMETA, XR_TYPE_EVENT_DATA_PASSTHROUGH_LAYER_RESUMED_META) \ + _(XrBodyTrackingCalibrationStatusMETA, XR_TYPE_BODY_TRACKING_CALIBRATION_STATUS_META) \ + _(XrBodyTrackingCalibrationInfoMETA, XR_TYPE_BODY_TRACKING_CALIBRATION_INFO_META) \ + _(XrSystemPropertiesBodyTrackingCalibrationMETA, XR_TYPE_SYSTEM_PROPERTIES_BODY_TRACKING_CALIBRATION_META) \ _(XrSystemFaceTrackingProperties2FB, XR_TYPE_SYSTEM_FACE_TRACKING_PROPERTIES2_FB) \ _(XrFaceTrackerCreateInfo2FB, XR_TYPE_FACE_TRACKER_CREATE_INFO2_FB) \ _(XrFaceExpressionInfo2FB, XR_TYPE_FACE_EXPRESSION_INFO2_FB) \ @@ -8191,24 +9282,23 @@ XR_ENUM_STR(XrResult); _(XrEnvironmentDepthImageViewMETA, XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_VIEW_META) \ _(XrEnvironmentDepthImageMETA, XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_META) \ _(XrEnvironmentDepthHandRemovalSetInfoMETA, XR_TYPE_ENVIRONMENT_DEPTH_HAND_REMOVAL_SET_INFO_META) \ - _(XrSystemEnvironmentDepthPropertiesMETA, XR_TYPE_SYSTEM_ENVIRONMENT_DEPTH_PROPERTIES_META) \ - _(XrRenderModelCreateInfoEXT, XR_TYPE_RENDER_MODEL_CREATE_INFO_EXT) \ - _(XrRenderModelPropertiesGetInfoEXT, XR_TYPE_RENDER_MODEL_PROPERTIES_GET_INFO_EXT) \ - _(XrRenderModelPropertiesEXT, XR_TYPE_RENDER_MODEL_PROPERTIES_EXT) \ - _(XrRenderModelSpaceCreateInfoEXT, XR_TYPE_RENDER_MODEL_SPACE_CREATE_INFO_EXT) \ - _(XrRenderModelStateGetInfoEXT, XR_TYPE_RENDER_MODEL_STATE_GET_INFO_EXT) \ - _(XrRenderModelStateEXT, XR_TYPE_RENDER_MODEL_STATE_EXT) \ - _(XrRenderModelAssetCreateInfoEXT, XR_TYPE_RENDER_MODEL_ASSET_CREATE_INFO_EXT) \ - _(XrRenderModelAssetDataGetInfoEXT, XR_TYPE_RENDER_MODEL_ASSET_DATA_GET_INFO_EXT) \ - _(XrRenderModelAssetDataEXT, XR_TYPE_RENDER_MODEL_ASSET_DATA_EXT) \ - _(XrRenderModelAssetPropertiesGetInfoEXT, XR_TYPE_RENDER_MODEL_ASSET_PROPERTIES_GET_INFO_EXT) \ - _(XrRenderModelAssetPropertiesEXT, XR_TYPE_RENDER_MODEL_ASSET_PROPERTIES_EXT) \ - _(XrInteractionRenderModelIdsEnumerateInfoEXT, XR_TYPE_INTERACTION_RENDER_MODEL_IDS_ENUMERATE_INFO_EXT) \ - _(XrInteractionRenderModelSubactionPathInfoEXT, XR_TYPE_INTERACTION_RENDER_MODEL_SUBACTION_PATH_INFO_EXT) \ - _(XrInteractionRenderModelTopLevelUserPathGetInfoEXT, \ - XR_TYPE_INTERACTION_RENDER_MODEL_TOP_LEVEL_USER_PATH_GET_INFO_EXT) \ - _(XrEventDataInteractionRenderModelsChangedEXT, XR_TYPE_EVENT_DATA_INTERACTION_RENDER_MODELS_CHANGED_EXT) \ - _(XrPassthroughCreateInfoHTC, XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC) \ + _(XrSystemEnvironmentDepthPropertiesMETA, XR_TYPE_SYSTEM_ENVIRONMENT_DEPTH_PROPERTIES_META) \ + _(XrRenderModelCreateInfoEXT, XR_TYPE_RENDER_MODEL_CREATE_INFO_EXT) \ + _(XrRenderModelPropertiesGetInfoEXT, XR_TYPE_RENDER_MODEL_PROPERTIES_GET_INFO_EXT) \ + _(XrRenderModelPropertiesEXT, XR_TYPE_RENDER_MODEL_PROPERTIES_EXT) \ + _(XrRenderModelSpaceCreateInfoEXT, XR_TYPE_RENDER_MODEL_SPACE_CREATE_INFO_EXT) \ + _(XrRenderModelStateGetInfoEXT, XR_TYPE_RENDER_MODEL_STATE_GET_INFO_EXT) \ + _(XrRenderModelStateEXT, XR_TYPE_RENDER_MODEL_STATE_EXT) \ + _(XrRenderModelAssetCreateInfoEXT, XR_TYPE_RENDER_MODEL_ASSET_CREATE_INFO_EXT) \ + _(XrRenderModelAssetDataGetInfoEXT, XR_TYPE_RENDER_MODEL_ASSET_DATA_GET_INFO_EXT) \ + _(XrRenderModelAssetDataEXT, XR_TYPE_RENDER_MODEL_ASSET_DATA_EXT) \ + _(XrRenderModelAssetPropertiesGetInfoEXT, XR_TYPE_RENDER_MODEL_ASSET_PROPERTIES_GET_INFO_EXT) \ + _(XrRenderModelAssetPropertiesEXT, XR_TYPE_RENDER_MODEL_ASSET_PROPERTIES_EXT) \ + _(XrInteractionRenderModelIdsEnumerateInfoEXT, XR_TYPE_INTERACTION_RENDER_MODEL_IDS_ENUMERATE_INFO_EXT) \ + _(XrInteractionRenderModelSubactionPathInfoEXT, XR_TYPE_INTERACTION_RENDER_MODEL_SUBACTION_PATH_INFO_EXT) \ + _(XrInteractionRenderModelTopLevelUserPathGetInfoEXT, XR_TYPE_INTERACTION_RENDER_MODEL_TOP_LEVEL_USER_PATH_GET_INFO_EXT) \ + _(XrEventDataInteractionRenderModelsChangedEXT, XR_TYPE_EVENT_DATA_INTERACTION_RENDER_MODELS_CHANGED_EXT) \ + _(XrPassthroughCreateInfoHTC, XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC) \ _(XrPassthroughColorHTC, XR_TYPE_PASSTHROUGH_COLOR_HTC) \ _(XrPassthroughMeshTransformInfoHTC, XR_TYPE_PASSTHROUGH_MESH_TRANSFORM_INFO_HTC) \ _(XrCompositionLayerPassthroughHTC, XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_HTC) \ @@ -8228,47 +9318,52 @@ XR_ENUM_STR(XrResult); _(XrSystemBodyTrackingPropertiesBD, XR_TYPE_SYSTEM_BODY_TRACKING_PROPERTIES_BD) \ _(XrBodyTrackerCreateInfoBD, XR_TYPE_BODY_TRACKER_CREATE_INFO_BD) \ _(XrBodyJointsLocateInfoBD, XR_TYPE_BODY_JOINTS_LOCATE_INFO_BD) \ - _(XrBodyJointLocationsBD, XR_TYPE_BODY_JOINT_LOCATIONS_BD) \ - _(XrSystemSpatialSensingPropertiesBD, XR_TYPE_SYSTEM_SPATIAL_SENSING_PROPERTIES_BD) \ - _(XrSpatialEntityComponentGetInfoBD, XR_TYPE_SPATIAL_ENTITY_COMPONENT_GET_INFO_BD) \ - _(XrSpatialEntityLocationGetInfoBD, XR_TYPE_SPATIAL_ENTITY_LOCATION_GET_INFO_BD) \ - _(XrSpatialEntityComponentDataLocationBD, XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_LOCATION_BD) \ - _(XrSpatialEntityComponentDataSemanticBD, XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_SEMANTIC_BD) \ - _(XrSpatialEntityComponentDataBoundingBox2DBD, XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_BOUNDING_BOX_2D_BD) \ - _(XrSpatialEntityComponentDataPolygonBD, XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_POLYGON_BD) \ - _(XrSpatialEntityComponentDataBoundingBox3DBD, XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_BOUNDING_BOX_3D_BD) \ - _(XrSpatialEntityComponentDataTriangleMeshBD, XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_TRIANGLE_MESH_BD) \ - _(XrSenseDataProviderCreateInfoBD, XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_BD) \ - _(XrSenseDataProviderStartInfoBD, XR_TYPE_SENSE_DATA_PROVIDER_START_INFO_BD) \ - _(XrEventDataSenseDataProviderStateChangedBD, XR_TYPE_EVENT_DATA_SENSE_DATA_PROVIDER_STATE_CHANGED_BD) \ - _(XrEventDataSenseDataUpdatedBD, XR_TYPE_EVENT_DATA_SENSE_DATA_UPDATED_BD) \ - _(XrSenseDataQueryInfoBD, XR_TYPE_SENSE_DATA_QUERY_INFO_BD) \ - _(XrSenseDataQueryCompletionBD, XR_TYPE_SENSE_DATA_QUERY_COMPLETION_BD) \ - _(XrQueriedSenseDataGetInfoBD, XR_TYPE_QUERIED_SENSE_DATA_GET_INFO_BD) \ - _(XrSpatialEntityStateBD, XR_TYPE_SPATIAL_ENTITY_STATE_BD) \ - _(XrQueriedSenseDataBD, XR_TYPE_QUERIED_SENSE_DATA_BD) \ - _(XrSenseDataFilterUuidBD, XR_TYPE_SENSE_DATA_FILTER_UUID_BD) \ - _(XrSenseDataFilterSemanticBD, XR_TYPE_SENSE_DATA_FILTER_SEMANTIC_BD) \ - _(XrSpatialEntityAnchorCreateInfoBD, XR_TYPE_SPATIAL_ENTITY_ANCHOR_CREATE_INFO_BD) \ - _(XrAnchorSpaceCreateInfoBD, XR_TYPE_ANCHOR_SPACE_CREATE_INFO_BD) \ - _(XrFutureCompletionEXT, XR_TYPE_FUTURE_COMPLETION_EXT) \ - _(XrSystemSpatialAnchorPropertiesBD, XR_TYPE_SYSTEM_SPATIAL_ANCHOR_PROPERTIES_BD) \ - _(XrSpatialAnchorCreateInfoBD, XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_BD) \ - _(XrSpatialAnchorCreateCompletionBD, XR_TYPE_SPATIAL_ANCHOR_CREATE_COMPLETION_BD) \ - _(XrSpatialAnchorPersistInfoBD, XR_TYPE_SPATIAL_ANCHOR_PERSIST_INFO_BD) \ - _(XrSpatialAnchorUnpersistInfoBD, XR_TYPE_SPATIAL_ANCHOR_UNPERSIST_INFO_BD) \ - _(XrSystemSpatialAnchorSharingPropertiesBD, XR_TYPE_SYSTEM_SPATIAL_ANCHOR_SHARING_PROPERTIES_BD) \ - _(XrSpatialAnchorShareInfoBD, XR_TYPE_SPATIAL_ANCHOR_SHARE_INFO_BD) \ - _(XrSharedSpatialAnchorDownloadInfoBD, XR_TYPE_SHARED_SPATIAL_ANCHOR_DOWNLOAD_INFO_BD) \ - _(XrSystemSpatialScenePropertiesBD, XR_TYPE_SYSTEM_SPATIAL_SCENE_PROPERTIES_BD) \ - _(XrSceneCaptureInfoBD, XR_TYPE_SCENE_CAPTURE_INFO_BD) \ - _(XrSystemSpatialMeshPropertiesBD, XR_TYPE_SYSTEM_SPATIAL_MESH_PROPERTIES_BD) \ - _(XrSenseDataProviderCreateInfoSpatialMeshBD, XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SPATIAL_MESH_BD) \ - _(XrFuturePollResultProgressBD, XR_TYPE_FUTURE_POLL_RESULT_PROGRESS_BD) \ - _(XrSystemSpatialPlanePropertiesBD, XR_TYPE_SYSTEM_SPATIAL_PLANE_PROPERTIES_BD) \ - _(XrSpatialEntityComponentDataPlaneOrientationBD, XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_PLANE_ORIENTATION_BD) \ - _(XrSenseDataFilterPlaneOrientationBD, XR_TYPE_SENSE_DATA_FILTER_PLANE_ORIENTATION_BD) \ - _(XrHandTrackingDataSourceInfoEXT, XR_TYPE_HAND_TRACKING_DATA_SOURCE_INFO_EXT) \ + _(XrBodyJointLocationsBD, XR_TYPE_BODY_JOINT_LOCATIONS_BD) \ + _(XrSystemFacialSimulationPropertiesBD, XR_TYPE_SYSTEM_FACIAL_SIMULATION_PROPERTIES_BD) \ + _(XrFaceTrackerCreateInfoBD, XR_TYPE_FACE_TRACKER_CREATE_INFO_BD) \ + _(XrFacialSimulationDataGetInfoBD, XR_TYPE_FACIAL_SIMULATION_DATA_GET_INFO_BD) \ + _(XrFacialSimulationDataBD, XR_TYPE_FACIAL_SIMULATION_DATA_BD) \ + _(XrLipExpressionDataBD, XR_TYPE_LIP_EXPRESSION_DATA_BD) \ + _(XrSystemSpatialSensingPropertiesBD, XR_TYPE_SYSTEM_SPATIAL_SENSING_PROPERTIES_BD) \ + _(XrSpatialEntityComponentGetInfoBD, XR_TYPE_SPATIAL_ENTITY_COMPONENT_GET_INFO_BD) \ + _(XrSpatialEntityLocationGetInfoBD, XR_TYPE_SPATIAL_ENTITY_LOCATION_GET_INFO_BD) \ + _(XrSpatialEntityComponentDataLocationBD, XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_LOCATION_BD) \ + _(XrSpatialEntityComponentDataSemanticBD, XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_SEMANTIC_BD) \ + _(XrSpatialEntityComponentDataBoundingBox2DBD, XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_BOUNDING_BOX_2D_BD) \ + _(XrSpatialEntityComponentDataPolygonBD, XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_POLYGON_BD) \ + _(XrSpatialEntityComponentDataBoundingBox3DBD, XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_BOUNDING_BOX_3D_BD) \ + _(XrSpatialEntityComponentDataTriangleMeshBD, XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_TRIANGLE_MESH_BD) \ + _(XrSenseDataProviderCreateInfoBD, XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_BD) \ + _(XrSenseDataProviderStartInfoBD, XR_TYPE_SENSE_DATA_PROVIDER_START_INFO_BD) \ + _(XrEventDataSenseDataProviderStateChangedBD, XR_TYPE_EVENT_DATA_SENSE_DATA_PROVIDER_STATE_CHANGED_BD) \ + _(XrEventDataSenseDataUpdatedBD, XR_TYPE_EVENT_DATA_SENSE_DATA_UPDATED_BD) \ + _(XrSenseDataQueryInfoBD, XR_TYPE_SENSE_DATA_QUERY_INFO_BD) \ + _(XrSenseDataQueryCompletionBD, XR_TYPE_SENSE_DATA_QUERY_COMPLETION_BD) \ + _(XrQueriedSenseDataGetInfoBD, XR_TYPE_QUERIED_SENSE_DATA_GET_INFO_BD) \ + _(XrSpatialEntityStateBD, XR_TYPE_SPATIAL_ENTITY_STATE_BD) \ + _(XrQueriedSenseDataBD, XR_TYPE_QUERIED_SENSE_DATA_BD) \ + _(XrSenseDataFilterUuidBD, XR_TYPE_SENSE_DATA_FILTER_UUID_BD) \ + _(XrSenseDataFilterSemanticBD, XR_TYPE_SENSE_DATA_FILTER_SEMANTIC_BD) \ + _(XrSpatialEntityAnchorCreateInfoBD, XR_TYPE_SPATIAL_ENTITY_ANCHOR_CREATE_INFO_BD) \ + _(XrAnchorSpaceCreateInfoBD, XR_TYPE_ANCHOR_SPACE_CREATE_INFO_BD) \ + _(XrFutureCompletionEXT, XR_TYPE_FUTURE_COMPLETION_EXT) \ + _(XrSystemSpatialAnchorPropertiesBD, XR_TYPE_SYSTEM_SPATIAL_ANCHOR_PROPERTIES_BD) \ + _(XrSpatialAnchorCreateInfoBD, XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_BD) \ + _(XrSpatialAnchorCreateCompletionBD, XR_TYPE_SPATIAL_ANCHOR_CREATE_COMPLETION_BD) \ + _(XrSpatialAnchorPersistInfoBD, XR_TYPE_SPATIAL_ANCHOR_PERSIST_INFO_BD) \ + _(XrSpatialAnchorUnpersistInfoBD, XR_TYPE_SPATIAL_ANCHOR_UNPERSIST_INFO_BD) \ + _(XrSystemSpatialAnchorSharingPropertiesBD, XR_TYPE_SYSTEM_SPATIAL_ANCHOR_SHARING_PROPERTIES_BD) \ + _(XrSpatialAnchorShareInfoBD, XR_TYPE_SPATIAL_ANCHOR_SHARE_INFO_BD) \ + _(XrSharedSpatialAnchorDownloadInfoBD, XR_TYPE_SHARED_SPATIAL_ANCHOR_DOWNLOAD_INFO_BD) \ + _(XrSystemSpatialScenePropertiesBD, XR_TYPE_SYSTEM_SPATIAL_SCENE_PROPERTIES_BD) \ + _(XrSceneCaptureInfoBD, XR_TYPE_SCENE_CAPTURE_INFO_BD) \ + _(XrSystemSpatialMeshPropertiesBD, XR_TYPE_SYSTEM_SPATIAL_MESH_PROPERTIES_BD) \ + _(XrSenseDataProviderCreateInfoSpatialMeshBD, XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SPATIAL_MESH_BD) \ + _(XrFuturePollResultProgressBD, XR_TYPE_FUTURE_POLL_RESULT_PROGRESS_BD) \ + _(XrSystemSpatialPlanePropertiesBD, XR_TYPE_SYSTEM_SPATIAL_PLANE_PROPERTIES_BD) \ + _(XrSpatialEntityComponentDataPlaneOrientationBD, XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_PLANE_ORIENTATION_BD) \ + _(XrSenseDataFilterPlaneOrientationBD, XR_TYPE_SENSE_DATA_FILTER_PLANE_ORIENTATION_BD) \ + _(XrHandTrackingDataSourceInfoEXT, XR_TYPE_HAND_TRACKING_DATA_SOURCE_INFO_EXT) \ _(XrHandTrackingDataSourceStateEXT, XR_TYPE_HAND_TRACKING_DATA_SOURCE_STATE_EXT) \ _(XrSystemPlaneDetectionPropertiesEXT, XR_TYPE_SYSTEM_PLANE_DETECTION_PROPERTIES_EXT) \ _(XrPlaneDetectorCreateInfoEXT, XR_TYPE_PLANE_DETECTOR_CREATE_INFO_EXT) \ @@ -8277,6 +9372,25 @@ XR_ENUM_STR(XrResult); _(XrPlaneDetectorLocationEXT, XR_TYPE_PLANE_DETECTOR_LOCATION_EXT) \ _(XrPlaneDetectorLocationsEXT, XR_TYPE_PLANE_DETECTOR_LOCATIONS_EXT) \ _(XrPlaneDetectorPolygonBufferEXT, XR_TYPE_PLANE_DETECTOR_POLYGON_BUFFER_EXT) \ + _(XrTrackableTrackerCreateInfoANDROID, XR_TYPE_TRACKABLE_TRACKER_CREATE_INFO_ANDROID) \ + _(XrTrackableGetInfoANDROID, XR_TYPE_TRACKABLE_GET_INFO_ANDROID) \ + _(XrTrackablePlaneANDROID, XR_TYPE_TRACKABLE_PLANE_ANDROID) \ + _(XrAnchorSpaceCreateInfoANDROID, XR_TYPE_ANCHOR_SPACE_CREATE_INFO_ANDROID) \ + _(XrSystemTrackablesPropertiesANDROID, XR_TYPE_SYSTEM_TRACKABLES_PROPERTIES_ANDROID) \ + _(XrDeviceAnchorPersistenceCreateInfoANDROID, XR_TYPE_DEVICE_ANCHOR_PERSISTENCE_CREATE_INFO_ANDROID) \ + _(XrPersistedAnchorSpaceCreateInfoANDROID, XR_TYPE_PERSISTED_ANCHOR_SPACE_CREATE_INFO_ANDROID) \ + _(XrPersistedAnchorSpaceInfoANDROID, XR_TYPE_PERSISTED_ANCHOR_SPACE_INFO_ANDROID) \ + _(XrSystemDeviceAnchorPersistencePropertiesANDROID, XR_TYPE_SYSTEM_DEVICE_ANCHOR_PERSISTENCE_PROPERTIES_ANDROID) \ + _(XrFaceTrackerCreateInfoANDROID, XR_TYPE_FACE_TRACKER_CREATE_INFO_ANDROID) \ + _(XrFaceStateGetInfoANDROID, XR_TYPE_FACE_STATE_GET_INFO_ANDROID) \ + _(XrFaceStateANDROID, XR_TYPE_FACE_STATE_ANDROID) \ + _(XrSystemFaceTrackingPropertiesANDROID, XR_TYPE_SYSTEM_FACE_TRACKING_PROPERTIES_ANDROID) \ + _(XrSystemPassthroughCameraStatePropertiesANDROID, XR_TYPE_SYSTEM_PASSTHROUGH_CAMERA_STATE_PROPERTIES_ANDROID) \ + _(XrPassthroughCameraStateGetInfoANDROID, XR_TYPE_PASSTHROUGH_CAMERA_STATE_GET_INFO_ANDROID) \ + _(XrRaycastInfoANDROID, XR_TYPE_RAYCAST_INFO_ANDROID) \ + _(XrRaycastHitResultsANDROID, XR_TYPE_RAYCAST_HIT_RESULTS_ANDROID) \ + _(XrTrackableObjectANDROID, XR_TYPE_TRACKABLE_OBJECT_ANDROID) \ + _(XrTrackableObjectConfigurationANDROID, XR_TYPE_TRACKABLE_OBJECT_CONFIGURATION_ANDROID) \ _(XrFutureCancelInfoEXT, XR_TYPE_FUTURE_CANCEL_INFO_EXT) \ _(XrFuturePollInfoEXT, XR_TYPE_FUTURE_POLL_INFO_EXT) \ _(XrFuturePollResultEXT, XR_TYPE_FUTURE_POLL_RESULT_EXT) \ @@ -8301,14 +9415,11 @@ XR_ENUM_STR(XrResult); _(XrSystemFacialExpressionPropertiesML, XR_TYPE_SYSTEM_FACIAL_EXPRESSION_PROPERTIES_ML) \ _(XrFacialExpressionClientCreateInfoML, XR_TYPE_FACIAL_EXPRESSION_CLIENT_CREATE_INFO_ML) \ _(XrFacialExpressionBlendShapeGetInfoML, XR_TYPE_FACIAL_EXPRESSION_BLEND_SHAPE_GET_INFO_ML) \ - _(XrFacialExpressionBlendShapePropertiesML, XR_TYPE_FACIAL_EXPRESSION_BLEND_SHAPE_PROPERTIES_ML) \ - _(XrSystemSimultaneousHandsAndControllersPropertiesMETA, \ - XR_TYPE_SYSTEM_SIMULTANEOUS_HANDS_AND_CONTROLLERS_PROPERTIES_META) \ - _(XrSimultaneousHandsAndControllersTrackingResumeInfoMETA, \ - XR_TYPE_SIMULTANEOUS_HANDS_AND_CONTROLLERS_TRACKING_RESUME_INFO_META) \ - _(XrSimultaneousHandsAndControllersTrackingPauseInfoMETA, \ - XR_TYPE_SIMULTANEOUS_HANDS_AND_CONTROLLERS_TRACKING_PAUSE_INFO_META) \ - _(XrColocationDiscoveryStartInfoMETA, XR_TYPE_COLOCATION_DISCOVERY_START_INFO_META) \ + _(XrFacialExpressionBlendShapePropertiesML, XR_TYPE_FACIAL_EXPRESSION_BLEND_SHAPE_PROPERTIES_ML) \ + _(XrSystemSimultaneousHandsAndControllersPropertiesMETA, XR_TYPE_SYSTEM_SIMULTANEOUS_HANDS_AND_CONTROLLERS_PROPERTIES_META) \ + _(XrSimultaneousHandsAndControllersTrackingResumeInfoMETA, XR_TYPE_SIMULTANEOUS_HANDS_AND_CONTROLLERS_TRACKING_RESUME_INFO_META) \ + _(XrSimultaneousHandsAndControllersTrackingPauseInfoMETA, XR_TYPE_SIMULTANEOUS_HANDS_AND_CONTROLLERS_TRACKING_PAUSE_INFO_META) \ + _(XrColocationDiscoveryStartInfoMETA, XR_TYPE_COLOCATION_DISCOVERY_START_INFO_META) \ _(XrColocationDiscoveryStopInfoMETA, XR_TYPE_COLOCATION_DISCOVERY_STOP_INFO_META) \ _(XrColocationAdvertisementStartInfoMETA, XR_TYPE_COLOCATION_ADVERTISEMENT_START_INFO_META) \ _(XrColocationAdvertisementStopInfoMETA, XR_TYPE_COLOCATION_ADVERTISEMENT_STOP_INFO_META) \ @@ -8322,49 +9433,53 @@ XR_ENUM_STR(XrResult); _(XrSystemColocationDiscoveryPropertiesMETA, XR_TYPE_SYSTEM_COLOCATION_DISCOVERY_PROPERTIES_META) \ _(XrSystemSpatialEntityGroupSharingPropertiesMETA, XR_TYPE_SYSTEM_SPATIAL_ENTITY_GROUP_SHARING_PROPERTIES_META) \ _(XrShareSpacesRecipientGroupsMETA, XR_TYPE_SHARE_SPACES_RECIPIENT_GROUPS_META) \ - _(XrSpaceGroupUuidFilterInfoMETA, XR_TYPE_SPACE_GROUP_UUID_FILTER_INFO_META) \ - _(XrSpatialCapabilityComponentTypesEXT, XR_TYPE_SPATIAL_CAPABILITY_COMPONENT_TYPES_EXT) \ - _(XrSpatialContextCreateInfoEXT, XR_TYPE_SPATIAL_CONTEXT_CREATE_INFO_EXT) \ - _(XrCreateSpatialContextCompletionEXT, XR_TYPE_CREATE_SPATIAL_CONTEXT_COMPLETION_EXT) \ - _(XrSpatialDiscoverySnapshotCreateInfoEXT, XR_TYPE_SPATIAL_DISCOVERY_SNAPSHOT_CREATE_INFO_EXT) \ - _(XrCreateSpatialDiscoverySnapshotCompletionInfoEXT, XR_TYPE_CREATE_SPATIAL_DISCOVERY_SNAPSHOT_COMPLETION_INFO_EXT) \ - _(XrCreateSpatialDiscoverySnapshotCompletionEXT, XR_TYPE_CREATE_SPATIAL_DISCOVERY_SNAPSHOT_COMPLETION_EXT) \ - _(XrSpatialComponentDataQueryConditionEXT, XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_CONDITION_EXT) \ - _(XrSpatialComponentDataQueryResultEXT, XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_RESULT_EXT) \ - _(XrSpatialBufferGetInfoEXT, XR_TYPE_SPATIAL_BUFFER_GET_INFO_EXT) \ - _(XrSpatialComponentBounded2DListEXT, XR_TYPE_SPATIAL_COMPONENT_BOUNDED_2D_LIST_EXT) \ - _(XrSpatialComponentBounded3DListEXT, XR_TYPE_SPATIAL_COMPONENT_BOUNDED_3D_LIST_EXT) \ - _(XrSpatialComponentParentListEXT, XR_TYPE_SPATIAL_COMPONENT_PARENT_LIST_EXT) \ - _(XrSpatialComponentMesh3DListEXT, XR_TYPE_SPATIAL_COMPONENT_MESH_3D_LIST_EXT) \ - _(XrSpatialEntityFromIdCreateInfoEXT, XR_TYPE_SPATIAL_ENTITY_FROM_ID_CREATE_INFO_EXT) \ - _(XrSpatialUpdateSnapshotCreateInfoEXT, XR_TYPE_SPATIAL_UPDATE_SNAPSHOT_CREATE_INFO_EXT) \ - _(XrEventDataSpatialDiscoveryRecommendedEXT, XR_TYPE_EVENT_DATA_SPATIAL_DISCOVERY_RECOMMENDED_EXT) \ - _(XrSpatialFilterTrackingStateEXT, XR_TYPE_SPATIAL_FILTER_TRACKING_STATE_EXT) \ - _(XrSpatialCapabilityConfigurationPlaneTrackingEXT, XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_PLANE_TRACKING_EXT) \ - _(XrSpatialComponentPlaneAlignmentListEXT, XR_TYPE_SPATIAL_COMPONENT_PLANE_ALIGNMENT_LIST_EXT) \ - _(XrSpatialComponentMesh2DListEXT, XR_TYPE_SPATIAL_COMPONENT_MESH_2D_LIST_EXT) \ - _(XrSpatialComponentPolygon2DListEXT, XR_TYPE_SPATIAL_COMPONENT_POLYGON_2D_LIST_EXT) \ - _(XrSpatialComponentPlaneSemanticLabelListEXT, XR_TYPE_SPATIAL_COMPONENT_PLANE_SEMANTIC_LABEL_LIST_EXT) \ - _(XrSpatialCapabilityConfigurationQrCodeEXT, XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_QR_CODE_EXT) \ - _(XrSpatialCapabilityConfigurationMicroQrCodeEXT, XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_MICRO_QR_CODE_EXT) \ - _(XrSpatialCapabilityConfigurationArucoMarkerEXT, XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_ARUCO_MARKER_EXT) \ - _(XrSpatialCapabilityConfigurationAprilTagEXT, XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_APRIL_TAG_EXT) \ - _(XrSpatialMarkerSizeEXT, XR_TYPE_SPATIAL_MARKER_SIZE_EXT) \ - _(XrSpatialMarkerStaticOptimizationEXT, XR_TYPE_SPATIAL_MARKER_STATIC_OPTIMIZATION_EXT) \ - _(XrSpatialComponentMarkerListEXT, XR_TYPE_SPATIAL_COMPONENT_MARKER_LIST_EXT) \ - _(XrSpatialCapabilityConfigurationAnchorEXT, XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_ANCHOR_EXT) \ - _(XrSpatialComponentAnchorListEXT, XR_TYPE_SPATIAL_COMPONENT_ANCHOR_LIST_EXT) \ - _(XrSpatialAnchorCreateInfoEXT, XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_EXT) \ - _(XrSpatialPersistenceContextCreateInfoEXT, XR_TYPE_SPATIAL_PERSISTENCE_CONTEXT_CREATE_INFO_EXT) \ - _(XrCreateSpatialPersistenceContextCompletionEXT, XR_TYPE_CREATE_SPATIAL_PERSISTENCE_CONTEXT_COMPLETION_EXT) \ - _(XrSpatialContextPersistenceConfigEXT, XR_TYPE_SPATIAL_CONTEXT_PERSISTENCE_CONFIG_EXT) \ - _(XrSpatialDiscoveryPersistenceUuidFilterEXT, XR_TYPE_SPATIAL_DISCOVERY_PERSISTENCE_UUID_FILTER_EXT) \ - _(XrSpatialComponentPersistenceListEXT, XR_TYPE_SPATIAL_COMPONENT_PERSISTENCE_LIST_EXT) \ - _(XrSpatialEntityPersistInfoEXT, XR_TYPE_SPATIAL_ENTITY_PERSIST_INFO_EXT) \ - _(XrPersistSpatialEntityCompletionEXT, XR_TYPE_PERSIST_SPATIAL_ENTITY_COMPLETION_EXT) \ - _(XrSpatialEntityUnpersistInfoEXT, XR_TYPE_SPATIAL_ENTITY_UNPERSIST_INFO_EXT) \ - _(XrUnpersistSpatialEntityCompletionEXT, XR_TYPE_UNPERSIST_SPATIAL_ENTITY_COMPLETION_EXT) \ - _(XrEventDataMrcStatusChangedPICO, XR_TYPE_EVENT_DATA_MRC_STATUS_CHANGED_PICO) \ + _(XrSpaceGroupUuidFilterInfoMETA, XR_TYPE_SPACE_GROUP_UUID_FILTER_INFO_META) \ + _(XrSystemMarkerTrackingPropertiesANDROID, XR_TYPE_SYSTEM_MARKER_TRACKING_PROPERTIES_ANDROID) \ + _(XrTrackableMarkerConfigurationANDROID, XR_TYPE_TRACKABLE_MARKER_CONFIGURATION_ANDROID) \ + _(XrTrackableMarkerANDROID, XR_TYPE_TRACKABLE_MARKER_ANDROID) \ + _(XrSpatialCapabilityComponentTypesEXT, XR_TYPE_SPATIAL_CAPABILITY_COMPONENT_TYPES_EXT) \ + _(XrSpatialContextCreateInfoEXT, XR_TYPE_SPATIAL_CONTEXT_CREATE_INFO_EXT) \ + _(XrCreateSpatialContextCompletionEXT, XR_TYPE_CREATE_SPATIAL_CONTEXT_COMPLETION_EXT) \ + _(XrSpatialDiscoverySnapshotCreateInfoEXT, XR_TYPE_SPATIAL_DISCOVERY_SNAPSHOT_CREATE_INFO_EXT) \ + _(XrCreateSpatialDiscoverySnapshotCompletionInfoEXT, XR_TYPE_CREATE_SPATIAL_DISCOVERY_SNAPSHOT_COMPLETION_INFO_EXT) \ + _(XrCreateSpatialDiscoverySnapshotCompletionEXT, XR_TYPE_CREATE_SPATIAL_DISCOVERY_SNAPSHOT_COMPLETION_EXT) \ + _(XrSpatialComponentDataQueryConditionEXT, XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_CONDITION_EXT) \ + _(XrSpatialComponentDataQueryResultEXT, XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_RESULT_EXT) \ + _(XrSpatialBufferGetInfoEXT, XR_TYPE_SPATIAL_BUFFER_GET_INFO_EXT) \ + _(XrSpatialComponentBounded2DListEXT, XR_TYPE_SPATIAL_COMPONENT_BOUNDED_2D_LIST_EXT) \ + _(XrSpatialComponentBounded3DListEXT, XR_TYPE_SPATIAL_COMPONENT_BOUNDED_3D_LIST_EXT) \ + _(XrSpatialComponentParentListEXT, XR_TYPE_SPATIAL_COMPONENT_PARENT_LIST_EXT) \ + _(XrSpatialComponentMesh3DListEXT, XR_TYPE_SPATIAL_COMPONENT_MESH_3D_LIST_EXT) \ + _(XrSpatialEntityFromIdCreateInfoEXT, XR_TYPE_SPATIAL_ENTITY_FROM_ID_CREATE_INFO_EXT) \ + _(XrSpatialUpdateSnapshotCreateInfoEXT, XR_TYPE_SPATIAL_UPDATE_SNAPSHOT_CREATE_INFO_EXT) \ + _(XrEventDataSpatialDiscoveryRecommendedEXT, XR_TYPE_EVENT_DATA_SPATIAL_DISCOVERY_RECOMMENDED_EXT) \ + _(XrSpatialFilterTrackingStateEXT, XR_TYPE_SPATIAL_FILTER_TRACKING_STATE_EXT) \ + _(XrSpatialCapabilityConfigurationPlaneTrackingEXT, XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_PLANE_TRACKING_EXT) \ + _(XrSpatialComponentPlaneAlignmentListEXT, XR_TYPE_SPATIAL_COMPONENT_PLANE_ALIGNMENT_LIST_EXT) \ + _(XrSpatialComponentMesh2DListEXT, XR_TYPE_SPATIAL_COMPONENT_MESH_2D_LIST_EXT) \ + _(XrSpatialComponentPolygon2DListEXT, XR_TYPE_SPATIAL_COMPONENT_POLYGON_2D_LIST_EXT) \ + _(XrSpatialComponentPlaneSemanticLabelListEXT, XR_TYPE_SPATIAL_COMPONENT_PLANE_SEMANTIC_LABEL_LIST_EXT) \ + _(XrSpatialCapabilityConfigurationQrCodeEXT, XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_QR_CODE_EXT) \ + _(XrSpatialCapabilityConfigurationMicroQrCodeEXT, XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_MICRO_QR_CODE_EXT) \ + _(XrSpatialCapabilityConfigurationArucoMarkerEXT, XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_ARUCO_MARKER_EXT) \ + _(XrSpatialCapabilityConfigurationAprilTagEXT, XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_APRIL_TAG_EXT) \ + _(XrSpatialMarkerSizeEXT, XR_TYPE_SPATIAL_MARKER_SIZE_EXT) \ + _(XrSpatialMarkerStaticOptimizationEXT, XR_TYPE_SPATIAL_MARKER_STATIC_OPTIMIZATION_EXT) \ + _(XrSpatialComponentMarkerListEXT, XR_TYPE_SPATIAL_COMPONENT_MARKER_LIST_EXT) \ + _(XrSpatialCapabilityConfigurationAnchorEXT, XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_ANCHOR_EXT) \ + _(XrSpatialComponentAnchorListEXT, XR_TYPE_SPATIAL_COMPONENT_ANCHOR_LIST_EXT) \ + _(XrSpatialAnchorCreateInfoEXT, XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_EXT) \ + _(XrSpatialPersistenceContextCreateInfoEXT, XR_TYPE_SPATIAL_PERSISTENCE_CONTEXT_CREATE_INFO_EXT) \ + _(XrCreateSpatialPersistenceContextCompletionEXT, XR_TYPE_CREATE_SPATIAL_PERSISTENCE_CONTEXT_COMPLETION_EXT) \ + _(XrSpatialContextPersistenceConfigEXT, XR_TYPE_SPATIAL_CONTEXT_PERSISTENCE_CONFIG_EXT) \ + _(XrSpatialDiscoveryPersistenceUuidFilterEXT, XR_TYPE_SPATIAL_DISCOVERY_PERSISTENCE_UUID_FILTER_EXT) \ + _(XrSpatialComponentPersistenceListEXT, XR_TYPE_SPATIAL_COMPONENT_PERSISTENCE_LIST_EXT) \ + _(XrSpatialEntityPersistInfoEXT, XR_TYPE_SPATIAL_ENTITY_PERSIST_INFO_EXT) \ + _(XrPersistSpatialEntityCompletionEXT, XR_TYPE_PERSIST_SPATIAL_ENTITY_COMPLETION_EXT) \ + _(XrSpatialEntityUnpersistInfoEXT, XR_TYPE_SPATIAL_ENTITY_UNPERSIST_INFO_EXT) \ + _(XrUnpersistSpatialEntityCompletionEXT, XR_TYPE_UNPERSIST_SPATIAL_ENTITY_COMPLETION_EXT) \ + _(XrLoaderInitInfoPropertiesEXT, XR_TYPE_LOADER_INIT_INFO_PROPERTIES_EXT) \ + _(XrEventDataMrcStatusChangedPICO, XR_TYPE_EVENT_DATA_MRC_STATUS_CHANGED_PICO) \ _(XrMrcSpaceCreateInfoPICO, XR_TYPE_MRC_SPACE_CREATE_INFO_PICO) \ _(XrExternalCameraParameterPICO, XR_TYPE_EXTERNAL_CAMERA_PARAMETER_PICO) \ _(XrVirtualBoundaryInfoPICO, XR_TYPE_VIRTUAL_BOUNDARY_INFO_PICO) \ @@ -8382,9 +9497,13 @@ XR_ENUM_STR(XrResult); _(XrCompositionLayerFisheyePICO, XR_TYPE_COMPOSITION_LAYER_FISHEYE_PICO) \ _(XrLayerSettingsPICO, XR_TYPE_LAYER_SETTINGS_PICO) \ _(XrEyeTrackerCreateInfoPICO, XR_TYPE_EYE_TRACKER_CREATE_INFO_PICO) \ + _(XrEyeTrackerGazeInfoPICO, XR_TYPE_EYE_TRACKER_GAZE_INFO_PICO) \ _(XrEyeTrackerDataInfoPICO, XR_TYPE_EYE_TRACKER_DATA_INFO_PICO) \ _(XrEyeDataPICO, XR_TYPE_EYE_DATA_PICO) \ _(XrEyeTrackerDataPICO, XR_TYPE_EYE_TRACKER_DATA_PICO) \ + _(XrEyeGazePICO, XR_TYPE_EYE_GAZE_PICO) \ + _(XrEyeTrackerGazePICO, XR_TYPE_EYE_TRACKER_GAZE_PICO) \ + _(XrEyeTrackerGazeDepthPICO, XR_TYPE_EYE_TRACKER_GAZE_DEPTH_PICO) \ _(XrSecureMrFrameworkCreateInfoPICO, XR_TYPE_SECURE_MR_FRAMEWORK_CREATE_INFO_PICO) \ _(XrSecureMrPipelineCreateInfoPICO, XR_TYPE_SECURE_MR_PIPELINE_CREATE_INFO_PICO) \ _(XrSecureMrOperatorCreateInfoPICO, XR_TYPE_SECURE_MR_OPERATOR_CREATE_INFO_PICO) \ @@ -8404,6 +9523,7 @@ XR_ENUM_STR(XrResult); _(XrSecureMrOperatorComparisonPICO, XR_TYPE_SECURE_MR_OPERATOR_COMPARISON_PICO) \ _(XrSecureMrOperatorSortMatrixPICO, XR_TYPE_SECURE_MR_OPERATOR_SORT_MATRIX_PICO) \ _(XrSecureMrOperatorColorConvertPICO, XR_TYPE_SECURE_MR_OPERATOR_COLOR_CONVERT_PICO) \ + _(XrSecureMrOperatorJavascriptPICO, XR_TYPE_SECURE_MR_OPERATOR_JAVASCRIPT_PICO) \ _(XrExpandDeviceMotorVibratePICO, XR_TYPE_EXPAND_DEVICE_MOTOR_VIBRATE_PICO) \ _(XrExpandDeviceCustomDataPICO, XR_TYPE_EXPAND_DEVICE_CUSTOM_DATA_PICO) \ _(XrExpandDeviceBatteryStatePICO, XR_TYPE_EXPAND_DEVICE_BATTERY_STATE_PICO) \ @@ -8415,64 +9535,97 @@ XR_ENUM_STR(XrResult); _(XrBodyJointVelocitiesPICO, XR_TYPE_BODY_JOINT_VELOCITIES_PICO) \ _(XrBodyJointAccelerationsPICO, XR_TYPE_BODY_JOINT_ACCELERATIONS_PICO) \ _(XrBodyTrackingStatePICO, XR_TYPE_BODY_TRACKING_STATE_PICO) \ - _(XrSymmetricFovReprojectionViewConfigurationPICO, XR_TYPE_SYMMETRIC_FOV_REPROJECTION_VIEW_CONFIGURATION_PICO) \ - _(XrSystemDynamicObjectTrackingPropertiesPICO, XR_TYPE_SYSTEM_DYNAMIC_OBJECT_TRACKING_PROPERTIES_PICO) \ - _(XrSenseDataProviderCreateInfoDynamicObjectPICO, XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_DYNAMIC_OBJECT_PICO) \ - _(XrSpatialEntityDynamicObjectGetInfoPICO, XR_TYPE_SPATIAL_ENTITY_DYNAMIC_OBJECT_GET_INFO_PICO) \ - _(XrDynamicObjectDataPICO, XR_TYPE_DYNAMIC_OBJECT_DATA_PICO) \ - _(XrSpatialEntityComponentDataDynamicObjectPICO, XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_DYNAMIC_OBJECT_PICO) \ - _(XrSenseDataFilterDynamicObjectTypePICO, XR_TYPE_SENSE_DATA_FILTER_DYNAMIC_OBJECT_TYPE_PICO) \ - _(XrSystemDynamicObjectKeyboardPropertiesPICO, XR_TYPE_SYSTEM_DYNAMIC_OBJECT_KEYBOARD_PROPERTIES_PICO) \ - _(XrSystemDynamicObjectMousePropertiesPICO, XR_TYPE_SYSTEM_DYNAMIC_OBJECT_MOUSE_PROPERTIES_PICO) \ - _(XrLayerColorMatrixPICO, XR_TYPE_LAYER_COLOR_MATRIX_PICO) \ - _(XrSystemSpatialSensingPropertiesPICO, XR_TYPE_SYSTEM_SPATIAL_SENSING_PROPERTIES_PICO) \ - _(XrSpatialEntityLocationGetInfoPICO, XR_TYPE_SPATIAL_ENTITY_LOCATION_GET_INFO_PICO) \ - _(XrSpatialEntityLocationDataPICO, XR_TYPE_SPATIAL_ENTITY_LOCATION_DATA_PICO) \ - _(XrSpatialEntitySemanticGetInfoPICO, XR_TYPE_SPATIAL_ENTITY_SEMANTIC_GET_INFO_PICO) \ - _(XrSpatialEntitySemanticDataPICO, XR_TYPE_SPATIAL_ENTITY_SEMANTIC_DATA_PICO) \ - _(XrSpatialEntityBoundingBox2DGetInfoPICO, XR_TYPE_SPATIAL_ENTITY_BOUNDING_BOX_2D_GET_INFO_PICO) \ - _(XrSpatialEntityBoundingBox2DDataPICO, XR_TYPE_SPATIAL_ENTITY_BOUNDING_BOX_2D_DATA_PICO) \ - _(XrSpatialEntityPolygonGetInfoPICO, XR_TYPE_SPATIAL_ENTITY_POLYGON_GET_INFO_PICO) \ - _(XrSpatialEntityPolygonDataPICO, XR_TYPE_SPATIAL_ENTITY_POLYGON_DATA_PICO) \ - _(XrSpatialEntityBoundingBox3DGetInfoPICO, XR_TYPE_SPATIAL_ENTITY_BOUNDING_BOX_3D_GET_INFO_PICO) \ - _(XrSpatialEntityBoundingBox3DDataPICO, XR_TYPE_SPATIAL_ENTITY_BOUNDING_BOX_3D_DATA_PICO) \ - _(XrSpatialEntityTriangleMeshGetInfoPICO, XR_TYPE_SPATIAL_ENTITY_TRIANGLE_MESH_GET_INFO_PICO) \ - _(XrSpatialEntityTriangleMeshDataPICO, XR_TYPE_SPATIAL_ENTITY_TRIANGLE_MESH_DATA_PICO) \ - _(XrSpatialEntitySphereGetInfoPICO, XR_TYPE_SPATIAL_ENTITY_SPHERE_GET_INFO_PICO) \ - _(XrSpatialEntityComponentDataSpherePICO, XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_SPHERE_PICO) \ - _(XrSenseDataProviderStartInfoPICO, XR_TYPE_SENSE_DATA_PROVIDER_START_INFO_PICO) \ - _(XrSenseDataProviderStartCompletionPICO, XR_TYPE_SENSE_DATA_PROVIDER_START_COMPLETION_PICO) \ - _(XrEventDataSenseDataProviderStateChangedPICO, XR_TYPE_EVENT_DATA_SENSE_DATA_PROVIDER_STATE_CHANGED_PICO) \ - _(XrEventDataSenseDataUpdatedPICO, XR_TYPE_EVENT_DATA_SENSE_DATA_UPDATED_PICO) \ - _(XrSenseDataQueryInfoPICO, XR_TYPE_SENSE_DATA_QUERY_INFO_PICO) \ - _(XrSenseDataQueryCompletionPICO, XR_TYPE_SENSE_DATA_QUERY_COMPLETION_PICO) \ - _(XrQueriedSenseDataGetInfoPICO, XR_TYPE_QUERIED_SENSE_DATA_GET_INFO_PICO) \ - _(XrSpatialEntityStatePICO, XR_TYPE_SPATIAL_ENTITY_STATE_PICO) \ - _(XrQueriedSenseDataPICO, XR_TYPE_QUERIED_SENSE_DATA_PICO) \ - _(XrSenseDataFilterUuidPICO, XR_TYPE_SENSE_DATA_FILTER_UUID_PICO) \ - _(XrSenseDataFilterSemanticPICO, XR_TYPE_SENSE_DATA_FILTER_SEMANTIC_PICO) \ - _(XrSpatialEntityAnchorRetrieveInfoPICO, XR_TYPE_SPATIAL_ENTITY_ANCHOR_RETRIEVE_INFO_PICO) \ - _(XrAnchorLocateInfoPICO, XR_TYPE_ANCHOR_LOCATE_INFO_PICO) \ - _(XrFuturePollResultProgressPICO, XR_TYPE_FUTURE_POLL_RESULT_PROGRESS_PICO) \ - _(XrSystemSpatialAnchorPropertiesPICO, XR_TYPE_SYSTEM_SPATIAL_ANCHOR_PROPERTIES_PICO) \ - _(XrSenseDataProviderCreateInfoSpatialAnchorPICO, XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SPATIAL_ANCHOR_PICO) \ - _(XrSpatialAnchorCreateInfoPICO, XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_PICO) \ - _(XrSpatialAnchorCreateCompletionPICO, XR_TYPE_SPATIAL_ANCHOR_CREATE_COMPLETION_PICO) \ - _(XrSpatialAnchorPersistInfoPICO, XR_TYPE_SPATIAL_ANCHOR_PERSIST_INFO_PICO) \ - _(XrSpatialAnchorPersistCompletionPICO, XR_TYPE_SPATIAL_ANCHOR_PERSIST_COMPLETION_PICO) \ - _(XrSpatialAnchorUnpersistInfoPICO, XR_TYPE_SPATIAL_ANCHOR_UNPERSIST_INFO_PICO) \ - _(XrSpatialAnchorUnpersistCompletionPICO, XR_TYPE_SPATIAL_ANCHOR_UNPERSIST_COMPLETION_PICO) \ - _(XrSystemSpatialAnchorSharingPropertiesPICO, XR_TYPE_SYSTEM_SPATIAL_ANCHOR_SHARING_PROPERTIES_PICO) \ - _(XrSpatialAnchorShareInfoPICO, XR_TYPE_SPATIAL_ANCHOR_SHARE_INFO_PICO) \ - _(XrSpatialAnchorShareCompletionPICO, XR_TYPE_SPATIAL_ANCHOR_SHARE_COMPLETION_PICO) \ - _(XrSharedSpatialAnchorDownloadInfoPICO, XR_TYPE_SPATIAL_ANCHOR_DOWNLOAD_INFO_PICO) \ - _(XrSharedSpatialAnchorDownloadCompletionPICO, XR_TYPE_SPATIAL_ANCHOR_DOWNLOAD_COMPLETION_PICO) \ - _(XrSystemSceneCapturePropertiesPICO, XR_TYPE_SYSTEM_SCENE_CAPTURE_PROPERTIES_PICO) \ - _(XrSenseDataProviderCreateInfoSceneCapturePICO, XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SCENE_CAPTURE_PICO) \ - _(XrSceneCaptureStartInfoPICO, XR_TYPE_SCENE_CAPTURE_START_INFO_PICO) \ - _(XrSceneCaptureStartCompletionPICO, XR_TYPE_SCENE_CAPTURE_START_COMPLETION_PICO) \ - _(XrSystemSpatialMeshPropertiesPICO, XR_TYPE_SYSTEM_SPATIAL_MESH_PROPERTIES_PICO) \ - _(XrSenseDataProviderCreateInfoSpatialMeshPICO, XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SPATIAL_MESH_PICO) + _(XrSymmetricFovReprojectionViewConfigurationPICO, XR_TYPE_SYMMETRIC_FOV_REPROJECTION_VIEW_CONFIGURATION_PICO) \ + _(XrSystemDynamicObjectTrackingPropertiesPICO, XR_TYPE_SYSTEM_DYNAMIC_OBJECT_TRACKING_PROPERTIES_PICO) \ + _(XrSenseDataProviderCreateInfoDynamicObjectPICO, XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_DYNAMIC_OBJECT_PICO) \ + _(XrSpatialEntityDynamicObjectGetInfoPICO, XR_TYPE_SPATIAL_ENTITY_DYNAMIC_OBJECT_GET_INFO_PICO) \ + _(XrDynamicObjectDataPICO, XR_TYPE_DYNAMIC_OBJECT_DATA_PICO) \ + _(XrSpatialEntityComponentDataDynamicObjectPICO, XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_DYNAMIC_OBJECT_PICO) \ + _(XrSenseDataFilterDynamicObjectTypePICO, XR_TYPE_SENSE_DATA_FILTER_DYNAMIC_OBJECT_TYPE_PICO) \ + _(XrSystemDynamicObjectKeyboardPropertiesPICO, XR_TYPE_SYSTEM_DYNAMIC_OBJECT_KEYBOARD_PROPERTIES_PICO) \ + _(XrSystemDynamicObjectMousePropertiesPICO, XR_TYPE_SYSTEM_DYNAMIC_OBJECT_MOUSE_PROPERTIES_PICO) \ + _(XrLayerColorMatrixPICO, XR_TYPE_LAYER_COLOR_MATRIX_PICO) \ + _(XrReadbackTensorBufferPICO, XR_TYPE_READBACK_TENSOR_BUFFER_PICO) \ + _(XrCreateBufferFromGlobalTensorCompletionPICO, XR_TYPE_CREATE_BUFFER_FROM_GLOBAL_TENSOR_COMPLETION_PICO) \ + _(XrCreateTextureFromGlobalTensorCompletionPICO, XR_TYPE_CREATE_TEXTURE_FROM_GLOBAL_TENSOR_COMPLETION_PICO) \ + _(XrCameraPropertiesPICO, XR_TYPE_CAMERA_PROPERTIES_PICO) \ + _(XrCameraCapabilitiesPICO, XR_TYPE_CAMERA_CAPABILITIES_PICO) \ + _(XrAvailableCamerasEnumerateInfoPICO, XR_TYPE_AVAILABLE_CAMERAS_ENUMERATE_INFO_PICO) \ + _(XrCameraPropertiesGetInfoPICO, XR_TYPE_CAMERA_PROPERTIES_GET_INFO_PICO) \ + _(XrCameraPropertyFacingPICO, XR_TYPE_CAMERA_PROPERTY_FACING_PICO) \ + _(XrCameraPropertyPositionPICO, XR_TYPE_CAMERA_PROPERTY_POSITION_PICO) \ + _(XrCameraPropertyCameraTypePICO, XR_TYPE_CAMERA_PROPERTY_CAMERA_TYPE_PICO) \ + _(XrCameraSupportedCapabilitiesGetInfoPICO, XR_TYPE_CAMERA_SUPPORTED_CAPABILITIES_GET_INFO_PICO) \ + _(XrCameraSupportedCapabilitiesPICO, XR_TYPE_CAMERA_SUPPORTED_CAPABILITIES_PICO) \ + _(XrCameraSupportedCapabilityImageResolutionPICO, XR_TYPE_CAMERA_SUPPORTED_CAPABILITY_IMAGE_RESOLUTION_PICO) \ + _(XrCameraCapabilityImageResolutionPICO, XR_TYPE_CAMERA_CAPABILITY_IMAGE_RESOLUTION_PICO) \ + _(XrCameraSupportedCapabilityDataTransferTypePICO, XR_TYPE_CAMERA_SUPPORTED_CAPABILITY_DATA_TRANSFER_TYPE_PICO) \ + _(XrCameraCapabilityDataTransferTypePICO, XR_TYPE_CAMERA_CAPABILITY_DATA_TRANSFER_TYPE_PICO) \ + _(XrCameraSupportedCapabilityImageFormatPICO, XR_TYPE_CAMERA_SUPPORTED_CAPABILITY_IMAGE_FORMAT_PICO) \ + _(XrCameraCapabilityImageFormatPICO, XR_TYPE_CAMERA_CAPABILITY_IMAGE_FORMAT_PICO) \ + _(XrCameraSupportedCapabilityCameraModelPICO, XR_TYPE_CAMERA_SUPPORTED_CAPABILITY_CAMERA_MODEL_PICO) \ + _(XrCameraCapabilityCameraModelPICO, XR_TYPE_CAMERA_CAPABILITY_CAMERA_MODEL_PICO) \ + _(XrCameraSupportedCapabilityImageFpsPICO, XR_TYPE_CAMERA_SUPPORTED_CAPABILITY_IMAGE_FPS_PICO) \ + _(XrCameraCapabilityImageFpsPICO, XR_TYPE_CAMERA_CAPABILITY_IMAGE_FPS_PICO) \ + _(XrCameraDeviceCreateInfoPICO, XR_TYPE_CAMERA_DEVICE_CREATE_INFO_PICO) \ + _(XrCreateCameraDeviceCompletionPICO, XR_TYPE_CREATE_CAMERA_DEVICE_COMPLETION_PICO) \ + _(XrCameraCaptureSessionCreateInfoPICO, XR_TYPE_CAMERA_CAPTURE_SESSION_CREATE_INFO_PICO) \ + _(XrCreateCameraCaptureSessionCompletionPICO, XR_TYPE_CREATE_CAMERA_CAPTURE_SESSION_COMPLETION_PICO) \ + _(XrCameraIntrinsicsPICO, XR_TYPE_CAMERA_INTRINSICS_PICO) \ + _(XrCameraExtrinsicsPICO, XR_TYPE_CAMERA_EXTRINSICS_PICO) \ + _(XrCameraCaptureBeginInfoPICO, XR_TYPE_CAMERA_CAPTURE_BEGIN_INFO_PICO) \ + _(XrCameraImageAcquireInfoPICO, XR_TYPE_CAMERA_IMAGE_ACQUIRE_INFO_PICO) \ + _(XrCameraImagePICO, XR_TYPE_CAMERA_IMAGE_PICO) \ + _(XrCameraImageDataRawBufferPICO, XR_TYPE_CAMERA_IMAGE_DATA_RAW_BUFFER_PICO) \ + _(XrSystemSpatialSensingPropertiesPICO, XR_TYPE_SYSTEM_SPATIAL_SENSING_PROPERTIES_PICO) \ + _(XrSpatialEntityLocationGetInfoPICO, XR_TYPE_SPATIAL_ENTITY_LOCATION_GET_INFO_PICO) \ + _(XrSpatialEntityLocationDataPICO, XR_TYPE_SPATIAL_ENTITY_LOCATION_DATA_PICO) \ + _(XrSpatialEntitySemanticGetInfoPICO, XR_TYPE_SPATIAL_ENTITY_SEMANTIC_GET_INFO_PICO) \ + _(XrSpatialEntitySemanticDataPICO, XR_TYPE_SPATIAL_ENTITY_SEMANTIC_DATA_PICO) \ + _(XrSpatialEntityBoundingBox2DGetInfoPICO, XR_TYPE_SPATIAL_ENTITY_BOUNDING_BOX_2D_GET_INFO_PICO) \ + _(XrSpatialEntityBoundingBox2DDataPICO, XR_TYPE_SPATIAL_ENTITY_BOUNDING_BOX_2D_DATA_PICO) \ + _(XrSpatialEntityPolygonGetInfoPICO, XR_TYPE_SPATIAL_ENTITY_POLYGON_GET_INFO_PICO) \ + _(XrSpatialEntityPolygonDataPICO, XR_TYPE_SPATIAL_ENTITY_POLYGON_DATA_PICO) \ + _(XrSpatialEntityBoundingBox3DGetInfoPICO, XR_TYPE_SPATIAL_ENTITY_BOUNDING_BOX_3D_GET_INFO_PICO) \ + _(XrSpatialEntityBoundingBox3DDataPICO, XR_TYPE_SPATIAL_ENTITY_BOUNDING_BOX_3D_DATA_PICO) \ + _(XrSpatialEntityTriangleMeshGetInfoPICO, XR_TYPE_SPATIAL_ENTITY_TRIANGLE_MESH_GET_INFO_PICO) \ + _(XrSpatialEntityTriangleMeshDataPICO, XR_TYPE_SPATIAL_ENTITY_TRIANGLE_MESH_DATA_PICO) \ + _(XrSpatialEntitySphereGetInfoPICO, XR_TYPE_SPATIAL_ENTITY_SPHERE_GET_INFO_PICO) \ + _(XrSpatialEntityComponentDataSpherePICO, XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_SPHERE_PICO) \ + _(XrSenseDataProviderStartInfoPICO, XR_TYPE_SENSE_DATA_PROVIDER_START_INFO_PICO) \ + _(XrSenseDataProviderStartCompletionPICO, XR_TYPE_SENSE_DATA_PROVIDER_START_COMPLETION_PICO) \ + _(XrEventDataSenseDataProviderStateChangedPICO, XR_TYPE_EVENT_DATA_SENSE_DATA_PROVIDER_STATE_CHANGED_PICO) \ + _(XrEventDataSenseDataUpdatedPICO, XR_TYPE_EVENT_DATA_SENSE_DATA_UPDATED_PICO) \ + _(XrSenseDataQueryInfoPICO, XR_TYPE_SENSE_DATA_QUERY_INFO_PICO) \ + _(XrSenseDataQueryCompletionPICO, XR_TYPE_SENSE_DATA_QUERY_COMPLETION_PICO) \ + _(XrQueriedSenseDataGetInfoPICO, XR_TYPE_QUERIED_SENSE_DATA_GET_INFO_PICO) \ + _(XrSpatialEntityStatePICO, XR_TYPE_SPATIAL_ENTITY_STATE_PICO) \ + _(XrQueriedSenseDataPICO, XR_TYPE_QUERIED_SENSE_DATA_PICO) \ + _(XrSenseDataFilterUuidPICO, XR_TYPE_SENSE_DATA_FILTER_UUID_PICO) \ + _(XrSenseDataFilterSemanticPICO, XR_TYPE_SENSE_DATA_FILTER_SEMANTIC_PICO) \ + _(XrSpatialEntityAnchorRetrieveInfoPICO, XR_TYPE_SPATIAL_ENTITY_ANCHOR_RETRIEVE_INFO_PICO) \ + _(XrAnchorLocateInfoPICO, XR_TYPE_ANCHOR_LOCATE_INFO_PICO) \ + _(XrFuturePollResultProgressPICO, XR_TYPE_FUTURE_POLL_RESULT_PROGRESS_PICO) \ + _(XrSystemSpatialAnchorPropertiesPICO, XR_TYPE_SYSTEM_SPATIAL_ANCHOR_PROPERTIES_PICO) \ + _(XrSenseDataProviderCreateInfoSpatialAnchorPICO, XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SPATIAL_ANCHOR_PICO) \ + _(XrSpatialAnchorCreateInfoPICO, XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_PICO) \ + _(XrSpatialAnchorCreateCompletionPICO, XR_TYPE_SPATIAL_ANCHOR_CREATE_COMPLETION_PICO) \ + _(XrSpatialAnchorPersistInfoPICO, XR_TYPE_SPATIAL_ANCHOR_PERSIST_INFO_PICO) \ + _(XrSpatialAnchorPersistCompletionPICO, XR_TYPE_SPATIAL_ANCHOR_PERSIST_COMPLETION_PICO) \ + _(XrSpatialAnchorUnpersistInfoPICO, XR_TYPE_SPATIAL_ANCHOR_UNPERSIST_INFO_PICO) \ + _(XrSpatialAnchorUnpersistCompletionPICO, XR_TYPE_SPATIAL_ANCHOR_UNPERSIST_COMPLETION_PICO) \ + _(XrSystemSpatialAnchorSharingPropertiesPICO, XR_TYPE_SYSTEM_SPATIAL_ANCHOR_SHARING_PROPERTIES_PICO) \ + _(XrSpatialAnchorShareInfoPICO, XR_TYPE_SPATIAL_ANCHOR_SHARE_INFO_PICO) \ + _(XrSpatialAnchorShareCompletionPICO, XR_TYPE_SPATIAL_ANCHOR_SHARE_COMPLETION_PICO) \ + _(XrSharedSpatialAnchorDownloadInfoPICO, XR_TYPE_SHARED_SPATIAL_ANCHOR_DOWNLOAD_INFO_PICO) \ + _(XrSharedSpatialAnchorDownloadCompletionPICO, XR_TYPE_SHARED_SPATIAL_ANCHOR_DOWNLOAD_COMPLETION_PICO) \ + _(XrSystemSceneCapturePropertiesPICO, XR_TYPE_SYSTEM_SCENE_CAPTURE_PROPERTIES_PICO) \ + _(XrSenseDataProviderCreateInfoSceneCapturePICO, XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SCENE_CAPTURE_PICO) \ + _(XrSceneCaptureStartInfoPICO, XR_TYPE_SCENE_CAPTURE_START_INFO_PICO) \ + _(XrSceneCaptureStartCompletionPICO, XR_TYPE_SCENE_CAPTURE_START_COMPLETION_PICO) \ + _(XrSystemSpatialMeshPropertiesPICO, XR_TYPE_SYSTEM_SPATIAL_MESH_PROPERTIES_PICO) \ + _(XrSenseDataProviderCreateInfoSpatialMeshPICO, XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SPATIAL_MESH_PICO) \ + #if defined(XR_USE_GRAPHICS_API_D3D11) /// Implementation detail of XR_LIST_STRUCTURE_TYPES() @@ -8568,6 +9721,7 @@ XR_ENUM_STR(XrResult); _(XrSwapchainImageOpenGLESKHR, XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR) \ _(XrGraphicsRequirementsOpenGLESKHR, XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_ES_KHR) \ _(XrSwapchainStateSamplerOpenGLESFB, XR_TYPE_SWAPCHAIN_STATE_SAMPLER_OPENGL_ES_FB) \ + _(XrReadbackTextureImageOpenGLPICO, XR_TYPE_READBACK_TEXTURE_IMAGE_OPENGL_PICO) \ #else #define XR_LIST_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_OPENGL_ES(_) @@ -8597,6 +9751,7 @@ XR_ENUM_STR(XrResult); _(XrSwapchainImageFoveationVulkanFB, XR_TYPE_SWAPCHAIN_IMAGE_FOVEATION_VULKAN_FB) \ _(XrSwapchainStateSamplerVulkanFB, XR_TYPE_SWAPCHAIN_STATE_SAMPLER_VULKAN_FB) \ _(XrVulkanSwapchainCreateInfoMETA, XR_TYPE_VULKAN_SWAPCHAIN_CREATE_INFO_META) \ + _(XrReadbackTextureImageVulkanPICO, XR_TYPE_READBACK_TEXTURE_IMAGE_VULKAN_PICO) \ #else #define XR_LIST_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_VULKAN(_) @@ -8610,6 +9765,9 @@ XR_ENUM_STR(XrResult); _(XrLoaderInitInfoAndroidKHR, XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR) \ _(XrAndroidSurfaceSwapchainCreateInfoFB, XR_TYPE_ANDROID_SURFACE_SWAPCHAIN_CREATE_INFO_FB) \ _(XrSwapchainStateAndroidSurfaceDimensionsFB, XR_TYPE_SWAPCHAIN_STATE_ANDROID_SURFACE_DIMENSIONS_FB) \ + _(XrAnchorSharingInfoANDROID, XR_TYPE_ANCHOR_SHARING_INFO_ANDROID) \ + _(XrAnchorSharingTokenANDROID, XR_TYPE_ANCHOR_SHARING_TOKEN_ANDROID) \ + _(XrSystemAnchorSharingExportPropertiesANDROID, XR_TYPE_SYSTEM_ANCHOR_SHARING_EXPORT_PROPERTIES_ANDROID) \ #else #define XR_LIST_STRUCTURE_TYPES_XR_USE_PLATFORM_ANDROID(_) @@ -8746,9 +9904,9 @@ XR_ENUM_STR(XrResult); _(XR_ML_spatial_anchors, 141) \ _(XR_ML_spatial_anchors_storage, 142) \ _(XR_MSFT_spatial_anchor_persistence, 143) \ - _(XR_MSFT_scene_marker, 148) \ - _(XR_KHR_extended_struct_name_lengths, 149) \ - _(XR_ULTRALEAP_hand_tracking_forearm, 150) \ + _(XR_MSFT_scene_marker, 148) \ + _(XR_KHR_extended_struct_name_lengths, 149) \ + _(XR_ULTRALEAP_hand_tracking_forearm, 150) \ _(XR_FB_spatial_entity_query, 157) \ _(XR_FB_spatial_entity_storage, 159) \ _(XR_OCULUS_audio_device_guid, 160) \ @@ -8781,25 +9939,28 @@ XR_ENUM_STR(XrResult); _(XR_OCULUS_external_camera, 227) \ _(XR_META_vulkan_swapchain_create_info, 228) \ _(XR_META_performance_metrics, 233) \ - _(XR_FB_spatial_entity_storage_batch, 239) \ - _(XR_META_detached_controllers, 241) \ - _(XR_FB_spatial_entity_user, 242) \ - _(XR_META_headset_id, 246) \ - _(XR_META_hand_tracking_microgestures, 253) \ - _(XR_META_recommended_layer_resolution, 255) \ + _(XR_FB_spatial_entity_storage_batch, 239) \ + _(XR_META_detached_controllers, 241) \ + _(XR_FB_spatial_entity_user, 242) \ + _(XR_META_headset_id, 246) \ + _(XR_META_spatial_entity_discovery, 248) \ + _(XR_META_hand_tracking_microgestures, 253) \ + _(XR_META_recommended_layer_resolution, 255) \ + _(XR_META_spatial_entity_persistence, 260) \ _(XR_META_passthrough_color_lut, 267) \ _(XR_META_spatial_entity_mesh, 270) \ - _(XR_META_automatic_layer_filter, 272) \ - _(XR_META_body_tracking_full_body, 275) \ - _(XR_META_touch_controller_plus, 280) \ + _(XR_META_automatic_layer_filter, 272) \ + _(XR_META_body_tracking_full_body, 275) \ + _(XR_META_touch_controller_plus, 280) \ _(XR_META_passthrough_layer_resumed_event, 283) \ + _(XR_META_body_tracking_calibration, 284) \ _(XR_FB_face_tracking2, 288) \ _(XR_META_spatial_entity_sharing, 291) \ _(XR_META_environment_depth, 292) \ - _(XR_EXT_uuid, 300) \ - _(XR_EXT_render_model, 301) \ - _(XR_EXT_interaction_render_model, 302) \ - _(XR_EXT_hand_interaction, 303) \ + _(XR_EXT_uuid, 300) \ + _(XR_EXT_render_model, 301) \ + _(XR_EXT_interaction_render_model, 302) \ + _(XR_EXT_hand_interaction, 303) \ _(XR_QCOM_tracking_optimization_settings, 307) \ _(XR_HTC_passthrough, 318) \ _(XR_HTC_foveation, 319) \ @@ -8808,18 +9969,26 @@ XR_ENUM_STR(XrResult); _(XR_EXT_active_action_set_priority, 374) \ _(XR_MNDX_force_feedback_curl, 376) \ _(XR_BD_controller_interaction, 385) \ - _(XR_BD_body_tracking, 386) \ - _(XR_BD_spatial_sensing, 390) \ - _(XR_BD_spatial_anchor, 391) \ - _(XR_BD_spatial_anchor_sharing, 392) \ - _(XR_BD_spatial_scene, 393) \ - _(XR_BD_spatial_mesh, 394) \ - _(XR_BD_future_progress, 395) \ - _(XR_BD_spatial_plane, 397) \ - _(XR_EXT_local_floor, 427) \ + _(XR_BD_body_tracking, 386) \ + _(XR_BD_facial_simulation, 387) \ + _(XR_BD_spatial_sensing, 390) \ + _(XR_BD_spatial_anchor, 391) \ + _(XR_BD_spatial_anchor_sharing, 392) \ + _(XR_BD_spatial_scene, 393) \ + _(XR_BD_spatial_mesh, 394) \ + _(XR_BD_future_progress, 395) \ + _(XR_BD_spatial_plane, 397) \ + _(XR_BD_ultra_controller_interaction, 404) \ + _(XR_EXT_local_floor, 427) \ _(XR_EXT_hand_tracking_data_source, 429) \ _(XR_EXT_plane_detection, 430) \ _(XR_OPPO_controller_interaction, 454) \ + _(XR_ANDROID_trackables, 456) \ + _(XR_ANDROID_device_anchor_persistence, 458) \ + _(XR_ANDROID_face_tracking, 459) \ + _(XR_ANDROID_passthrough_camera_state, 461) \ + _(XR_ANDROID_raycast, 464) \ + _(XR_ANDROID_trackables_object, 467) \ _(XR_EXT_future, 470) \ _(XR_EXT_user_presence, 471) \ _(XR_KHR_locate_spaces, 472) \ @@ -8828,20 +9997,24 @@ XR_ENUM_STR(XrResult); _(XR_ML_world_mesh_detection, 475) \ _(XR_ML_facial_expression, 483) \ _(XR_ML_view_configuration_depth_range_change, 484) \ - _(XR_YVR_controller_interaction, 498) \ - _(XR_META_simultaneous_hands_and_controllers, 533) \ - _(XR_EXT_composition_layer_inverted_alpha, 555) \ + _(XR_YVR_controller_interaction, 498) \ + _(XR_META_simultaneous_hands_and_controllers, 533) \ + _(XR_EXT_composition_layer_inverted_alpha, 555) \ _(XR_META_colocation_discovery, 572) \ _(XR_META_spatial_entity_group_sharing, 573) \ - _(XR_KHR_maintenance1, 711) \ - _(XR_EXT_spatial_entity, 741) \ - _(XR_EXT_spatial_plane_tracking, 742) \ - _(XR_EXT_spatial_marker_tracking, 744) \ - _(XR_LOGITECH_mx_ink_stylus_interaction, 746) \ - _(XR_EXT_spatial_anchor, 763) \ - _(XR_EXT_spatial_persistence, 764) \ - _(XR_EXT_spatial_persistence_operations, 782) \ - _(XR_PICO_external_camera, 10001) \ + _(XR_ANDROID_anchor_sharing_export, 702) \ + _(XR_ANDROID_trackables_marker, 708) \ + _(XR_KHR_maintenance1, 711) \ + _(XR_KHR_generic_controller, 712) \ + _(XR_EXT_spatial_entity, 741) \ + _(XR_EXT_spatial_plane_tracking, 742) \ + _(XR_EXT_spatial_marker_tracking, 744) \ + _(XR_LOGITECH_mx_ink_stylus_interaction, 746) \ + _(XR_EXT_spatial_anchor, 763) \ + _(XR_EXT_spatial_persistence, 764) \ + _(XR_EXT_spatial_persistence_operations, 782) \ + _(XR_EXT_loader_init_properties, 839) \ + _(XR_PICO_external_camera, 10001) \ _(XR_PICO_virtual_boundary, 10002) \ _(XR_PICO_motion_tracking, 10003) \ _(XR_PICO_composition_layer_fisheye, 10004) \ @@ -8852,15 +10025,21 @@ XR_ENUM_STR(XrResult); _(XR_PICO_body_tracking2, 10010) \ _(XR_PICO_adaptive_resolution, 10011) \ _(XR_PICO_symmetric_fov_reprojection_view_configuration, 10017) \ - _(XR_PICO_dynamic_object_tracking, 10018) \ - _(XR_PICO_dynamic_object_keyboard, 10019) \ - _(XR_PICO_dynamic_object_mouse, 10020) \ - _(XR_PICO_layer_color_matrix, 10027) \ - _(XR_PICO_spatial_sensing, 200390) \ - _(XR_PICO_spatial_anchor, 200391) \ - _(XR_PICO_spatial_anchor_sharing, 200392) \ - _(XR_PICO_scene_capture, 200393) \ - _(XR_PICO_spatial_mesh, 200394) + _(XR_PICO_dynamic_object_tracking, 10018) \ + _(XR_PICO_dynamic_object_keyboard, 10019) \ + _(XR_PICO_dynamic_object_mouse, 10020) \ + _(XR_PICO_layer_color_matrix, 10027) \ + _(XR_PICO_readback_tensor, 10028) \ + _(XR_PICO_readback_tensor_vulkan, 10029) \ + _(XR_PICO_readback_tensor_opengles, 10030) \ + _(XR_PICO_camera_image, 10034) \ + _(XR_PICO_spatial_sensing, 200390) \ + _(XR_PICO_spatial_anchor, 200391) \ + _(XR_PICO_spatial_anchor_sharing, 200392) \ + _(XR_PICO_scene_capture, 200393) \ + _(XR_PICO_spatial_mesh, 200394) \ + + /// For every function defined by XR_VERSION_1_0 in this version of the spec, /// calls your macro with the function name and extension name. @@ -9050,14 +10229,25 @@ XR_ENUM_STR(XrResult); #define XR_LIST_FUNCTIONS_XR_KHR_vulkan_enable2(_) \ _(CreateVulkanInstanceKHR, KHR_vulkan_enable2) \ _(CreateVulkanDeviceKHR, KHR_vulkan_enable2) \ - _(GetVulkanGraphicsDevice2KHR, KHR_vulkan_enable2) + _(GetVulkanGraphicsDevice2KHR, KHR_vulkan_enable2) \ + _(GetVulkanGraphicsRequirements2KHR, KHR_vulkan_enable2) \ + /// For every function defined by XR_KHR_extended_struct_name_lengths in this version of the spec, /// calls your macro with the function name and extension name. /// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, /// because it is easy to add back but impossible to remove with the preprocessor. #define XR_LIST_FUNCTIONS_XR_KHR_extended_struct_name_lengths(_) \ - _(StructureTypeToString2KHR, KHR_extended_struct_name_lengths) + _(StructureTypeToString2KHR, KHR_extended_struct_name_lengths) \ + + +/// For every function defined by XR_KHR_locate_spaces in this version of the spec, +/// calls your macro with the function name and extension name. +/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, +/// because it is easy to add back but impossible to remove with the preprocessor. +#define XR_LIST_FUNCTIONS_XR_KHR_locate_spaces(_) \ + _(LocateSpacesKHR, KHR_locate_spaces) \ + /// For every function defined by XR_EXT_performance_settings in this version of the spec, /// calls your macro with the function name and extension name. @@ -9629,6 +10819,15 @@ XR_ENUM_STR(XrResult); _(DestroySpaceUserFB, FB_spatial_entity_user) \ +/// For every function defined by XR_META_spatial_entity_discovery in this version of the spec, +/// calls your macro with the function name and extension name. +/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, +/// because it is easy to add back but impossible to remove with the preprocessor. +#define XR_LIST_FUNCTIONS_XR_META_spatial_entity_discovery(_) \ + _(DiscoverSpacesMETA, META_spatial_entity_discovery) \ + _(RetrieveSpaceDiscoveryResultsMETA, META_spatial_entity_discovery) \ + + /// For every function defined by XR_META_recommended_layer_resolution in this version of the spec, /// calls your macro with the function name and extension name. /// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, @@ -9637,6 +10836,15 @@ XR_ENUM_STR(XrResult); _(GetRecommendedLayerResolutionMETA, META_recommended_layer_resolution) \ +/// For every function defined by XR_META_spatial_entity_persistence in this version of the spec, +/// calls your macro with the function name and extension name. +/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, +/// because it is easy to add back but impossible to remove with the preprocessor. +#define XR_LIST_FUNCTIONS_XR_META_spatial_entity_persistence(_) \ + _(SaveSpacesMETA, META_spatial_entity_persistence) \ + _(EraseSpacesMETA, META_spatial_entity_persistence) \ + + /// For every function defined by XR_META_passthrough_color_lut in this version of the spec, /// calls your macro with the function name and extension name. /// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, @@ -9655,6 +10863,15 @@ XR_ENUM_STR(XrResult); _(GetSpaceTriangleMeshMETA, META_spatial_entity_mesh) \ +/// For every function defined by XR_META_body_tracking_calibration in this version of the spec, +/// calls your macro with the function name and extension name. +/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, +/// because it is easy to add back but impossible to remove with the preprocessor. +#define XR_LIST_FUNCTIONS_XR_META_body_tracking_calibration(_) \ + _(SuggestBodyTrackingCalibrationOverrideMETA, META_body_tracking_calibration) \ + _(ResetBodyTrackingCalibrationMETA, META_body_tracking_calibration) \ + + /// For every function defined by XR_FB_face_tracking2 in this version of the spec, /// calls your macro with the function name and extension name. /// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, @@ -9687,31 +10904,34 @@ XR_ENUM_STR(XrResult); _(EnumerateEnvironmentDepthSwapchainImagesMETA, META_environment_depth) \ _(GetEnvironmentDepthSwapchainStateMETA, META_environment_depth) \ _(AcquireEnvironmentDepthImageMETA, META_environment_depth) \ - _(SetEnvironmentDepthHandRemovalMETA, META_environment_depth) + _(SetEnvironmentDepthHandRemovalMETA, META_environment_depth) \ + /// For every function defined by XR_EXT_render_model in this version of the spec, /// calls your macro with the function name and extension name. /// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, /// because it is easy to add back but impossible to remove with the preprocessor. -#define XR_LIST_FUNCTIONS_XR_EXT_render_model(_) \ - _(CreateRenderModelEXT, EXT_render_model) \ - _(DestroyRenderModelEXT, EXT_render_model) \ - _(GetRenderModelPropertiesEXT, EXT_render_model) \ - _(CreateRenderModelSpaceEXT, EXT_render_model) \ - _(CreateRenderModelAssetEXT, EXT_render_model) \ - _(DestroyRenderModelAssetEXT, EXT_render_model) \ - _(GetRenderModelAssetDataEXT, EXT_render_model) \ - _(GetRenderModelAssetPropertiesEXT, EXT_render_model) \ - _(GetRenderModelStateEXT, EXT_render_model) +#define XR_LIST_FUNCTIONS_XR_EXT_render_model(_) \ + _(CreateRenderModelEXT, EXT_render_model) \ + _(DestroyRenderModelEXT, EXT_render_model) \ + _(GetRenderModelPropertiesEXT, EXT_render_model) \ + _(CreateRenderModelSpaceEXT, EXT_render_model) \ + _(CreateRenderModelAssetEXT, EXT_render_model) \ + _(DestroyRenderModelAssetEXT, EXT_render_model) \ + _(GetRenderModelAssetDataEXT, EXT_render_model) \ + _(GetRenderModelAssetPropertiesEXT, EXT_render_model) \ + _(GetRenderModelStateEXT, EXT_render_model) \ + /// For every function defined by XR_EXT_interaction_render_model in this version of the spec, /// calls your macro with the function name and extension name. /// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, /// because it is easy to add back but impossible to remove with the preprocessor. -#define XR_LIST_FUNCTIONS_XR_EXT_interaction_render_model(_) \ - _(EnumerateInteractionRenderModelIdsEXT, EXT_interaction_render_model) \ - _(EnumerateRenderModelSubactionPathsEXT, EXT_interaction_render_model) \ - _(GetRenderModelPoseTopLevelUserPathEXT, EXT_interaction_render_model) +#define XR_LIST_FUNCTIONS_XR_EXT_interaction_render_model(_) \ + _(EnumerateInteractionRenderModelIdsEXT, EXT_interaction_render_model) \ + _(EnumerateRenderModelSubactionPathsEXT, EXT_interaction_render_model) \ + _(GetRenderModelPoseTopLevelUserPathEXT, EXT_interaction_render_model) \ + /// For every function defined by XR_QCOM_tracking_optimization_settings in this version of the spec, /// calls your macro with the function name and extension name. @@ -9773,60 +10993,78 @@ XR_ENUM_STR(XrResult); #define XR_LIST_FUNCTIONS_XR_BD_body_tracking(_) \ _(CreateBodyTrackerBD, BD_body_tracking) \ _(DestroyBodyTrackerBD, BD_body_tracking) \ - _(LocateBodyJointsBD, BD_body_tracking) + _(LocateBodyJointsBD, BD_body_tracking) \ + + +/// For every function defined by XR_BD_facial_simulation in this version of the spec, +/// calls your macro with the function name and extension name. +/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, +/// because it is easy to add back but impossible to remove with the preprocessor. +#define XR_LIST_FUNCTIONS_XR_BD_facial_simulation(_) \ + _(EnumerateFacialSimulationModesBD, BD_facial_simulation) \ + _(CreateFaceTrackerBD, BD_facial_simulation) \ + _(DestroyFaceTrackerBD, BD_facial_simulation) \ + _(GetFacialSimulationDataBD, BD_facial_simulation) \ + _(SetFacialSimulationModeBD, BD_facial_simulation) \ + _(GetFacialSimulationModeBD, BD_facial_simulation) \ + /// For every function defined by XR_BD_spatial_sensing in this version of the spec, /// calls your macro with the function name and extension name. /// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, /// because it is easy to add back but impossible to remove with the preprocessor. -#define XR_LIST_FUNCTIONS_XR_BD_spatial_sensing(_) \ - _(EnumerateSpatialEntityComponentTypesBD, BD_spatial_sensing) \ - _(GetSpatialEntityUuidBD, BD_spatial_sensing) \ - _(GetSpatialEntityComponentDataBD, BD_spatial_sensing) \ - _(CreateSenseDataProviderBD, BD_spatial_sensing) \ - _(StartSenseDataProviderAsyncBD, BD_spatial_sensing) \ - _(StartSenseDataProviderCompleteBD, BD_spatial_sensing) \ - _(GetSenseDataProviderStateBD, BD_spatial_sensing) \ - _(QuerySenseDataAsyncBD, BD_spatial_sensing) \ - _(QuerySenseDataCompleteBD, BD_spatial_sensing) \ - _(DestroySenseDataSnapshotBD, BD_spatial_sensing) \ - _(GetQueriedSenseDataBD, BD_spatial_sensing) \ - _(StopSenseDataProviderBD, BD_spatial_sensing) \ - _(DestroySenseDataProviderBD, BD_spatial_sensing) \ - _(CreateSpatialEntityAnchorBD, BD_spatial_sensing) \ - _(DestroyAnchorBD, BD_spatial_sensing) \ - _(GetAnchorUuidBD, BD_spatial_sensing) \ - _(CreateAnchorSpaceBD, BD_spatial_sensing) +#define XR_LIST_FUNCTIONS_XR_BD_spatial_sensing(_) \ + _(EnumerateSpatialEntityComponentTypesBD, BD_spatial_sensing) \ + _(GetSpatialEntityUuidBD, BD_spatial_sensing) \ + _(GetSpatialEntityComponentDataBD, BD_spatial_sensing) \ + _(CreateSenseDataProviderBD, BD_spatial_sensing) \ + _(StartSenseDataProviderAsyncBD, BD_spatial_sensing) \ + _(StartSenseDataProviderCompleteBD, BD_spatial_sensing) \ + _(GetSenseDataProviderStateBD, BD_spatial_sensing) \ + _(QuerySenseDataAsyncBD, BD_spatial_sensing) \ + _(QuerySenseDataCompleteBD, BD_spatial_sensing) \ + _(DestroySenseDataSnapshotBD, BD_spatial_sensing) \ + _(GetQueriedSenseDataBD, BD_spatial_sensing) \ + _(StopSenseDataProviderBD, BD_spatial_sensing) \ + _(DestroySenseDataProviderBD, BD_spatial_sensing) \ + _(CreateSpatialEntityAnchorBD, BD_spatial_sensing) \ + _(DestroyAnchorBD, BD_spatial_sensing) \ + _(GetAnchorUuidBD, BD_spatial_sensing) \ + _(CreateAnchorSpaceBD, BD_spatial_sensing) \ + /// For every function defined by XR_BD_spatial_anchor in this version of the spec, /// calls your macro with the function name and extension name. /// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, /// because it is easy to add back but impossible to remove with the preprocessor. -#define XR_LIST_FUNCTIONS_XR_BD_spatial_anchor(_) \ - _(CreateSpatialAnchorAsyncBD, BD_spatial_anchor) \ - _(CreateSpatialAnchorCompleteBD, BD_spatial_anchor) \ - _(PersistSpatialAnchorAsyncBD, BD_spatial_anchor) \ - _(PersistSpatialAnchorCompleteBD, BD_spatial_anchor) \ - _(UnpersistSpatialAnchorAsyncBD, BD_spatial_anchor) \ - _(UnpersistSpatialAnchorCompleteBD, BD_spatial_anchor) +#define XR_LIST_FUNCTIONS_XR_BD_spatial_anchor(_) \ + _(CreateSpatialAnchorAsyncBD, BD_spatial_anchor) \ + _(CreateSpatialAnchorCompleteBD, BD_spatial_anchor) \ + _(PersistSpatialAnchorAsyncBD, BD_spatial_anchor) \ + _(PersistSpatialAnchorCompleteBD, BD_spatial_anchor) \ + _(UnpersistSpatialAnchorAsyncBD, BD_spatial_anchor) \ + _(UnpersistSpatialAnchorCompleteBD, BD_spatial_anchor) \ + /// For every function defined by XR_BD_spatial_anchor_sharing in this version of the spec, /// calls your macro with the function name and extension name. /// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, /// because it is easy to add back but impossible to remove with the preprocessor. -#define XR_LIST_FUNCTIONS_XR_BD_spatial_anchor_sharing(_) \ - _(ShareSpatialAnchorAsyncBD, BD_spatial_anchor_sharing) \ - _(ShareSpatialAnchorCompleteBD, BD_spatial_anchor_sharing) \ - _(DownloadSharedSpatialAnchorAsyncBD, BD_spatial_anchor_sharing) \ - _(DownloadSharedSpatialAnchorCompleteBD, BD_spatial_anchor_sharing) +#define XR_LIST_FUNCTIONS_XR_BD_spatial_anchor_sharing(_) \ + _(ShareSpatialAnchorAsyncBD, BD_spatial_anchor_sharing) \ + _(ShareSpatialAnchorCompleteBD, BD_spatial_anchor_sharing) \ + _(DownloadSharedSpatialAnchorAsyncBD, BD_spatial_anchor_sharing) \ + _(DownloadSharedSpatialAnchorCompleteBD, BD_spatial_anchor_sharing) \ + /// For every function defined by XR_BD_spatial_scene in this version of the spec, /// calls your macro with the function name and extension name. /// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, /// because it is easy to add back but impossible to remove with the preprocessor. #define XR_LIST_FUNCTIONS_XR_BD_spatial_scene(_) \ - _(CaptureSceneAsyncBD, BD_spatial_scene) \ - _(CaptureSceneCompleteBD, BD_spatial_scene) + _(CaptureSceneAsyncBD, BD_spatial_scene) \ + _(CaptureSceneCompleteBD, BD_spatial_scene) \ + /// For every function defined by XR_EXT_plane_detection in this version of the spec, /// calls your macro with the function name and extension name. @@ -9841,6 +11079,71 @@ XR_ENUM_STR(XrResult); _(GetPlanePolygonBufferEXT, EXT_plane_detection) \ +/// For every function defined by XR_ANDROID_trackables in this version of the spec, +/// calls your macro with the function name and extension name. +/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, +/// because it is easy to add back but impossible to remove with the preprocessor. +#define XR_LIST_FUNCTIONS_XR_ANDROID_trackables(_) \ + _(EnumerateSupportedTrackableTypesANDROID, ANDROID_trackables) \ + _(EnumerateSupportedAnchorTrackableTypesANDROID, ANDROID_trackables) \ + _(CreateTrackableTrackerANDROID, ANDROID_trackables) \ + _(DestroyTrackableTrackerANDROID, ANDROID_trackables) \ + _(GetAllTrackablesANDROID, ANDROID_trackables) \ + _(GetTrackablePlaneANDROID, ANDROID_trackables) \ + _(CreateAnchorSpaceANDROID, ANDROID_trackables) \ + + +/// For every function defined by XR_ANDROID_device_anchor_persistence in this version of the spec, +/// calls your macro with the function name and extension name. +/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, +/// because it is easy to add back but impossible to remove with the preprocessor. +#define XR_LIST_FUNCTIONS_XR_ANDROID_device_anchor_persistence(_) \ + _(EnumerateSupportedPersistenceAnchorTypesANDROID, ANDROID_device_anchor_persistence) \ + _(CreateDeviceAnchorPersistenceANDROID, ANDROID_device_anchor_persistence) \ + _(DestroyDeviceAnchorPersistenceANDROID, ANDROID_device_anchor_persistence) \ + _(PersistAnchorANDROID, ANDROID_device_anchor_persistence) \ + _(GetAnchorPersistStateANDROID, ANDROID_device_anchor_persistence) \ + _(CreatePersistedAnchorSpaceANDROID, ANDROID_device_anchor_persistence) \ + _(EnumeratePersistedAnchorsANDROID, ANDROID_device_anchor_persistence) \ + _(UnpersistAnchorANDROID, ANDROID_device_anchor_persistence) \ + + +/// For every function defined by XR_ANDROID_face_tracking in this version of the spec, +/// calls your macro with the function name and extension name. +/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, +/// because it is easy to add back but impossible to remove with the preprocessor. +#define XR_LIST_FUNCTIONS_XR_ANDROID_face_tracking(_) \ + _(CreateFaceTrackerANDROID, ANDROID_face_tracking) \ + _(DestroyFaceTrackerANDROID, ANDROID_face_tracking) \ + _(GetFaceStateANDROID, ANDROID_face_tracking) \ + _(GetFaceCalibrationStateANDROID, ANDROID_face_tracking) \ + + +/// For every function defined by XR_ANDROID_passthrough_camera_state in this version of the spec, +/// calls your macro with the function name and extension name. +/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, +/// because it is easy to add back but impossible to remove with the preprocessor. +#define XR_LIST_FUNCTIONS_XR_ANDROID_passthrough_camera_state(_) \ + _(GetPassthroughCameraStateANDROID, ANDROID_passthrough_camera_state) \ + + +/// For every function defined by XR_ANDROID_raycast in this version of the spec, +/// calls your macro with the function name and extension name. +/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, +/// because it is easy to add back but impossible to remove with the preprocessor. +#define XR_LIST_FUNCTIONS_XR_ANDROID_raycast(_) \ + _(EnumerateRaycastSupportedTrackableTypesANDROID, ANDROID_raycast) \ + _(RaycastANDROID, ANDROID_raycast) \ + + +/// For every function defined by XR_ANDROID_trackables_object in this version of the spec, +/// calls your macro with the function name and extension name. +/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, +/// because it is easy to add back but impossible to remove with the preprocessor. +#define XR_LIST_FUNCTIONS_XR_ANDROID_trackables_object(_) \ + _(GetTrackableObjectANDROID, ANDROID_trackables_object) \ + + /// For every function defined by XR_EXT_future in this version of the spec, /// calls your macro with the function name and extension name. /// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, @@ -9889,15 +11192,17 @@ XR_ENUM_STR(XrResult); #define XR_LIST_FUNCTIONS_XR_ML_facial_expression(_) \ _(CreateFacialExpressionClientML, ML_facial_expression) \ _(DestroyFacialExpressionClientML, ML_facial_expression) \ - _(GetFacialExpressionBlendShapePropertiesML, ML_facial_expression) + _(GetFacialExpressionBlendShapePropertiesML, ML_facial_expression) \ + /// For every function defined by XR_META_simultaneous_hands_and_controllers in this version of the spec, /// calls your macro with the function name and extension name. /// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, /// because it is easy to add back but impossible to remove with the preprocessor. -#define XR_LIST_FUNCTIONS_XR_META_simultaneous_hands_and_controllers(_) \ - _(ResumeSimultaneousHandsAndControllersTrackingMETA, META_simultaneous_hands_and_controllers) \ - _(PauseSimultaneousHandsAndControllersTrackingMETA, META_simultaneous_hands_and_controllers) +#define XR_LIST_FUNCTIONS_XR_META_simultaneous_hands_and_controllers(_) \ + _(ResumeSimultaneousHandsAndControllersTrackingMETA, META_simultaneous_hands_and_controllers) \ + _(PauseSimultaneousHandsAndControllersTrackingMETA, META_simultaneous_hands_and_controllers) \ + /// For every function defined by XR_META_colocation_discovery in this version of the spec, /// calls your macro with the function name and extension name. @@ -9907,59 +11212,82 @@ XR_ENUM_STR(XrResult); _(StartColocationDiscoveryMETA, META_colocation_discovery) \ _(StopColocationDiscoveryMETA, META_colocation_discovery) \ _(StartColocationAdvertisementMETA, META_colocation_discovery) \ - _(StopColocationAdvertisementMETA, META_colocation_discovery) + _(StopColocationAdvertisementMETA, META_colocation_discovery) \ + + +/// For every function defined by XR_ANDROID_anchor_sharing_export in this version of the spec, +/// calls your macro with the function name and extension name. +/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, +/// because it is easy to add back but impossible to remove with the preprocessor. +#define XR_LIST_FUNCTIONS_XR_ANDROID_anchor_sharing_export(_) \ + _(ShareAnchorANDROID, ANDROID_anchor_sharing_export) \ + _(UnshareAnchorANDROID, ANDROID_anchor_sharing_export) \ + + +/// For every function defined by XR_ANDROID_trackables_marker in this version of the spec, +/// calls your macro with the function name and extension name. +/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, +/// because it is easy to add back but impossible to remove with the preprocessor. +#define XR_LIST_FUNCTIONS_XR_ANDROID_trackables_marker(_) \ + _(GetTrackableMarkerANDROID, ANDROID_trackables_marker) \ + /// For every function defined by XR_EXT_spatial_entity in this version of the spec, /// calls your macro with the function name and extension name. /// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, /// because it is easy to add back but impossible to remove with the preprocessor. -#define XR_LIST_FUNCTIONS_XR_EXT_spatial_entity(_) \ - _(EnumerateSpatialCapabilitiesEXT, EXT_spatial_entity) \ - _(EnumerateSpatialCapabilityComponentTypesEXT, EXT_spatial_entity) \ - _(EnumerateSpatialCapabilityFeaturesEXT, EXT_spatial_entity) \ - _(CreateSpatialContextAsyncEXT, EXT_spatial_entity) \ - _(CreateSpatialContextCompleteEXT, EXT_spatial_entity) \ - _(DestroySpatialContextEXT, EXT_spatial_entity) \ - _(CreateSpatialDiscoverySnapshotAsyncEXT, EXT_spatial_entity) \ - _(CreateSpatialDiscoverySnapshotCompleteEXT, EXT_spatial_entity) \ - _(QuerySpatialComponentDataEXT, EXT_spatial_entity) \ - _(DestroySpatialSnapshotEXT, EXT_spatial_entity) \ - _(CreateSpatialEntityFromIdEXT, EXT_spatial_entity) \ - _(DestroySpatialEntityEXT, EXT_spatial_entity) \ - _(CreateSpatialUpdateSnapshotEXT, EXT_spatial_entity) \ - _(GetSpatialBufferStringEXT, EXT_spatial_entity) \ - _(GetSpatialBufferUint8EXT, EXT_spatial_entity) \ - _(GetSpatialBufferUint16EXT, EXT_spatial_entity) \ - _(GetSpatialBufferUint32EXT, EXT_spatial_entity) \ - _(GetSpatialBufferFloatEXT, EXT_spatial_entity) \ - _(GetSpatialBufferVector2fEXT, EXT_spatial_entity) \ - _(GetSpatialBufferVector3fEXT, EXT_spatial_entity) +#define XR_LIST_FUNCTIONS_XR_EXT_spatial_entity(_) \ + _(EnumerateSpatialCapabilitiesEXT, EXT_spatial_entity) \ + _(EnumerateSpatialCapabilityComponentTypesEXT, EXT_spatial_entity) \ + _(EnumerateSpatialCapabilityFeaturesEXT, EXT_spatial_entity) \ + _(CreateSpatialContextAsyncEXT, EXT_spatial_entity) \ + _(CreateSpatialContextCompleteEXT, EXT_spatial_entity) \ + _(DestroySpatialContextEXT, EXT_spatial_entity) \ + _(CreateSpatialDiscoverySnapshotAsyncEXT, EXT_spatial_entity) \ + _(CreateSpatialDiscoverySnapshotCompleteEXT, EXT_spatial_entity) \ + _(QuerySpatialComponentDataEXT, EXT_spatial_entity) \ + _(DestroySpatialSnapshotEXT, EXT_spatial_entity) \ + _(CreateSpatialEntityFromIdEXT, EXT_spatial_entity) \ + _(DestroySpatialEntityEXT, EXT_spatial_entity) \ + _(CreateSpatialUpdateSnapshotEXT, EXT_spatial_entity) \ + _(GetSpatialBufferStringEXT, EXT_spatial_entity) \ + _(GetSpatialBufferUint8EXT, EXT_spatial_entity) \ + _(GetSpatialBufferUint16EXT, EXT_spatial_entity) \ + _(GetSpatialBufferUint32EXT, EXT_spatial_entity) \ + _(GetSpatialBufferFloatEXT, EXT_spatial_entity) \ + _(GetSpatialBufferVector2fEXT, EXT_spatial_entity) \ + _(GetSpatialBufferVector3fEXT, EXT_spatial_entity) \ + /// For every function defined by XR_EXT_spatial_anchor in this version of the spec, /// calls your macro with the function name and extension name. /// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, /// because it is easy to add back but impossible to remove with the preprocessor. -#define XR_LIST_FUNCTIONS_XR_EXT_spatial_anchor(_) _(CreateSpatialAnchorEXT, EXT_spatial_anchor) +#define XR_LIST_FUNCTIONS_XR_EXT_spatial_anchor(_) \ + _(CreateSpatialAnchorEXT, EXT_spatial_anchor) \ + /// For every function defined by XR_EXT_spatial_persistence in this version of the spec, /// calls your macro with the function name and extension name. /// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, /// because it is easy to add back but impossible to remove with the preprocessor. -#define XR_LIST_FUNCTIONS_XR_EXT_spatial_persistence(_) \ - _(EnumerateSpatialPersistenceScopesEXT, EXT_spatial_persistence) \ - _(CreateSpatialPersistenceContextAsyncEXT, EXT_spatial_persistence) \ - _(CreateSpatialPersistenceContextCompleteEXT, EXT_spatial_persistence) \ - _(DestroySpatialPersistenceContextEXT, EXT_spatial_persistence) +#define XR_LIST_FUNCTIONS_XR_EXT_spatial_persistence(_) \ + _(EnumerateSpatialPersistenceScopesEXT, EXT_spatial_persistence) \ + _(CreateSpatialPersistenceContextAsyncEXT, EXT_spatial_persistence) \ + _(CreateSpatialPersistenceContextCompleteEXT, EXT_spatial_persistence) \ + _(DestroySpatialPersistenceContextEXT, EXT_spatial_persistence) \ + /// For every function defined by XR_EXT_spatial_persistence_operations in this version of the spec, /// calls your macro with the function name and extension name. /// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, /// because it is easy to add back but impossible to remove with the preprocessor. -#define XR_LIST_FUNCTIONS_XR_EXT_spatial_persistence_operations(_) \ - _(PersistSpatialEntityAsyncEXT, EXT_spatial_persistence_operations) \ - _(PersistSpatialEntityCompleteEXT, EXT_spatial_persistence_operations) \ - _(UnpersistSpatialEntityAsyncEXT, EXT_spatial_persistence_operations) \ - _(UnpersistSpatialEntityCompleteEXT, EXT_spatial_persistence_operations) +#define XR_LIST_FUNCTIONS_XR_EXT_spatial_persistence_operations(_) \ + _(PersistSpatialEntityAsyncEXT, EXT_spatial_persistence_operations) \ + _(PersistSpatialEntityCompleteEXT, EXT_spatial_persistence_operations) \ + _(UnpersistSpatialEntityAsyncEXT, EXT_spatial_persistence_operations) \ + _(UnpersistSpatialEntityCompleteEXT, EXT_spatial_persistence_operations) \ + /// For every function defined by XR_PICO_external_camera in this version of the spec, /// calls your macro with the function name and extension name. @@ -10001,6 +11329,7 @@ XR_ENUM_STR(XrResult); _(CreateEyeTrackerPICO, PICO_eye_tracker) \ _(DestroyEyeTrackerPICO, PICO_eye_tracker) \ _(GetEyeDataPICO, PICO_eye_tracker) \ + _(GetEyeGazePICO, PICO_eye_tracker) \ /// For every function defined by XR_PICO_secure_mixed_reality in this version of the spec, @@ -10052,60 +11381,105 @@ XR_ENUM_STR(XrResult); /// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, /// because it is easy to add back but impossible to remove with the preprocessor. #define XR_LIST_FUNCTIONS_XR_PICO_adaptive_resolution(_) \ - _(UpdateAdaptiveResolutionPICO, PICO_adaptive_resolution) + _(UpdateAdaptiveResolutionPICO, PICO_adaptive_resolution) \ + + +/// For every function defined by XR_PICO_readback_tensor in this version of the spec, +/// calls your macro with the function name and extension name. +/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, +/// because it is easy to add back but impossible to remove with the preprocessor. +#define XR_LIST_FUNCTIONS_XR_PICO_readback_tensor(_) \ + _(CreateBufferFromGlobalTensorAsyncPICO, PICO_readback_tensor) \ + _(CreateBufferFromGlobalTensorCompletePICO, PICO_readback_tensor) \ + _(CreateTextureFromGlobalTensorAsyncPICO, PICO_readback_tensor) \ + _(CreateTextureFromGlobalTensorCompletePICO, PICO_readback_tensor) \ + _(GetReadbackTextureImagePICO, PICO_readback_tensor) \ + _(ReleaseReadbackTexturePICO, PICO_readback_tensor) \ + + +/// For every function defined by XR_PICO_camera_image in this version of the spec, +/// calls your macro with the function name and extension name. +/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, +/// because it is easy to add back but impossible to remove with the preprocessor. +#define XR_LIST_FUNCTIONS_XR_PICO_camera_image(_) \ + _(EnumerateAvailableCamerasPICO, PICO_camera_image) \ + _(EnumerateCameraPropertyTypesPICO, PICO_camera_image) \ + _(GetCameraPropertiesPICO, PICO_camera_image) \ + _(EnumerateCameraCapabilityTypesPICO, PICO_camera_image) \ + _(GetCameraSupportedCapabilitiesPICO, PICO_camera_image) \ + _(CreateCameraDeviceAsyncPICO, PICO_camera_image) \ + _(CreateCameraDeviceCompletePICO, PICO_camera_image) \ + _(DestroyCameraDevicePICO, PICO_camera_image) \ + _(CreateCameraCaptureSessionAsyncPICO, PICO_camera_image) \ + _(CreateCameraCaptureSessionCompletePICO, PICO_camera_image) \ + _(DestroyCameraCaptureSessionPICO, PICO_camera_image) \ + _(GetCameraIntrinsicsPICO, PICO_camera_image) \ + _(GetCameraExtrinsicsPICO, PICO_camera_image) \ + _(BeginCameraCapturePICO, PICO_camera_image) \ + _(EndCameraCapturePICO, PICO_camera_image) \ + _(AcquireCameraImagePICO, PICO_camera_image) \ + _(GetCameraImageDataPICO, PICO_camera_image) \ + _(ReleaseCameraImagePICO, PICO_camera_image) \ + /// For every function defined by XR_PICO_spatial_sensing in this version of the spec, /// calls your macro with the function name and extension name. /// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, /// because it is easy to add back but impossible to remove with the preprocessor. -#define XR_LIST_FUNCTIONS_XR_PICO_spatial_sensing(_) \ - _(EnumerateSpatialEntityComponentTypesPICO, PICO_spatial_sensing) \ - _(GetSpatialEntityUuidPICO, PICO_spatial_sensing) \ - _(GetSpatialEntityComponentDataPICO, PICO_spatial_sensing) \ - _(CreateSenseDataProviderPICO, PICO_spatial_sensing) \ - _(StartSenseDataProviderAsyncPICO, PICO_spatial_sensing) \ - _(StartSenseDataProviderCompletePICO, PICO_spatial_sensing) \ - _(GetSenseDataProviderStatePICO, PICO_spatial_sensing) \ - _(QuerySenseDataAsyncPICO, PICO_spatial_sensing) \ - _(QuerySenseDataCompletePICO, PICO_spatial_sensing) \ - _(DestroySenseDataSnapshotPICO, PICO_spatial_sensing) \ - _(GetQueriedSenseDataPICO, PICO_spatial_sensing) \ - _(StopSenseDataProviderPICO, PICO_spatial_sensing) \ - _(DestroySenseDataProviderPICO, PICO_spatial_sensing) \ - _(RetrieveSpatialEntityAnchorPICO, PICO_spatial_sensing) \ - _(DestroyAnchorPICO, PICO_spatial_sensing) \ - _(GetAnchorUuidPICO, PICO_spatial_sensing) \ - _(LocateAnchorPICO, PICO_spatial_sensing) +#define XR_LIST_FUNCTIONS_XR_PICO_spatial_sensing(_) \ + _(EnumerateSpatialEntityComponentTypesPICO, PICO_spatial_sensing) \ + _(GetSpatialEntityUuidPICO, PICO_spatial_sensing) \ + _(GetSpatialEntityComponentDataPICO, PICO_spatial_sensing) \ + _(CreateSenseDataProviderPICO, PICO_spatial_sensing) \ + _(StartSenseDataProviderAsyncPICO, PICO_spatial_sensing) \ + _(StartSenseDataProviderCompletePICO, PICO_spatial_sensing) \ + _(GetSenseDataProviderStatePICO, PICO_spatial_sensing) \ + _(QuerySenseDataAsyncPICO, PICO_spatial_sensing) \ + _(QuerySenseDataCompletePICO, PICO_spatial_sensing) \ + _(DestroySenseDataSnapshotPICO, PICO_spatial_sensing) \ + _(GetQueriedSenseDataPICO, PICO_spatial_sensing) \ + _(StopSenseDataProviderPICO, PICO_spatial_sensing) \ + _(DestroySenseDataProviderPICO, PICO_spatial_sensing) \ + _(RetrieveSpatialEntityAnchorPICO, PICO_spatial_sensing) \ + _(DestroyAnchorPICO, PICO_spatial_sensing) \ + _(GetAnchorUuidPICO, PICO_spatial_sensing) \ + _(LocateAnchorPICO, PICO_spatial_sensing) \ + /// For every function defined by XR_PICO_spatial_anchor in this version of the spec, /// calls your macro with the function name and extension name. /// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, /// because it is easy to add back but impossible to remove with the preprocessor. -#define XR_LIST_FUNCTIONS_XR_PICO_spatial_anchor(_) \ - _(CreateSpatialAnchorAsyncPICO, PICO_spatial_anchor) \ - _(CreateSpatialAnchorCompletePICO, PICO_spatial_anchor) \ - _(PersistSpatialAnchorAsyncPICO, PICO_spatial_anchor) \ - _(PersistSpatialAnchorCompletePICO, PICO_spatial_anchor) \ - _(UnpersistSpatialAnchorAsyncPICO, PICO_spatial_anchor) \ - _(UnpersistSpatialAnchorCompletePICO, PICO_spatial_anchor) +#define XR_LIST_FUNCTIONS_XR_PICO_spatial_anchor(_) \ + _(CreateSpatialAnchorAsyncPICO, PICO_spatial_anchor) \ + _(CreateSpatialAnchorCompletePICO, PICO_spatial_anchor) \ + _(PersistSpatialAnchorAsyncPICO, PICO_spatial_anchor) \ + _(PersistSpatialAnchorCompletePICO, PICO_spatial_anchor) \ + _(UnpersistSpatialAnchorAsyncPICO, PICO_spatial_anchor) \ + _(UnpersistSpatialAnchorCompletePICO, PICO_spatial_anchor) \ + /// For every function defined by XR_PICO_spatial_anchor_sharing in this version of the spec, /// calls your macro with the function name and extension name. /// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, /// because it is easy to add back but impossible to remove with the preprocessor. -#define XR_LIST_FUNCTIONS_XR_PICO_spatial_anchor_sharing(_) \ - _(ShareSpatialAnchorAsyncPICO, PICO_spatial_anchor_sharing) \ - _(ShareSpatialAnchorCompletePICO, PICO_spatial_anchor_sharing) \ - _(DownloadSharedSpatialAnchorAsyncPICO, PICO_spatial_anchor_sharing) \ - _(DownloadSharedSpatialAnchorCompletePICO, PICO_spatial_anchor_sharing) +#define XR_LIST_FUNCTIONS_XR_PICO_spatial_anchor_sharing(_) \ + _(ShareSpatialAnchorAsyncPICO, PICO_spatial_anchor_sharing) \ + _(ShareSpatialAnchorCompletePICO, PICO_spatial_anchor_sharing) \ + _(DownloadSharedSpatialAnchorAsyncPICO, PICO_spatial_anchor_sharing) \ + _(DownloadSharedSpatialAnchorCompletePICO, PICO_spatial_anchor_sharing) \ + /// For every function defined by XR_PICO_scene_capture in this version of the spec, /// calls your macro with the function name and extension name. /// Trims the leading `xr` from the function name and the leading `XR_` from the feature name, /// because it is easy to add back but impossible to remove with the preprocessor. -#define XR_LIST_FUNCTIONS_XR_PICO_scene_capture(_) \ - _(StartSceneCaptureAsyncPICO, PICO_scene_capture) \ - _(StartSceneCaptureCompletePICO, PICO_scene_capture) +#define XR_LIST_FUNCTIONS_XR_PICO_scene_capture(_) \ + _(StartSceneCaptureAsyncPICO, PICO_scene_capture) \ + _(StartSceneCaptureCompletePICO, PICO_scene_capture) \ + + + #endif diff --git a/external/openxr/include/openxr/openxr_reflection_parent_structs.h b/external/openxr/include/openxr/openxr_reflection_parent_structs.h index 46b280e..94c20c3 100644 --- a/external/openxr/include/openxr/openxr_reflection_parent_structs.h +++ b/external/openxr/include/openxr/openxr_reflection_parent_structs.h @@ -58,6 +58,7 @@ This file contains expansion macros (X Macros) for OpenXR structures that have a _avail(XrEventDataViveTrackerConnectedHTCX, XR_TYPE_EVENT_DATA_VIVE_TRACKER_CONNECTED_HTCX) \ _avail(XrEventDataSpatialAnchorCreateCompleteFB, XR_TYPE_EVENT_DATA_SPATIAL_ANCHOR_CREATE_COMPLETE_FB) \ _avail(XrEventDataSpaceSetStatusCompleteFB, XR_TYPE_EVENT_DATA_SPACE_SET_STATUS_COMPLETE_FB) \ + _avail(XrEventDataPassthroughStateChangedFB, XR_TYPE_EVENT_DATA_PASSTHROUGH_STATE_CHANGED_FB) \ _avail(XrEventDataMarkerTrackingUpdateVARJO, XR_TYPE_EVENT_DATA_MARKER_TRACKING_UPDATE_VARJO) \ _avail(XrEventDataLocalizationChangedML, XR_TYPE_EVENT_DATA_LOCALIZATION_CHANGED_ML) \ _avail(XrEventDataSpaceQueryResultsAvailableFB, XR_TYPE_EVENT_DATA_SPACE_QUERY_RESULTS_AVAILABLE_FB) \ @@ -65,22 +66,46 @@ This file contains expansion macros (X Macros) for OpenXR structures that have a _avail(XrEventDataSpaceSaveCompleteFB, XR_TYPE_EVENT_DATA_SPACE_SAVE_COMPLETE_FB) \ _avail(XrEventDataSpaceEraseCompleteFB, XR_TYPE_EVENT_DATA_SPACE_ERASE_COMPLETE_FB) \ _avail(XrEventDataSpaceShareCompleteFB, XR_TYPE_EVENT_DATA_SPACE_SHARE_COMPLETE_FB) \ + _avail(XrEventDataSceneCaptureCompleteFB, XR_TYPE_EVENT_DATA_SCENE_CAPTURE_COMPLETE_FB) \ + _avail(XrEventDataVirtualKeyboardCommitTextMETA, XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_COMMIT_TEXT_META) \ + _avail(XrEventDataVirtualKeyboardBackspaceMETA, XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_BACKSPACE_META) \ + _avail(XrEventDataVirtualKeyboardEnterMETA, XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_ENTER_META) \ + _avail(XrEventDataVirtualKeyboardShownMETA, XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_SHOWN_META) \ + _avail(XrEventDataVirtualKeyboardHiddenMETA, XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_HIDDEN_META) \ _avail(XrEventDataSpaceListSaveCompleteFB, XR_TYPE_EVENT_DATA_SPACE_LIST_SAVE_COMPLETE_FB) \ - _avail(XrEventDataPassthroughLayerResumedMETA, XR_TYPE_EVENT_DATA_PASSTHROUGH_LAYER_RESUMED_META) \ - _avail(XrEventDataSenseDataProviderStateChangedBD, \ - XR_TYPE_EVENT_DATA_SENSE_DATA_PROVIDER_STATE_CHANGED_BD) \ - _avail(XrEventDataSenseDataUpdatedBD, XR_TYPE_EVENT_DATA_SENSE_DATA_UPDATED_BD) _avail(XrEventDataHeadsetFitChangedML, XR_TYPE_EVENT_DATA_HEADSET_FIT_CHANGED_ML) \ - _avail(XrEventDataEyeCalibrationChangedML, XR_TYPE_EVENT_DATA_EYE_CALIBRATION_CHANGED_ML) \ - _avail(XrEventDataSpatialDiscoveryRecommendedEXT, \ - XR_TYPE_EVENT_DATA_SPATIAL_DISCOVERY_RECOMMENDED_EXT) \ - _avail(XrEventDataRequestMotionTrackerCompletePICO, XR_TYPE_EVENT_DATA_REQUEST_MOTION_TRACKER_COMPLETE_PICO) \ + _avail(XrEventDataSpaceDiscoveryResultsAvailableMETA, XR_TYPE_EVENT_DATA_SPACE_DISCOVERY_RESULTS_AVAILABLE_META) \ + _avail(XrEventDataSpaceDiscoveryCompleteMETA, XR_TYPE_EVENT_DATA_SPACE_DISCOVERY_COMPLETE_META) \ + _avail(XrEventDataSpacesSaveResultMETA, XR_TYPE_EVENT_DATA_SPACES_SAVE_RESULT_META) \ + _avail(XrEventDataSpacesEraseResultMETA, XR_TYPE_EVENT_DATA_SPACES_ERASE_RESULT_META) \ + _avail(XrEventDataPassthroughLayerResumedMETA, XR_TYPE_EVENT_DATA_PASSTHROUGH_LAYER_RESUMED_META) \ + _avail(XrEventDataShareSpacesCompleteMETA, XR_TYPE_EVENT_DATA_SHARE_SPACES_COMPLETE_META) \ + _avail(XrEventDataInteractionRenderModelsChangedEXT, XR_TYPE_EVENT_DATA_INTERACTION_RENDER_MODELS_CHANGED_EXT) \ + _avail(XrEventDataSenseDataProviderStateChangedBD, XR_TYPE_EVENT_DATA_SENSE_DATA_PROVIDER_STATE_CHANGED_BD) \ + _avail(XrEventDataSenseDataUpdatedBD, XR_TYPE_EVENT_DATA_SENSE_DATA_UPDATED_BD) \ + _avail(XrEventDataUserPresenceChangedEXT, XR_TYPE_EVENT_DATA_USER_PRESENCE_CHANGED_EXT) \ + _avail(XrEventDataHeadsetFitChangedML, XR_TYPE_EVENT_DATA_HEADSET_FIT_CHANGED_ML) \ + _avail(XrEventDataEyeCalibrationChangedML, XR_TYPE_EVENT_DATA_EYE_CALIBRATION_CHANGED_ML) \ + _avail(XrEventDataStartColocationAdvertisementCompleteMETA, XR_TYPE_EVENT_DATA_START_COLOCATION_ADVERTISEMENT_COMPLETE_META) \ + _avail(XrEventDataStopColocationAdvertisementCompleteMETA, XR_TYPE_EVENT_DATA_STOP_COLOCATION_ADVERTISEMENT_COMPLETE_META) \ + _avail(XrEventDataColocationAdvertisementCompleteMETA, XR_TYPE_EVENT_DATA_COLOCATION_ADVERTISEMENT_COMPLETE_META) \ + _avail(XrEventDataStartColocationDiscoveryCompleteMETA, XR_TYPE_EVENT_DATA_START_COLOCATION_DISCOVERY_COMPLETE_META) \ + _avail(XrEventDataColocationDiscoveryResultMETA, XR_TYPE_EVENT_DATA_COLOCATION_DISCOVERY_RESULT_META) \ + _avail(XrEventDataColocationDiscoveryCompleteMETA, XR_TYPE_EVENT_DATA_COLOCATION_DISCOVERY_COMPLETE_META) \ + _avail(XrEventDataStopColocationDiscoveryCompleteMETA, XR_TYPE_EVENT_DATA_STOP_COLOCATION_DISCOVERY_COMPLETE_META) \ + _avail(XrEventDataSpatialDiscoveryRecommendedEXT, XR_TYPE_EVENT_DATA_SPATIAL_DISCOVERY_RECOMMENDED_EXT) \ + _avail(XrEventDataMrcStatusChangedPICO, XR_TYPE_EVENT_DATA_MRC_STATUS_CHANGED_PICO) \ + _avail(XrEventDataRequestMotionTrackerCompletePICO, XR_TYPE_EVENT_DATA_REQUEST_MOTION_TRACKER_COMPLETE_PICO) \ _avail(XrEventDataMotionTrackerConnectionStateChangedPICO, XR_TYPE_EVENT_DATA_MOTION_TRACKER_CONNECTION_STATE_CHANGED_PICO) \ - _avail(XrEventDataMotionTrackerPowerKeyEventPICO, XR_TYPE_EVENT_DATA_MOTION_TRACKER_POWER_KEY_EVENT_PICO) \ - _avail( \ - XrEventDataSenseDataProviderStateChangedPICO, \ - XR_TYPE_EVENT_DATA_SENSE_DATA_PROVIDER_STATE_CHANGED_PICO) \ - _avail(XrEventDataSenseDataUpdatedPICO, \ - XR_TYPE_EVENT_DATA_SENSE_DATA_UPDATED_PICO) + _avail(XrEventDataMotionTrackerPowerKeyEventPICO, XR_TYPE_EVENT_DATA_MOTION_TRACKER_POWER_KEY_EVENT_PICO) \ + _avail(XrEventDataExpandDeviceConnectionStateChangedPICO, XR_TYPE_EVENT_DATA_EXPAND_DEVICE_CONNECTION_STATE_CHANGED_PICO) \ + _avail(XrEventDataExpandDeviceBatteryStateChangedPICO, XR_TYPE_EVENT_DATA_EXPAND_DEVICE_BATTERY_STATE_CHANGED_PICO) \ + _avail(XrEventDataExpandDeviceCustomDataStateChangedPICO, XR_TYPE_EVENT_DATA_EXPAND_DEVICE_CUSTOM_DATA_STATE_CHANGED_PICO) \ + _avail(XrEventDataSenseDataProviderStateChangedPICO, XR_TYPE_EVENT_DATA_SENSE_DATA_PROVIDER_STATE_CHANGED_PICO) \ + _avail(XrEventDataSenseDataUpdatedPICO, XR_TYPE_EVENT_DATA_SENSE_DATA_UPDATED_PICO) \ + + + + /// Like XR_LIST_ALL_STRUCTURE_TYPES, but only includes types whose parent struct type is XrHapticBaseHeader #define XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrHapticBaseHeader(_avail, _unavail) \ @@ -172,6 +197,7 @@ This file contains expansion macros (X Macros) for OpenXR structures that have a // Implementation detail of XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrLoaderInitInfoBaseHeaderKHR() #define _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrLoaderInitInfoBaseHeaderKHR_CORE(_avail, _unavail) \ + _avail(XrLoaderInitInfoPropertiesEXT, XR_TYPE_LOADER_INIT_INFO_PROPERTIES_EXT) \ #if defined(XR_USE_PLATFORM_ANDROID) @@ -272,33 +298,33 @@ This file contains expansion macros (X Macros) for OpenXR structures that have a _avail(XrSpatialAnchorsQueryCompletionML, XR_TYPE_SPATIAL_ANCHORS_QUERY_COMPLETION_ML) \ _avail(XrSpatialAnchorsPublishCompletionML, XR_TYPE_SPATIAL_ANCHORS_PUBLISH_COMPLETION_ML) \ _avail(XrSpatialAnchorsDeleteCompletionML, XR_TYPE_SPATIAL_ANCHORS_DELETE_COMPLETION_ML) \ - _avail(XrSpatialAnchorsUpdateExpirationCompletionML, XR_TYPE_SPATIAL_ANCHORS_UPDATE_EXPIRATION_COMPLETION_ML) \ - _avail(XrSenseDataQueryCompletionBD, XR_TYPE_SENSE_DATA_QUERY_COMPLETION_BD) _avail(XrFutureCompletionEXT, XR_TYPE_FUTURE_COMPLETION_EXT) \ - _avail(XrSpatialAnchorCreateCompletionBD, XR_TYPE_SPATIAL_ANCHOR_CREATE_COMPLETION_BD) _avail(XrWorldMeshStateRequestCompletionML, XR_TYPE_WORLD_MESH_STATE_REQUEST_COMPLETION_ML) \ - _avail(XrWorldMeshRequestCompletionML, XR_TYPE_WORLD_MESH_REQUEST_COMPLETION_ML) \ - _avail(XrCreateSpatialContextCompletionEXT, XR_TYPE_CREATE_SPATIAL_CONTEXT_COMPLETION_EXT) _avail( \ - XrCreateSpatialDiscoverySnapshotCompletionEXT, \ - XR_TYPE_CREATE_SPATIAL_DISCOVERY_SNAPSHOT_COMPLETION_EXT) \ - _avail(XrCreateSpatialPersistenceContextCompletionEXT, \ - XR_TYPE_CREATE_SPATIAL_PERSISTENCE_CONTEXT_COMPLETION_EXT) \ - _avail(XrPersistSpatialEntityCompletionEXT, XR_TYPE_PERSIST_SPATIAL_ENTITY_COMPLETION_EXT) \ - _avail(XrUnpersistSpatialEntityCompletionEXT, \ - XR_TYPE_UNPERSIST_SPATIAL_ENTITY_COMPLETION_EXT) \ - _avail(XrSenseDataProviderStartCompletionPICO, \ - XR_TYPE_SENSE_DATA_PROVIDER_START_COMPLETION_PICO) \ - _avail(XrSenseDataQueryCompletionPICO, XR_TYPE_SENSE_DATA_QUERY_COMPLETION_PICO) \ - _avail(XrSpatialAnchorCreateCompletionPICO, \ - XR_TYPE_SPATIAL_ANCHOR_CREATE_COMPLETION_PICO) \ - _avail(XrSpatialAnchorPersistCompletionPICO, \ - XR_TYPE_SPATIAL_ANCHOR_PERSIST_COMPLETION_PICO) \ - _avail(XrSpatialAnchorUnpersistCompletionPICO, \ - XR_TYPE_SPATIAL_ANCHOR_UNPERSIST_COMPLETION_PICO) \ - _avail(XrSpatialAnchorShareCompletionPICO, \ - XR_TYPE_SPATIAL_ANCHOR_SHARE_COMPLETION_PICO) \ - _avail(XrSharedSpatialAnchorDownloadCompletionPICO, \ - XR_TYPE_SPATIAL_ANCHOR_DOWNLOAD_COMPLETION_PICO) \ - _avail(XrSceneCaptureStartCompletionPICO, \ - XR_TYPE_SCENE_CAPTURE_START_COMPLETION_PICO) + _avail(XrSpatialAnchorsUpdateExpirationCompletionML, XR_TYPE_SPATIAL_ANCHORS_UPDATE_EXPIRATION_COMPLETION_ML) \ + _avail(XrSenseDataQueryCompletionBD, XR_TYPE_SENSE_DATA_QUERY_COMPLETION_BD) \ + _avail(XrFutureCompletionEXT, XR_TYPE_FUTURE_COMPLETION_EXT) \ + _avail(XrSpatialAnchorCreateCompletionBD, XR_TYPE_SPATIAL_ANCHOR_CREATE_COMPLETION_BD) \ + _avail(XrWorldMeshStateRequestCompletionML, XR_TYPE_WORLD_MESH_STATE_REQUEST_COMPLETION_ML) \ + _avail(XrWorldMeshRequestCompletionML, XR_TYPE_WORLD_MESH_REQUEST_COMPLETION_ML) \ + _avail(XrCreateSpatialContextCompletionEXT, XR_TYPE_CREATE_SPATIAL_CONTEXT_COMPLETION_EXT) \ + _avail(XrCreateSpatialDiscoverySnapshotCompletionEXT, XR_TYPE_CREATE_SPATIAL_DISCOVERY_SNAPSHOT_COMPLETION_EXT) \ + _avail(XrCreateSpatialPersistenceContextCompletionEXT, XR_TYPE_CREATE_SPATIAL_PERSISTENCE_CONTEXT_COMPLETION_EXT) \ + _avail(XrPersistSpatialEntityCompletionEXT, XR_TYPE_PERSIST_SPATIAL_ENTITY_COMPLETION_EXT) \ + _avail(XrUnpersistSpatialEntityCompletionEXT, XR_TYPE_UNPERSIST_SPATIAL_ENTITY_COMPLETION_EXT) \ + _avail(XrCreateBufferFromGlobalTensorCompletionPICO, XR_TYPE_CREATE_BUFFER_FROM_GLOBAL_TENSOR_COMPLETION_PICO) \ + _avail(XrCreateTextureFromGlobalTensorCompletionPICO, XR_TYPE_CREATE_TEXTURE_FROM_GLOBAL_TENSOR_COMPLETION_PICO) \ + _avail(XrCreateCameraDeviceCompletionPICO, XR_TYPE_CREATE_CAMERA_DEVICE_COMPLETION_PICO) \ + _avail(XrCreateCameraCaptureSessionCompletionPICO, XR_TYPE_CREATE_CAMERA_CAPTURE_SESSION_COMPLETION_PICO) \ + _avail(XrSenseDataProviderStartCompletionPICO, XR_TYPE_SENSE_DATA_PROVIDER_START_COMPLETION_PICO) \ + _avail(XrSenseDataQueryCompletionPICO, XR_TYPE_SENSE_DATA_QUERY_COMPLETION_PICO) \ + _avail(XrSpatialAnchorCreateCompletionPICO, XR_TYPE_SPATIAL_ANCHOR_CREATE_COMPLETION_PICO) \ + _avail(XrSpatialAnchorPersistCompletionPICO, XR_TYPE_SPATIAL_ANCHOR_PERSIST_COMPLETION_PICO) \ + _avail(XrSpatialAnchorUnpersistCompletionPICO, XR_TYPE_SPATIAL_ANCHOR_UNPERSIST_COMPLETION_PICO) \ + _avail(XrSpatialAnchorShareCompletionPICO, XR_TYPE_SPATIAL_ANCHOR_SHARE_COMPLETION_PICO) \ + _avail(XrSharedSpatialAnchorDownloadCompletionPICO, XR_TYPE_SHARED_SPATIAL_ANCHOR_DOWNLOAD_COMPLETION_PICO) \ + _avail(XrSceneCaptureStartCompletionPICO, XR_TYPE_SCENE_CAPTURE_START_COMPLETION_PICO) \ + + + + /// Like XR_LIST_ALL_STRUCTURE_TYPES, but only includes types whose parent struct type is XrSpatialAnchorsQueryInfoBaseHeaderML #define XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSpatialAnchorsQueryInfoBaseHeaderML(_avail, _unavail) \ @@ -340,6 +366,20 @@ This file contains expansion macros (X Macros) for OpenXR structures that have a +/// Like XR_LIST_ALL_STRUCTURE_TYPES, but only includes types whose parent struct type is XrSpaceFilterBaseHeaderMETA +#define XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSpaceFilterBaseHeaderMETA(_avail, _unavail) \ + _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSpaceFilterBaseHeaderMETA_CORE(_avail, _unavail) \ + + +// Implementation detail of XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSpaceFilterBaseHeaderMETA() +#define _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSpaceFilterBaseHeaderMETA_CORE(_avail, _unavail) \ + _avail(XrSpaceFilterUuidMETA, XR_TYPE_SPACE_FILTER_UUID_META) \ + _avail(XrSpaceFilterComponentMETA, XR_TYPE_SPACE_FILTER_COMPONENT_META) \ + + + + + /// Like XR_LIST_ALL_STRUCTURE_TYPES, but only includes types whose parent struct type is XrShareSpacesRecipientBaseHeaderMETA #define XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrShareSpacesRecipientBaseHeaderMETA(_avail, _unavail) \ _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrShareSpacesRecipientBaseHeaderMETA_CORE(_avail, _unavail) \ @@ -347,23 +387,29 @@ This file contains expansion macros (X Macros) for OpenXR structures that have a // Implementation detail of XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrShareSpacesRecipientBaseHeaderMETA() #define _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrShareSpacesRecipientBaseHeaderMETA_CORE(_avail, _unavail) \ - _avail(XrShareSpacesRecipientGroupsMETA, XR_TYPE_SHARE_SPACES_RECIPIENT_GROUPS_META) + _avail(XrShareSpacesRecipientGroupsMETA, XR_TYPE_SHARE_SPACES_RECIPIENT_GROUPS_META) \ + + -/// Like XR_LIST_ALL_STRUCTURE_TYPES, but only includes types whose parent struct type is -/// XrSpatialCapabilityConfigurationBaseHeaderEXT + + +/// Like XR_LIST_ALL_STRUCTURE_TYPES, but only includes types whose parent struct type is XrSpatialCapabilityConfigurationBaseHeaderEXT #define XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSpatialCapabilityConfigurationBaseHeaderEXT(_avail, _unavail) \ - _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSpatialCapabilityConfigurationBaseHeaderEXT_CORE(_avail, _unavail) + _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSpatialCapabilityConfigurationBaseHeaderEXT_CORE(_avail, _unavail) \ + // Implementation detail of XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSpatialCapabilityConfigurationBaseHeaderEXT() -#define _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSpatialCapabilityConfigurationBaseHeaderEXT_CORE(_avail, _unavail) \ - _avail(XrSpatialCapabilityConfigurationPlaneTrackingEXT, \ - XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_PLANE_TRACKING_EXT) \ - _avail(XrSpatialCapabilityConfigurationQrCodeEXT, XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_QR_CODE_EXT) _avail( \ - XrSpatialCapabilityConfigurationMicroQrCodeEXT, XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_MICRO_QR_CODE_EXT) \ - _avail(XrSpatialCapabilityConfigurationArucoMarkerEXT, \ - XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_ARUCO_MARKER_EXT) \ - _avail(XrSpatialCapabilityConfigurationAprilTagEXT, \ - XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_APRIL_TAG_EXT) +#define _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSpatialCapabilityConfigurationBaseHeaderEXT_CORE(_avail, _unavail) \ + _avail(XrSpatialCapabilityConfigurationPlaneTrackingEXT, XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_PLANE_TRACKING_EXT) \ + _avail(XrSpatialCapabilityConfigurationQrCodeEXT, XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_QR_CODE_EXT) \ + _avail(XrSpatialCapabilityConfigurationMicroQrCodeEXT, XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_MICRO_QR_CODE_EXT) \ + _avail(XrSpatialCapabilityConfigurationArucoMarkerEXT, XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_ARUCO_MARKER_EXT) \ + _avail(XrSpatialCapabilityConfigurationAprilTagEXT, XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_APRIL_TAG_EXT) \ + _avail(XrSpatialCapabilityConfigurationAnchorEXT, XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_ANCHOR_EXT) \ + + + + /// Like XR_LIST_ALL_STRUCTURE_TYPES, but only includes types whose parent struct type is XrVirtualBoundaryTriggerBaseHeaderPICO #define XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrVirtualBoundaryTriggerBaseHeaderPICO(_avail, _unavail) \ @@ -396,6 +442,7 @@ This file contains expansion macros (X Macros) for OpenXR structures that have a _avail(XrSecureMrOperatorComparisonPICO, XR_TYPE_SECURE_MR_OPERATOR_COMPARISON_PICO) \ _avail(XrSecureMrOperatorSortMatrixPICO, XR_TYPE_SECURE_MR_OPERATOR_SORT_MATRIX_PICO) \ _avail(XrSecureMrOperatorColorConvertPICO, XR_TYPE_SECURE_MR_OPERATOR_COLOR_CONVERT_PICO) \ + _avail(XrSecureMrOperatorJavascriptPICO, XR_TYPE_SECURE_MR_OPERATOR_JAVASCRIPT_PICO) \ @@ -409,31 +456,137 @@ This file contains expansion macros (X Macros) for OpenXR structures that have a // Implementation detail of XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSecureMrTensorCreateInfoBaseHeaderPICO() #define _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSecureMrTensorCreateInfoBaseHeaderPICO_CORE(_avail, _unavail) \ _avail(XrSecureMrTensorCreateInfoShapePICO, XR_TYPE_SECURE_MR_TENSOR_CREATE_INFO_SHAPE_PICO) \ - _avail(XrSecureMrTensorCreateInfoGltfPICO, XR_TYPE_SECURE_MR_TENSOR_CREATE_INFO_GLTF_PICO) + _avail(XrSecureMrTensorCreateInfoGltfPICO, XR_TYPE_SECURE_MR_TENSOR_CREATE_INFO_GLTF_PICO) \ + + + + -/// Like XR_LIST_ALL_STRUCTURE_TYPES, but only includes types whose parent struct type is -/// XrSenseDataFilterBaseHeaderPICO +/// Like XR_LIST_ALL_STRUCTURE_TYPES, but only includes types whose parent struct type is XrSenseDataFilterBaseHeaderPICO #define XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSenseDataFilterBaseHeaderPICO(_avail, _unavail) \ - _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSenseDataFilterBaseHeaderPICO_CORE(_avail, _unavail) + _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSenseDataFilterBaseHeaderPICO_CORE(_avail, _unavail) \ + // Implementation detail of XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSenseDataFilterBaseHeaderPICO() #define _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSenseDataFilterBaseHeaderPICO_CORE(_avail, _unavail) \ - _avail(XrSenseDataFilterDynamicObjectTypePICO, XR_TYPE_SENSE_DATA_FILTER_DYNAMIC_OBJECT_TYPE_PICO) \ - _avail(XrSenseDataFilterUuidPICO, XR_TYPE_SENSE_DATA_FILTER_UUID_PICO) \ - _avail(XrSenseDataFilterSemanticPICO, XR_TYPE_SENSE_DATA_FILTER_SEMANTIC_PICO) + _avail(XrSenseDataFilterDynamicObjectTypePICO, XR_TYPE_SENSE_DATA_FILTER_DYNAMIC_OBJECT_TYPE_PICO) \ + _avail(XrSenseDataFilterUuidPICO, XR_TYPE_SENSE_DATA_FILTER_UUID_PICO) \ + _avail(XrSenseDataFilterSemanticPICO, XR_TYPE_SENSE_DATA_FILTER_SEMANTIC_PICO) \ + + + + + +/// Like XR_LIST_ALL_STRUCTURE_TYPES, but only includes types whose parent struct type is XrReadbackTextureImageBaseHeaderPICO +#define XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrReadbackTextureImageBaseHeaderPICO(_avail, _unavail) \ + _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrReadbackTextureImageBaseHeaderPICO_CORE(_avail, _unavail) \ + _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrReadbackTextureImageBaseHeaderPICO_XR_USE_GRAPHICS_API_OPENGL_ES(_avail, _unavail) \ + _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrReadbackTextureImageBaseHeaderPICO_XR_USE_GRAPHICS_API_VULKAN(_avail, _unavail) \ + + +// Implementation detail of XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrReadbackTextureImageBaseHeaderPICO() +#define _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrReadbackTextureImageBaseHeaderPICO_CORE(_avail, _unavail) \ + + +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) +#define _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrReadbackTextureImageBaseHeaderPICO_XR_USE_GRAPHICS_API_OPENGL_ES(_avail, _unavail) \ + _avail(XrReadbackTextureImageOpenGLPICO, XR_TYPE_READBACK_TEXTURE_IMAGE_OPENGL_PICO) \ + +#else +#define _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrReadbackTextureImageBaseHeaderPICO_XR_USE_GRAPHICS_API_OPENGL_ES(_avail, _unavail) \ + _unavail(XrReadbackTextureImageOpenGLPICO, XR_TYPE_READBACK_TEXTURE_IMAGE_OPENGL_PICO) \ + +#endif -/// Like XR_LIST_ALL_STRUCTURE_TYPES, but only includes types whose parent struct type is -/// XrSenseDataProviderCreateInfoBaseHeaderPICO +#if defined(XR_USE_GRAPHICS_API_VULKAN) +#define _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrReadbackTextureImageBaseHeaderPICO_XR_USE_GRAPHICS_API_VULKAN(_avail, _unavail) \ + _avail(XrReadbackTextureImageVulkanPICO, XR_TYPE_READBACK_TEXTURE_IMAGE_VULKAN_PICO) \ + +#else +#define _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrReadbackTextureImageBaseHeaderPICO_XR_USE_GRAPHICS_API_VULKAN(_avail, _unavail) \ + _unavail(XrReadbackTextureImageVulkanPICO, XR_TYPE_READBACK_TEXTURE_IMAGE_VULKAN_PICO) \ + +#endif + + + + +/// Like XR_LIST_ALL_STRUCTURE_TYPES, but only includes types whose parent struct type is XrCameraPropertyBaseHeaderPICO +#define XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrCameraPropertyBaseHeaderPICO(_avail, _unavail) \ + _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrCameraPropertyBaseHeaderPICO_CORE(_avail, _unavail) \ + + +// Implementation detail of XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrCameraPropertyBaseHeaderPICO() +#define _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrCameraPropertyBaseHeaderPICO_CORE(_avail, _unavail) \ + _avail(XrCameraPropertyFacingPICO, XR_TYPE_CAMERA_PROPERTY_FACING_PICO) \ + _avail(XrCameraPropertyPositionPICO, XR_TYPE_CAMERA_PROPERTY_POSITION_PICO) \ + _avail(XrCameraPropertyCameraTypePICO, XR_TYPE_CAMERA_PROPERTY_CAMERA_TYPE_PICO) \ + + + + + +/// Like XR_LIST_ALL_STRUCTURE_TYPES, but only includes types whose parent struct type is XrCameraSupportedCapabilityBaseHeaderPICO +#define XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrCameraSupportedCapabilityBaseHeaderPICO(_avail, _unavail) \ + _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrCameraSupportedCapabilityBaseHeaderPICO_CORE(_avail, _unavail) \ + + +// Implementation detail of XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrCameraSupportedCapabilityBaseHeaderPICO() +#define _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrCameraSupportedCapabilityBaseHeaderPICO_CORE(_avail, _unavail) \ + _avail(XrCameraSupportedCapabilityImageResolutionPICO, XR_TYPE_CAMERA_SUPPORTED_CAPABILITY_IMAGE_RESOLUTION_PICO) \ + _avail(XrCameraSupportedCapabilityDataTransferTypePICO, XR_TYPE_CAMERA_SUPPORTED_CAPABILITY_DATA_TRANSFER_TYPE_PICO) \ + _avail(XrCameraSupportedCapabilityImageFormatPICO, XR_TYPE_CAMERA_SUPPORTED_CAPABILITY_IMAGE_FORMAT_PICO) \ + _avail(XrCameraSupportedCapabilityCameraModelPICO, XR_TYPE_CAMERA_SUPPORTED_CAPABILITY_CAMERA_MODEL_PICO) \ + _avail(XrCameraSupportedCapabilityImageFpsPICO, XR_TYPE_CAMERA_SUPPORTED_CAPABILITY_IMAGE_FPS_PICO) \ + + + + + +/// Like XR_LIST_ALL_STRUCTURE_TYPES, but only includes types whose parent struct type is XrCameraCapabilityBaseHeaderPICO +#define XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrCameraCapabilityBaseHeaderPICO(_avail, _unavail) \ + _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrCameraCapabilityBaseHeaderPICO_CORE(_avail, _unavail) \ + + +// Implementation detail of XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrCameraCapabilityBaseHeaderPICO() +#define _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrCameraCapabilityBaseHeaderPICO_CORE(_avail, _unavail) \ + _avail(XrCameraCapabilityImageResolutionPICO, XR_TYPE_CAMERA_CAPABILITY_IMAGE_RESOLUTION_PICO) \ + _avail(XrCameraCapabilityDataTransferTypePICO, XR_TYPE_CAMERA_CAPABILITY_DATA_TRANSFER_TYPE_PICO) \ + _avail(XrCameraCapabilityImageFormatPICO, XR_TYPE_CAMERA_CAPABILITY_IMAGE_FORMAT_PICO) \ + _avail(XrCameraCapabilityCameraModelPICO, XR_TYPE_CAMERA_CAPABILITY_CAMERA_MODEL_PICO) \ + _avail(XrCameraCapabilityImageFpsPICO, XR_TYPE_CAMERA_CAPABILITY_IMAGE_FPS_PICO) \ + + + + + +/// Like XR_LIST_ALL_STRUCTURE_TYPES, but only includes types whose parent struct type is XrCameraImageDataBaseHeaderPICO +#define XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrCameraImageDataBaseHeaderPICO(_avail, _unavail) \ + _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrCameraImageDataBaseHeaderPICO_CORE(_avail, _unavail) \ + + +// Implementation detail of XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrCameraImageDataBaseHeaderPICO() +#define _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrCameraImageDataBaseHeaderPICO_CORE(_avail, _unavail) \ + _avail(XrCameraImageDataRawBufferPICO, XR_TYPE_CAMERA_IMAGE_DATA_RAW_BUFFER_PICO) \ + + + + + +/// Like XR_LIST_ALL_STRUCTURE_TYPES, but only includes types whose parent struct type is XrSenseDataProviderCreateInfoBaseHeaderPICO #define XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSenseDataProviderCreateInfoBaseHeaderPICO(_avail, _unavail) \ - _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSenseDataProviderCreateInfoBaseHeaderPICO_CORE(_avail, _unavail) + _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSenseDataProviderCreateInfoBaseHeaderPICO_CORE(_avail, _unavail) \ + // Implementation detail of XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSenseDataProviderCreateInfoBaseHeaderPICO() -#define _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSenseDataProviderCreateInfoBaseHeaderPICO_CORE(_avail, _unavail) \ - _avail(XrSenseDataProviderCreateInfoSpatialAnchorPICO, XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SPATIAL_ANCHOR_PICO) \ - _avail(XrSenseDataProviderCreateInfoSceneCapturePICO, \ - XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SCENE_CAPTURE_PICO) \ - _avail(XrSenseDataProviderCreateInfoSpatialMeshPICO, \ - XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SPATIAL_MESH_PICO) +#define _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSenseDataProviderCreateInfoBaseHeaderPICO_CORE(_avail, _unavail) \ + _avail(XrSenseDataProviderCreateInfoSpatialAnchorPICO, XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SPATIAL_ANCHOR_PICO) \ + _avail(XrSenseDataProviderCreateInfoSceneCapturePICO, XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SCENE_CAPTURE_PICO) \ + _avail(XrSenseDataProviderCreateInfoSpatialMeshPICO, XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SPATIAL_MESH_PICO) \ + + + + #endif diff --git a/external/openxr/include/openxr/openxr_reflection_structs.h b/external/openxr/include/openxr/openxr_reflection_structs.h index 60736ac..d15c4e3 100644 --- a/external/openxr/include/openxr/openxr_reflection_structs.h +++ b/external/openxr/include/openxr/openxr_reflection_structs.h @@ -330,15 +330,32 @@ This file contains expansion macros (X Macros) for OpenXR structures. _avail(XrEventDataSpaceListSaveCompleteFB, XR_TYPE_EVENT_DATA_SPACE_LIST_SAVE_COMPLETE_FB) \ _avail(XrSpaceUserCreateInfoFB, XR_TYPE_SPACE_USER_CREATE_INFO_FB) \ _avail(XrSystemHeadsetIdPropertiesMETA, XR_TYPE_SYSTEM_HEADSET_ID_PROPERTIES_META) \ + _avail(XrSystemSpaceDiscoveryPropertiesMETA, XR_TYPE_SYSTEM_SPACE_DISCOVERY_PROPERTIES_META) \ + _avail(XrSpaceDiscoveryInfoMETA, XR_TYPE_SPACE_DISCOVERY_INFO_META) \ + _avail(XrSpaceFilterUuidMETA, XR_TYPE_SPACE_FILTER_UUID_META) \ + _avail(XrSpaceFilterComponentMETA, XR_TYPE_SPACE_FILTER_COMPONENT_META) \ + _avail(XrSpaceDiscoveryResultsMETA, XR_TYPE_SPACE_DISCOVERY_RESULTS_META) \ + _avail(XrEventDataSpaceDiscoveryResultsAvailableMETA, XR_TYPE_EVENT_DATA_SPACE_DISCOVERY_RESULTS_AVAILABLE_META) \ + _avail(XrEventDataSpaceDiscoveryCompleteMETA, XR_TYPE_EVENT_DATA_SPACE_DISCOVERY_COMPLETE_META) \ _avail(XrRecommendedLayerResolutionMETA, XR_TYPE_RECOMMENDED_LAYER_RESOLUTION_META) \ _avail(XrRecommendedLayerResolutionGetInfoMETA, XR_TYPE_RECOMMENDED_LAYER_RESOLUTION_GET_INFO_META) \ + _avail(XrSystemSpacePersistencePropertiesMETA, XR_TYPE_SYSTEM_SPACE_PERSISTENCE_PROPERTIES_META) \ + _avail(XrSpacesSaveInfoMETA, XR_TYPE_SPACES_SAVE_INFO_META) \ + _avail(XrEventDataSpacesSaveResultMETA, XR_TYPE_EVENT_DATA_SPACES_SAVE_RESULT_META) \ + _avail(XrSpacesEraseInfoMETA, XR_TYPE_SPACES_ERASE_INFO_META) \ + _avail(XrEventDataSpacesEraseResultMETA, XR_TYPE_EVENT_DATA_SPACES_ERASE_RESULT_META) \ _avail(XrPassthroughColorLutCreateInfoMETA, XR_TYPE_PASSTHROUGH_COLOR_LUT_CREATE_INFO_META) \ _avail(XrPassthroughColorLutUpdateInfoMETA, XR_TYPE_PASSTHROUGH_COLOR_LUT_UPDATE_INFO_META) \ _avail(XrPassthroughColorMapLutMETA, XR_TYPE_PASSTHROUGH_COLOR_MAP_LUT_META) \ _avail(XrPassthroughColorMapInterpolatedLutMETA, XR_TYPE_PASSTHROUGH_COLOR_MAP_INTERPOLATED_LUT_META) \ _avail(XrSystemPassthroughColorLutPropertiesMETA, XR_TYPE_SYSTEM_PASSTHROUGH_COLOR_LUT_PROPERTIES_META) \ _avail(XrSpaceTriangleMeshGetInfoMETA, XR_TYPE_SPACE_TRIANGLE_MESH_GET_INFO_META) \ - _avail(XrSpaceTriangleMeshMETA, XR_TYPE_SPACE_TRIANGLE_MESH_META) _avail(XrSystemPropertiesBodyTrackingFullBodyMETA, XR_TYPE_SYSTEM_PROPERTIES_BODY_TRACKING_FULL_BODY_META) _avail(XrEventDataPassthroughLayerResumedMETA, XR_TYPE_EVENT_DATA_PASSTHROUGH_LAYER_RESUMED_META) \ + _avail(XrSpaceTriangleMeshMETA, XR_TYPE_SPACE_TRIANGLE_MESH_META) \ + _avail(XrSystemPropertiesBodyTrackingFullBodyMETA, XR_TYPE_SYSTEM_PROPERTIES_BODY_TRACKING_FULL_BODY_META) \ + _avail(XrEventDataPassthroughLayerResumedMETA, XR_TYPE_EVENT_DATA_PASSTHROUGH_LAYER_RESUMED_META) \ + _avail(XrBodyTrackingCalibrationStatusMETA, XR_TYPE_BODY_TRACKING_CALIBRATION_STATUS_META) \ + _avail(XrBodyTrackingCalibrationInfoMETA, XR_TYPE_BODY_TRACKING_CALIBRATION_INFO_META) \ + _avail(XrSystemPropertiesBodyTrackingCalibrationMETA, XR_TYPE_SYSTEM_PROPERTIES_BODY_TRACKING_CALIBRATION_META) \ _avail(XrSystemFaceTrackingProperties2FB, XR_TYPE_SYSTEM_FACE_TRACKING_PROPERTIES2_FB) \ _avail(XrFaceTrackerCreateInfo2FB, XR_TYPE_FACE_TRACKER_CREATE_INFO2_FB) \ _avail(XrFaceExpressionInfo2FB, XR_TYPE_FACE_EXPRESSION_INFO2_FB) \ @@ -353,11 +370,23 @@ This file contains expansion macros (X Macros) for OpenXR structures. _avail(XrEnvironmentDepthImageViewMETA, XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_VIEW_META) \ _avail(XrEnvironmentDepthImageMETA, XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_META) \ _avail(XrEnvironmentDepthHandRemovalSetInfoMETA, XR_TYPE_ENVIRONMENT_DEPTH_HAND_REMOVAL_SET_INFO_META) \ - _avail(XrSystemEnvironmentDepthPropertiesMETA, XR_TYPE_SYSTEM_ENVIRONMENT_DEPTH_PROPERTIES_META) _avail(XrRenderModelCreateInfoEXT, XR_TYPE_RENDER_MODEL_CREATE_INFO_EXT) _avail(XrRenderModelPropertiesGetInfoEXT, XR_TYPE_RENDER_MODEL_PROPERTIES_GET_INFO_EXT) _avail(XrRenderModelPropertiesEXT, XR_TYPE_RENDER_MODEL_PROPERTIES_EXT) _avail(XrRenderModelSpaceCreateInfoEXT, XR_TYPE_RENDER_MODEL_SPACE_CREATE_INFO_EXT) _avail(XrRenderModelStateGetInfoEXT, XR_TYPE_RENDER_MODEL_STATE_GET_INFO_EXT) _avail(XrRenderModelStateEXT, XR_TYPE_RENDER_MODEL_STATE_EXT) _avail(XrRenderModelAssetCreateInfoEXT, \ - XR_TYPE_RENDER_MODEL_ASSET_CREATE_INFO_EXT) _avail(XrRenderModelAssetDataGetInfoEXT, XR_TYPE_RENDER_MODEL_ASSET_DATA_GET_INFO_EXT) _avail(XrRenderModelAssetDataEXT, \ - XR_TYPE_RENDER_MODEL_ASSET_DATA_EXT) _avail(XrRenderModelAssetPropertiesGetInfoEXT, \ - XR_TYPE_RENDER_MODEL_ASSET_PROPERTIES_GET_INFO_EXT) _avail(XrRenderModelAssetPropertiesEXT, XR_TYPE_RENDER_MODEL_ASSET_PROPERTIES_EXT) _avail(XrInteractionRenderModelIdsEnumerateInfoEXT, XR_TYPE_INTERACTION_RENDER_MODEL_IDS_ENUMERATE_INFO_EXT) _avail(XrInteractionRenderModelSubactionPathInfoEXT, \ - XR_TYPE_INTERACTION_RENDER_MODEL_SUBACTION_PATH_INFO_EXT) _avail(XrInteractionRenderModelTopLevelUserPathGetInfoEXT, XR_TYPE_INTERACTION_RENDER_MODEL_TOP_LEVEL_USER_PATH_GET_INFO_EXT) _avail(XrEventDataInteractionRenderModelsChangedEXT, XR_TYPE_EVENT_DATA_INTERACTION_RENDER_MODELS_CHANGED_EXT) _avail(XrPassthroughCreateInfoHTC, XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC) \ + _avail(XrSystemEnvironmentDepthPropertiesMETA, XR_TYPE_SYSTEM_ENVIRONMENT_DEPTH_PROPERTIES_META) \ + _avail(XrRenderModelCreateInfoEXT, XR_TYPE_RENDER_MODEL_CREATE_INFO_EXT) \ + _avail(XrRenderModelPropertiesGetInfoEXT, XR_TYPE_RENDER_MODEL_PROPERTIES_GET_INFO_EXT) \ + _avail(XrRenderModelPropertiesEXT, XR_TYPE_RENDER_MODEL_PROPERTIES_EXT) \ + _avail(XrRenderModelSpaceCreateInfoEXT, XR_TYPE_RENDER_MODEL_SPACE_CREATE_INFO_EXT) \ + _avail(XrRenderModelStateGetInfoEXT, XR_TYPE_RENDER_MODEL_STATE_GET_INFO_EXT) \ + _avail(XrRenderModelStateEXT, XR_TYPE_RENDER_MODEL_STATE_EXT) \ + _avail(XrRenderModelAssetCreateInfoEXT, XR_TYPE_RENDER_MODEL_ASSET_CREATE_INFO_EXT) \ + _avail(XrRenderModelAssetDataGetInfoEXT, XR_TYPE_RENDER_MODEL_ASSET_DATA_GET_INFO_EXT) \ + _avail(XrRenderModelAssetDataEXT, XR_TYPE_RENDER_MODEL_ASSET_DATA_EXT) \ + _avail(XrRenderModelAssetPropertiesGetInfoEXT, XR_TYPE_RENDER_MODEL_ASSET_PROPERTIES_GET_INFO_EXT) \ + _avail(XrRenderModelAssetPropertiesEXT, XR_TYPE_RENDER_MODEL_ASSET_PROPERTIES_EXT) \ + _avail(XrInteractionRenderModelIdsEnumerateInfoEXT, XR_TYPE_INTERACTION_RENDER_MODEL_IDS_ENUMERATE_INFO_EXT) \ + _avail(XrInteractionRenderModelSubactionPathInfoEXT, XR_TYPE_INTERACTION_RENDER_MODEL_SUBACTION_PATH_INFO_EXT) \ + _avail(XrInteractionRenderModelTopLevelUserPathGetInfoEXT, XR_TYPE_INTERACTION_RENDER_MODEL_TOP_LEVEL_USER_PATH_GET_INFO_EXT) \ + _avail(XrEventDataInteractionRenderModelsChangedEXT, XR_TYPE_EVENT_DATA_INTERACTION_RENDER_MODELS_CHANGED_EXT) \ + _avail(XrPassthroughCreateInfoHTC, XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC) \ _avail(XrPassthroughColorHTC, XR_TYPE_PASSTHROUGH_COLOR_HTC) \ _avail(XrPassthroughMeshTransformInfoHTC, XR_TYPE_PASSTHROUGH_MESH_TRANSFORM_INFO_HTC) \ _avail(XrCompositionLayerPassthroughHTC, XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_HTC) \ @@ -377,25 +406,52 @@ This file contains expansion macros (X Macros) for OpenXR structures. _avail(XrSystemBodyTrackingPropertiesBD, XR_TYPE_SYSTEM_BODY_TRACKING_PROPERTIES_BD) \ _avail(XrBodyTrackerCreateInfoBD, XR_TYPE_BODY_TRACKER_CREATE_INFO_BD) \ _avail(XrBodyJointsLocateInfoBD, XR_TYPE_BODY_JOINTS_LOCATE_INFO_BD) \ - _avail(XrBodyJointLocationsBD, XR_TYPE_BODY_JOINT_LOCATIONS_BD) _avail(XrSystemSpatialSensingPropertiesBD, XR_TYPE_SYSTEM_SPATIAL_SENSING_PROPERTIES_BD) _avail(XrSpatialEntityComponentGetInfoBD, XR_TYPE_SPATIAL_ENTITY_COMPONENT_GET_INFO_BD) _avail(XrSpatialEntityLocationGetInfoBD, XR_TYPE_SPATIAL_ENTITY_LOCATION_GET_INFO_BD) _avail(XrSpatialEntityComponentDataLocationBD, XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_LOCATION_BD) _avail(XrSpatialEntityComponentDataSemanticBD, \ - XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_SEMANTIC_BD) _avail(XrSpatialEntityComponentDataBoundingBox2DBD, XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_BOUNDING_BOX_2D_BD) \ - _avail(XrSpatialEntityComponentDataPolygonBD, XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_POLYGON_BD) _avail( \ - XrSpatialEntityComponentDataBoundingBox3DBD, \ - XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_BOUNDING_BOX_3D_BD) _avail(XrSpatialEntityComponentDataTriangleMeshBD, XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_TRIANGLE_MESH_BD) _avail(XrSenseDataProviderCreateInfoBD, XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_BD) _avail(XrSenseDataProviderStartInfoBD, XR_TYPE_SENSE_DATA_PROVIDER_START_INFO_BD) _avail(XrEventDataSenseDataProviderStateChangedBD, \ - XR_TYPE_EVENT_DATA_SENSE_DATA_PROVIDER_STATE_CHANGED_BD) _avail(XrEventDataSenseDataUpdatedBD, \ - XR_TYPE_EVENT_DATA_SENSE_DATA_UPDATED_BD) _avail(XrSenseDataQueryInfoBD, XR_TYPE_SENSE_DATA_QUERY_INFO_BD) _avail(XrSenseDataQueryCompletionBD, \ - XR_TYPE_SENSE_DATA_QUERY_COMPLETION_BD) _avail(XrQueriedSenseDataGetInfoBD, \ - XR_TYPE_QUERIED_SENSE_DATA_GET_INFO_BD) _avail(XrSpatialEntityStateBD, XR_TYPE_SPATIAL_ENTITY_STATE_BD) _avail(XrQueriedSenseDataBD, XR_TYPE_QUERIED_SENSE_DATA_BD) _avail(XrSenseDataFilterUuidBD, XR_TYPE_SENSE_DATA_FILTER_UUID_BD) _avail(XrSenseDataFilterSemanticBD, XR_TYPE_SENSE_DATA_FILTER_SEMANTIC_BD) _avail(XrSpatialEntityAnchorCreateInfoBD, XR_TYPE_SPATIAL_ENTITY_ANCHOR_CREATE_INFO_BD) _avail(XrAnchorSpaceCreateInfoBD, XR_TYPE_ANCHOR_SPACE_CREATE_INFO_BD) _avail(XrFutureCompletionEXT, XR_TYPE_FUTURE_COMPLETION_EXT) _avail(XrSystemSpatialAnchorPropertiesBD, \ - XR_TYPE_SYSTEM_SPATIAL_ANCHOR_PROPERTIES_BD) _avail(XrSpatialAnchorCreateInfoBD, \ - XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_BD) _avail(XrSpatialAnchorCreateCompletionBD, \ - XR_TYPE_SPATIAL_ANCHOR_CREATE_COMPLETION_BD) _avail(XrSpatialAnchorPersistInfoBD, XR_TYPE_SPATIAL_ANCHOR_PERSIST_INFO_BD) _avail(XrSpatialAnchorUnpersistInfoBD, XR_TYPE_SPATIAL_ANCHOR_UNPERSIST_INFO_BD) _avail(XrSystemSpatialAnchorSharingPropertiesBD, \ - XR_TYPE_SYSTEM_SPATIAL_ANCHOR_SHARING_PROPERTIES_BD) _avail(XrSpatialAnchorShareInfoBD, XR_TYPE_SPATIAL_ANCHOR_SHARE_INFO_BD) _avail(XrSharedSpatialAnchorDownloadInfoBD, XR_TYPE_SHARED_SPATIAL_ANCHOR_DOWNLOAD_INFO_BD) _avail(XrSystemSpatialScenePropertiesBD, \ - XR_TYPE_SYSTEM_SPATIAL_SCENE_PROPERTIES_BD) _avail(XrSceneCaptureInfoBD, \ - XR_TYPE_SCENE_CAPTURE_INFO_BD) _avail(XrSystemSpatialMeshPropertiesBD, \ - XR_TYPE_SYSTEM_SPATIAL_MESH_PROPERTIES_BD) _avail(XrSenseDataProviderCreateInfoSpatialMeshBD, \ - XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SPATIAL_MESH_BD) _avail(XrFuturePollResultProgressBD, \ - XR_TYPE_FUTURE_POLL_RESULT_PROGRESS_BD) _avail(XrSystemSpatialPlanePropertiesBD, XR_TYPE_SYSTEM_SPATIAL_PLANE_PROPERTIES_BD) _avail(XrSpatialEntityComponentDataPlaneOrientationBD, XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_PLANE_ORIENTATION_BD) _avail(XrSenseDataFilterPlaneOrientationBD, \ - XR_TYPE_SENSE_DATA_FILTER_PLANE_ORIENTATION_BD) _avail(XrHandTrackingDataSourceInfoEXT, XR_TYPE_HAND_TRACKING_DATA_SOURCE_INFO_EXT) \ + _avail(XrBodyJointLocationsBD, XR_TYPE_BODY_JOINT_LOCATIONS_BD) \ + _avail(XrSystemFacialSimulationPropertiesBD, XR_TYPE_SYSTEM_FACIAL_SIMULATION_PROPERTIES_BD) \ + _avail(XrFaceTrackerCreateInfoBD, XR_TYPE_FACE_TRACKER_CREATE_INFO_BD) \ + _avail(XrFacialSimulationDataGetInfoBD, XR_TYPE_FACIAL_SIMULATION_DATA_GET_INFO_BD) \ + _avail(XrFacialSimulationDataBD, XR_TYPE_FACIAL_SIMULATION_DATA_BD) \ + _avail(XrLipExpressionDataBD, XR_TYPE_LIP_EXPRESSION_DATA_BD) \ + _avail(XrSystemSpatialSensingPropertiesBD, XR_TYPE_SYSTEM_SPATIAL_SENSING_PROPERTIES_BD) \ + _avail(XrSpatialEntityComponentGetInfoBD, XR_TYPE_SPATIAL_ENTITY_COMPONENT_GET_INFO_BD) \ + _avail(XrSpatialEntityLocationGetInfoBD, XR_TYPE_SPATIAL_ENTITY_LOCATION_GET_INFO_BD) \ + _avail(XrSpatialEntityComponentDataLocationBD, XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_LOCATION_BD) \ + _avail(XrSpatialEntityComponentDataSemanticBD, XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_SEMANTIC_BD) \ + _avail(XrSpatialEntityComponentDataBoundingBox2DBD, XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_BOUNDING_BOX_2D_BD) \ + _avail(XrSpatialEntityComponentDataPolygonBD, XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_POLYGON_BD) \ + _avail(XrSpatialEntityComponentDataBoundingBox3DBD, XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_BOUNDING_BOX_3D_BD) \ + _avail(XrSpatialEntityComponentDataTriangleMeshBD, XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_TRIANGLE_MESH_BD) \ + _avail(XrSenseDataProviderCreateInfoBD, XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_BD) \ + _avail(XrSenseDataProviderStartInfoBD, XR_TYPE_SENSE_DATA_PROVIDER_START_INFO_BD) \ + _avail(XrEventDataSenseDataProviderStateChangedBD, XR_TYPE_EVENT_DATA_SENSE_DATA_PROVIDER_STATE_CHANGED_BD) \ + _avail(XrEventDataSenseDataUpdatedBD, XR_TYPE_EVENT_DATA_SENSE_DATA_UPDATED_BD) \ + _avail(XrSenseDataQueryInfoBD, XR_TYPE_SENSE_DATA_QUERY_INFO_BD) \ + _avail(XrSenseDataQueryCompletionBD, XR_TYPE_SENSE_DATA_QUERY_COMPLETION_BD) \ + _avail(XrQueriedSenseDataGetInfoBD, XR_TYPE_QUERIED_SENSE_DATA_GET_INFO_BD) \ + _avail(XrSpatialEntityStateBD, XR_TYPE_SPATIAL_ENTITY_STATE_BD) \ + _avail(XrQueriedSenseDataBD, XR_TYPE_QUERIED_SENSE_DATA_BD) \ + _avail(XrSenseDataFilterUuidBD, XR_TYPE_SENSE_DATA_FILTER_UUID_BD) \ + _avail(XrSenseDataFilterSemanticBD, XR_TYPE_SENSE_DATA_FILTER_SEMANTIC_BD) \ + _avail(XrSpatialEntityAnchorCreateInfoBD, XR_TYPE_SPATIAL_ENTITY_ANCHOR_CREATE_INFO_BD) \ + _avail(XrAnchorSpaceCreateInfoBD, XR_TYPE_ANCHOR_SPACE_CREATE_INFO_BD) \ + _avail(XrFutureCompletionEXT, XR_TYPE_FUTURE_COMPLETION_EXT) \ + _avail(XrSystemSpatialAnchorPropertiesBD, XR_TYPE_SYSTEM_SPATIAL_ANCHOR_PROPERTIES_BD) \ + _avail(XrSpatialAnchorCreateInfoBD, XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_BD) \ + _avail(XrSpatialAnchorCreateCompletionBD, XR_TYPE_SPATIAL_ANCHOR_CREATE_COMPLETION_BD) \ + _avail(XrSpatialAnchorPersistInfoBD, XR_TYPE_SPATIAL_ANCHOR_PERSIST_INFO_BD) \ + _avail(XrSpatialAnchorUnpersistInfoBD, XR_TYPE_SPATIAL_ANCHOR_UNPERSIST_INFO_BD) \ + _avail(XrSystemSpatialAnchorSharingPropertiesBD, XR_TYPE_SYSTEM_SPATIAL_ANCHOR_SHARING_PROPERTIES_BD) \ + _avail(XrSpatialAnchorShareInfoBD, XR_TYPE_SPATIAL_ANCHOR_SHARE_INFO_BD) \ + _avail(XrSharedSpatialAnchorDownloadInfoBD, XR_TYPE_SHARED_SPATIAL_ANCHOR_DOWNLOAD_INFO_BD) \ + _avail(XrSystemSpatialScenePropertiesBD, XR_TYPE_SYSTEM_SPATIAL_SCENE_PROPERTIES_BD) \ + _avail(XrSceneCaptureInfoBD, XR_TYPE_SCENE_CAPTURE_INFO_BD) \ + _avail(XrSystemSpatialMeshPropertiesBD, XR_TYPE_SYSTEM_SPATIAL_MESH_PROPERTIES_BD) \ + _avail(XrSenseDataProviderCreateInfoSpatialMeshBD, XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SPATIAL_MESH_BD) \ + _avail(XrFuturePollResultProgressBD, XR_TYPE_FUTURE_POLL_RESULT_PROGRESS_BD) \ + _avail(XrSystemSpatialPlanePropertiesBD, XR_TYPE_SYSTEM_SPATIAL_PLANE_PROPERTIES_BD) \ + _avail(XrSpatialEntityComponentDataPlaneOrientationBD, XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_PLANE_ORIENTATION_BD) \ + _avail(XrSenseDataFilterPlaneOrientationBD, XR_TYPE_SENSE_DATA_FILTER_PLANE_ORIENTATION_BD) \ + _avail(XrHandTrackingDataSourceInfoEXT, XR_TYPE_HAND_TRACKING_DATA_SOURCE_INFO_EXT) \ _avail(XrHandTrackingDataSourceStateEXT, XR_TYPE_HAND_TRACKING_DATA_SOURCE_STATE_EXT) \ _avail(XrSystemPlaneDetectionPropertiesEXT, XR_TYPE_SYSTEM_PLANE_DETECTION_PROPERTIES_EXT) \ _avail(XrPlaneDetectorCreateInfoEXT, XR_TYPE_PLANE_DETECTOR_CREATE_INFO_EXT) \ @@ -404,6 +460,25 @@ This file contains expansion macros (X Macros) for OpenXR structures. _avail(XrPlaneDetectorLocationEXT, XR_TYPE_PLANE_DETECTOR_LOCATION_EXT) \ _avail(XrPlaneDetectorLocationsEXT, XR_TYPE_PLANE_DETECTOR_LOCATIONS_EXT) \ _avail(XrPlaneDetectorPolygonBufferEXT, XR_TYPE_PLANE_DETECTOR_POLYGON_BUFFER_EXT) \ + _avail(XrTrackableTrackerCreateInfoANDROID, XR_TYPE_TRACKABLE_TRACKER_CREATE_INFO_ANDROID) \ + _avail(XrTrackableGetInfoANDROID, XR_TYPE_TRACKABLE_GET_INFO_ANDROID) \ + _avail(XrTrackablePlaneANDROID, XR_TYPE_TRACKABLE_PLANE_ANDROID) \ + _avail(XrAnchorSpaceCreateInfoANDROID, XR_TYPE_ANCHOR_SPACE_CREATE_INFO_ANDROID) \ + _avail(XrSystemTrackablesPropertiesANDROID, XR_TYPE_SYSTEM_TRACKABLES_PROPERTIES_ANDROID) \ + _avail(XrDeviceAnchorPersistenceCreateInfoANDROID, XR_TYPE_DEVICE_ANCHOR_PERSISTENCE_CREATE_INFO_ANDROID) \ + _avail(XrPersistedAnchorSpaceCreateInfoANDROID, XR_TYPE_PERSISTED_ANCHOR_SPACE_CREATE_INFO_ANDROID) \ + _avail(XrPersistedAnchorSpaceInfoANDROID, XR_TYPE_PERSISTED_ANCHOR_SPACE_INFO_ANDROID) \ + _avail(XrSystemDeviceAnchorPersistencePropertiesANDROID, XR_TYPE_SYSTEM_DEVICE_ANCHOR_PERSISTENCE_PROPERTIES_ANDROID) \ + _avail(XrFaceTrackerCreateInfoANDROID, XR_TYPE_FACE_TRACKER_CREATE_INFO_ANDROID) \ + _avail(XrFaceStateGetInfoANDROID, XR_TYPE_FACE_STATE_GET_INFO_ANDROID) \ + _avail(XrFaceStateANDROID, XR_TYPE_FACE_STATE_ANDROID) \ + _avail(XrSystemFaceTrackingPropertiesANDROID, XR_TYPE_SYSTEM_FACE_TRACKING_PROPERTIES_ANDROID) \ + _avail(XrSystemPassthroughCameraStatePropertiesANDROID, XR_TYPE_SYSTEM_PASSTHROUGH_CAMERA_STATE_PROPERTIES_ANDROID) \ + _avail(XrPassthroughCameraStateGetInfoANDROID, XR_TYPE_PASSTHROUGH_CAMERA_STATE_GET_INFO_ANDROID) \ + _avail(XrRaycastInfoANDROID, XR_TYPE_RAYCAST_INFO_ANDROID) \ + _avail(XrRaycastHitResultsANDROID, XR_TYPE_RAYCAST_HIT_RESULTS_ANDROID) \ + _avail(XrTrackableObjectANDROID, XR_TYPE_TRACKABLE_OBJECT_ANDROID) \ + _avail(XrTrackableObjectConfigurationANDROID, XR_TYPE_TRACKABLE_OBJECT_CONFIGURATION_ANDROID) \ _avail(XrFutureCancelInfoEXT, XR_TYPE_FUTURE_CANCEL_INFO_EXT) \ _avail(XrFuturePollInfoEXT, XR_TYPE_FUTURE_POLL_INFO_EXT) \ _avail(XrFuturePollResultEXT, XR_TYPE_FUTURE_POLL_RESULT_EXT) \ @@ -428,8 +503,11 @@ This file contains expansion macros (X Macros) for OpenXR structures. _avail(XrSystemFacialExpressionPropertiesML, XR_TYPE_SYSTEM_FACIAL_EXPRESSION_PROPERTIES_ML) \ _avail(XrFacialExpressionClientCreateInfoML, XR_TYPE_FACIAL_EXPRESSION_CLIENT_CREATE_INFO_ML) \ _avail(XrFacialExpressionBlendShapeGetInfoML, XR_TYPE_FACIAL_EXPRESSION_BLEND_SHAPE_GET_INFO_ML) \ - _avail(XrFacialExpressionBlendShapePropertiesML, XR_TYPE_FACIAL_EXPRESSION_BLEND_SHAPE_PROPERTIES_ML) _avail(XrSystemSimultaneousHandsAndControllersPropertiesMETA, XR_TYPE_SYSTEM_SIMULTANEOUS_HANDS_AND_CONTROLLERS_PROPERTIES_META) _avail(XrSimultaneousHandsAndControllersTrackingResumeInfoMETA, XR_TYPE_SIMULTANEOUS_HANDS_AND_CONTROLLERS_TRACKING_RESUME_INFO_META) _avail(XrSimultaneousHandsAndControllersTrackingPauseInfoMETA, \ - XR_TYPE_SIMULTANEOUS_HANDS_AND_CONTROLLERS_TRACKING_PAUSE_INFO_META) _avail(XrColocationDiscoveryStartInfoMETA, XR_TYPE_COLOCATION_DISCOVERY_START_INFO_META) \ + _avail(XrFacialExpressionBlendShapePropertiesML, XR_TYPE_FACIAL_EXPRESSION_BLEND_SHAPE_PROPERTIES_ML) \ + _avail(XrSystemSimultaneousHandsAndControllersPropertiesMETA, XR_TYPE_SYSTEM_SIMULTANEOUS_HANDS_AND_CONTROLLERS_PROPERTIES_META) \ + _avail(XrSimultaneousHandsAndControllersTrackingResumeInfoMETA, XR_TYPE_SIMULTANEOUS_HANDS_AND_CONTROLLERS_TRACKING_RESUME_INFO_META) \ + _avail(XrSimultaneousHandsAndControllersTrackingPauseInfoMETA, XR_TYPE_SIMULTANEOUS_HANDS_AND_CONTROLLERS_TRACKING_PAUSE_INFO_META) \ + _avail(XrColocationDiscoveryStartInfoMETA, XR_TYPE_COLOCATION_DISCOVERY_START_INFO_META) \ _avail(XrColocationDiscoveryStopInfoMETA, XR_TYPE_COLOCATION_DISCOVERY_STOP_INFO_META) \ _avail(XrColocationAdvertisementStartInfoMETA, XR_TYPE_COLOCATION_ADVERTISEMENT_START_INFO_META) \ _avail(XrColocationAdvertisementStopInfoMETA, XR_TYPE_COLOCATION_ADVERTISEMENT_STOP_INFO_META) \ @@ -443,22 +521,53 @@ This file contains expansion macros (X Macros) for OpenXR structures. _avail(XrSystemColocationDiscoveryPropertiesMETA, XR_TYPE_SYSTEM_COLOCATION_DISCOVERY_PROPERTIES_META) \ _avail(XrSystemSpatialEntityGroupSharingPropertiesMETA, XR_TYPE_SYSTEM_SPATIAL_ENTITY_GROUP_SHARING_PROPERTIES_META) \ _avail(XrShareSpacesRecipientGroupsMETA, XR_TYPE_SHARE_SPACES_RECIPIENT_GROUPS_META) \ - _avail(XrSpaceGroupUuidFilterInfoMETA, XR_TYPE_SPACE_GROUP_UUID_FILTER_INFO_META) _avail(XrSpatialCapabilityComponentTypesEXT, XR_TYPE_SPATIAL_CAPABILITY_COMPONENT_TYPES_EXT) _avail(XrSpatialContextCreateInfoEXT, \ - XR_TYPE_SPATIAL_CONTEXT_CREATE_INFO_EXT) _avail(XrCreateSpatialContextCompletionEXT, XR_TYPE_CREATE_SPATIAL_CONTEXT_COMPLETION_EXT) _avail(XrSpatialDiscoverySnapshotCreateInfoEXT, \ - XR_TYPE_SPATIAL_DISCOVERY_SNAPSHOT_CREATE_INFO_EXT) _avail(XrCreateSpatialDiscoverySnapshotCompletionInfoEXT, XR_TYPE_CREATE_SPATIAL_DISCOVERY_SNAPSHOT_COMPLETION_INFO_EXT) _avail(XrCreateSpatialDiscoverySnapshotCompletionEXT, XR_TYPE_CREATE_SPATIAL_DISCOVERY_SNAPSHOT_COMPLETION_EXT) _avail(XrSpatialComponentDataQueryConditionEXT, \ - XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_CONDITION_EXT) _avail(XrSpatialComponentDataQueryResultEXT, XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_RESULT_EXT) _avail(XrSpatialBufferGetInfoEXT, XR_TYPE_SPATIAL_BUFFER_GET_INFO_EXT) _avail(XrSpatialComponentBounded2DListEXT, \ - XR_TYPE_SPATIAL_COMPONENT_BOUNDED_2D_LIST_EXT) _avail(XrSpatialComponentBounded3DListEXT, \ - XR_TYPE_SPATIAL_COMPONENT_BOUNDED_3D_LIST_EXT) _avail(XrSpatialComponentParentListEXT, \ - XR_TYPE_SPATIAL_COMPONENT_PARENT_LIST_EXT) _avail(XrSpatialComponentMesh3DListEXT, XR_TYPE_SPATIAL_COMPONENT_MESH_3D_LIST_EXT) _avail(XrSpatialEntityFromIdCreateInfoEXT, \ - XR_TYPE_SPATIAL_ENTITY_FROM_ID_CREATE_INFO_EXT) _avail(XrSpatialUpdateSnapshotCreateInfoEXT, \ - XR_TYPE_SPATIAL_UPDATE_SNAPSHOT_CREATE_INFO_EXT) _avail(XrEventDataSpatialDiscoveryRecommendedEXT, \ - XR_TYPE_EVENT_DATA_SPATIAL_DISCOVERY_RECOMMENDED_EXT) _avail(XrSpatialFilterTrackingStateEXT, \ - XR_TYPE_SPATIAL_FILTER_TRACKING_STATE_EXT) _avail(XrSpatialCapabilityConfigurationPlaneTrackingEXT, XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_PLANE_TRACKING_EXT) _avail(XrSpatialComponentPlaneAlignmentListEXT, XR_TYPE_SPATIAL_COMPONENT_PLANE_ALIGNMENT_LIST_EXT) _avail(XrSpatialComponentMesh2DListEXT, \ - XR_TYPE_SPATIAL_COMPONENT_MESH_2D_LIST_EXT) _avail(XrSpatialComponentPolygon2DListEXT, XR_TYPE_SPATIAL_COMPONENT_POLYGON_2D_LIST_EXT) _avail(XrSpatialComponentPlaneSemanticLabelListEXT, XR_TYPE_SPATIAL_COMPONENT_PLANE_SEMANTIC_LABEL_LIST_EXT) _avail(XrSpatialCapabilityConfigurationQrCodeEXT, \ - XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_QR_CODE_EXT) _avail(XrSpatialCapabilityConfigurationMicroQrCodeEXT, XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_MICRO_QR_CODE_EXT) _avail(XrSpatialCapabilityConfigurationArucoMarkerEXT, XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_ARUCO_MARKER_EXT) _avail(XrSpatialCapabilityConfigurationAprilTagEXT, XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_APRIL_TAG_EXT) _avail(XrSpatialMarkerSizeEXT, XR_TYPE_SPATIAL_MARKER_SIZE_EXT) _avail(XrSpatialMarkerStaticOptimizationEXT, XR_TYPE_SPATIAL_MARKER_STATIC_OPTIMIZATION_EXT) _avail(XrSpatialComponentMarkerListEXT, XR_TYPE_SPATIAL_COMPONENT_MARKER_LIST_EXT) _avail(XrSpatialCapabilityConfigurationAnchorEXT, \ - XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_ANCHOR_EXT) _avail(XrSpatialComponentAnchorListEXT, XR_TYPE_SPATIAL_COMPONENT_ANCHOR_LIST_EXT) _avail(XrSpatialAnchorCreateInfoEXT, XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_EXT) _avail(XrSpatialPersistenceContextCreateInfoEXT, XR_TYPE_SPATIAL_PERSISTENCE_CONTEXT_CREATE_INFO_EXT) _avail(XrCreateSpatialPersistenceContextCompletionEXT, \ - XR_TYPE_CREATE_SPATIAL_PERSISTENCE_CONTEXT_COMPLETION_EXT) _avail(XrSpatialContextPersistenceConfigEXT, XR_TYPE_SPATIAL_CONTEXT_PERSISTENCE_CONFIG_EXT) _avail(XrSpatialDiscoveryPersistenceUuidFilterEXT, XR_TYPE_SPATIAL_DISCOVERY_PERSISTENCE_UUID_FILTER_EXT) _avail(XrSpatialComponentPersistenceListEXT, XR_TYPE_SPATIAL_COMPONENT_PERSISTENCE_LIST_EXT) _avail(XrSpatialEntityPersistInfoEXT, XR_TYPE_SPATIAL_ENTITY_PERSIST_INFO_EXT) _avail(XrPersistSpatialEntityCompletionEXT, XR_TYPE_PERSIST_SPATIAL_ENTITY_COMPLETION_EXT) _avail(XrSpatialEntityUnpersistInfoEXT, XR_TYPE_SPATIAL_ENTITY_UNPERSIST_INFO_EXT) _avail(XrUnpersistSpatialEntityCompletionEXT, \ - XR_TYPE_UNPERSIST_SPATIAL_ENTITY_COMPLETION_EXT) _avail(XrEventDataMrcStatusChangedPICO, XR_TYPE_EVENT_DATA_MRC_STATUS_CHANGED_PICO) \ + _avail(XrSpaceGroupUuidFilterInfoMETA, XR_TYPE_SPACE_GROUP_UUID_FILTER_INFO_META) \ + _avail(XrSystemMarkerTrackingPropertiesANDROID, XR_TYPE_SYSTEM_MARKER_TRACKING_PROPERTIES_ANDROID) \ + _avail(XrTrackableMarkerConfigurationANDROID, XR_TYPE_TRACKABLE_MARKER_CONFIGURATION_ANDROID) \ + _avail(XrTrackableMarkerANDROID, XR_TYPE_TRACKABLE_MARKER_ANDROID) \ + _avail(XrSpatialCapabilityComponentTypesEXT, XR_TYPE_SPATIAL_CAPABILITY_COMPONENT_TYPES_EXT) \ + _avail(XrSpatialContextCreateInfoEXT, XR_TYPE_SPATIAL_CONTEXT_CREATE_INFO_EXT) \ + _avail(XrCreateSpatialContextCompletionEXT, XR_TYPE_CREATE_SPATIAL_CONTEXT_COMPLETION_EXT) \ + _avail(XrSpatialDiscoverySnapshotCreateInfoEXT, XR_TYPE_SPATIAL_DISCOVERY_SNAPSHOT_CREATE_INFO_EXT) \ + _avail(XrCreateSpatialDiscoverySnapshotCompletionInfoEXT, XR_TYPE_CREATE_SPATIAL_DISCOVERY_SNAPSHOT_COMPLETION_INFO_EXT) \ + _avail(XrCreateSpatialDiscoverySnapshotCompletionEXT, XR_TYPE_CREATE_SPATIAL_DISCOVERY_SNAPSHOT_COMPLETION_EXT) \ + _avail(XrSpatialComponentDataQueryConditionEXT, XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_CONDITION_EXT) \ + _avail(XrSpatialComponentDataQueryResultEXT, XR_TYPE_SPATIAL_COMPONENT_DATA_QUERY_RESULT_EXT) \ + _avail(XrSpatialBufferGetInfoEXT, XR_TYPE_SPATIAL_BUFFER_GET_INFO_EXT) \ + _avail(XrSpatialComponentBounded2DListEXT, XR_TYPE_SPATIAL_COMPONENT_BOUNDED_2D_LIST_EXT) \ + _avail(XrSpatialComponentBounded3DListEXT, XR_TYPE_SPATIAL_COMPONENT_BOUNDED_3D_LIST_EXT) \ + _avail(XrSpatialComponentParentListEXT, XR_TYPE_SPATIAL_COMPONENT_PARENT_LIST_EXT) \ + _avail(XrSpatialComponentMesh3DListEXT, XR_TYPE_SPATIAL_COMPONENT_MESH_3D_LIST_EXT) \ + _avail(XrSpatialEntityFromIdCreateInfoEXT, XR_TYPE_SPATIAL_ENTITY_FROM_ID_CREATE_INFO_EXT) \ + _avail(XrSpatialUpdateSnapshotCreateInfoEXT, XR_TYPE_SPATIAL_UPDATE_SNAPSHOT_CREATE_INFO_EXT) \ + _avail(XrEventDataSpatialDiscoveryRecommendedEXT, XR_TYPE_EVENT_DATA_SPATIAL_DISCOVERY_RECOMMENDED_EXT) \ + _avail(XrSpatialFilterTrackingStateEXT, XR_TYPE_SPATIAL_FILTER_TRACKING_STATE_EXT) \ + _avail(XrSpatialCapabilityConfigurationPlaneTrackingEXT, XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_PLANE_TRACKING_EXT) \ + _avail(XrSpatialComponentPlaneAlignmentListEXT, XR_TYPE_SPATIAL_COMPONENT_PLANE_ALIGNMENT_LIST_EXT) \ + _avail(XrSpatialComponentMesh2DListEXT, XR_TYPE_SPATIAL_COMPONENT_MESH_2D_LIST_EXT) \ + _avail(XrSpatialComponentPolygon2DListEXT, XR_TYPE_SPATIAL_COMPONENT_POLYGON_2D_LIST_EXT) \ + _avail(XrSpatialComponentPlaneSemanticLabelListEXT, XR_TYPE_SPATIAL_COMPONENT_PLANE_SEMANTIC_LABEL_LIST_EXT) \ + _avail(XrSpatialCapabilityConfigurationQrCodeEXT, XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_QR_CODE_EXT) \ + _avail(XrSpatialCapabilityConfigurationMicroQrCodeEXT, XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_MICRO_QR_CODE_EXT) \ + _avail(XrSpatialCapabilityConfigurationArucoMarkerEXT, XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_ARUCO_MARKER_EXT) \ + _avail(XrSpatialCapabilityConfigurationAprilTagEXT, XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_APRIL_TAG_EXT) \ + _avail(XrSpatialMarkerSizeEXT, XR_TYPE_SPATIAL_MARKER_SIZE_EXT) \ + _avail(XrSpatialMarkerStaticOptimizationEXT, XR_TYPE_SPATIAL_MARKER_STATIC_OPTIMIZATION_EXT) \ + _avail(XrSpatialComponentMarkerListEXT, XR_TYPE_SPATIAL_COMPONENT_MARKER_LIST_EXT) \ + _avail(XrSpatialCapabilityConfigurationAnchorEXT, XR_TYPE_SPATIAL_CAPABILITY_CONFIGURATION_ANCHOR_EXT) \ + _avail(XrSpatialComponentAnchorListEXT, XR_TYPE_SPATIAL_COMPONENT_ANCHOR_LIST_EXT) \ + _avail(XrSpatialAnchorCreateInfoEXT, XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_EXT) \ + _avail(XrSpatialPersistenceContextCreateInfoEXT, XR_TYPE_SPATIAL_PERSISTENCE_CONTEXT_CREATE_INFO_EXT) \ + _avail(XrCreateSpatialPersistenceContextCompletionEXT, XR_TYPE_CREATE_SPATIAL_PERSISTENCE_CONTEXT_COMPLETION_EXT) \ + _avail(XrSpatialContextPersistenceConfigEXT, XR_TYPE_SPATIAL_CONTEXT_PERSISTENCE_CONFIG_EXT) \ + _avail(XrSpatialDiscoveryPersistenceUuidFilterEXT, XR_TYPE_SPATIAL_DISCOVERY_PERSISTENCE_UUID_FILTER_EXT) \ + _avail(XrSpatialComponentPersistenceListEXT, XR_TYPE_SPATIAL_COMPONENT_PERSISTENCE_LIST_EXT) \ + _avail(XrSpatialEntityPersistInfoEXT, XR_TYPE_SPATIAL_ENTITY_PERSIST_INFO_EXT) \ + _avail(XrPersistSpatialEntityCompletionEXT, XR_TYPE_PERSIST_SPATIAL_ENTITY_COMPLETION_EXT) \ + _avail(XrSpatialEntityUnpersistInfoEXT, XR_TYPE_SPATIAL_ENTITY_UNPERSIST_INFO_EXT) \ + _avail(XrUnpersistSpatialEntityCompletionEXT, XR_TYPE_UNPERSIST_SPATIAL_ENTITY_COMPLETION_EXT) \ + _avail(XrLoaderInitInfoPropertiesEXT, XR_TYPE_LOADER_INIT_INFO_PROPERTIES_EXT) \ + _avail(XrEventDataMrcStatusChangedPICO, XR_TYPE_EVENT_DATA_MRC_STATUS_CHANGED_PICO) \ _avail(XrMrcSpaceCreateInfoPICO, XR_TYPE_MRC_SPACE_CREATE_INFO_PICO) \ _avail(XrExternalCameraParameterPICO, XR_TYPE_EXTERNAL_CAMERA_PARAMETER_PICO) \ _avail(XrVirtualBoundaryInfoPICO, XR_TYPE_VIRTUAL_BOUNDARY_INFO_PICO) \ @@ -476,9 +585,13 @@ This file contains expansion macros (X Macros) for OpenXR structures. _avail(XrCompositionLayerFisheyePICO, XR_TYPE_COMPOSITION_LAYER_FISHEYE_PICO) \ _avail(XrLayerSettingsPICO, XR_TYPE_LAYER_SETTINGS_PICO) \ _avail(XrEyeTrackerCreateInfoPICO, XR_TYPE_EYE_TRACKER_CREATE_INFO_PICO) \ + _avail(XrEyeTrackerGazeInfoPICO, XR_TYPE_EYE_TRACKER_GAZE_INFO_PICO) \ _avail(XrEyeTrackerDataInfoPICO, XR_TYPE_EYE_TRACKER_DATA_INFO_PICO) \ _avail(XrEyeDataPICO, XR_TYPE_EYE_DATA_PICO) \ _avail(XrEyeTrackerDataPICO, XR_TYPE_EYE_TRACKER_DATA_PICO) \ + _avail(XrEyeGazePICO, XR_TYPE_EYE_GAZE_PICO) \ + _avail(XrEyeTrackerGazePICO, XR_TYPE_EYE_TRACKER_GAZE_PICO) \ + _avail(XrEyeTrackerGazeDepthPICO, XR_TYPE_EYE_TRACKER_GAZE_DEPTH_PICO) \ _avail(XrSecureMrFrameworkCreateInfoPICO, XR_TYPE_SECURE_MR_FRAMEWORK_CREATE_INFO_PICO) \ _avail(XrSecureMrPipelineCreateInfoPICO, XR_TYPE_SECURE_MR_PIPELINE_CREATE_INFO_PICO) \ _avail(XrSecureMrOperatorCreateInfoPICO, XR_TYPE_SECURE_MR_OPERATOR_CREATE_INFO_PICO) \ @@ -498,6 +611,7 @@ This file contains expansion macros (X Macros) for OpenXR structures. _avail(XrSecureMrOperatorComparisonPICO, XR_TYPE_SECURE_MR_OPERATOR_COMPARISON_PICO) \ _avail(XrSecureMrOperatorSortMatrixPICO, XR_TYPE_SECURE_MR_OPERATOR_SORT_MATRIX_PICO) \ _avail(XrSecureMrOperatorColorConvertPICO, XR_TYPE_SECURE_MR_OPERATOR_COLOR_CONVERT_PICO) \ + _avail(XrSecureMrOperatorJavascriptPICO, XR_TYPE_SECURE_MR_OPERATOR_JAVASCRIPT_PICO) \ _avail(XrExpandDeviceMotorVibratePICO, XR_TYPE_EXPAND_DEVICE_MOTOR_VIBRATE_PICO) \ _avail(XrExpandDeviceCustomDataPICO, XR_TYPE_EXPAND_DEVICE_CUSTOM_DATA_PICO) \ _avail(XrExpandDeviceBatteryStatePICO, XR_TYPE_EXPAND_DEVICE_BATTERY_STATE_PICO) \ @@ -509,47 +623,97 @@ This file contains expansion macros (X Macros) for OpenXR structures. _avail(XrBodyJointVelocitiesPICO, XR_TYPE_BODY_JOINT_VELOCITIES_PICO) \ _avail(XrBodyJointAccelerationsPICO, XR_TYPE_BODY_JOINT_ACCELERATIONS_PICO) \ _avail(XrBodyTrackingStatePICO, XR_TYPE_BODY_TRACKING_STATE_PICO) \ - _avail(XrSymmetricFovReprojectionViewConfigurationPICO, XR_TYPE_SYMMETRIC_FOV_REPROJECTION_VIEW_CONFIGURATION_PICO) _avail(XrSystemDynamicObjectTrackingPropertiesPICO, \ - XR_TYPE_SYSTEM_DYNAMIC_OBJECT_TRACKING_PROPERTIES_PICO) _avail(XrSenseDataProviderCreateInfoDynamicObjectPICO, XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_DYNAMIC_OBJECT_PICO) _avail(XrSpatialEntityDynamicObjectGetInfoPICO, \ - XR_TYPE_SPATIAL_ENTITY_DYNAMIC_OBJECT_GET_INFO_PICO) _avail(XrDynamicObjectDataPICO, \ - XR_TYPE_DYNAMIC_OBJECT_DATA_PICO) _avail(XrSpatialEntityComponentDataDynamicObjectPICO, XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_DYNAMIC_OBJECT_PICO) _avail(XrSenseDataFilterDynamicObjectTypePICO, \ - XR_TYPE_SENSE_DATA_FILTER_DYNAMIC_OBJECT_TYPE_PICO) _avail(XrSystemDynamicObjectKeyboardPropertiesPICO, \ - XR_TYPE_SYSTEM_DYNAMIC_OBJECT_KEYBOARD_PROPERTIES_PICO) _avail(XrSystemDynamicObjectMousePropertiesPICO, \ - XR_TYPE_SYSTEM_DYNAMIC_OBJECT_MOUSE_PROPERTIES_PICO) _avail(XrLayerColorMatrixPICO, XR_TYPE_LAYER_COLOR_MATRIX_PICO) _avail(XrSystemSpatialSensingPropertiesPICO, \ - XR_TYPE_SYSTEM_SPATIAL_SENSING_PROPERTIES_PICO) _avail(XrSpatialEntityLocationGetInfoPICO, XR_TYPE_SPATIAL_ENTITY_LOCATION_GET_INFO_PICO) _avail(XrSpatialEntityLocationDataPICO, \ - XR_TYPE_SPATIAL_ENTITY_LOCATION_DATA_PICO) _avail(XrSpatialEntitySemanticGetInfoPICO, XR_TYPE_SPATIAL_ENTITY_SEMANTIC_GET_INFO_PICO) _avail(XrSpatialEntitySemanticDataPICO, XR_TYPE_SPATIAL_ENTITY_SEMANTIC_DATA_PICO) _avail(XrSpatialEntityBoundingBox2DGetInfoPICO, \ - XR_TYPE_SPATIAL_ENTITY_BOUNDING_BOX_2D_GET_INFO_PICO) _avail(XrSpatialEntityBoundingBox2DDataPICO, \ - XR_TYPE_SPATIAL_ENTITY_BOUNDING_BOX_2D_DATA_PICO) \ - _avail(XrSpatialEntityPolygonGetInfoPICO, XR_TYPE_SPATIAL_ENTITY_POLYGON_GET_INFO_PICO) _avail(XrSpatialEntityPolygonDataPICO, XR_TYPE_SPATIAL_ENTITY_POLYGON_DATA_PICO) _avail(XrSpatialEntityBoundingBox3DGetInfoPICO, XR_TYPE_SPATIAL_ENTITY_BOUNDING_BOX_3D_GET_INFO_PICO) _avail(XrSpatialEntityBoundingBox3DDataPICO, XR_TYPE_SPATIAL_ENTITY_BOUNDING_BOX_3D_DATA_PICO) _avail(XrSpatialEntityTriangleMeshGetInfoPICO, \ - XR_TYPE_SPATIAL_ENTITY_TRIANGLE_MESH_GET_INFO_PICO) _avail(XrSpatialEntityTriangleMeshDataPICO, XR_TYPE_SPATIAL_ENTITY_TRIANGLE_MESH_DATA_PICO) _avail(XrSpatialEntitySphereGetInfoPICO, \ - XR_TYPE_SPATIAL_ENTITY_SPHERE_GET_INFO_PICO) _avail(XrSpatialEntityComponentDataSpherePICO, \ - XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_SPHERE_PICO) \ - _avail(XrSenseDataProviderStartInfoPICO, XR_TYPE_SENSE_DATA_PROVIDER_START_INFO_PICO) _avail(XrSenseDataProviderStartCompletionPICO, XR_TYPE_SENSE_DATA_PROVIDER_START_COMPLETION_PICO) _avail(XrEventDataSenseDataProviderStateChangedPICO, XR_TYPE_EVENT_DATA_SENSE_DATA_PROVIDER_STATE_CHANGED_PICO) _avail(XrEventDataSenseDataUpdatedPICO, \ - XR_TYPE_EVENT_DATA_SENSE_DATA_UPDATED_PICO) _avail(XrSenseDataQueryInfoPICO, \ - XR_TYPE_SENSE_DATA_QUERY_INFO_PICO) _avail(XrSenseDataQueryCompletionPICO, \ - XR_TYPE_SENSE_DATA_QUERY_COMPLETION_PICO) _avail(XrQueriedSenseDataGetInfoPICO, \ - XR_TYPE_QUERIED_SENSE_DATA_GET_INFO_PICO) _avail(XrSpatialEntityStatePICO, XR_TYPE_SPATIAL_ENTITY_STATE_PICO) _avail(XrQueriedSenseDataPICO, \ - XR_TYPE_QUERIED_SENSE_DATA_PICO) _avail(XrSenseDataFilterUuidPICO, XR_TYPE_SENSE_DATA_FILTER_UUID_PICO) _avail(XrSenseDataFilterSemanticPICO, \ - XR_TYPE_SENSE_DATA_FILTER_SEMANTIC_PICO) _avail(XrSpatialEntityAnchorRetrieveInfoPICO, XR_TYPE_SPATIAL_ENTITY_ANCHOR_RETRIEVE_INFO_PICO) _avail(XrAnchorLocateInfoPICO, XR_TYPE_ANCHOR_LOCATE_INFO_PICO) _avail(XrFuturePollResultProgressPICO, XR_TYPE_FUTURE_POLL_RESULT_PROGRESS_PICO) _avail(XrSystemSpatialAnchorPropertiesPICO, \ - XR_TYPE_SYSTEM_SPATIAL_ANCHOR_PROPERTIES_PICO) _avail(XrSenseDataProviderCreateInfoSpatialAnchorPICO, XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SPATIAL_ANCHOR_PICO) _avail(XrSpatialAnchorCreateInfoPICO, XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_PICO) _avail(XrSpatialAnchorCreateCompletionPICO, XR_TYPE_SPATIAL_ANCHOR_CREATE_COMPLETION_PICO) _avail(XrSpatialAnchorPersistInfoPICO, XR_TYPE_SPATIAL_ANCHOR_PERSIST_INFO_PICO) _avail(XrSpatialAnchorPersistCompletionPICO, XR_TYPE_SPATIAL_ANCHOR_PERSIST_COMPLETION_PICO) _avail(XrSpatialAnchorUnpersistInfoPICO, \ - XR_TYPE_SPATIAL_ANCHOR_UNPERSIST_INFO_PICO) _avail(XrSpatialAnchorUnpersistCompletionPICO, \ - XR_TYPE_SPATIAL_ANCHOR_UNPERSIST_COMPLETION_PICO) _avail(XrSystemSpatialAnchorSharingPropertiesPICO, \ - XR_TYPE_SYSTEM_SPATIAL_ANCHOR_SHARING_PROPERTIES_PICO) _avail(XrSpatialAnchorShareInfoPICO, XR_TYPE_SPATIAL_ANCHOR_SHARE_INFO_PICO) _avail(XrSpatialAnchorShareCompletionPICO, \ - XR_TYPE_SPATIAL_ANCHOR_SHARE_COMPLETION_PICO) _avail(XrSharedSpatialAnchorDownloadInfoPICO, XR_TYPE_SPATIAL_ANCHOR_DOWNLOAD_INFO_PICO) _avail(XrSharedSpatialAnchorDownloadCompletionPICO, \ - XR_TYPE_SPATIAL_ANCHOR_DOWNLOAD_COMPLETION_PICO) \ - _avail(XrSystemSceneCapturePropertiesPICO, XR_TYPE_SYSTEM_SCENE_CAPTURE_PROPERTIES_PICO) _avail( \ - XrSenseDataProviderCreateInfoSceneCapturePICO, \ - XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SCENE_CAPTURE_PICO) _avail(XrSceneCaptureStartInfoPICO, \ - XR_TYPE_SCENE_CAPTURE_START_INFO_PICO) \ - _avail( \ - XrSceneCaptureStartCompletionPICO, \ - XR_TYPE_SCENE_CAPTURE_START_COMPLETION_PICO) \ - _avail( \ - XrSystemSpatialMeshPropertiesPICO, \ - XR_TYPE_SYSTEM_SPATIAL_MESH_PROPERTIES_PICO) \ - _avail( \ - XrSenseDataProviderCreateInfoSpatialMeshPICO, \ - XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SPATIAL_MESH_PICO) + _avail(XrSymmetricFovReprojectionViewConfigurationPICO, XR_TYPE_SYMMETRIC_FOV_REPROJECTION_VIEW_CONFIGURATION_PICO) \ + _avail(XrSystemDynamicObjectTrackingPropertiesPICO, XR_TYPE_SYSTEM_DYNAMIC_OBJECT_TRACKING_PROPERTIES_PICO) \ + _avail(XrSenseDataProviderCreateInfoDynamicObjectPICO, XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_DYNAMIC_OBJECT_PICO) \ + _avail(XrSpatialEntityDynamicObjectGetInfoPICO, XR_TYPE_SPATIAL_ENTITY_DYNAMIC_OBJECT_GET_INFO_PICO) \ + _avail(XrDynamicObjectDataPICO, XR_TYPE_DYNAMIC_OBJECT_DATA_PICO) \ + _avail(XrSpatialEntityComponentDataDynamicObjectPICO, XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_DYNAMIC_OBJECT_PICO) \ + _avail(XrSenseDataFilterDynamicObjectTypePICO, XR_TYPE_SENSE_DATA_FILTER_DYNAMIC_OBJECT_TYPE_PICO) \ + _avail(XrSystemDynamicObjectKeyboardPropertiesPICO, XR_TYPE_SYSTEM_DYNAMIC_OBJECT_KEYBOARD_PROPERTIES_PICO) \ + _avail(XrSystemDynamicObjectMousePropertiesPICO, XR_TYPE_SYSTEM_DYNAMIC_OBJECT_MOUSE_PROPERTIES_PICO) \ + _avail(XrLayerColorMatrixPICO, XR_TYPE_LAYER_COLOR_MATRIX_PICO) \ + _avail(XrReadbackTensorBufferPICO, XR_TYPE_READBACK_TENSOR_BUFFER_PICO) \ + _avail(XrCreateBufferFromGlobalTensorCompletionPICO, XR_TYPE_CREATE_BUFFER_FROM_GLOBAL_TENSOR_COMPLETION_PICO) \ + _avail(XrCreateTextureFromGlobalTensorCompletionPICO, XR_TYPE_CREATE_TEXTURE_FROM_GLOBAL_TENSOR_COMPLETION_PICO) \ + _avail(XrCameraPropertiesPICO, XR_TYPE_CAMERA_PROPERTIES_PICO) \ + _avail(XrCameraCapabilitiesPICO, XR_TYPE_CAMERA_CAPABILITIES_PICO) \ + _avail(XrAvailableCamerasEnumerateInfoPICO, XR_TYPE_AVAILABLE_CAMERAS_ENUMERATE_INFO_PICO) \ + _avail(XrCameraPropertiesGetInfoPICO, XR_TYPE_CAMERA_PROPERTIES_GET_INFO_PICO) \ + _avail(XrCameraPropertyFacingPICO, XR_TYPE_CAMERA_PROPERTY_FACING_PICO) \ + _avail(XrCameraPropertyPositionPICO, XR_TYPE_CAMERA_PROPERTY_POSITION_PICO) \ + _avail(XrCameraPropertyCameraTypePICO, XR_TYPE_CAMERA_PROPERTY_CAMERA_TYPE_PICO) \ + _avail(XrCameraSupportedCapabilitiesGetInfoPICO, XR_TYPE_CAMERA_SUPPORTED_CAPABILITIES_GET_INFO_PICO) \ + _avail(XrCameraSupportedCapabilitiesPICO, XR_TYPE_CAMERA_SUPPORTED_CAPABILITIES_PICO) \ + _avail(XrCameraSupportedCapabilityImageResolutionPICO, XR_TYPE_CAMERA_SUPPORTED_CAPABILITY_IMAGE_RESOLUTION_PICO) \ + _avail(XrCameraCapabilityImageResolutionPICO, XR_TYPE_CAMERA_CAPABILITY_IMAGE_RESOLUTION_PICO) \ + _avail(XrCameraSupportedCapabilityDataTransferTypePICO, XR_TYPE_CAMERA_SUPPORTED_CAPABILITY_DATA_TRANSFER_TYPE_PICO) \ + _avail(XrCameraCapabilityDataTransferTypePICO, XR_TYPE_CAMERA_CAPABILITY_DATA_TRANSFER_TYPE_PICO) \ + _avail(XrCameraSupportedCapabilityImageFormatPICO, XR_TYPE_CAMERA_SUPPORTED_CAPABILITY_IMAGE_FORMAT_PICO) \ + _avail(XrCameraCapabilityImageFormatPICO, XR_TYPE_CAMERA_CAPABILITY_IMAGE_FORMAT_PICO) \ + _avail(XrCameraSupportedCapabilityCameraModelPICO, XR_TYPE_CAMERA_SUPPORTED_CAPABILITY_CAMERA_MODEL_PICO) \ + _avail(XrCameraCapabilityCameraModelPICO, XR_TYPE_CAMERA_CAPABILITY_CAMERA_MODEL_PICO) \ + _avail(XrCameraSupportedCapabilityImageFpsPICO, XR_TYPE_CAMERA_SUPPORTED_CAPABILITY_IMAGE_FPS_PICO) \ + _avail(XrCameraCapabilityImageFpsPICO, XR_TYPE_CAMERA_CAPABILITY_IMAGE_FPS_PICO) \ + _avail(XrCameraDeviceCreateInfoPICO, XR_TYPE_CAMERA_DEVICE_CREATE_INFO_PICO) \ + _avail(XrCreateCameraDeviceCompletionPICO, XR_TYPE_CREATE_CAMERA_DEVICE_COMPLETION_PICO) \ + _avail(XrCameraCaptureSessionCreateInfoPICO, XR_TYPE_CAMERA_CAPTURE_SESSION_CREATE_INFO_PICO) \ + _avail(XrCreateCameraCaptureSessionCompletionPICO, XR_TYPE_CREATE_CAMERA_CAPTURE_SESSION_COMPLETION_PICO) \ + _avail(XrCameraIntrinsicsPICO, XR_TYPE_CAMERA_INTRINSICS_PICO) \ + _avail(XrCameraExtrinsicsPICO, XR_TYPE_CAMERA_EXTRINSICS_PICO) \ + _avail(XrCameraCaptureBeginInfoPICO, XR_TYPE_CAMERA_CAPTURE_BEGIN_INFO_PICO) \ + _avail(XrCameraImageAcquireInfoPICO, XR_TYPE_CAMERA_IMAGE_ACQUIRE_INFO_PICO) \ + _avail(XrCameraImagePICO, XR_TYPE_CAMERA_IMAGE_PICO) \ + _avail(XrCameraImageDataRawBufferPICO, XR_TYPE_CAMERA_IMAGE_DATA_RAW_BUFFER_PICO) \ + _avail(XrSystemSpatialSensingPropertiesPICO, XR_TYPE_SYSTEM_SPATIAL_SENSING_PROPERTIES_PICO) \ + _avail(XrSpatialEntityLocationGetInfoPICO, XR_TYPE_SPATIAL_ENTITY_LOCATION_GET_INFO_PICO) \ + _avail(XrSpatialEntityLocationDataPICO, XR_TYPE_SPATIAL_ENTITY_LOCATION_DATA_PICO) \ + _avail(XrSpatialEntitySemanticGetInfoPICO, XR_TYPE_SPATIAL_ENTITY_SEMANTIC_GET_INFO_PICO) \ + _avail(XrSpatialEntitySemanticDataPICO, XR_TYPE_SPATIAL_ENTITY_SEMANTIC_DATA_PICO) \ + _avail(XrSpatialEntityBoundingBox2DGetInfoPICO, XR_TYPE_SPATIAL_ENTITY_BOUNDING_BOX_2D_GET_INFO_PICO) \ + _avail(XrSpatialEntityBoundingBox2DDataPICO, XR_TYPE_SPATIAL_ENTITY_BOUNDING_BOX_2D_DATA_PICO) \ + _avail(XrSpatialEntityPolygonGetInfoPICO, XR_TYPE_SPATIAL_ENTITY_POLYGON_GET_INFO_PICO) \ + _avail(XrSpatialEntityPolygonDataPICO, XR_TYPE_SPATIAL_ENTITY_POLYGON_DATA_PICO) \ + _avail(XrSpatialEntityBoundingBox3DGetInfoPICO, XR_TYPE_SPATIAL_ENTITY_BOUNDING_BOX_3D_GET_INFO_PICO) \ + _avail(XrSpatialEntityBoundingBox3DDataPICO, XR_TYPE_SPATIAL_ENTITY_BOUNDING_BOX_3D_DATA_PICO) \ + _avail(XrSpatialEntityTriangleMeshGetInfoPICO, XR_TYPE_SPATIAL_ENTITY_TRIANGLE_MESH_GET_INFO_PICO) \ + _avail(XrSpatialEntityTriangleMeshDataPICO, XR_TYPE_SPATIAL_ENTITY_TRIANGLE_MESH_DATA_PICO) \ + _avail(XrSpatialEntitySphereGetInfoPICO, XR_TYPE_SPATIAL_ENTITY_SPHERE_GET_INFO_PICO) \ + _avail(XrSpatialEntityComponentDataSpherePICO, XR_TYPE_SPATIAL_ENTITY_COMPONENT_DATA_SPHERE_PICO) \ + _avail(XrSenseDataProviderStartInfoPICO, XR_TYPE_SENSE_DATA_PROVIDER_START_INFO_PICO) \ + _avail(XrSenseDataProviderStartCompletionPICO, XR_TYPE_SENSE_DATA_PROVIDER_START_COMPLETION_PICO) \ + _avail(XrEventDataSenseDataProviderStateChangedPICO, XR_TYPE_EVENT_DATA_SENSE_DATA_PROVIDER_STATE_CHANGED_PICO) \ + _avail(XrEventDataSenseDataUpdatedPICO, XR_TYPE_EVENT_DATA_SENSE_DATA_UPDATED_PICO) \ + _avail(XrSenseDataQueryInfoPICO, XR_TYPE_SENSE_DATA_QUERY_INFO_PICO) \ + _avail(XrSenseDataQueryCompletionPICO, XR_TYPE_SENSE_DATA_QUERY_COMPLETION_PICO) \ + _avail(XrQueriedSenseDataGetInfoPICO, XR_TYPE_QUERIED_SENSE_DATA_GET_INFO_PICO) \ + _avail(XrSpatialEntityStatePICO, XR_TYPE_SPATIAL_ENTITY_STATE_PICO) \ + _avail(XrQueriedSenseDataPICO, XR_TYPE_QUERIED_SENSE_DATA_PICO) \ + _avail(XrSenseDataFilterUuidPICO, XR_TYPE_SENSE_DATA_FILTER_UUID_PICO) \ + _avail(XrSenseDataFilterSemanticPICO, XR_TYPE_SENSE_DATA_FILTER_SEMANTIC_PICO) \ + _avail(XrSpatialEntityAnchorRetrieveInfoPICO, XR_TYPE_SPATIAL_ENTITY_ANCHOR_RETRIEVE_INFO_PICO) \ + _avail(XrAnchorLocateInfoPICO, XR_TYPE_ANCHOR_LOCATE_INFO_PICO) \ + _avail(XrFuturePollResultProgressPICO, XR_TYPE_FUTURE_POLL_RESULT_PROGRESS_PICO) \ + _avail(XrSystemSpatialAnchorPropertiesPICO, XR_TYPE_SYSTEM_SPATIAL_ANCHOR_PROPERTIES_PICO) \ + _avail(XrSenseDataProviderCreateInfoSpatialAnchorPICO, XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SPATIAL_ANCHOR_PICO) \ + _avail(XrSpatialAnchorCreateInfoPICO, XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_PICO) \ + _avail(XrSpatialAnchorCreateCompletionPICO, XR_TYPE_SPATIAL_ANCHOR_CREATE_COMPLETION_PICO) \ + _avail(XrSpatialAnchorPersistInfoPICO, XR_TYPE_SPATIAL_ANCHOR_PERSIST_INFO_PICO) \ + _avail(XrSpatialAnchorPersistCompletionPICO, XR_TYPE_SPATIAL_ANCHOR_PERSIST_COMPLETION_PICO) \ + _avail(XrSpatialAnchorUnpersistInfoPICO, XR_TYPE_SPATIAL_ANCHOR_UNPERSIST_INFO_PICO) \ + _avail(XrSpatialAnchorUnpersistCompletionPICO, XR_TYPE_SPATIAL_ANCHOR_UNPERSIST_COMPLETION_PICO) \ + _avail(XrSystemSpatialAnchorSharingPropertiesPICO, XR_TYPE_SYSTEM_SPATIAL_ANCHOR_SHARING_PROPERTIES_PICO) \ + _avail(XrSpatialAnchorShareInfoPICO, XR_TYPE_SPATIAL_ANCHOR_SHARE_INFO_PICO) \ + _avail(XrSpatialAnchorShareCompletionPICO, XR_TYPE_SPATIAL_ANCHOR_SHARE_COMPLETION_PICO) \ + _avail(XrSharedSpatialAnchorDownloadInfoPICO, XR_TYPE_SHARED_SPATIAL_ANCHOR_DOWNLOAD_INFO_PICO) \ + _avail(XrSharedSpatialAnchorDownloadCompletionPICO, XR_TYPE_SHARED_SPATIAL_ANCHOR_DOWNLOAD_COMPLETION_PICO) \ + _avail(XrSystemSceneCapturePropertiesPICO, XR_TYPE_SYSTEM_SCENE_CAPTURE_PROPERTIES_PICO) \ + _avail(XrSenseDataProviderCreateInfoSceneCapturePICO, XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SCENE_CAPTURE_PICO) \ + _avail(XrSceneCaptureStartInfoPICO, XR_TYPE_SCENE_CAPTURE_START_INFO_PICO) \ + _avail(XrSceneCaptureStartCompletionPICO, XR_TYPE_SCENE_CAPTURE_START_COMPLETION_PICO) \ + _avail(XrSystemSpatialMeshPropertiesPICO, XR_TYPE_SYSTEM_SPATIAL_MESH_PROPERTIES_PICO) \ + _avail(XrSenseDataProviderCreateInfoSpatialMeshPICO, XR_TYPE_SENSE_DATA_PROVIDER_CREATE_INFO_SPATIAL_MESH_PICO) \ + #if defined(XR_USE_GRAPHICS_API_D3D11) #define _impl_XR_LIST_ALL_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_D3D11(_avail, _unavail) \ @@ -650,12 +814,14 @@ This file contains expansion macros (X Macros) for OpenXR structures. _avail(XrSwapchainImageOpenGLESKHR, XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR) \ _avail(XrGraphicsRequirementsOpenGLESKHR, XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_ES_KHR) \ _avail(XrSwapchainStateSamplerOpenGLESFB, XR_TYPE_SWAPCHAIN_STATE_SAMPLER_OPENGL_ES_FB) \ + _avail(XrReadbackTextureImageOpenGLPICO, XR_TYPE_READBACK_TEXTURE_IMAGE_OPENGL_PICO) \ #else #define _impl_XR_LIST_ALL_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_OPENGL_ES(_avail, _unavail) \ _unavail(XrSwapchainImageOpenGLESKHR, XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR) \ _unavail(XrGraphicsRequirementsOpenGLESKHR, XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_ES_KHR) \ _unavail(XrSwapchainStateSamplerOpenGLESFB, XR_TYPE_SWAPCHAIN_STATE_SAMPLER_OPENGL_ES_FB) \ + _unavail(XrReadbackTextureImageOpenGLPICO, XR_TYPE_READBACK_TEXTURE_IMAGE_OPENGL_PICO) \ #endif @@ -681,6 +847,7 @@ This file contains expansion macros (X Macros) for OpenXR structures. _avail(XrSwapchainImageFoveationVulkanFB, XR_TYPE_SWAPCHAIN_IMAGE_FOVEATION_VULKAN_FB) \ _avail(XrSwapchainStateSamplerVulkanFB, XR_TYPE_SWAPCHAIN_STATE_SAMPLER_VULKAN_FB) \ _avail(XrVulkanSwapchainCreateInfoMETA, XR_TYPE_VULKAN_SWAPCHAIN_CREATE_INFO_META) \ + _avail(XrReadbackTextureImageVulkanPICO, XR_TYPE_READBACK_TEXTURE_IMAGE_VULKAN_PICO) \ #else #define _impl_XR_LIST_ALL_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_VULKAN(_avail, _unavail) \ @@ -694,6 +861,7 @@ This file contains expansion macros (X Macros) for OpenXR structures. _unavail(XrSwapchainImageFoveationVulkanFB, XR_TYPE_SWAPCHAIN_IMAGE_FOVEATION_VULKAN_FB) \ _unavail(XrSwapchainStateSamplerVulkanFB, XR_TYPE_SWAPCHAIN_STATE_SAMPLER_VULKAN_FB) \ _unavail(XrVulkanSwapchainCreateInfoMETA, XR_TYPE_VULKAN_SWAPCHAIN_CREATE_INFO_META) \ + _unavail(XrReadbackTextureImageVulkanPICO, XR_TYPE_READBACK_TEXTURE_IMAGE_VULKAN_PICO) \ #endif @@ -703,6 +871,9 @@ This file contains expansion macros (X Macros) for OpenXR structures. _avail(XrLoaderInitInfoAndroidKHR, XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR) \ _avail(XrAndroidSurfaceSwapchainCreateInfoFB, XR_TYPE_ANDROID_SURFACE_SWAPCHAIN_CREATE_INFO_FB) \ _avail(XrSwapchainStateAndroidSurfaceDimensionsFB, XR_TYPE_SWAPCHAIN_STATE_ANDROID_SURFACE_DIMENSIONS_FB) \ + _avail(XrAnchorSharingInfoANDROID, XR_TYPE_ANCHOR_SHARING_INFO_ANDROID) \ + _avail(XrAnchorSharingTokenANDROID, XR_TYPE_ANCHOR_SHARING_TOKEN_ANDROID) \ + _avail(XrSystemAnchorSharingExportPropertiesANDROID, XR_TYPE_SYSTEM_ANCHOR_SHARING_EXPORT_PROPERTIES_ANDROID) \ #else #define _impl_XR_LIST_ALL_STRUCTURE_TYPES_XR_USE_PLATFORM_ANDROID(_avail, _unavail) \ @@ -710,6 +881,9 @@ This file contains expansion macros (X Macros) for OpenXR structures. _unavail(XrLoaderInitInfoAndroidKHR, XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR) \ _unavail(XrAndroidSurfaceSwapchainCreateInfoFB, XR_TYPE_ANDROID_SURFACE_SWAPCHAIN_CREATE_INFO_FB) \ _unavail(XrSwapchainStateAndroidSurfaceDimensionsFB, XR_TYPE_SWAPCHAIN_STATE_ANDROID_SURFACE_DIMENSIONS_FB) \ + _unavail(XrAnchorSharingInfoANDROID, XR_TYPE_ANCHOR_SHARING_INFO_ANDROID) \ + _unavail(XrAnchorSharingTokenANDROID, XR_TYPE_ANCHOR_SHARING_TOKEN_ANDROID) \ + _unavail(XrSystemAnchorSharingExportPropertiesANDROID, XR_TYPE_SYSTEM_ANCHOR_SHARING_EXPORT_PROPERTIES_ANDROID) \ #endif diff --git a/gradle.properties b/gradle.properties index d1148b7..ca684b2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1,2 @@ -org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 \ No newline at end of file +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +android.useAndroidX=true \ No newline at end of file diff --git a/samples/mnistwild/AndroidManifest.xml b/samples/mnistwild/AndroidManifest.xml new file mode 100644 index 0000000..2f785d8 --- /dev/null +++ b/samples/mnistwild/AndroidManifest.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/mnistwild/CMakeLists.txt b/samples/mnistwild/CMakeLists.txt new file mode 100644 index 0000000..c459668 --- /dev/null +++ b/samples/mnistwild/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.22) +project(mnistwild) + +set(SAMPLE_SRCS ${CMAKE_CURRENT_LIST_DIR}/cpp/mnistwild.cpp) +set(SAMPLE_DIR ${CMAKE_CURRENT_LIST_DIR}/cpp) +set(USE_SECURE_MR_UTILS ON) +# set(READBACK_USE_OPENGL ON) + +include(../../base/base.cmake) + +set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 17) +set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD_REQUIRED ON) diff --git a/samples/mnistwild/README.md b/samples/mnistwild/README.md new file mode 100644 index 0000000..12d855a --- /dev/null +++ b/samples/mnistwild/README.md @@ -0,0 +1,75 @@ +## Sample: MNIST Recognition + +This sample builds a SecureMR application that recognizes hand-written digits that appear in the +VST camera feed. Unlike the other samples, both the preprocessing and inference pipelines are +described entirely in JSON ([mnist_pipeline.json](../../assets/mnistwild/mnist_pipeline.json)) and deserialized at runtime to create the +SecureMR pipeline graph. + +The experience renders three virtual TV in front of the user. The cropped digit, its predicted class, +and the confidence score are displayed on the screen in real time. + +![](../../assets/mnistwild/mnist_app.png) + +### Assets + +- Model: `mnist.serialized.bin` (generated from the classic MNIST network) +- Pipeline spec: `mnist_pipeline.json` +- Display mesh: `tv.gltf` + +All assets are packaged under `assets/mnistwild` and loaded through Android's `AAssetManager`. + +### Runtime Flow + +1. **Framework setup** + `MnistWildApp` spins up a `FrameworkSession`, creates required global tensors, and loads the JSON + pipeline specification plus GLTF assets on a background thread. + +2. **Inference pipeline (JSON)** + The `mnist_pipeline.json` specification expands to the following operator chain: + - `RECTIFIED_VST_ACCESS` acquires synchronized left/right RGB frames, timestamps, and camera parameters. + - `GET_AFFINE` produces a perspective transform from hard-coded source/destination points so that the + drawing area is cropped from the high-resolution VST frame. + - `APPLY_AFFINE` crops the digit region (`crop_rgb_tensor`), after which + `CONVERT_COLOR` converts it to grayscale. + - Two `ASSIGNMENT` operators duplicate buffers for later use: one keeps the RGB crop for rendering, + the other prepares a float copy. + - `ARITHMETIC_COMPOSE` normalizes the grayscale image to `[0, 1]` by dividing by 255. + - `RUN_MODEL_INFERENCE` feeds the normalized tensor into the MNIST model and produces + `predicted_score` (confidence) and `predicted_class` (digit index). + + Tensors marked as placeholders in the JSON (`cropped_image`, `predicted_score`, `predicted_class`) + are bound to global tensors so the app can read the results directly after each submission. + +3. **Render pipeline (C++)** + A native pipeline (`CreateRenderPipeline`) maps the inference outputs onto the GLTF TV: + - Updates dynamic textures so the RGB crop appears on the TV screen. + - Draws the predicted class and score using `RenderCommand_DrawText`. + - Positions three GLTF instances (digit text, score text, TV) via pose tensors to keep the + visualization fixed in front of the user. + +4. **Execution loop** + Two worker threads call `RunInferencePipeline` (~20 Hz) and `RunRenderPipeline` (~25 Hz) once all + pipelines finish initializing. + +### How to Build and Run + +1. **Build and install** + Connect your PICO 4 Ultra device and run: + ```bash + ./gradlew :samples:mnistwild:installDebug + ``` + +2. **Run the app** + Launch the "MnistWild" app from the PICO launcher. The app will request permissions for the VST camera; grant them to start the recognition pipeline. + +### Customizing the JSON Pipeline + +- Adjust `src_points` in the JSON if the digit capture area shifts in the physical world. +- Update `mnist.serialized.bin` for a retrained model; the binding names in the JSON must match the + ONNX/TensorRT export (`input_1`, `_538`, `_539` in the default export). +- Additional preprocessing steps (e.g., blurring, adaptive thresholding) can be inserted by editing + the JSON without touching the C++ side, as long as the placeholder tensors remain consistent. + +Because the JSON is deserialized at runtime, developers can iterate on pipeline topology and operators +without recompiling the sample; copying an updated `mnist_pipeline.json` to the writable path is +enough to override the bundled asset. diff --git a/samples/mnistwild/build.gradle.kts b/samples/mnistwild/build.gradle.kts new file mode 100644 index 0000000..aaa0158 --- /dev/null +++ b/samples/mnistwild/build.gradle.kts @@ -0,0 +1,65 @@ +plugins { + id("com.android.application") + id("org.jetbrains.kotlin.android") +} +android { + compileSdk = 34 + ndkVersion = "26.3.11579264" + namespace = "com.bytedance.pico.secure_mr_demo.mnistwild" + defaultConfig { + minSdk = 34 + targetSdk = 34 + versionCode = 1 + versionName = "1.0" + applicationId = "com.bytedance.pico.secure_mr_demo.mnistwild" + externalNativeBuild { + cmake { + arguments.add("-DANDROID_STL=c++_shared") + arguments.add("-DANDROID_USE_LEGACY_TOOLCHAIN_FILE=OFF") + } + ndk { + abiFilters.add("arm64-v8a") + } + } + } + lint { + disable.add("ExpiredTargetSdkVersion") + } + buildFeatures { + prefab = true + } + buildTypes { + getByName("debug") { + isDebuggable = true + isJniDebuggable = true + } + getByName("release") { + isDebuggable = false + isJniDebuggable = false + } + } + externalNativeBuild { + cmake { + version = "3.22.1" + path("CMakeLists.txt") + } + } + sourceSets { + getByName("main") { + manifest.srcFile("AndroidManifest.xml") + assets.srcDirs("../../assets/common", "../../assets/mnistwild") + java.srcDirs("src/main/java") + } + } + packaging { + jniLibs { + keepDebugSymbols.add("**.so") + } + } + kotlinOptions { + jvmTarget = "1.8" + } +} +dependencies { + implementation("androidx.appcompat:appcompat:1.7.0") +} diff --git a/samples/mnistwild/cpp/mnistwild.cpp b/samples/mnistwild/cpp/mnistwild.cpp new file mode 100644 index 0000000..3ae2cbd --- /dev/null +++ b/samples/mnistwild/cpp/mnistwild.cpp @@ -0,0 +1,464 @@ +#include "mnistwild.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "securemr_utils/serialization.h" + +extern AAssetManager* g_assetManager; +extern std::string g_internalDataPath; + +using json = SecureMR::Json; + +namespace SecureMR { +namespace { +constexpr std::array kCropSrcPoints{1444.0F, 1332.0F, 2045.0F, 1332.0F, 2045.0F, 1933.0F}; +constexpr std::array kCropDstPoints{0.0F, 0.0F, static_cast(MnistWildApp::kCropWidth), 0.0F, + static_cast(MnistWildApp::kCropWidth), + static_cast(MnistWildApp::kCropHeight)}; +constexpr int kCvColorRgb2Gray = 7; // Matches cv::COLOR_RGB2GRAY + +constexpr char kInferencePipelineJson[] = "mnist_pipeline.json"; +constexpr char kTensorPredictedClass[] = "predicted_class"; +constexpr char kTensorPredictedScore[] = "predicted_score"; +constexpr char kTensorCropImage[] = "cropped_image"; + +std::filesystem::path ResolveWritablePath(const std::string& fileName) { + if (!g_internalDataPath.empty()) { + return std::filesystem::path(g_internalDataPath) / fileName; + } + return {}; +} + +std::shared_ptr MakeScalarTensor(const std::shared_ptr& pipeline, float value) { + auto tensor = std::make_shared( + pipeline, TensorAttribute{.dimensions = {1}, + .channels = 1, + .usage = XR_SECURE_MR_TENSOR_TYPE_SCALAR_PICO, + .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}); + tensor->setData(reinterpret_cast(&value), sizeof(float)); + return tensor; +} + +std::shared_ptr MakeScalarTensor(const std::shared_ptr& pipeline, uint16_t value) { + auto tensor = std::make_shared( + pipeline, TensorAttribute{.dimensions = {1}, + .channels = 1, + .usage = XR_SECURE_MR_TENSOR_TYPE_SCALAR_PICO, + .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_UINT16_PICO}); + tensor->setData(reinterpret_cast(&value), sizeof(uint16_t)); + return tensor; +} + +std::shared_ptr MakeScalarTensor(const std::shared_ptr& pipeline, uint8_t value) { + auto tensor = std::make_shared( + pipeline, TensorAttribute{.dimensions = {1}, + .channels = 1, + .usage = XR_SECURE_MR_TENSOR_TYPE_SCALAR_PICO, + .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_UINT8_PICO}); + tensor->setData(reinterpret_cast(&value), sizeof(uint8_t)); + return tensor; +} + +std::shared_ptr MakePointTensor(const std::shared_ptr& pipeline, const std::array& p) { + auto tensor = std::make_shared( + pipeline, TensorAttribute{.dimensions = {1}, + .channels = 2, + .usage = XR_SECURE_MR_TENSOR_TYPE_POINT_PICO, + .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}); + tensor->setData(reinterpret_cast(const_cast(p.data())), sizeof(float) * 2); + return tensor; +} + +std::shared_ptr MakeColorTensor(const std::shared_ptr& pipeline, + const std::array& rgbaPair) { + auto tensor = std::make_shared( + pipeline, TensorAttribute{.dimensions = {2}, + .channels = 4, + .usage = XR_SECURE_MR_TENSOR_TYPE_COLOR_PICO, + .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_UINT8_PICO}); + tensor->setData(reinterpret_cast(const_cast(rgbaPair.data())), rgbaPair.size()); + return tensor; +} + +std::shared_ptr MakePoseTensor(const std::shared_ptr& pipeline, + const std::array& mat) { + auto tensor = std::make_shared( + pipeline, TensorAttribute{.dimensions = {4, 4}, + .channels = 1, + .usage = XR_SECURE_MR_TENSOR_TYPE_MAT_PICO, + .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}); + tensor->setData(reinterpret_cast(const_cast(mat.data())), sizeof(float) * mat.size()); + return tensor; +} +} // namespace + +MnistWildApp::MnistWildApp(const XrInstance& instance, const XrSession& session) + : xr_instance(instance), xr_session(session) {} + +MnistWildApp::~MnistWildApp() { + if (croppedImageReadback_) { + croppedImageReadback_->Stop(); + } + keepRunning = false; + initCv.notify_all(); + if (pipelineInitializer && pipelineInitializer->joinable()) { + pipelineInitializer->join(); + } + for (auto& runner : pipelineRunners) { + if (runner.joinable()) { + runner.join(); + } + } +} + +bool MnistWildApp::LoadAsset(const std::string& filePath, std::vector& data) const { + if (g_assetManager == nullptr) { + Log::Write(Log::Level::Error, "LoadAsset failed: AssetManager not available"); + return false; + } + AAsset* asset = AAssetManager_open(g_assetManager, filePath.c_str(), AASSET_MODE_BUFFER); + if (asset == nullptr) { + Log::Write(Log::Level::Error, Fmt("LoadAsset failed: unable to open %s", filePath.c_str())); + return false; + } + const off_t length = AAsset_getLength(asset); + data.resize(static_cast(length)); + const int64_t read = AAsset_read(asset, data.data(), length); + AAsset_close(asset); + if (read != length) { + Log::Write(Log::Level::Error, Fmt("LoadAsset failed: read %ld of %ld bytes", static_cast(read), + static_cast(length))); + data.clear(); + return false; + } + return true; +} + +bool MnistWildApp::DeserializeInferencePipeline(const std::filesystem::path& jsonPath) { + auto finalizeResult = [&](PipelineDeserializationResult&& result) -> bool { + inferencePipeline = std::move(result.pipeline); + try { + predClassPlaceholder = result.tensorMap.at(kTensorPredictedClass); + predScorePlaceholder = result.tensorMap.at(kTensorPredictedScore); + cropImagePlaceholder = result.tensorMap.at(kTensorCropImage); + } catch (const std::exception& e) { + Log::Write(Log::Level::Error, + Fmt("DeserializeInferencePipeline failed: required placeholder missing (%s)", e.what())); + return false; + } + return true; + }; + + auto tryDeserialize = [&](const json& spec, const std::string& source) -> bool { + if (!spec.is_object()) { + Log::Write(Log::Level::Warning, + Fmt("DeserializeInferencePipeline failed (%s): JSON root is not an object", source.c_str())); + return false; + } + PipelineDeserializationResult result; + std::string error; + if (!DeserializePipelineFromJson(spec, frameworkSession, result, error)) { + Log::Write(Log::Level::Warning, + Fmt("DeserializeInferencePipeline failed (%s): %s", source.c_str(), + error.empty() ? "unknown error" : error.c_str())); + return false; + } + return finalizeResult(std::move(result)); + }; + + if (!jsonPath.empty()) { + const auto pathStr = jsonPath.string(); + const json specFromFile = LoadJsonFromFile(jsonPath); + if (tryDeserialize(specFromFile, pathStr)) { + return true; + } + Log::Write(Log::Level::Info, Fmt("Falling back to asset pipeline spec after failure reading %s", pathStr.c_str())); + } + + std::vector assetBuffer; + if (!LoadAsset(kInferencePipelineJson, assetBuffer)) { + Log::Write(Log::Level::Error, + Fmt("DeserializeInferencePipeline failed: unable to load asset %s", kInferencePipelineJson)); + return false; + } + + try { + const json assetSpec = json::parse(assetBuffer.begin(), assetBuffer.end()); + return tryDeserialize(assetSpec, Fmt("asset:%s", kInferencePipelineJson)); + } catch (const std::exception& e) { + Log::Write(Log::Level::Error, + Fmt("DeserializeInferencePipeline failed: cannot parse asset %s (%s)", kInferencePipelineJson, + e.what())); + return false; + } +} + +void MnistWildApp::CreateFramework() { + Log::Write(Log::Level::Info, "CreateFramework ..."); + frameworkSession = std::make_shared(xr_instance, xr_session, kImageWidth, kImageHeight); + Log::Write(Log::Level::Info, "CreateFramework done."); +} + +void MnistWildApp::CreatePipelines() { + pipelineInitializer = std::make_unique([this]() { + CreateGlobalTensors(); + CreateInferencePipeline(); + CreateRenderPipeline(); + CreateReadback(); + { + std::lock_guard lk(initMutex); + pipelinesReady = true; + } + initCv.notify_all(); + }); +} + +void MnistWildApp::RunPipelines() { + pipelineRunners.emplace_back([this]() { + std::unique_lock lk(initMutex); + initCv.wait(lk, [this]() { return pipelinesReady || !keepRunning; }); + lk.unlock(); + while (keepRunning) { + RunInferencePipeline(); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + }); + + pipelineRunners.emplace_back([this]() { + std::unique_lock lk(initMutex); + initCv.wait(lk, [this]() { return pipelinesReady || !keepRunning; }); + lk.unlock(); + while (keepRunning) { + RunRenderPipeline(); + std::this_thread::sleep_for(std::chrono::milliseconds(40)); + } + }); +} + +void MnistWildApp::CreateGlobalTensors() { + Log::Write(Log::Level::Info, "Creating global tensors ..."); + + predictedClassGlobal = std::make_shared( + frameworkSession, TensorAttribute_ScalarArray{.size = 1, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_INT32_PICO}); + int32_t defaultClass = -1; + predictedClassGlobal->setData(reinterpret_cast(&defaultClass), sizeof(defaultClass)); + + predictedScoreGlobal = std::make_shared( + frameworkSession, TensorAttribute_ScalarArray{.size = 1, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}); + float zeroScore = 0.0F; + predictedScoreGlobal->setData(reinterpret_cast(&zeroScore), sizeof(zeroScore)); + + croppedImageGlobal = std::make_shared( + frameworkSession, + TensorAttribute{.dimensions = {kCropHeight, kCropWidth}, + .channels = 3, + .usage = XR_SECURE_MR_TENSOR_TYPE_MAT_DYNAMIC_TEXTURE_PICO, + .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_DYNAMIC_TEXTURE_UINT8_PICO}); + std::vector blankImage(static_cast(kCropHeight * kCropWidth * 3), 0); + croppedImageGlobal->setData(reinterpret_cast(blankImage.data()), blankImage.size()); + + std::vector gltfData; + if (LoadAsset("tv.gltf", gltfData)) { + gltfClassAsset = std::make_shared(frameworkSession, gltfData.data(), gltfData.size()); + gltfScoreAsset = std::make_shared(frameworkSession, gltfData.data(), gltfData.size()); + gltfImageAsset = std::make_shared(frameworkSession, gltfData.data(), gltfData.size()); + } else { + Log::Write(Log::Level::Error, "Failed to load tv.gltf"); + } + + if (!LoadAsset("mnist.serialized.bin", mnistModelBuffer)) { + Log::Write(Log::Level::Error, "Failed to load mnist.serialized.bin"); + } + + Log::Write(Log::Level::Info, "Global tensors ready."); +} + +void MnistWildApp::CreateInferencePipeline() { + Log::Write(Log::Level::Info, "Creating inference pipeline ..."); + + const std::filesystem::path jsonPath = ResolveWritablePath(kInferencePipelineJson); + if (!DeserializeInferencePipeline(jsonPath)) { + const std::string pathStr = jsonPath.empty() ? "" : jsonPath.string(); + Log::Write(Log::Level::Error, + Fmt("Failed to load inference pipeline from %s", pathStr.c_str())); + } + + Log::Write(Log::Level::Info, "Inference pipeline ready."); +} + +void MnistWildApp::CreateRenderPipeline() { + Log::Write(Log::Level::Info, "Creating render pipeline ..."); + renderPipeline = std::make_shared(frameworkSession); + + renderClassPlaceholder = PipelineTensor::PipelinePlaceholderLike(renderPipeline, predictedClassGlobal); + renderScorePlaceholder = PipelineTensor::PipelinePlaceholderLike(renderPipeline, predictedScoreGlobal); + renderCropPlaceholder = PipelineTensor::PipelinePlaceholderLike(renderPipeline, croppedImageGlobal); + renderClassGltfPlaceholder = PipelineTensor::PipelineGLTFPlaceholder(renderPipeline); + renderScoreGltfPlaceholder = PipelineTensor::PipelineGLTFPlaceholder(renderPipeline); + renderImageGltfPlaceholder = PipelineTensor::PipelineGLTFPlaceholder(renderPipeline); + + auto digitTextStart = MakePointTensor(renderPipeline, {0.1F, 0.3F}); + auto scoreTextStart = MakePointTensor(renderPipeline, {0.1F, 0.3F}); + auto textColors = MakeColorTensor(renderPipeline, {255, 255, 255, 255, 0, 0, 0, 255}); + auto textTextureIdClass = MakeScalarTensor(renderPipeline, static_cast(0)); + auto textTextureIdScore = MakeScalarTensor(renderPipeline, static_cast(0)); + auto fontSizeDigit = MakeScalarTensor(renderPipeline, 144.0F); + auto fontSizeScore = MakeScalarTensor(renderPipeline, 144.0F); + + auto newTextureId = std::make_shared( + renderPipeline, + TensorAttribute{.dimensions = {1}, + .channels = 1, + .usage = XR_SECURE_MR_TENSOR_TYPE_SCALAR_PICO, + .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_UINT16_PICO}); + + auto classPose = MakePoseTensor(renderPipeline, + {0.5F, 0.0F, 0.0F, -0.5F, + 0.0F, 0.5F, 0.0F, 0.0F, + 0.0F, 0.0F, 0.5F, -1.5F, + 0.0F, 0.0F, 0.0F, 1.0F}); + auto scorePose = MakePoseTensor(renderPipeline, + {0.5F, 0.0F, 0.0F, 0.5F, + 0.0F, 0.5F, 0.0F, 0.0F, + 0.0F, 0.0F, 0.5F, -1.5F, + 0.0F, 0.0F, 0.0F, 1.0F}); + auto imagePose = MakePoseTensor(renderPipeline, + {0.5F, 0.0F, 0.0F, 0.0F, + 0.0F, 0.5F, 0.0F, 1.0F, + 0.0F, 0.0F, 0.5F, -1.5F, + 0.0F, 0.0F, 0.0F, 1.0F}); + auto visibleTensor = MakeScalarTensor(renderPipeline, static_cast(1)); + + (*renderPipeline) + .execRenderCommand(std::make_shared(renderClassGltfPlaceholder, "en-US", + RenderCommand_DrawText::TypeFaceTypes::SANS_SERIF, + 1440, 960, renderClassPlaceholder, digitTextStart, + fontSizeDigit, textColors, textTextureIdClass)) + .execRenderCommand(std::make_shared(renderScoreGltfPlaceholder, "en-US", + RenderCommand_DrawText::TypeFaceTypes::SANS_SERIF, + 1440, 960, renderScorePlaceholder, scoreTextStart, + fontSizeScore, textColors, textTextureIdScore)) + .newTextureToGLTF(renderImageGltfPlaceholder, renderCropPlaceholder, newTextureId); + + auto updateMaterialCmd = std::make_shared(); + updateMaterialCmd->gltfTensor = renderImageGltfPlaceholder; + updateMaterialCmd->materialIds = std::vector{0}; + updateMaterialCmd->attribute = RenderCommand_UpdateMaterial::MaterialAttribute::TEXTURE_BASE_COLOR; + updateMaterialCmd->materialValues = newTextureId; + (*renderPipeline).execRenderCommand(updateMaterialCmd); + + auto renderClassCmd = std::make_shared(renderClassGltfPlaceholder, classPose); + renderClassCmd->visible = visibleTensor; + (*renderPipeline).execRenderCommand(renderClassCmd); + + auto renderScoreCmd = std::make_shared(renderScoreGltfPlaceholder, scorePose); + renderScoreCmd->visible = visibleTensor; + (*renderPipeline).execRenderCommand(renderScoreCmd); + + auto renderImageCmd = std::make_shared(renderImageGltfPlaceholder, imagePose); + renderImageCmd->visible = visibleTensor; + (*renderPipeline).execRenderCommand(renderImageCmd); + + Log::Write(Log::Level::Info, "Render pipeline ready."); +} + +void MnistWildApp::CreateReadback() { + if (!frameworkSession || !croppedImageGlobal) { + Log::Write(Log::Level::Error, "CreateReadback skipped: framework session or cropped image tensor missing"); + return; + } + if (croppedImageReadback_) { + return; + } + + Log::Write(Log::Level::Info, "Initializing TensorReadback for vst_output_left_u8 target"); + + TensorReadback::Target target{ + .tensor = croppedImageGlobal, + .callback = [this](TensorReadbackResult&& result) { HandleReadbackResult(std::move(result)); }, + .name = "crop_image"}; + + TensorReadback::Config config{.pollingInterval = std::chrono::milliseconds(33)}; + croppedImageReadback_ = std::make_unique(frameworkSession, std::vector{std::move(target)}, config); + croppedImageReadback_->Start(); +} + +void MnistWildApp::HandleReadbackResult(TensorReadbackResult&& result) { + if (result.data.empty()) { + Log::Write(Log::Level::Info, + Fmt("TensorReadback for %.*s returned empty buffer", + static_cast(result.name.size()), result.name.data())); + return; + } + + std::string dims = "unknown"; + if (!result.dimensions.empty()) { + dims = std::to_string(result.dimensions[0]); + for (std::size_t i = 1; i < result.dimensions.size(); ++i) { + dims.append("x").append(std::to_string(result.dimensions[i])); + } + } + + Log::Write(Log::Level::Info, + Fmt("TensorReadback for %.*s: dataSize=%zu bytes (dims=%s, channels=%d, dataType=%d)", + static_cast(result.name.size()), result.name.data(), + result.data.size(), dims.c_str(), result.channels, + static_cast(result.dataType))); +} + +void MnistWildApp::RunInferencePipeline() { + if (!inferencePipeline) { + return; + } + inferencePipeline->submit({{predClassPlaceholder, predictedClassGlobal}, + {predScorePlaceholder, predictedScoreGlobal}, + {cropImagePlaceholder, croppedImageGlobal}}, + XR_NULL_HANDLE, nullptr); +} + +void MnistWildApp::RunRenderPipeline() { + if (!renderPipeline || gltfClassAsset == nullptr || gltfScoreAsset == nullptr || gltfImageAsset == nullptr) { + return; + } + renderPipeline->submit({{renderClassPlaceholder, predictedClassGlobal}, + {renderScorePlaceholder, predictedScoreGlobal}, + {renderCropPlaceholder, croppedImageGlobal}, + {renderClassGltfPlaceholder, gltfClassAsset}, + {renderScoreGltfPlaceholder, gltfScoreAsset}, + {renderImageGltfPlaceholder, gltfImageAsset}}, + XR_NULL_HANDLE, nullptr); +} + +void MnistWildApp::RequestPermission(struct android_app* app) { + Log::Write(Log::Level::Info, "Requesting camera permission from activity"); + if (app == nullptr || app->activity == nullptr || app->activity->vm == nullptr) { + Log::Write(Log::Level::Error, "Invalid android_app passed to RequestPermission"); + return; + } + + JNIEnv* env = nullptr; + app->activity->vm->AttachCurrentThread(&env, nullptr); + if (env == nullptr) { + Log::Write(Log::Level::Error, "Failed to attach JNI environment while requesting permission"); + return; + } + jobject activity = app->activity->clazz; + jclass cls = env->GetObjectClass(activity); + jmethodID requestCamera = env->GetMethodID(cls, "requestCameraFromNative", "()V"); + env->CallVoidMethod(activity, requestCamera); +} + +std::shared_ptr CreateSecureMrProgram(const XrInstance& instance, const XrSession& session) { + return std::make_shared(instance, session); +} + +} // namespace SecureMR diff --git a/samples/mnistwild/cpp/mnistwild.h b/samples/mnistwild/cpp/mnistwild.h new file mode 100644 index 0000000..136d2fa --- /dev/null +++ b/samples/mnistwild/cpp/mnistwild.h @@ -0,0 +1,91 @@ +#pragma once + +#include "pch.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "logger.h" +#include "common.h" +#include "securemr_base.h" +#include "securemr_utils/adapter.hpp" +#include "securemr_utils/pipeline.h" +#include "securemr_utils/readback_async.h" +#include "securemr_utils/rendercommand.h" +#include "securemr_utils/session.h" +#include "securemr_utils/tensor.h" + +namespace SecureMR { + +class MnistWildApp : public ISecureMR { + public: + static constexpr int kImageWidth = 3248; + static constexpr int kImageHeight = 2464; + static constexpr int kCropWidth = 224; + static constexpr int kCropHeight = 224; + + MnistWildApp(const XrInstance& instance, const XrSession& session); + ~MnistWildApp() override; + + void CreateFramework() override; + void CreatePipelines() override; + void RunPipelines() override; + void RequestPermission(struct android_app* app) override; + [[nodiscard]] bool LoadingFinished() const override { return pipelinesReady; } + + private: + void CreateGlobalTensors(); + void CreateInferencePipeline(); + void CreateRenderPipeline(); + void CreateReadback(); + void HandleReadbackResult(TensorReadbackResult&& result); + void RunInferencePipeline(); + void RunRenderPipeline(); + bool LoadAsset(const std::string& filePath, std::vector& data) const; + bool DeserializeInferencePipeline(const std::filesystem::path& jsonPath); + + XrInstance xr_instance; + XrSession xr_session; + + std::shared_ptr frameworkSession; + std::vector mnistModelBuffer; + + std::shared_ptr predictedClassGlobal; + std::shared_ptr predictedScoreGlobal; + std::shared_ptr croppedImageGlobal; + std::shared_ptr gltfClassAsset; + std::shared_ptr gltfScoreAsset; + std::shared_ptr gltfImageAsset; + + std::shared_ptr inferencePipeline; + std::shared_ptr renderPipeline; + + std::shared_ptr predClassPlaceholder; + std::shared_ptr predScorePlaceholder; + std::shared_ptr cropImagePlaceholder; + + std::shared_ptr renderClassPlaceholder; + std::shared_ptr renderScorePlaceholder; + std::shared_ptr renderCropPlaceholder; + std::shared_ptr renderClassGltfPlaceholder; + std::shared_ptr renderScoreGltfPlaceholder; + std::shared_ptr renderImageGltfPlaceholder; + std::unique_ptr croppedImageReadback_; + + std::unique_ptr pipelineInitializer; + std::vector pipelineRunners; + mutable std::mutex initMutex; + std::condition_variable initCv; + bool pipelinesReady = false; + bool keepRunning = true; +}; + +std::shared_ptr CreateSecureMrProgram(const XrInstance& instance, const XrSession& session); + +} // namespace SecureMR diff --git a/samples/mnistwild/src/main/java/com/bytedance/pico/secure_mr_demo/mnistwild/MnistWildActivity.java b/samples/mnistwild/src/main/java/com/bytedance/pico/secure_mr_demo/mnistwild/MnistWildActivity.java new file mode 100644 index 0000000..04fa7ca --- /dev/null +++ b/samples/mnistwild/src/main/java/com/bytedance/pico/secure_mr_demo/mnistwild/MnistWildActivity.java @@ -0,0 +1,32 @@ +package com.bytedance.pico.secure_mr_demo.mnistwild; + +import android.Manifest; +import android.app.NativeActivity; +import android.content.pm.PackageManager; + +public class MnistWildActivity extends NativeActivity { + + static { + System.loadLibrary("mnistwild"); + } + + private static final int REQ_CAMERA = 1001; + + public void requestCameraFromNative() { + if (checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { + requestPermissions(new String[]{Manifest.permission.CAMERA}, REQ_CAMERA); + } else { +// nativeOnPermissionResult(REQ_CAMERA, +// new String[]{Manifest.permission.CAMERA}, +// new int[]{PackageManager.PERMISSION_GRANTED}); + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); +// nativeOnPermissionResult(requestCode, permissions, grantResults); + } + +// private static native void nativeOnPermissionResult(int rc, String[] perms, int[] grants); +} diff --git a/samples/model_inspect/AndroidManifest.xml b/samples/model_inspect/AndroidManifest.xml new file mode 100644 index 0000000..8ae7673 --- /dev/null +++ b/samples/model_inspect/AndroidManifest.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/model_inspect/CMakeLists.txt b/samples/model_inspect/CMakeLists.txt new file mode 100644 index 0000000..dd7d70e --- /dev/null +++ b/samples/model_inspect/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.22) +project(model_inspect) + +set(SAMPLE_SRCS ${CMAKE_CURRENT_LIST_DIR}/cpp/model_inspect.cpp) +set(SAMPLE_DIR ${CMAKE_CURRENT_LIST_DIR}/cpp) +set(USE_SECURE_MR_UTILS ON) + +include(../../base/base.cmake) + +set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 17) +set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD_REQUIRED ON) diff --git a/samples/model_inspect/README.md b/samples/model_inspect/README.md new file mode 100644 index 0000000..746b68a --- /dev/null +++ b/samples/model_inspect/README.md @@ -0,0 +1,35 @@ +# Sample: Model Inspect Tool + +This sample runs a single SecureMR model inference to validate a serialized model on device. It is primarily used as a diagnostic tool for checking model compatibility and performance on PICO 4 Ultra (OS 5.15+). + +## Prerequisites + +- PICO 4 Ultra with OS version >= 5.15.0 +- Serialized SecureMR model files (.bin and .json) + +## Usage + +1. Push your model files to the device and set the properties before launching the app: + +``` +adb push model.serialized.bin /data/local/tmp/my_model/ +adb push mode.serialized.json /data/local/tmp/my_model/ # falls back to model.serialized.json if needed +adb shell setprop debug.securemr.model_inspect.model_dir /data/local/tmp/my_model + +# Optional input file applied to all model inputs (random data is generated if omitted) +adb push input.bin /data/local/tmp/my_model/input.bin +adb shell setprop debug.securemr.model_inspect.input /data/local/tmp/my_model/input.bin +``` + +2. Build and install: + +``` +./gradlew :samples:model_inspect:installDebug +``` + +3. Launch the app. Logs will describe the parsed tensors, input loading/generation, pipeline submission, and readback. + +## Outputs + +- Outputs are read back via the SecureMR readback API and written under the app's external files directory (for example `/sdcard/Android/data/com.bytedance.pico.secure_mr_demo.model_inspect/files/model_inspect/`) as `model_inspect_output_.bin`. +- Logcat will print a short preview of each output along with the tensor shape and data type for quick verification. diff --git a/samples/model_inspect/build.gradle.kts b/samples/model_inspect/build.gradle.kts new file mode 100644 index 0000000..436267d --- /dev/null +++ b/samples/model_inspect/build.gradle.kts @@ -0,0 +1,65 @@ +plugins { + id("com.android.application") + id("org.jetbrains.kotlin.android") +} + +android { + compileSdk = 34 + ndkVersion = "26.3.11579264" + namespace = "com.bytedance.pico.secure_mr_demo.model_inspect" + defaultConfig { + minSdk = 34 + targetSdk = 34 + versionCode = 1 + versionName = "1.0" + applicationId = "com.bytedance.pico.secure_mr_demo.model_inspect" + externalNativeBuild { + cmake { + arguments.add("-DANDROID_STL=c++_shared") + arguments.add("-DANDROID_USE_LEGACY_TOOLCHAIN_FILE=OFF") + } + ndk { + abiFilters.add("arm64-v8a") + } + } + } + lint { + disable.add("ExpiredTargetSdkVersion") + } + buildFeatures { + prefab = true + } + buildTypes { + getByName("debug") { + isDebuggable = true + isJniDebuggable = true + } + getByName("release") { + isDebuggable = false + isJniDebuggable = false + } + } + externalNativeBuild { + cmake { + version = "3.22.1" + path("CMakeLists.txt") + } + } + sourceSets { + getByName("main") { + manifest.srcFile("AndroidManifest.xml") + assets.srcDirs("../../assets/common") + } + } + packaging { + jniLibs { + keepDebugSymbols.add("**.so") + } + } + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { +} diff --git a/samples/model_inspect/cpp/model_inspect.cpp b/samples/model_inspect/cpp/model_inspect.cpp new file mode 100644 index 0000000..2eac32f --- /dev/null +++ b/samples/model_inspect/cpp/model_inspect.cpp @@ -0,0 +1,834 @@ +// Copyright (2025) Bytedance Ltd. and/or its affiliates +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "pch.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "common.h" +#include "logger.h" +#include "securemr_base.h" +#include "securemr_utils/pipeline.h" +#include "securemr_utils/readback_async.h" +#include "securemr_utils/serialization.h" +#include "securemr_utils/session.h" +#include "securemr_utils/tensor.h" +#include "securemr_utils/utils.h" +#include "openxr_program.h" + +extern std::string g_internalDataPath; + +namespace SecureMR { +namespace { + +constexpr char kModelDirProp[] = "debug.securemr.model_inspect.model_dir"; +constexpr char kInputFileProp[] = "debug.securemr.model_inspect.input"; +constexpr char kModelBinName[] = "model.serialized.bin"; +constexpr char kModelJsonName[] = "model.serialized.json"; + +std::string ReadProp(const char* key) { + char value[PROP_VALUE_MAX] = {}; + if (__system_property_get(key, value) == 0) { + return {}; + } + return std::string(value); +} + +std::optional GetExternalFilesDir() { + android_app* app = IOpenXrProgram::gapp; + if (app == nullptr || app->activity == nullptr || app->activity->vm == nullptr || app->activity->clazz == nullptr) { + Log::Write(Log::Level::Warning, "ModelInspect: android_app not ready; cannot query external files dir"); + return std::nullopt; + } + JNIEnv* env = nullptr; + if (app->activity->vm->AttachCurrentThread(&env, nullptr) != JNI_OK || env == nullptr) { + Log::Write(Log::Level::Warning, "ModelInspect: failed to attach JNI env; cannot query external files dir"); + return std::nullopt; + } + + jclass activityCls = env->GetObjectClass(app->activity->clazz); + if (activityCls == nullptr) { + Log::Write(Log::Level::Warning, "ModelInspect: failed to get Activity class"); + return std::nullopt; + } + + jmethodID midGetExternalFilesDir = + env->GetMethodID(activityCls, "getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;"); + if (midGetExternalFilesDir == nullptr) { + Log::Write(Log::Level::Warning, "ModelInspect: getExternalFilesDir method not found"); + env->DeleteLocalRef(activityCls); + return std::nullopt; + } + + jobject fileObj = env->CallObjectMethod(app->activity->clazz, midGetExternalFilesDir, nullptr); + if (fileObj == nullptr) { + Log::Write(Log::Level::Warning, "ModelInspect: getExternalFilesDir returned null"); + env->DeleteLocalRef(activityCls); + return std::nullopt; + } + + jclass fileCls = env->GetObjectClass(fileObj); + jmethodID midGetAbsolutePath = env->GetMethodID(fileCls, "getAbsolutePath", "()Ljava/lang/String;"); + if (midGetAbsolutePath == nullptr) { + Log::Write(Log::Level::Warning, "ModelInspect: getAbsolutePath not found on File"); + env->DeleteLocalRef(fileCls); + env->DeleteLocalRef(fileObj); + env->DeleteLocalRef(activityCls); + return std::nullopt; + } + + jstring pathStr = static_cast(env->CallObjectMethod(fileObj, midGetAbsolutePath)); + std::optional result; + if (pathStr != nullptr) { + const char* pathChars = env->GetStringUTFChars(pathStr, nullptr); + if (pathChars != nullptr) { + result = std::filesystem::path(pathChars); + env->ReleaseStringUTFChars(pathStr, pathChars); + } + env->DeleteLocalRef(pathStr); + } + + env->DeleteLocalRef(fileCls); + env->DeleteLocalRef(fileObj); + env->DeleteLocalRef(activityCls); + if (!result.has_value()) { + Log::Write(Log::Level::Warning, "ModelInspect: external files dir path unavailable"); + } + return result; +} +} // namespace + +class ModelInspectApp : public ISecureMR { + public: + ModelInspectApp(const XrInstance& instance, const XrSession& session) + : xrInstance_(instance), xrSession_(session) {} + + ~ModelInspectApp() override { + if (readback_) { + readback_->Stop(); + } + if (runThread_ && runThread_->joinable()) { + runThread_->join(); + } + if (initThread_ && initThread_->joinable()) { + initThread_->join(); + } + } + + void CreateFramework() override { + Log::Write(Log::Level::Info, "ModelInspect: creating framework session"); + frameworkSession_ = std::make_shared(xrInstance_, xrSession_, 640, 480); + // Prefer external app-specific storage so dumps are readable without root. + const auto externalDir = GetExternalFilesDir(); + const std::filesystem::path preferredOutputDir = + externalDir.has_value() ? *externalDir / "model_inspect" : std::filesystem::path(); + const std::filesystem::path fallbackOutputDir("/data/local/tmp/securemr_model_inspect"); + std::error_code ec; + if (externalDir.has_value()) { + outputDir_ = preferredOutputDir; + std::filesystem::create_directories(outputDir_, ec); + if (ec) { + Log::Write(Log::Level::Warning, + Fmt("ModelInspect: failed to ensure output dir %s (%s), falling back to %s", + outputDir_.string().c_str(), ec.message().c_str(), fallbackOutputDir.string().c_str())); + } + } + if (!externalDir.has_value() || ec) { + outputDir_ = fallbackOutputDir; + ec.clear(); + std::filesystem::create_directories(outputDir_, ec); + } + if (ec) { + Log::Write(Log::Level::Warning, + Fmt("ModelInspect: failed to ensure fallback output dir %s (%s)", + outputDir_.string().c_str(), ec.message().c_str())); + } else { + Log::Write(Log::Level::Info, Fmt("ModelInspect: output dir %s", outputDir_.string().c_str())); + } + } + + void CreatePipelines() override { + initThread_ = std::make_unique([this]() { Initialize(); }); + } + + void RunPipelines() override { + runThread_ = std::make_unique([this]() { + std::unique_lock lock(initMutex_); + initCv_.wait(lock, [this]() { return initFinished_; }); + if (!pipelineReady_ || !pipeline_) { + Log::Write(Log::Level::Error, "ModelInspect: initialization failed, skip inference run"); + return; + } + lock.unlock(); + + Log::Write(Log::Level::Info, "ModelInspect: starting readback"); + if (readback_) { + readback_->Start(); + } + + LogPlaceholderMappings(); + Log::Write(Log::Level::Info, "ModelInspect: submitting inference pipeline"); + pipeline_->submit(placeholderMap_, XR_NULL_HANDLE, nullptr); + + { + std::unique_lock rbLock(readbackMutex_); + if (!readbackCv_.wait_for(rbLock, std::chrono::seconds(5), + [this]() { return readbackDone_.load(std::memory_order_acquire); })) { + Log::Write(Log::Level::Warning, "ModelInspect: readback did not complete within timeout"); + } + } + }); + } + + void LogPlaceholderMappings() { + Log::Write(Log::Level::Info, + Fmt("ModelInspect: submitting with %zu placeholder mappings", placeholderMap_.size())); + for (const auto& entry : placeholderMap_) { + const auto placeholderAttr = entry.first->getAttribute(); + const auto globalAttr = entry.second->getAttribute(); + std::string placeholderDesc = std::holds_alternative(placeholderAttr) + ? DataTypeName(std::get(placeholderAttr).dataType) + : "GLTF"; + std::string globalDesc = std::holds_alternative(globalAttr) + ? DataTypeName(std::get(globalAttr).dataType) + : "GLTF"; + Log::Write(Log::Level::Info, + Fmt(" placeholder=%p (%s) -> global=%p (%s)", static_cast(entry.first.get()), + placeholderDesc.c_str(), static_cast(entry.second.get()), globalDesc.c_str())); + } + } + + void Tick() override {} + + [[nodiscard]] bool LoadingFinished() const override { return initFinished_; } + + private: + void Initialize() { + const std::string modelDirProp = ReadProp(kModelDirProp); + const std::string inputProp = ReadProp(kInputFileProp); + Log::Write(Log::Level::Info, Fmt("ModelInspect: %s=%s", kModelDirProp, modelDirProp.c_str())); + Log::Write(Log::Level::Info, Fmt("ModelInspect: %s=%s", kInputFileProp, inputProp.c_str())); + + if (modelDirProp.empty()) { + Log::Write(Log::Level::Error, "ModelInspect: model_dir property is empty; aborting initialization"); + FinishInit(false); + return; + } + + const std::filesystem::path modelDir(modelDirProp); + const std::filesystem::path binPath = modelDir / kModelBinName; + std::filesystem::path jsonPath = modelDir / kModelJsonName; + + if (!std::filesystem::exists(binPath)) { + Log::Write(Log::Level::Error, Fmt("ModelInspect: missing %s", binPath.string().c_str())); + FinishInit(false); + return; + } + if (!std::filesystem::exists(jsonPath)) { + Log::Write(Log::Level::Error, Fmt("ModelInspect: missing %s", jsonPath.string().c_str())); + FinishInit(false); + return; + } + + auto jsonSpec = SecureMrUtils::LoadModelJson(jsonPath); + if (!jsonSpec.has_value()) { + FinishInit(false); + return; + } + + if (!SecureMrUtils::PrepareBindings(*jsonSpec, inputBindings_, outputBindings_, modelName_)) { + FinishInit(false); + return; + } + + if (!LoadModel(binPath)) { + FinishInit(false); + return; + } + + if (!SetupGlobals(inputProp)) { + FinishInit(false); + return; + } + + CreatePipeline(); + SetupReadback(); + pipelineReady_ = true; + FinishInit(true); + } + + bool LoadModel(const std::filesystem::path& binPath) { + std::ifstream ifs(binPath, std::ios::binary | std::ios::ate); + if (!ifs) { + Log::Write(Log::Level::Error, Fmt("ModelInspect: failed to open %s", binPath.string().c_str())); + return false; + } + std::streamsize size = ifs.tellg(); + ifs.seekg(0, std::ios::beg); + modelBuffer_.resize(static_cast(size)); + if (!ifs.read(reinterpret_cast(modelBuffer_.data()), size)) { + Log::Write(Log::Level::Error, Fmt("ModelInspect: failed to read %s", binPath.string().c_str())); + modelBuffer_.clear(); + return false; + } + Log::Write(Log::Level::Info, Fmt("ModelInspect: loaded model %s (%zu bytes)", binPath.string().c_str(), + modelBuffer_.size())); + return true; + } + + bool SetupGlobals(const std::string& inputPath) { + if (!frameworkSession_) { + Log::Write(Log::Level::Error, "ModelInspect: framework session not ready"); + return false; + } + + for (auto& binding : inputBindings_) { + binding.global = std::make_shared(frameworkSession_, binding.attr); + LogBindingInfo("input", binding); + if (!LoadInputData(binding, inputPath)) { + return false; + } + } + for (auto& binding : outputBindings_) { + binding.global = std::make_shared(frameworkSession_, binding.attr); + LogBindingInfo("output", binding); + const size_t bytes = + SecureMrUtils::ElementCount(binding.attr) * SecureMrUtils::BytesPerElement(binding.attr.dataType); + if (bytes > 0) { + std::vector zero(bytes, 0); + binding.global->setData(reinterpret_cast(zero.data()), zero.size()); + } + } + return true; + } + + void LogBindingInfo(const std::string& type, const TensorBinding& binding) { + std::string modelShape = JoinDims(binding.qnnDims); + Log::Write(Log::Level::Info, + Fmt("ModelInspect: %s %s (Model) shape=%s dtype=%s", type.c_str(), binding.name.c_str(), + modelShape.c_str(), binding.qnnType.c_str())); + + const auto elements = SecureMrUtils::ElementCount(binding.attr); + Log::Write(Log::Level::Info, + Fmt("ModelInspect: %s %s (SecureMR) shape=%s c=%d dtype=%s elements=%zu", type.c_str(), + binding.name.c_str(), JoinDims(binding.attr.dimensions).c_str(), binding.attr.channels, + DataTypeName(binding.attr.dataType).c_str(), elements)); + } + + static std::string JoinDims(const std::vector& dims) { + std::string out; + for (size_t i = 0; i < dims.size(); ++i) { + out.append(std::to_string(dims[i])); + if (i + 1 < dims.size()) { + out.push_back('x'); + } + } + if (out.empty()) { + out = "1"; + } + return out; + } + + static std::string DataTypeName(XrSecureMrTensorDataTypePICO type) { + switch (type) { + case XR_SECURE_MR_TENSOR_DATA_TYPE_UINT8_PICO: + return "UINT8"; + case XR_SECURE_MR_TENSOR_DATA_TYPE_INT8_PICO: + return "INT8"; + case XR_SECURE_MR_TENSOR_DATA_TYPE_UINT16_PICO: + return "UINT16"; + case XR_SECURE_MR_TENSOR_DATA_TYPE_INT16_PICO: + return "INT16"; + case XR_SECURE_MR_TENSOR_DATA_TYPE_INT32_PICO: + return "INT32"; + case XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO: + return "FLOAT32"; + case XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT64_PICO: + return "FLOAT64"; + case XR_SECURE_MR_TENSOR_DATA_TYPE_DYNAMIC_TEXTURE_UINT8_PICO: + return "DYNAMIC_TEXTURE_UINT8"; + case XR_SECURE_MR_TENSOR_DATA_TYPE_DYNAMIC_TEXTURE_FLOAT32_PICO: + return "DYNAMIC_TEXTURE_FLOAT32"; + default: + return "UNKNOWN"; + } + } + + template + struct TensorStats { + T min{}; + T max{}; + double mean = 0.0; + double std = 0.0; + size_t count = 0; + }; + + template + TensorStats ComputeStats(const T* data, size_t count) { + TensorStats stats; + if (count == 0) { + return stats; + } + stats.count = count; + stats.min = data[0]; + stats.max = data[0]; + double sum = 0.0; + for (size_t i = 0; i < count; ++i) { + stats.min = std::min(stats.min, data[i]); + stats.max = std::max(stats.max, data[i]); + sum += static_cast(data[i]); + } + stats.mean = sum / static_cast(count); + + double varSum = 0.0; + for (size_t i = 0; i < count; ++i) { + const double diff = static_cast(data[i]) - stats.mean; + varSum += diff * diff; + } + stats.std = std::sqrt(varSum / static_cast(count)); + return stats; + } + + void LogInputPreview(const TensorBinding& binding, + const std::vector& buffer, + size_t bytesUsed, + size_t elementBytes) { + const size_t elements = bytesUsed / elementBytes; + if (elements == 0) { + Log::Write(Log::Level::Warning, + Fmt("ModelInspect: input %s has no elements to preview", binding.name.c_str())); + return; + } + + const size_t previewCount = std::min(elements, 8); + auto logPreview = [&](auto ptr) { + std::string msg; + for (size_t i = 0; i < previewCount; ++i) { + msg.append(std::to_string(ptr[i])); + if (i + 1 < previewCount) { + msg.append(", "); + } + } + Log::Write(Log::Level::Info, + Fmt("ModelInspect: input %s preview [%s]", binding.name.c_str(), msg.c_str())); + }; + + switch (binding.attr.dataType) { + case XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO: + case XR_SECURE_MR_TENSOR_DATA_TYPE_DYNAMIC_TEXTURE_FLOAT32_PICO: { + auto* ptr = reinterpret_cast(buffer.data()); + const auto stats = ComputeStats(ptr, elements); + Log::Write(Log::Level::Info, + Fmt("ModelInspect: input %s stats count=%zu min=%.6g max=%.6g mean=%.6g std=%.6g", + binding.name.c_str(), stats.count, static_cast(stats.min), + static_cast(stats.max), stats.mean, stats.std)); + logPreview(ptr); + break; + } + case XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT64_PICO: { + auto* ptr = reinterpret_cast(buffer.data()); + const auto stats = ComputeStats(ptr, elements); + Log::Write(Log::Level::Info, + Fmt("ModelInspect: input %s stats count=%zu min=%.6g max=%.6g mean=%.6g std=%.6g", + binding.name.c_str(), stats.count, stats.min, stats.max, stats.mean, stats.std)); + logPreview(ptr); + break; + } + case XR_SECURE_MR_TENSOR_DATA_TYPE_INT32_PICO: { + auto* ptr = reinterpret_cast(buffer.data()); + const auto stats = ComputeStats(ptr, elements); + Log::Write(Log::Level::Info, + Fmt("ModelInspect: input %s stats count=%zu min=%.6g max=%.6g mean=%.6g std=%.6g", + binding.name.c_str(), stats.count, static_cast(stats.min), + static_cast(stats.max), stats.mean, stats.std)); + logPreview(ptr); + break; + } + case XR_SECURE_MR_TENSOR_DATA_TYPE_INT16_PICO: { + auto* ptr = reinterpret_cast(buffer.data()); + const auto stats = ComputeStats(ptr, elements); + Log::Write(Log::Level::Info, + Fmt("ModelInspect: input %s stats count=%zu min=%.6g max=%.6g mean=%.6g std=%.6g", + binding.name.c_str(), stats.count, static_cast(stats.min), + static_cast(stats.max), stats.mean, stats.std)); + logPreview(ptr); + break; + } + case XR_SECURE_MR_TENSOR_DATA_TYPE_UINT16_PICO: { + auto* ptr = reinterpret_cast(buffer.data()); + const auto stats = ComputeStats(ptr, elements); + Log::Write(Log::Level::Info, + Fmt("ModelInspect: input %s stats count=%zu min=%.6g max=%.6g mean=%.6g std=%.6g", + binding.name.c_str(), stats.count, static_cast(stats.min), + static_cast(stats.max), stats.mean, stats.std)); + logPreview(ptr); + break; + } + case XR_SECURE_MR_TENSOR_DATA_TYPE_INT8_PICO: { + auto* ptr = reinterpret_cast(buffer.data()); + const auto stats = ComputeStats(ptr, elements); + Log::Write(Log::Level::Info, + Fmt("ModelInspect: input %s stats count=%zu min=%.6g max=%.6g mean=%.6g std=%.6g", + binding.name.c_str(), stats.count, static_cast(stats.min), + static_cast(stats.max), stats.mean, stats.std)); + logPreview(ptr); + break; + } + case XR_SECURE_MR_TENSOR_DATA_TYPE_UINT8_PICO: + case XR_SECURE_MR_TENSOR_DATA_TYPE_DYNAMIC_TEXTURE_UINT8_PICO: { + auto* ptr = reinterpret_cast(buffer.data()); + const auto stats = ComputeStats(ptr, elements); + Log::Write(Log::Level::Info, + Fmt("ModelInspect: input %s stats count=%zu min=%.6g max=%.6g mean=%.6g std=%.6g", + binding.name.c_str(), stats.count, static_cast(stats.min), + static_cast(stats.max), stats.mean, stats.std)); + logPreview(ptr); + break; + } + default: { + const size_t byteCount = std::min(bytesUsed, 8); + std::string msg; + for (size_t i = 0; i < byteCount; ++i) { + msg.append(std::to_string(buffer[i])); + if (i + 1 < byteCount) { + msg.append(", "); + } + } + Log::Write(Log::Level::Info, + Fmt("ModelInspect: input %s preview bytes [%s]", binding.name.c_str(), msg.c_str())); + break; + } + } + } + + bool LoadInputData(const TensorBinding& binding, const std::string& inputPath) { + const size_t elementBytes = SecureMrUtils::BytesPerElement(binding.attr.dataType); + if (elementBytes == 0) { + Log::Write(Log::Level::Error, + Fmt("ModelInspect: unsupported data type %d for input %s", binding.attr.dataType, binding.name.c_str())); + return false; + } + + const size_t expectedBytes = SecureMrUtils::ElementCount(binding.attr) * elementBytes; + std::vector buffer(expectedBytes, 0); + size_t bytesUsed = expectedBytes; + const bool hasInputFile = !inputPath.empty(); + + if (hasInputFile) { + std::ifstream ifs(inputPath, std::ios::binary | std::ios::ate); + if (!ifs) { + Log::Write(Log::Level::Error, + Fmt("ModelInspect: failed to open input file %s for %s", inputPath.c_str(), binding.name.c_str())); + return false; + } + std::streamsize fileSize = ifs.tellg(); + ifs.seekg(0, std::ios::beg); + + bytesUsed = std::min(static_cast(fileSize), expectedBytes); + if (bytesUsed > 0) { + ifs.read(reinterpret_cast(buffer.data()), bytesUsed); + } + + if (static_cast(fileSize) < expectedBytes) { + Log::Write(Log::Level::Warning, + Fmt("ModelInspect: input file shorter than expected for %s (%zu < %zu), padding with zeros", + binding.name.c_str(), static_cast(fileSize), expectedBytes)); + } + Log::Write(Log::Level::Info, + Fmt("ModelInspect: loaded input file %s for %s (%zu/%zu bytes)", inputPath.c_str(), + binding.name.c_str(), bytesUsed, expectedBytes)); + } else { + FillRandom(buffer, binding.attr.dataType); + Log::Write(Log::Level::Info, + Fmt("ModelInspect: generated random input for %s (%zu bytes)", binding.name.c_str(), expectedBytes)); + } + + binding.global->setData(reinterpret_cast(buffer.data()), buffer.size()); + LogInputPreview(binding, buffer, bytesUsed, elementBytes); + return true; + } + + void FillRandom(std::vector& buffer, XrSecureMrTensorDataTypePICO type) { + std::mt19937 rng(std::random_device{}()); + if (buffer.empty()) { + return; + } + Log::Write(Log::Level::Info, + Fmt("ModelInspect: FillRandom for dtype=%s (%d), bytes=%zu", + DataTypeName(type).c_str(), static_cast(type), buffer.size())); + switch (type) { + case XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO: { + auto* ptr = reinterpret_cast(buffer.data()); + const size_t count = buffer.size() / sizeof(float); + std::uniform_real_distribution dist(-1.0f, 1.0f); + for (size_t i = 0; i < count; ++i) { + ptr[i] = dist(rng); + } + break; + } + case XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT64_PICO: { + auto* ptr = reinterpret_cast(buffer.data()); + const size_t count = buffer.size() / sizeof(double); + std::uniform_real_distribution dist(-1.0, 1.0); + for (size_t i = 0; i < count; ++i) { + ptr[i] = dist(rng); + } + break; + } + case XR_SECURE_MR_TENSOR_DATA_TYPE_INT32_PICO: { + auto* ptr = reinterpret_cast(buffer.data()); + const size_t count = buffer.size() / sizeof(int32_t); + std::uniform_int_distribution dist(-128, 128); + for (size_t i = 0; i < count; ++i) { + ptr[i] = dist(rng); + } + break; + } + case XR_SECURE_MR_TENSOR_DATA_TYPE_INT16_PICO: { + auto* ptr = reinterpret_cast(buffer.data()); + const size_t count = buffer.size() / sizeof(int16_t); + std::uniform_int_distribution dist(-128, 128); + for (size_t i = 0; i < count; ++i) { + ptr[i] = dist(rng); + } + break; + } + case XR_SECURE_MR_TENSOR_DATA_TYPE_INT8_PICO: { + std::uniform_int_distribution dist(-16, 16); + for (auto& b : buffer) { + b = static_cast(dist(rng)); + } + break; + } + case XR_SECURE_MR_TENSOR_DATA_TYPE_UINT16_PICO: { + auto* ptr = reinterpret_cast(buffer.data()); + const size_t count = buffer.size() / sizeof(uint16_t); + std::uniform_int_distribution dist(0, 255); + for (size_t i = 0; i < count; ++i) { + ptr[i] = dist(rng); + } + break; + } + case XR_SECURE_MR_TENSOR_DATA_TYPE_UINT8_PICO: + case XR_SECURE_MR_TENSOR_DATA_TYPE_DYNAMIC_TEXTURE_UINT8_PICO: { + std::uniform_int_distribution dist(0, 255); + for (auto& b : buffer) { + b = static_cast(dist(rng)); + } + break; + } + default: + break; + } + } + + void CreatePipeline() { + pipeline_ = std::make_shared(frameworkSession_); + std::unordered_map> inputPlaceholders; + std::unordered_map> outputPlaceholders; + + for (auto& binding : inputBindings_) { + auto placeholder = PipelineTensor::PipelinePlaceholderLike(pipeline_, binding.global); + inputPlaceholders.emplace(binding.name, placeholder); + placeholderMap_.emplace(placeholder, binding.global); + Log::Write(Log::Level::Info, + Fmt("ModelInspect: placeholder created for input %s (placeholder=%p, global=%p)", + binding.name.c_str(), static_cast(placeholder.get()), + static_cast(binding.global.get()))); + } + for (auto& binding : outputBindings_) { + auto placeholder = PipelineTensor::PipelinePlaceholderLike(pipeline_, binding.global); + outputPlaceholders.emplace(binding.name, placeholder); + placeholderMap_.emplace(placeholder, binding.global); + Log::Write(Log::Level::Info, + Fmt("ModelInspect: placeholder created for output %s (placeholder=%p, global=%p)", + binding.name.c_str(), static_cast(placeholder.get()), + static_cast(binding.global.get()))); + } + + pipeline_->runAlgorithm(reinterpret_cast(modelBuffer_.data()), modelBuffer_.size(), inputPlaceholders, {}, + outputPlaceholders, {}, modelName_); + + Log::Write(Log::Level::Info, Fmt("ModelInspect: pipeline created for model %s", modelName_.c_str())); + } + + void SetupReadback() { + if (outputBindings_.empty()) { + return; + } + std::vector targets; + targets.reserve(outputBindings_.size()); + for (auto& binding : outputBindings_) { + targets.push_back(TensorReadback::Target{ + .tensor = binding.global, + .callback = [this, name = binding.name](TensorReadbackResult&& result) { HandleReadback(name, std::move(result)); }, + .name = binding.name}); + } + TensorReadback::Config cfg{.pollingInterval = std::chrono::milliseconds(50)}; + readback_ = std::make_unique(frameworkSession_, std::move(targets), cfg); + remainingOutputs_.store(static_cast(outputBindings_.size())); + } + + void HandleReadback(const std::string& name, TensorReadbackResult&& result) { + + const auto outPath = outputDir_ / Fmt("model_inspect_output_%s.bin", name.c_str()); + std::ofstream ofs(outPath, std::ios::binary); + if (!ofs) { + Log::Write(Log::Level::Error, Fmt("ModelInspect: failed to open %s for write", outPath.string().c_str())); + } else { + ofs.write(reinterpret_cast(result.data.data()), + static_cast(result.data.size())); + ofs.flush(); + const auto wroteBytes = static_cast(ofs.tellp()); + if (!ofs.good()) { + Log::Write(Log::Level::Error, Fmt("ModelInspect: write error for %s", outPath.string().c_str())); + } + Log::Write(Log::Level::Info, + Fmt("ModelInspect: dumped %s (%zu bytes) -> %s", name.c_str(), result.data.size(), + outPath.string().c_str())); + std::error_code ec; + const auto onDiskBytes = std::filesystem::file_size(outPath, ec); + if (!ec && static_cast(onDiskBytes) != result.data.size()) { + Log::Write(Log::Level::Warning, + Fmt("ModelInspect: file_size mismatch for %s (expected %zu, tellp %zu, on-disk %lld)", + outPath.string().c_str(), result.data.size(), wroteBytes, + static_cast(onDiskBytes))); + } + } + + auto logPreview = [&](auto ptr, size_t count) { + std::string msg; + for (size_t i = 0; i < count; ++i) { + msg.append(std::to_string(ptr[i])); + if (i + 1 < count) { + msg.append(", "); + } + } + Log::Write(Log::Level::Info, Fmt("ModelInspect: %s preview [%s]", name.c_str(), msg.c_str())); + }; + + switch (result.dataType) { + case XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO: { + auto* ptr = reinterpret_cast(result.data.data()); + const size_t floatCount = result.data.size() / sizeof(float); + Log::Write(Log::Level::Info, + Fmt("ModelInspect: %s has %zu float32 values", name.c_str(), floatCount)); + const size_t count = std::min(floatCount, 8); + logPreview(ptr, count); + break; + } + case XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT64_PICO: { + auto* ptr = reinterpret_cast(result.data.data()); + const size_t floatCount = result.data.size() / sizeof(double); + Log::Write(Log::Level::Info, + Fmt("ModelInspect: %s has %zu float64 values", name.c_str(), floatCount)); + const size_t count = std::min(floatCount, 8); + logPreview(ptr, count); + break; + } + case XR_SECURE_MR_TENSOR_DATA_TYPE_INT32_PICO: { + auto* ptr = reinterpret_cast(result.data.data()); + const size_t count = std::min(result.data.size() / sizeof(int32_t), 8); + logPreview(ptr, count); + break; + } + case XR_SECURE_MR_TENSOR_DATA_TYPE_INT16_PICO: { + auto* ptr = reinterpret_cast(result.data.data()); + const size_t count = std::min(result.data.size() / sizeof(int16_t), 8); + logPreview(ptr, count); + break; + } + case XR_SECURE_MR_TENSOR_DATA_TYPE_UINT16_PICO: { + auto* ptr = reinterpret_cast(result.data.data()); + const size_t count = std::min(result.data.size() / sizeof(uint16_t), 8); + logPreview(ptr, count); + break; + } + default: { + const size_t count = std::min(result.data.size(), 8); + logPreview(result.data.data(), count); + break; + } + } + + if (remainingOutputs_.fetch_sub(1) == 1) { + readbackDone_ = true; + if (readback_) { + readback_->Stop(); + } + readbackCv_.notify_all(); + } + } + + void FinishInit(bool ok) { + { + std::lock_guard lock(initMutex_); + initFinished_ = true; + if (!ok) { + Log::Write(Log::Level::Error, "ModelInspect: initialization failed"); + } else { + Log::Write(Log::Level::Info, "ModelInspect: initialization finished"); + } + } + initCv_.notify_all(); + } + + XrInstance xrInstance_; + XrSession xrSession_; + + std::shared_ptr frameworkSession_; + std::shared_ptr pipeline_; + std::vector modelBuffer_; + std::vector inputBindings_; + std::vector outputBindings_; + std::map, std::shared_ptr> placeholderMap_; + + std::unique_ptr readback_; + std::filesystem::path outputDir_; + + std::unique_ptr initThread_; + std::unique_ptr runThread_; + std::mutex initMutex_; + std::condition_variable initCv_; + bool initFinished_ = false; + bool pipelineReady_ = false; + + std::mutex readbackMutex_; + std::condition_variable readbackCv_; + std::atomic readbackDone_{false}; + std::atomic remainingOutputs_{0}; + + std::string modelName_ = "model_inspect"; +}; + +std::shared_ptr CreateSecureMrProgram(const XrInstance& instance, const XrSession& session) { + return std::make_shared(instance, session); +} + +} // namespace SecureMR diff --git a/samples/pose/CMakeLists.txt b/samples/pose/CMakeLists.txt index a6c8743..3a4fdbb 100644 --- a/samples/pose/CMakeLists.txt +++ b/samples/pose/CMakeLists.txt @@ -6,5 +6,4 @@ set(SAMPLE_SRCS ) set(SAMPLE_DIR ${CMAKE_CURRENT_LIST_DIR}/cpp) set(USE_SECURE_MR_UTILS ON) - include(../../base/base.cmake) diff --git a/samples/readback/AndroidManifest.xml b/samples/readback/AndroidManifest.xml new file mode 100644 index 0000000..22e803c --- /dev/null +++ b/samples/readback/AndroidManifest.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/readback/CMakeLists.txt b/samples/readback/CMakeLists.txt new file mode 100644 index 0000000..0140533 --- /dev/null +++ b/samples/readback/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.22) +project(readback) + +set(SAMPLE_SRCS + ${CMAKE_CURRENT_LIST_DIR}/cpp/readback_file.cpp + ${CMAKE_CURRENT_LIST_DIR}/cpp/readback_vulkan.cpp + ${CMAKE_CURRENT_LIST_DIR}/cpp/readback_opengl.cpp +) +set(SAMPLE_DIR ${CMAKE_CURRENT_LIST_DIR}/cpp) +set(USE_SECURE_MR_UTILS ON) +set(THIRD_PARTY_DIRS ${CMAKE_CURRENT_LIST_DIR}/third_party) + +include(../../base/base.cmake) diff --git a/samples/readback/README.md b/samples/readback/README.md new file mode 100644 index 0000000..08bd18f --- /dev/null +++ b/samples/readback/README.md @@ -0,0 +1,72 @@ +# Sample: Readback + +This sample demonstrates SecureMR readback on global tensor by both CPU buffers and GPU textures (Vulkan / OpenGL ES). It acquires the VST camera frame via a SecureMR pipeline and exports a PNG file to the app’s external storage for quick verification. + +## Expected Behavior +- After camera permission is granted, the app initializes the SecureMR pipeline. +- On each successful readback, a `output.png` is written to external storage. +- Exports PNG in external storage. + +## Variants +- readback_cpu: uses XrReadbackTensorBufferPICO to read back CPU buffer data +- readback_vulkan: uses XrReadbackTexturePICO and Vulkan +- readback_opengl: uses XrReadbackTexturePICO and OpenGL ES + +## Build & Install +- CPU buffer: + +``` +./gradlew :samples:readback:installReadback_cpuDebug +``` + +- Vulkan texture: + +``` +./gradlew :samples:readback:installReadback_vulkanDebug +``` + +- OpenGL ES texture: + +``` +./gradlew :samples:readback:installReadback_openglDebug +``` + +The Gradle flavors configure CMake flags automatically: +- readback_vulkan: `-DREADBACK_USE_GPU=1 -DREADBACK_USE_VULKAN=1` +- readback_opengl: `-DREADBACK_USE_GPU=1 -DREADBACK_USE_OPENGL=1` + +## Run +- Launch the installed app on the device. +- Grant camera permission when prompted. +- After the pipeline initializes, the app saves a PNG to external storage. + +## Output +- The exported file is written to the app’s external files directory: + - Example: `/sdcard/Android/data/com.bytedance.pico.secure_mr_demo.readback/files/output.png` + +## Implementation Notes +- Core controller: `readback.h` +- Sample logic and pipeline setup: `readback_file.cpp` +- OpenGL ES export: `readback_opengl.cpp` +- Vulkan export: `readback_vulkan.cpp` + +### Key Tensor: vstOutputLeftUint8Global +- Global tensor holding the latest left‑eye VST RGB frame. +- Dimensions: 512×512, Channels: 3. + CPU mode: `XR_SECURE_MR_TENSOR_DATA_TYPE_UINT8_PICO`. + GPU mode: usage `XR_SECURE_MR_TENSOR_TYPE_MAT_DYNAMIC_TEXTURE_PICO`, data type `XR_SECURE_MR_TENSOR_DATA_TYPE_DYNAMIC_TEXTURE_UINT8_PICO`. + +## Code Walk-through +- Controller initialization + - In the `readback.cpp` constructor, readback-related OpenXR PICO extension function pointers are fetched via `FrameworkSession` (e.g., `xrCreateBufferFromGlobalTensorAsyncPICO`, `xrCreateTextureFromGlobalTensorAsyncPICO`). + - Graphics context is initialized in `readback_vulkan.cpp` or `readback_opengl.cpp` depending on the build backend. +- CPU buffer readback + - Issue: `RequestReadbackBuffer(...)` + - Acquire: `TryAcquireReadbackBuffer(...)` uses `xrCreateBufferFromGlobalTensorCompletePICO` in two call idiom (query size, then allocate and read). + - Export: `OutputReadbackBufferToFile(...)` writes RGB data using `stbi_write_png`. +- GPU texture readback + - Issue: `RequestReadbackTexture(...)` + - Acquire: `TryAcquireReadbackTexture(...)` returns an `XrReadbackTexturePICO`. + - Export: + - Vulkan: `OutputReadbackTextureToPath(...)` copies from `VkImage` to a staging buffer and saves RGBA PNG. + - OpenGL ES: `OutputReadbackTextureToPath(...)` attaches texture to an FBO, `glReadPixels` RGBA, then saves PNG. diff --git a/samples/readback/build.gradle.kts b/samples/readback/build.gradle.kts new file mode 100644 index 0000000..dfd2f7a --- /dev/null +++ b/samples/readback/build.gradle.kts @@ -0,0 +1,93 @@ +plugins { + id("com.android.application") + id("org.jetbrains.kotlin.android") +} +android { + compileSdk = 34 + ndkVersion = "26.3.11579264" + namespace = "com.bytedance.pico.secure_mr_demo.readback" + defaultConfig { + minSdk = 34 + targetSdk = 34 + versionCode = 1 + versionName = "1.0" + applicationId = "com.bytedance.pico.secure_mr_demo.readback" + externalNativeBuild { + cmake { + arguments.add("-DANDROID_STL=c++_shared") + arguments.add("-DANDROID_USE_LEGACY_TOOLCHAIN_FILE=OFF") + } + ndk { + abiFilters.add("arm64-v8a") + } + } + } + lint { + disable.add("ExpiredTargetSdkVersion") + } + buildFeatures { + prefab = true + viewBinding = true + } + buildTypes { + debug { + isDebuggable = true + isJniDebuggable = true + } + release { + isDebuggable = false + isJniDebuggable = false + } + } + flavorDimensions += "version" + productFlavors { + create("readback_cpu") { + dimension = "version" + + } + create("readback_vulkan") { + dimension = "version" + externalNativeBuild { + cmake { + arguments.add("-DREADBACK_USE_GPU=1") + arguments.add("-DREADBACK_USE_VULKAN=1") + } + } + } + create("readback_opengl") { + dimension = "version" + externalNativeBuild { + cmake { + arguments.add("-DREADBACK_USE_GPU=1") + arguments.add("-DREADBACK_USE_OPENGL=1") + } + } + } + } + externalNativeBuild { + cmake { + version = "3.22.1" + path("CMakeLists.txt") + } + } + sourceSets { + getByName("main") { + manifest.srcFile("AndroidManifest.xml") + assets.srcDirs("../../assets/common", "../../assets/pose") + java.srcDirs("src/main/java") + } + } + packaging { + jniLibs { + keepDebugSymbols.add("**.so") + } + } + kotlinOptions { + jvmTarget = "1.8" + } +} +dependencies { +// implementation("com.android.support:appcompat-v7:28.0.0") +// implementation("androidx.activity:activity-ktx:1.11.0") + implementation("androidx.appcompat:appcompat:1.7.0") +} diff --git a/samples/readback/cpp/readback_file.cpp b/samples/readback/cpp/readback_file.cpp new file mode 100644 index 0000000..d89de34 --- /dev/null +++ b/samples/readback/cpp/readback_file.cpp @@ -0,0 +1,222 @@ +// Copyright (2025) Bytedance Ltd. and/or its affiliates +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#define STB_IMAGE_IMPLEMENTATION +#define STB_IMAGE_WRITE_IMPLEMENTATION + +#include "readback_file.h" +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +JNIEXPORT void JNICALL +Java_com_bytedance_pico_secure_1mr_1demo_readback_ReadbackActivity_nativeSetPermission( + JNIEnv* env, + jclass, + jstring permission, + jboolean granted) +{ + const char* permUtf = env->GetStringUTFChars(permission, nullptr); + std::string perm(permUtf); + env->ReleaseStringUTFChars(permission, permUtf); + + bool isGranted = (granted == JNI_TRUE); + + { + std::lock_guard lock(g_permMutex); + if (perm == "android.permission.CAMERA") + gPermissionCamera = isGranted; + } +} + +#ifdef __cplusplus +} +#endif + +namespace SecureMR { + +#ifdef XR_READBACK_USE_CPU +bool ReadbackCheck::isCpuBuffer = true; +#else +bool ReadbackCheck::isCpuBuffer = false; +#endif + +struct android_app* ReadbackCheck::gapp = nullptr; + +ReadbackCheck::ReadbackCheck(const XrInstance& instance, const XrSession& session) + : xr_instance(instance), xr_session(session) {} + +ReadbackCheck::~ReadbackCheck() { + keepRunning = false; + if (readbackTexture != XR_NULL_HANDLE) + { + mReadbackController->ReleaseReadbackTexture(readbackTexture); + } + + delete mReadbackController; + if (pipelineInitializer && pipelineInitializer->joinable()) { + pipelineInitializer->join(); + } + for (auto& runner : pipelineRunners) { + if (runner.joinable()) runner.join(); + } +} + +void ReadbackCheck::CreateFramework() { + LOGI("Request Permission"); + Log::Write(Log::Level::Info, "CreateFramework ..."); + frameworkSession = std::make_shared(xr_instance, xr_session, 512, 512); + Log::Write(Log::Level::Info, "CreateFramework done."); +} + +void ReadbackCheck::CreatePipelines() { + pipelineInitializer = std::make_unique([this]() { + // Note: global tensors must be created before they are referred + // in each individual pipeline + CreateGlobalTensor(); + CreateRelaxMrReadBackPipeline(); + initialized.notify_all(); + pipelineAllInitialized = true; + }); + +} + +void ReadbackCheck::CreateGlobalTensor() { + Log::Write(Log::Level::Info, "CreateGlobalTensor ..."); + if (isCpuBuffer) + { + vstOutputLeftUint8Global = std::make_shared( + frameworkSession, + TensorAttribute{.dimensions = {512, 512}, .channels = 3, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_UINT8_PICO}); + assert(vstOutputLeftUint8Global != nullptr); + } + else { + vstOutputLeftUint8Global = std::make_shared( + frameworkSession, TensorAttribute{.dimensions = {512, 512}, + .channels = 3, + .usage = XR_SECURE_MR_TENSOR_TYPE_MAT_DYNAMIC_TEXTURE_PICO, + .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_DYNAMIC_TEXTURE_UINT8_PICO}); + + assert(vstOutputLeftUint8Global != nullptr); + } + initializeGraphicsContext(); + mConfig.w = 512; mConfig.h = 512; + mReadbackController = new ReadbackController(frameworkSession, vstOutputLeftUint8Global); + mCurrentReadbackRequest = nullptr; + Log::Write(Log::Level::Info, "CreateGlobalTensor Done..."); +} + + +void ReadbackCheck::Tick() +{ + + if (!pipelineAllInitialized || !gPermissionCamera) return; + + if (isCpuBuffer) + { + // cpu buffer + if (!mCurrentReadbackRequest) { + mReadbackController->RequestReadbackBuffer(mCurrentReadbackRequest); + } + else { + auto result = new XrReadbackTensorBufferPICO(); + if (mReadbackController->TryAcquireReadbackBuffer(*mCurrentReadbackRequest, result)) { + mCurrentReadbackRequest = nullptr; + std::string path = gapp->activity->externalDataPath; + path = path + "/output.png"; + OutputReadbackBufferToFile(result, path); + delete((char*)result->buffer); + } + delete result; + } + + } + else + { + // hardware texture + if (!mCurrentReadbackRequest) { + mReadbackController->RequestReadbackTexture(mCurrentReadbackRequest); + } + else + { + if (readbackTexture == XR_NULL_HANDLE) { + if (mReadbackController->TryAcquireReadbackTexture(*mCurrentReadbackRequest, readbackTexture)) { + mCurrentReadbackRequest = nullptr; + } + } + else + { + std::string path = gapp->activity->externalDataPath; + path = path + "/output.png"; + OutputReadbackTextureToPath(readbackTexture, path); + } + } + } +} + +void ReadbackCheck::RunPipelines() { + pipelineRunners.emplace_back([this]() { + { + std::unique_lock guard(initialized_mtx); + initialized.wait(guard); + } + while (keepRunning) { + RunRelaxMrReadBackPipeline(); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + }); +} + +void ReadbackCheck::RequestPermission(struct android_app* app) { + LOGI("RelaxMR Request Permission"); + ReadbackCheck::gapp = app; + JNIEnv* env = nullptr; + app->activity->vm->AttachCurrentThread(&env, nullptr); + + jobject activity = app->activity->clazz; + jclass cls = env->GetObjectClass(activity); + jmethodID mid = env->GetMethodID(cls, "requestCameraFromNative", "()V"); + env->CallVoidMethod(activity, mid); +} +void ReadbackCheck::OutputReadbackBufferToFile(const XrReadbackTensorBufferPICO* tensorBuffer, const std::string &path) { + ImageRGB img; + img.rgb.resize(tensorBuffer->bufferCapacityInput); + std::memcpy(img.rgb.data(), tensorBuffer->buffer, tensorBuffer->bufferCapacityInput); + img.w = mConfig.w; + img.h = mConfig.h; + LOGI("output path %s", path.c_str()); + stbi_write_png(path.c_str(), img.w, img.h, 3, img.rgb.data(), img.w * 3); +} + +void ReadbackCheck::CreateRelaxMrReadBackPipeline() { + LOGI("RelaxMR CreateRelaxMrReadBackPipeline"); + m_RelaxMrReadBackPipeline = std::make_shared(frameworkSession); + vstOutputLeftUint8Placeholder = + PipelineTensor::PipelinePlaceholderLike(m_RelaxMrReadBackPipeline, vstOutputLeftUint8Global); + m_RelaxMrReadBackPipeline->cameraAccess(nullptr, vstOutputLeftUint8Placeholder, nullptr, nullptr); +} + +XrSecureMrPipelineRunPICO ReadbackCheck::RunRelaxMrReadBackPipeline(const XrSecureMrPipelineRunPICO pre) { + return m_RelaxMrReadBackPipeline->submit({{vstOutputLeftUint8Placeholder, vstOutputLeftUint8Global}}, + XR_NULL_HANDLE, nullptr); +} + +std::shared_ptr CreateSecureMrProgram(const XrInstance& instance, const XrSession& session) { + return std::make_shared(instance, session); +} +} // namespace SecureMR \ No newline at end of file diff --git a/samples/readback/cpp/readback_file.h b/samples/readback/cpp/readback_file.h new file mode 100644 index 0000000..4519ca0 --- /dev/null +++ b/samples/readback/cpp/readback_file.h @@ -0,0 +1,162 @@ +// Copyright (2025) Bytedance Ltd. and/or its affiliates +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "pch.h" +#include +#include +#include +#include "logger.h" +#include "common.h" +#include "securemr_base.h" +#include "securemr_utils/adapter.hpp" +#include "securemr_utils/pipeline.h" +#include "securemr_utils/tensor.h" +#include "securemr_utils/rendercommand.h" +#include "securemr_utils/session.h" +#include "securemr_utils/readback.h" + +#define READBACK_MODEL_PATH "detection.serialized.bin" +#define POSE_LANDMARK_MODEL_PATH "landmark.serialized.bin" +#define GLTF_PATH "pose_marker.gltf" +#define ANCHOR_MAT "anchors_1.mat" +#include "stb_image.h" +#include "stb_image_write.h" + +#ifdef __cplusplus +extern "C" { +#endif + +static std::mutex g_permMutex; +static bool gPermissionCamera = false; + +JNIEXPORT void JNICALL +Java_com_bytedance_pico_secure_1mr_1demo_readback_ReadbackActivity_nativeSetPermission( + JNIEnv* env, + jclass, + jstring permission, + jboolean granted); +#ifdef __cplusplus +} +#endif + +namespace SecureMR { + +class ReadbackCheck : public ISecureMR { + public: + struct Config { + int w = 512; + int h = 512; + }; + struct ImageRGB { + int w = 0, h = 0; + std::vector rgb; + bool valid() const { return w > 0 && h > 0 && rgb.size() == (size_t)w * h * 3; } + }; + + static struct android_app *gapp; + + ReadbackCheck(const XrInstance& instance, const XrSession& session); + ~ReadbackCheck() override; + void CreateFramework() override; + void CreatePipelines() override; + void RunPipelines() override; + void RequestPermission(struct android_app* app) override; + void Tick() override; + + [[nodiscard]] bool LoadingFinished() const override { return pipelineAllInitialized; } + + protected: + /** + * Create all the global tensors, must be called before create any pipelines + */ + void CreateGlobalTensor(); + void CreateRelaxMrReadBackPipeline(); + + XrSecureMrPipelineRunPICO RunRelaxMrReadBackPipeline(const XrSecureMrPipelineRunPICO pre = XR_NULL_HANDLE); + + void initializeGraphicsContext(); + void OutputReadbackBufferToFile(const XrReadbackTensorBufferPICO* tensorBuffer, const std::string &path); + bool OutputReadbackTextureToPath(const XrReadbackTexturePICO& texture, const std::string& path); +#ifdef XR_USE_GRAPHICS_API_VULKAN + VkQueue queue; + VkCommandPool cmdPool; + void OutputVulkanTextureToPath(XrReadbackTextureImageVulkanPICO * vTexture, const std::string &path); +#endif +#ifdef XR_USE_GRAPHICS_API_OPENGL_ES + void OutputOpenGLTextureToPath(XrReadbackTextureImageOpenGLPICO * vTexture, const std::string &path); +#endif + + XrInstance xr_instance; + XrSession xr_session; + static bool isCpuBuffer; + + private: + /** + * Root framework + */ + std::shared_ptr frameworkSession; + + // Global tensors + // Recall that global tensors are used to share data + // between pipelines, and can also server as pipeline + // execution condition + + /** + * Caching the latest left-eye image --- shared between + * the VST, the inference and the 2D-to-3D pipelines + *
+ * In R8G8B8 format + */ + std::shared_ptr vstOutputLeftUint8Global; + + std::shared_ptr vstOutputLeftEyeUint8Global; + + /** + * The pipeline where the detection algorithm is running. It intakes RGB image, + * determine the region which it believes contains a human body, and outputs a + * confidence score and an affine matrix from the RGB image to the region, in + * roiAffineGlobal + */ + std::shared_ptr m_RelaxMrReadBackPipeline; + + // Placeholders for each pipeline + // Recall placeholders are pipeline's local references to + // global tensors, to avoid memory copy and competition + // on the shared data between pipelines executed in different + // threads. + + // Placeholders for the VST pipeline + + std::shared_ptr vstOutputLeftUint8Placeholder; + + // Run-time control + + std::vector pipelineRunners; + std::unique_ptr pipelineInitializer; + std::condition_variable initialized; + std::mutex initialized_mtx; + + bool keepRunning = true; + bool pipelineAllInitialized = false; + + // controller for readback + ReadbackController* mReadbackController = nullptr; + ReadbackController::ReadbackRequest *mCurrentReadbackRequest = nullptr; + XrReadbackTexturePICO readbackTexture = XR_NULL_HANDLE; + Config mConfig; +}; + +} // namespace SecureMR diff --git a/samples/readback/cpp/readback_opengl.cpp b/samples/readback/cpp/readback_opengl.cpp new file mode 100644 index 0000000..1923daf --- /dev/null +++ b/samples/readback/cpp/readback_opengl.cpp @@ -0,0 +1,59 @@ +// +// Created by ByteDance on 12/17/25. +// +#ifdef XR_USE_GRAPHICS_API_OPENGL_ES +#include "readback_file.h" +#include "gfxwrapper_opengl.h" + +namespace SecureMR { + void ReadbackCheck::initializeGraphicsContext() {} + void ReadbackCheck::OutputOpenGLTextureToPath(XrReadbackTextureImageOpenGLPICO *vTexture, + const std::string &path) { + uint32_t srcImage = vTexture->texId; + uint32_t width = 512, height = 512; + GLuint fbo; + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, srcImage, 0); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + printf("Framebuffer not complete: 0x%x\n", status); + return; + } + + std::vector pixels(width * height * 4); // RGBA8 + + glReadBuffer(GL_COLOR_ATTACHMENT0); + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data()); + + // for (int y = 0; y < height / 2; ++y) { + // int opp = height - 1 - y; + // for (int x = 0; x < width * 4; ++x) std::swap(pixels[y * width * 4 + x], pixels[opp * width * 4 + x]); + // } + + auto ret = stbi_write_png(path.c_str(), (int)width, (int)height, 4, pixels.data(), (int)width * 4); + if (ret == 0) { + Log::Write(Log::Level::Info, Fmt("readback stb write failed: %d", ret)); + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &fbo); + } + + bool ReadbackCheck::OutputReadbackTextureToPath(const XrReadbackTexturePICO &texture, const std::string &path) { + XrReadbackTextureImageOpenGLPICO opengl_texture; + + auto ret = mReadbackController->RetrieveTexture(texture, (XrReadbackTextureImageBaseHeaderPICO*)&opengl_texture); + if (!ret) + { + return false; + } + OutputOpenGLTextureToPath(&opengl_texture, path); + return true; + } +} + +#endif + + diff --git a/samples/readback/cpp/readback_vulkan.cpp b/samples/readback/cpp/readback_vulkan.cpp new file mode 100644 index 0000000..5bd1ec3 --- /dev/null +++ b/samples/readback/cpp/readback_vulkan.cpp @@ -0,0 +1,195 @@ +// +// Created by ByteDance on 12/17/25. +// +#ifdef XR_USE_GRAPHICS_API_VULKAN +#include "readback_file.h" +#include + +extern VkDevice g_device; +extern VkPhysicalDevice g_pdevice; +namespace SecureMR { + void ReadbackCheck::initializeGraphicsContext() { + uint32_t queueFamilyCount; + vkGetPhysicalDeviceQueueFamilyProperties(g_pdevice, &queueFamilyCount, nullptr); + + std::vector families(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(g_pdevice, &queueFamilyCount, families.data()); + + int graphicsFamily = -1; + for (uint32_t i = 0; i < queueFamilyCount; i++) { + const auto& f = families[i]; + if (f.queueFlags & VK_QUEUE_GRAPHICS_BIT) graphicsFamily = i; + } + int queueFamilyIndex = graphicsFamily; + VkCommandPoolCreateInfo ci{}; + ci.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + ci.queueFamilyIndex = queueFamilyIndex; + ci.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + vkCreateCommandPool(g_device, &ci, nullptr, &cmdPool); + vkGetDeviceQueue(g_device, graphicsFamily, 0, &queue); + assert(queue != VK_NULL_HANDLE); + } + + void ReadbackCheck::OutputVulkanTextureToPath(XrReadbackTextureImageVulkanPICO * vTexture, const std::string &path) { + VkImage srcImage = vTexture->image; + uint32_t w = mConfig.w, h = mConfig.h; + + VkDeviceSize imageSize = (VkDeviceSize)w * h * 4; + VkBufferCreateInfo bci = {}; + bci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bci.size = imageSize; + bci.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT; + bci.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + VkBuffer stagingBuf; + vkCreateBuffer(g_device, &bci, NULL, &stagingBuf); + + VkMemoryRequirements mr; + vkGetBufferMemoryRequirements(g_device, stagingBuf, &mr); + + VkPhysicalDeviceMemoryProperties memProps; + vkGetPhysicalDeviceMemoryProperties(g_pdevice, &memProps); + uint32_t memType; + auto desiredProps = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + for (uint32_t i = 0; i < memProps.memoryTypeCount; i++) { + bool supported = mr.memoryTypeBits & (1 << i); + bool hasProps = (memProps.memoryTypes[i].propertyFlags & desiredProps) == desiredProps; + if (supported && hasProps) { + memType = i; + break; + } + } + + VkMemoryAllocateInfo mai = {}; + mai.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + mai.allocationSize = mr.size; + mai.memoryTypeIndex = memType; + + VkDeviceMemory stagingMem; + vkAllocateMemory(g_device, &mai, NULL, &stagingMem); + vkBindBufferMemory(g_device, stagingBuf, stagingMem, 0); + auto beginOneTime = [&](VkCommandBuffer* out) { + VkCommandBufferAllocateInfo a = {}; + a.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + a.commandPool = cmdPool; + a.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + a.commandBufferCount = 1; + vkAllocateCommandBuffers(g_device, &a, out); + + VkCommandBufferBeginInfo bi = {}; + bi.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + bi.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + vkBeginCommandBuffer(*out, &bi); + }; + auto endOneTime = [&](VkCommandBuffer cb) { + vkEndCommandBuffer(cb); + VkSubmitInfo si = {}; + si.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + si.commandBufferCount = 1; + si.pCommandBuffers = &cb; + vkQueueSubmit(queue, 1, &si, VK_NULL_HANDLE); + vkQueueWaitIdle(queue); + vkFreeCommandBuffers(g_device, cmdPool, 1, &cb); + }; + + VkCommandBuffer cb; + beginOneTime(&cb); + + VkImageMemoryBarrier toSrc{}; + toSrc.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + toSrc.pNext = nullptr; + + toSrc.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + toSrc.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + + toSrc.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + toSrc.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + + toSrc.image = srcImage; + + toSrc.subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }; + + toSrc.srcAccessMask = 0; + toSrc.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + + vkCmdPipelineBarrier( + cb, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, + 0, nullptr, + 0, nullptr, + 1, &toSrc + ); + VkBufferImageCopy region = {0}; + region.bufferOffset = 0; + region.bufferRowLength = 0; + region.bufferImageHeight = 0; + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.mipLevel = 0; + region.imageSubresource.baseArrayLayer = 0; + region.imageSubresource.layerCount = 1; + region.imageOffset = (VkOffset3D){0, 0, 0}; + region.imageExtent = (VkExtent3D){w, h, 1}; + + vkCmdCopyImageToBuffer(cb, srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, stagingBuf, 1, ®ion); + + VkBufferMemoryBarrier bufBarrier = {}; + bufBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; + bufBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + bufBarrier.dstAccessMask = VK_ACCESS_HOST_READ_BIT; + bufBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + bufBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + bufBarrier.buffer = stagingBuf; + bufBarrier.offset = 0; + bufBarrier.size = imageSize; + + vkCmdPipelineBarrier( + cb, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_HOST_BIT, + 0, 0, nullptr, 1, &bufBarrier, 0, nullptr); + + endOneTime(cb); + void* mapped = nullptr; + vkMapMemory(g_device, stagingMem, 0, imageSize, 0, &mapped); + + bool flipY = false; + unsigned char* pixels = (unsigned char*)mapped; + if (flipY) { + size_t stride = (size_t)w * 4; + for (uint32_t y = 0; y < h / 2; ++y) { + unsigned char* rowA = pixels + y * stride; + unsigned char* rowB = pixels + (h - 1 - y) * stride; + for (size_t i = 0; i < stride; ++i) { + unsigned char t = rowA[i]; + rowA[i] = rowB[i]; + rowB[i] = t; + } + } + } + auto ret = stbi_write_png(path.c_str(), (int)w, (int)h, 3, pixels, (int)w * 3); + if (ret == 0) { + LOGE("readback vulkan texture failed: %d", ret); + } + vkUnmapMemory(g_device, stagingMem); + } + + bool ReadbackCheck::OutputReadbackTextureToPath(const XrReadbackTexturePICO& texture, const std::string& path) { + XrReadbackTextureImageVulkanPICO vulkan_texture; + auto ret = mReadbackController->RetrieveTexture(texture, (XrReadbackTextureImageBaseHeaderPICO*)&vulkan_texture); + if (!ret) { + return false; + } + OutputVulkanTextureToPath(&vulkan_texture, path); + return true; + } + +} +#endif \ No newline at end of file diff --git a/samples/readback/src/main/java/com/bytedance/pico/secure_mr_demo/readback/ReadbackActivity.java b/samples/readback/src/main/java/com/bytedance/pico/secure_mr_demo/readback/ReadbackActivity.java new file mode 100644 index 0000000..2aeb8e8 --- /dev/null +++ b/samples/readback/src/main/java/com/bytedance/pico/secure_mr_demo/readback/ReadbackActivity.java @@ -0,0 +1,42 @@ +package com.bytedance.pico.secure_mr_demo.readback; + +import android.Manifest; +import android.app.NativeActivity; +import android.content.pm.PackageManager; +import android.os.Bundle; + +public class ReadbackActivity extends NativeActivity { + static { + System.loadLibrary("readback"); + } + + private static final int REQ_CAMERA = 1001; + /** + * This Activity requires the camera permission because the sample accesses + * camera input for VST image readback purposes. + * + * The SPATIAL_DATA permission is not required for this sample, but may be + * required for other readback use cases. + */ + public void requestCameraFromNative() { + if (checkSelfPermission(android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { + requestPermissions(new String[]{android.Manifest.permission.CAMERA}, REQ_CAMERA); + } + else + { + nativeSetPermission(android.Manifest.permission.CAMERA, true); + } + } + + @Override + public void onRequestPermissionsResult(int rc, String[] perms, int[] grants) { + super.onRequestPermissionsResult(rc, perms, grants); + for (int i = 0; i < perms.length; i++) + { + if (perms[i].equals(android.Manifest.permission.CAMERA)) + nativeSetPermission(perms[i], grants[i] == PackageManager.PERMISSION_GRANTED); + } + } + + public native void nativeSetPermission(String permission, boolean granted); +} \ No newline at end of file diff --git a/samples/readback/third_party/stb_image.h b/samples/readback/third_party/stb_image.h new file mode 100644 index 0000000..a41c6fb --- /dev/null +++ b/samples/readback/third_party/stb_image.h @@ -0,0 +1,8008 @@ +/* stb_image - v2.28 - public domain image loader - http://nothings.org/stb + no warranty implied; use at your own risk + + Do this: + + + before you include this file in *one* C or C++ file to create the implementation. + + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define STB_IMAGE_IMPLEMENTATION + #include "stb_image.h" + + You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. + And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free + + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) + PNG 1/2/4/8/16-bit-per-channel + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels, 8/16 bit-per-channel) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + PNM (PPM and PGM binary only) + + Animated GIF still needs a proper API, but here's one way to do it: + http://gist.github.com/urraka/685d9a6340b26b830d49 + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) + + Full documentation under "DOCUMENTATION" below. + + +LICENSE + + See end of file for license information. + +RECENT REVISION HISTORY: + + 2.28 (2023-01-29) many error fixes, security errors, just tons of stuff + 2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes + 2.26 (2020-07-13) many minor fixes + 2.25 (2020-02-02) fix warnings + 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically + 2.23 (2019-08-11) fix clang static analysis warning + 2.22 (2019-03-04) gif fixes, fix warnings + 2.21 (2019-02-25) fix typo in comment + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings + 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes + 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 + RGB-format JPEG; remove white matting in PSD; + allocate large structures on the stack; + correct channel count for PNG & BMP + 2.10 (2016-01-22) avoid warning introduced in 2.09 + 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED + + See end of file for full revision history. + + + ============================ Contributors ========================= + + Image formats Extensions, features + Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) + Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) + Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) + Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) + Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) + Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) + Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) + github:urraka (animated gif) Junggon Kim (PNM comments) + Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) + socks-the-fox (16-bit PNG) + Jeremy Sawicki (handle all ImageNet JPGs) + Optimizations & bugfixes Mikhail Morozov (1-bit BMP) + Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) + Arseny Kapoulkine Simon Breuss (16-bit PNM) + John-Mark Allen + Carmelo J Fdez-Aguera + + Bug & warning fixes + Marc LeBlanc David Woo Guillaume George Martins Mozeiko + Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski + Phil Jordan Dave Moore Roy Eltham + Hayaki Saito Nathan Reed Won Chun + Luke Graham Johan Duparc Nick Verigakis the Horde3D community + Thomas Ruf Ronny Chevalier github:rlyeh + Janez Zemva John Bartholomew Michal Cichon github:romigrou + Jonathan Blow Ken Hamada Tero Hanninen github:svdijk + Eugene Golushkov Laurent Gomila Cort Stratton github:snagar + Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex + Cass Everitt Ryamond Barbiero github:grim210 + Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw + Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus + Josh Tobin Neil Bickford Matthew Gregan github:poppolopoppo + Julian Raschke Gregory Mullen Christian Floisand github:darealshinji + Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 + Brad Weinberger Matvey Cherevko github:mosra + Luca Sas Alexander Veselov Zack Middleton [reserved] + Ryan C. Gordon [reserved] [reserved] + DO NOT ADD YOUR NAME HERE + + Jacko Dirks + + To add your name to the credits, pick a random blank space in the middle and fill it. + 80% of merge conflicts on stb PRs are due to people adding their name at the end + of the credits. +*/ + +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// DOCUMENTATION +// +// Limitations: +// - no 12-bit-per-channel JPEG +// - no JPEGs with arithmetic coding +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below for HDR usage): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// // ... but 'n' will always be the number that it would have been if you said 0 +// stbi_image_free(data); +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *channels_in_file -- outputs # of image components in image file +// int desired_channels -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data, or NULL on an allocation failure or if the image is +// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'desired_channels' if desired_channels is non-zero, or +// *channels_in_file otherwise. If desired_channels is non-zero, +// *channels_in_file has the number of components that _would_ have been +// output otherwise. E.g. if you set desired_channels to 4, you will always +// get RGBA output, but you can check *channels_in_file to see if it's trivially +// opaque because e.g. there were only 3 channels in the source image. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *channels_in_file will be unchanged. The function +// stbi_failure_reason() can be queried for an extremely brief, end-user +// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS +// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. +// +// To query the width, height and component count of an image without having to +// decode the full file, you can use the stbi_info family of functions: +// +// int x,y,n,ok; +// ok = stbi_info(filename, &x, &y, &n); +// // returns ok=1 and sets x, y, n if image is a supported format, +// // 0 otherwise. +// +// Note that stb_image pervasively uses ints in its public API for sizes, +// including sizes of memory buffers. This is now part of the API and thus +// hard to change without causing breakage. As a result, the various image +// loaders all have certain limits on image size; these differ somewhat +// by format but generally boil down to either just under 2GB or just under +// 1GB. When the decoded image would be larger than this, stb_image decoding +// will fail. +// +// Additionally, stb_image will reject image files that have any of their +// dimensions set to a larger value than the configurable STBI_MAX_DIMENSIONS, +// which defaults to 2**24 = 16777216 pixels. Due to the above memory limit, +// the only way to have an image with such dimensions load correctly +// is for it to have a rather extreme aspect ratio. Either way, the +// assumption here is that such larger images are likely to be malformed +// or malicious. If you do need to load an image with individual dimensions +// larger than that, and it still fits in the overall size limit, you can +// #define STBI_MAX_DIMENSIONS on your own to be something larger. +// +// =========================================================================== +// +// UNICODE: +// +// If compiling for Windows and you wish to use Unicode filenames, compile +// with +// #define STBI_WINDOWS_UTF8 +// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert +// Windows wchar_t filenames to utf8. +// +// =========================================================================== +// +// Philosophy +// +// stb libraries are designed with the following priorities: +// +// 1. easy to use +// 2. easy to maintain +// 3. good performance +// +// Sometimes I let "good performance" creep up in priority over "easy to maintain", +// and for best performance I may provide less-easy-to-use APIs that give higher +// performance, in addition to the easy-to-use ones. Nevertheless, it's important +// to keep in mind that from the standpoint of you, a client of this library, +// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. +// +// Some secondary priorities arise directly from the first two, some of which +// provide more explicit reasons why performance can't be emphasized. +// +// - Portable ("ease of use") +// - Small source code footprint ("easy to maintain") +// - No dependencies ("ease of use") +// +// =========================================================================== +// +// I/O callbacks +// +// I/O callbacks allow you to read from arbitrary sources, like packaged +// files or some other source. Data read from callbacks are processed +// through a small internal buffer (currently 128 bytes) to try to reduce +// overhead. +// +// The three functions you must define are "read" (reads some bytes of data), +// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). +// +// =========================================================================== +// +// SIMD support +// +// The JPEG decoder will try to automatically use SIMD kernels on x86 when +// supported by the compiler. For ARM Neon support, you must explicitly +// request it. +// +// (The old do-it-yourself SIMD API is no longer supported in the current +// code.) +// +// On x86, SSE2 will automatically be used when available based on a run-time +// test; if not, the generic C versions are used as a fall-back. On ARM targets, +// the typical path is to have separate builds for NEON and non-NEON devices +// (at least this is true for iOS and Android). Therefore, the NEON support is +// toggled by a build flag: define STBI_NEON to get NEON loops. +// +// If for some reason you do not want to use any of SIMD code, or if +// you have issues compiling it, you can disable it entirely by +// defining STBI_NO_SIMD. +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image supports loading HDR images in general, and currently the Radiance +// .HDR file format specifically. You can still load any file through the existing +// interface; if you attempt to load an HDR file, it will be automatically remapped +// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); +// +// =========================================================================== +// +// iPhone PNG support: +// +// We optionally support converting iPhone-formatted PNGs (which store +// premultiplied BGRA) back to RGB, even though they're internally encoded +// differently. To enable this conversion, call +// stbi_convert_iphone_png_to_rgb(1). +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). +// +// =========================================================================== +// +// ADDITIONAL CONFIGURATION +// +// - You can suppress implementation of any of the decoders to reduce +// your code footprint by #defining one or more of the following +// symbols before creating the implementation. +// +// STBI_NO_JPEG +// STBI_NO_PNG +// STBI_NO_BMP +// STBI_NO_PSD +// STBI_NO_TGA +// STBI_NO_GIF +// STBI_NO_HDR +// STBI_NO_PIC +// STBI_NO_PNM (.ppm and .pgm) +// +// - You can request *only* certain decoders and suppress all other ones +// (this will be more forward-compatible, as addition of new decoders +// doesn't require you to disable them explicitly): +// +// STBI_ONLY_JPEG +// STBI_ONLY_PNG +// STBI_ONLY_BMP +// STBI_ONLY_PSD +// STBI_ONLY_TGA +// STBI_ONLY_GIF +// STBI_ONLY_HDR +// STBI_ONLY_PIC +// STBI_ONLY_PNM (.ppm and .pgm) +// +// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still +// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB +// +// - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater +// than that size (in either width or height) without further processing. +// This is to let programs in the wild set an upper bound to prevent +// denial-of-service attacks on untrusted data, as one could generate a +// valid image of gigantic dimensions and force stb_image to allocate a +// huge block of memory and spend disproportionate time decoding it. By +// default this is set to (1 << 24), which is 16777216, but that's still +// very big. + +#ifndef STBI_NO_STDIO +#include +#endif // STBI_NO_STDIO + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for desired_channels + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +#include +typedef unsigned char stbi_uc; +typedef unsigned short stbi_us; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef STBIDEF +#ifdef STB_IMAGE_STATIC +#define STBIDEF static +#else +#define STBIDEF extern +#endif +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +//////////////////////////////////// +// +// 8-bits-per-channel interface +// + +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +#endif + +#ifdef STBI_WINDOWS_UTF8 +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif + +//////////////////////////////////// +// +// 16-bits-per-channel interface +// + +STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +#endif + +//////////////////////////////////// +// +// float-per-channel interface +// +#ifndef STBI_NO_LINEAR +STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +#endif +#endif + +#ifndef STBI_NO_HDR +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); +STBIDEF void stbi_hdr_to_ldr_scale(float scale); +#endif // STBI_NO_HDR + +#ifndef STBI_NO_LINEAR +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); +STBIDEF void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_LINEAR + +// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename); +STBIDEF int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// on most compilers (and ALL modern mainstream compilers) this is threadsafe +STBIDEF const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +STBIDEF void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit (char const *filename); +STBIDEF int stbi_is_16_bit_from_file(FILE *f); +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + +// flip the image vertically, so the first pixel in the output array is the bottom left +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); + +// as above, but only applies to images loaded on the thread that calls the function +// this function is only available if your compiler supports thread-local variables; +// calling it will fail to link if your compiler doesn't +STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply); +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert); +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); + +// ZLIB client - used by PNG, available for other purposes + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifdef STB_IMAGE_IMPLEMENTATION + +#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ + || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ + || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ + || defined(STBI_ONLY_ZLIB) + #ifndef STBI_ONLY_JPEG + #define STBI_NO_JPEG + #endif + #ifndef STBI_ONLY_PNG + #define STBI_NO_PNG + #endif + #ifndef STBI_ONLY_BMP + #define STBI_NO_BMP + #endif + #ifndef STBI_ONLY_PSD + #define STBI_NO_PSD + #endif + #ifndef STBI_ONLY_TGA + #define STBI_NO_TGA + #endif + #ifndef STBI_ONLY_GIF + #define STBI_NO_GIF + #endif + #ifndef STBI_ONLY_HDR + #define STBI_NO_HDR + #endif + #ifndef STBI_ONLY_PIC + #define STBI_NO_PIC + #endif + #ifndef STBI_ONLY_PNM + #define STBI_NO_PNM + #endif +#endif + +#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) +#define STBI_NO_ZLIB +#endif + + +#include +#include // ptrdiff_t on osx +#include +#include +#include + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#include // ldexp, pow +#endif + +#ifndef STBI_NO_STDIO +#include +#endif + +#ifndef STBI_ASSERT +#include +#define STBI_ASSERT(x) assert(x) +#endif + +#ifdef __cplusplus +#define STBI_EXTERN extern "C" +#else +#define STBI_EXTERN extern +#endif + + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + +#ifndef STBI_NO_THREAD_LOCALS + #if defined(__cplusplus) && __cplusplus >= 201103L + #define STBI_THREAD_LOCAL thread_local + #elif defined(__GNUC__) && __GNUC__ < 5 + #define STBI_THREAD_LOCAL __thread + #elif defined(_MSC_VER) + #define STBI_THREAD_LOCAL __declspec(thread) + #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) + #define STBI_THREAD_LOCAL _Thread_local + #endif + + #ifndef STBI_THREAD_LOCAL + #if defined(__GNUC__) + #define STBI_THREAD_LOCAL __thread + #endif + #endif +#endif + +#if defined(_MSC_VER) || defined(__SYMBIAN32__) +typedef unsigned short stbi__uint16; +typedef signed short stbi__int16; +typedef unsigned int stbi__uint32; +typedef signed int stbi__int32; +#else +#include +typedef uint16_t stbi__uint16; +typedef int16_t stbi__int16; +typedef uint32_t stbi__uint32; +typedef int32_t stbi__int32; +#endif + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBI_NOTUSED(v) (void)(v) +#else +#define STBI_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (-(y) & 31))) +#endif + +#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) +// ok +#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." +#endif + +#ifndef STBI_MALLOC +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,newsz) realloc(p,newsz) +#define STBI_FREE(p) free(p) +#endif + +#ifndef STBI_REALLOC_SIZED +#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) +#endif + +// x86/x64 detection +#if defined(__x86_64__) || defined(_M_X64) +#define STBI__X64_TARGET +#elif defined(__i386) || defined(_M_IX86) +#define STBI__X86_TARGET +#endif + +#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +// gcc doesn't support sse2 intrinsics unless you compile with -msse2, +// which in turn means it gets to use SSE2 everywhere. This is unfortunate, +// but previous attempts to provide the SSE2 functions with runtime +// detection caused numerous issues. The way architecture extensions are +// exposed in GCC/Clang is, sadly, not really suited for one-file libs. +// New behavior: if compiled with -msse2, we use SSE2 without any +// detection; if not, we don't use it at all. +#define STBI_NO_SIMD +#endif + +#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) +// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET +// +// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the +// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. +// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not +// simultaneously enabling "-mstackrealign". +// +// See https://github.com/nothings/stb/issues/81 for more information. +// +// So default to no SSE2 on 32-bit MinGW. If you've read this far and added +// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. +#define STBI_NO_SIMD +#endif + +#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) +#define STBI_SSE2 +#include + +#ifdef _MSC_VER + +#if _MSC_VER >= 1400 // not VC6 +#include // __cpuid +static int stbi__cpuid3(void) +{ + int info[4]; + __cpuid(info,1); + return info[3]; +} +#else +static int stbi__cpuid3(void) +{ + int res; + __asm { + mov eax,1 + cpuid + mov res,edx + } + return res; +} +#endif + +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + int info3 = stbi__cpuid3(); + return ((info3 >> 26) & 1) != 0; +} +#endif + +#else // assume GCC-style if not VC++ +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + // If we're even attempting to compile this on GCC/Clang, that means + // -msse2 is on, which means the compiler is allowed to use SSE2 + // instructions at will, and so are we. + return 1; +} +#endif + +#endif +#endif + +// ARM NEON +#if defined(STBI_NO_SIMD) && defined(STBI_NEON) +#undef STBI_NEON +#endif + +#ifdef STBI_NEON +#include +#ifdef _MSC_VER +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name +#else +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#endif +#endif + +#ifndef STBI_SIMD_ALIGN +#define STBI_SIMD_ALIGN(type, name) type name +#endif + +#ifndef STBI_MAX_DIMENSIONS +#define STBI_MAX_DIMENSIONS (1 << 24) +#endif + +/////////////////////////////////////////////// +// +// stbi__context struct and start_xxx functions + +// stbi__context structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + stbi__uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + stbi_uc buffer_start[128]; + int callback_already_read; + + stbi_uc *img_buffer, *img_buffer_end; + stbi_uc *img_buffer_original, *img_buffer_original_end; +} stbi__context; + + +static void stbi__refill_buffer(stbi__context *s); + +// initialize a memory-decode context +static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; +} + +// initialize a callback-based context +static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; +} + +#ifndef STBI_NO_STDIO + +static int stbi__stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stbi__stdio_skip(void *user, int n) +{ + int ch; + fseek((FILE*) user, n, SEEK_CUR); + ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ + if (ch != EOF) { + ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ + } +} + +static int stbi__stdio_eof(void *user) +{ + return feof((FILE*) user) || ferror((FILE *) user); +} + +static stbi_io_callbacks stbi__stdio_callbacks = +{ + stbi__stdio_read, + stbi__stdio_skip, + stbi__stdio_eof, +}; + +static void stbi__start_file(stbi__context *s, FILE *f) +{ + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi__context *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi__rewind(stbi__context *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; +} + +enum +{ + STBI_ORDER_RGB, + STBI_ORDER_BGR +}; + +typedef struct +{ + int bits_per_channel; + int num_channels; + int channel_order; +} stbi__result_info; + +#ifndef STBI_NO_JPEG +static int stbi__jpeg_test(stbi__context *s); +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNG +static int stbi__png_test(stbi__context *s); +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__png_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_BMP +static int stbi__bmp_test(stbi__context *s); +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_TGA +static int stbi__tga_test(stbi__context *s); +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s); +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__psd_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_HDR +static int stbi__hdr_test(stbi__context *s); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_test(stbi__context *s); +static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_GIF +static int stbi__gif_test(stbi__context *s); +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNM +static int stbi__pnm_test(stbi__context *s); +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__pnm_is16(stbi__context *s); +#endif + +static +#ifdef STBI_THREAD_LOCAL +STBI_THREAD_LOCAL +#endif +const char *stbi__g_failure_reason; + +STBIDEF const char *stbi_failure_reason(void) +{ + return stbi__g_failure_reason; +} + +#ifndef STBI_NO_FAILURE_STRINGS +static int stbi__err(const char *str) +{ + stbi__g_failure_reason = str; + return 0; +} +#endif + +static void *stbi__malloc(size_t size) +{ + return STBI_MALLOC(size); +} + +// stb_image uses ints pervasively, including for offset calculations. +// therefore the largest decoded image size we can support with the +// current code, even on 64-bit targets, is INT_MAX. this is not a +// significant limitation for the intended use case. +// +// we do, however, need to make sure our size calculations don't +// overflow. hence a few helper functions for size calculations that +// multiply integers together, making sure that they're non-negative +// and no overflow occurs. + +// return 1 if the sum is valid, 0 on overflow. +// negative terms are considered invalid. +static int stbi__addsizes_valid(int a, int b) +{ + if (b < 0) return 0; + // now 0 <= b <= INT_MAX, hence also + // 0 <= INT_MAX - b <= INTMAX. + // And "a + b <= INT_MAX" (which might overflow) is the + // same as a <= INT_MAX - b (no overflow) + return a <= INT_MAX - b; +} + +// returns 1 if the product is valid, 0 on overflow. +// negative factors are considered invalid. +static int stbi__mul2sizes_valid(int a, int b) +{ + if (a < 0 || b < 0) return 0; + if (b == 0) return 1; // mul-by-0 is always safe + // portable way to check for no overflows in a*b + return a <= INT_MAX/b; +} + +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) +// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow +static int stbi__mad2sizes_valid(int a, int b, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); +} +#endif + +// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow +static int stbi__mad3sizes_valid(int a, int b, int c, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__addsizes_valid(a*b*c, add); +} + +// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) +static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); +} +#endif + +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) +// mallocs with size overflow checking +static void *stbi__malloc_mad2(int a, int b, int add) +{ + if (!stbi__mad2sizes_valid(a, b, add)) return NULL; + return stbi__malloc(a*b + add); +} +#endif + +static void *stbi__malloc_mad3(int a, int b, int c, int add) +{ + if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; + return stbi__malloc(a*b*c + add); +} + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) +static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) +{ + if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; + return stbi__malloc(a*b*c*d + add); +} +#endif + +// returns 1 if the sum of two signed ints is valid (between -2^31 and 2^31-1 inclusive), 0 on overflow. +static int stbi__addints_valid(int a, int b) +{ + if ((a >= 0) != (b >= 0)) return 1; // a and b have different signs, so no overflow + if (a < 0 && b < 0) return a >= INT_MIN - b; // same as a + b >= INT_MIN; INT_MIN - b cannot overflow since b < 0. + return a <= INT_MAX - b; +} + +// returns 1 if the product of two signed shorts is valid, 0 on overflow. +static int stbi__mul2shorts_valid(short a, short b) +{ + if (b == 0 || b == -1) return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow + if ((a >= 0) == (b >= 0)) return a <= SHRT_MAX/b; // product is positive, so similar to mul2sizes_valid + if (b < 0) return a <= SHRT_MIN / b; // same as a * b >= SHRT_MIN + return a >= SHRT_MIN / b; +} + +// stbi__err - error +// stbi__errpf - error returning pointer to float +// stbi__errpuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define stbi__err(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define stbi__err(x,y) stbi__err(y) +#else + #define stbi__err(x,y) stbi__err(x) +#endif + +#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) + +STBIDEF void stbi_image_free(void *retval_from_stbi_load) +{ + STBI_FREE(retval_from_stbi_load); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_HDR +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static int stbi__vertically_flip_on_load_global = 0; + +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_global = flag_true_if_should_flip; +} + +#ifndef STBI_THREAD_LOCAL +#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global +#else +static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; + +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_local = flag_true_if_should_flip; + stbi__vertically_flip_on_load_set = 1; +} + +#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ + ? stbi__vertically_flip_on_load_local \ + : stbi__vertically_flip_on_load_global) +#endif // STBI_THREAD_LOCAL + +static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields + ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed + ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order + ri->num_channels = 0; + + // test the formats with a very explicit header first (at least a FOURCC + // or distinctive magic number first) + #ifndef STBI_NO_PNG + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_BMP + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_GIF + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PSD + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); + #else + STBI_NOTUSED(bpc); + #endif + #ifndef STBI_NO_PIC + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); + #endif + + // then the formats that can end up attempting to load with just 1 or 2 + // bytes matching expectations; these are prone to false positives, so + // try them later + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + #ifndef STBI_NO_TGA + // test tga last because it's a crappy test! + if (stbi__tga_test(s)) + return stbi__tga_load(s,x,y,comp,req_comp, ri); + #endif + + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); +} + +static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi_uc *reduced; + + reduced = (stbi_uc *) stbi__malloc(img_len); + if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling + + STBI_FREE(orig); + return reduced; +} + +static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi__uint16 *enlarged; + + enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); + if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff + + STBI_FREE(orig); + return enlarged; +} + +static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) +{ + int row; + size_t bytes_per_row = (size_t)w * bytes_per_pixel; + stbi_uc temp[2048]; + stbi_uc *bytes = (stbi_uc *)image; + + for (row = 0; row < (h>>1); row++) { + stbi_uc *row0 = bytes + row*bytes_per_row; + stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; + // swap row0 with row1 + size_t bytes_left = bytes_per_row; + while (bytes_left) { + size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); + memcpy(temp, row0, bytes_copy); + memcpy(row0, row1, bytes_copy); + memcpy(row1, temp, bytes_copy); + row0 += bytes_copy; + row1 += bytes_copy; + bytes_left -= bytes_copy; + } + } +} + +#ifndef STBI_NO_GIF +static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) +{ + int slice; + int slice_size = w * h * bytes_per_pixel; + + stbi_uc *bytes = (stbi_uc *)image; + for (slice = 0; slice < z; ++slice) { + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; + } +} +#endif + +static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); + + if (result == NULL) + return NULL; + + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + + if (ri.bits_per_channel != 8) { + result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 8; + } + + // @TODO: move stbi__convert_format to here + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); + } + + return (unsigned char *) result; +} + +static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); + + if (result == NULL) + return NULL; + + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + + if (ri.bits_per_channel != 16) { + result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 16; + } + + // @TODO: move stbi__convert_format16 to here + // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); + } + + return (stbi__uint16 *) result; +} + +#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) +static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) +{ + if (stbi__vertically_flip_on_load && result != NULL) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); + } +} +#endif + +#ifndef STBI_NO_STDIO + +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) +STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); +#endif + +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + +static FILE *stbi__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) + return 0; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + + +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + unsigned char *result; + if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__uint16 *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + stbi__uint16 *result; + if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file_16(f,x,y,comp,req_comp); + fclose(f); + return result; +} + + +#endif //!STBI_NO_STDIO + +STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_mem(&s,buffer,len); + + result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); + if (stbi__vertically_flip_on_load) { + stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); + } + + return result; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + stbi__result_info ri; + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); + if (hdr_data) + stbi__float_postprocess(hdr_data,x,y,comp,req_comp); + return hdr_data; + } + #endif + data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); + if (data) + return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); +} + +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + float *result; + FILE *f = stbi__fopen(filename, "rb"); + if (!f) return stbi__errpf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_file(&s,f); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_LINEAR + +// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is +// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always +// reports false! + +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +STBIDEF int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + long pos = ftell(f); + int res; + stbi__context s; + stbi__start_file(&s,f); + res = stbi__hdr_test(&s); + fseek(f, pos, SEEK_SET); + return res; + #else + STBI_NOTUSED(f); + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); + return 0; + #endif +} + +#ifndef STBI_NO_LINEAR +static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; + +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } +STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } +#endif + +static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; + +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } +STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + STBI__SCAN_load=0, + STBI__SCAN_type, + STBI__SCAN_header +}; + +static void stbi__refill_buffer(stbi__context *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static stbi_uc stbi__get8(stbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +stbi_inline static int stbi__at_eof(stbi__context *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} +#endif + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) +// nothing +#else +static void stbi__skip(stbi__context *s, int n) +{ + if (n == 0) return; // already there! + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) +// nothing +#else +static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} +#endif + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else +static int stbi__get16be(stbi__context *s) +{ + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else +static stbi__uint32 stbi__get32be(stbi__context *s) +{ + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); +} +#endif + +#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) +// nothing +#else +static int stbi__get16le(stbi__context *s) +{ + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); +} +#endif + +#ifndef STBI_NO_BMP +static stbi__uint32 stbi__get32le(stbi__context *s) +{ + stbi__uint32 z = stbi__get16le(s); + z += (stbi__uint32)stbi__get16le(s) << 16; + return z; +} +#endif + +#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static stbi_uc stbi__compute_y(int r, int g, int b) +{ + return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else +static stbi__uint16 stbi__compute_y_16(int r, int g, int b) +{ + return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else +static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + stbi__uint16 *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); + if (good == NULL) { + STBI_FREE(data); + return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + stbi__uint16 *src = data + j * x * img_n ; + stbi__uint16 *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output; + if (!data) return NULL; + output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + } + } + if (n < comp) { + for (i=0; i < x*y; ++i) { + output[i*comp + n] = data[i*comp + n]/255.0f; + } + } + STBI_FREE(data); + return output; +} +#endif + +#ifndef STBI_NO_HDR +#define stbi__float2int(x) ((int) (x)) +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output; + if (!data) return NULL; + output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + } + STBI_FREE(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder +// +// simple implementation +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - some SIMD kernels for common paths on targets with SSE2/NEON +// - uses a lot of intermediate memory, could cache poorly + +#ifndef STBI_NO_JPEG + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + stbi_uc fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi_uc values[256]; + stbi_uc size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} stbi__huffman; + +typedef struct +{ + stbi__context *s; + stbi__huffman huff_dc[4]; + stbi__huffman huff_ac[4]; + stbi__uint16 dequant[4][64]; + stbi__int16 fast_ac[4][1 << FAST_BITS]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + stbi_uc *data; + void *raw_data, *raw_coeff; + stbi_uc *linebuf; + short *coeff; // progressive only + int coeff_w, coeff_h; // number of 8x8 coefficient blocks + } img_comp[4]; + + stbi__uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int progressive; + int spec_start; + int spec_end; + int succ_high; + int succ_low; + int eob_run; + int jfif; + int app14_color_transform; // Adobe APP14 tag + int rgb; + + int scan_n, order[4]; + int restart_interval, todo; + +// kernels + void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); + void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); + stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); +} stbi__jpeg; + +static int stbi__build_huffman(stbi__huffman *h, int *count) +{ + int i,j,k=0; + unsigned int code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) { + for (j=0; j < count[i]; ++j) { + h->size[k++] = (stbi_uc) (i+1); + if(k >= 257) return stbi__err("bad size list","Corrupt JPEG"); + } + } + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16) (code++); + if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (stbi_uc) i; + } + } + } + return 1; +} + +// build a table that decodes both magnitude and value of small ACs in +// one go. +static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) +{ + int i; + for (i=0; i < (1 << FAST_BITS); ++i) { + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; + + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) k += (~0U << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); + } + } + } +} + +static void stbi__grow_buffer_unsafe(stbi__jpeg *j) +{ + do { + unsigned int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + if(c < 0 || c >= 256) // symbol id out of bounds! + return -1; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); + if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing + + sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative) + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k + (stbi__jbias[n] & (sgn - 1)); +} + +// get some unsigned bits +stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) +{ + unsigned int k; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k; +} + +stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) +{ + unsigned int k; + if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + if (j->code_bits < 1) return 0; // ran out of bits from stream, return 0s intead of continuing + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; + return k & 0x80000000; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static const stbi_uc stbi__jpeg_dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) +{ + int diff,dc,k; + int t; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) return stbi__err("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? stbi__extend_receive(j, t) : 0; + if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta","Corrupt JPEG"); + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + if ((dc > SHRT_MAX) || (dequant[0] > SHRT_MAX) || !stbi__mul2shorts_valid((short) dc, (short) dequant[0])) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + data[0] = (short) (dc * dequant[0]); + + // decode AC components, see JPEG spec + k = 1; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); + j->code_buffer <<= s; + j->code_bits -= s; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * dequant[zig]); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); + } + } + } while (k < 64); + return 1; +} + +static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) +{ + int diff,dc; + int t; + if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + if (j->succ_high == 0) { + // first scan for DC coefficient, must be first + memset(data,0,64*sizeof(data[0])); // 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + diff = t ? stbi__extend_receive(j, t) : 0; + + if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta", "Corrupt JPEG"); + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + if ((dc > SHRT_MAX) || !stbi__mul2shorts_valid((short) dc, 1 << j->succ_low)) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + data[0] = (short) (dc * (1 << j->succ_low)); + } else { + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short) (1 << j->succ_low); + } + return 1; +} + +// @OPTIMIZE: store non-zigzagged during the decode passes, +// and only de-zigzag when dequantizing +static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) +{ + int k; + if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->succ_high == 0) { + int shift = j->succ_low; + + if (j->eob_run) { + --j->eob_run; + return 1; + } + + k = j->spec_start; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * (1 << shift)); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * (1 << shift)); + } + } + } while (k <= j->spec_end); + } else { + // refinement scan for these AC coefficients + + short bit = (short) (1 << j->succ_low); + + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } else { + k = j->spec_start; + do { + int r,s; + int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64; // force end of block + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } + } else { + if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } + + // advance by r + while (k <= j->spec_end) { + short *p = &data[stbi__jpeg_dezigzag[k++]]; + if (*p != 0) { + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } else { + if (r == 0) { + *p = (short) s; + break; + } + --r; + } + } + } while (k <= j->spec_end); + } + } + return 1; +} + +// take a -128..127 value and stbi__clamp it and convert to 0..255 +stbi_inline static stbi_uc stbi__clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (stbi_uc) x; +} + +#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) +#define stbi__fsh(x) ((x) * 4096) + +// derived from jidctint -- DCT_ISLOW +#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * stbi__f2f(0.5411961f); \ + t2 = p1 + p3*stbi__f2f(-1.847759065f); \ + t3 = p1 + p2*stbi__f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = stbi__fsh(p2+p3); \ + t1 = stbi__fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ + t0 = t0*stbi__f2f( 0.298631336f); \ + t1 = t1*stbi__f2f( 2.053119869f); \ + t2 = t2*stbi__f2f( 3.072711026f); \ + t3 = t3*stbi__f2f( 1.501321110f); \ + p1 = p5 + p1*stbi__f2f(-0.899976223f); \ + p2 = p5 + p2*stbi__f2f(-2.562915447f); \ + p3 = p3*stbi__f2f(-1.961570560f); \ + p4 = p4*stbi__f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) +{ + int i,val[64],*v=val; + stbi_uc *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0]*4; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = stbi__clamp((x0+t3) >> 17); + o[7] = stbi__clamp((x0-t3) >> 17); + o[1] = stbi__clamp((x1+t2) >> 17); + o[6] = stbi__clamp((x1-t2) >> 17); + o[2] = stbi__clamp((x2+t1) >> 17); + o[5] = stbi__clamp((x2-t1) >> 17); + o[3] = stbi__clamp((x3+t0) >> 17); + o[4] = stbi__clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SSE2 +// sse2 integer IDCT. not the fastest possible implementation but it +// produces bit-identical results to the generic C version so it's +// fully "transparent". +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + // This is constructed to match our regular (generic) integer IDCT exactly. + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + __m128i tmp; + + // dot product constant: even elems=x, odd elems=y + #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) + + // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) + // out(1) = c1[even]*x + c1[odd]*y + #define dct_rot(out0,out1, x,y,c0,c1) \ + __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ + __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ + __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ + __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ + __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ + __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) + + // out = in << 12 (in 16-bit, out 32-bit) + #define dct_widen(out, in) \ + __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ + __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) + + // wide add + #define dct_wadd(out, a, b) \ + __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_add_epi32(a##_h, b##_h) + + // wide sub + #define dct_wsub(out, a, b) \ + __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) + + // butterfly a/b, add bias, then shift by "s" and pack + #define dct_bfly32o(out0, out1, a,b,bias,s) \ + { \ + __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ + __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ + dct_wadd(sum, abiased, b); \ + dct_wsub(dif, abiased, b); \ + out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ + out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ + } + + // 8-bit interleave step (for transposes) + #define dct_interleave8(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi8(a, b); \ + b = _mm_unpackhi_epi8(tmp, b) + + // 16-bit interleave step (for transposes) + #define dct_interleave16(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi16(a, b); \ + b = _mm_unpackhi_epi16(tmp, b) + + #define dct_pass(bias,shift) \ + { \ + /* even part */ \ + dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ + __m128i sum04 = _mm_add_epi16(row0, row4); \ + __m128i dif04 = _mm_sub_epi16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ + dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ + __m128i sum17 = _mm_add_epi16(row1, row7); \ + __m128i sum35 = _mm_add_epi16(row3, row5); \ + dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ + dct_wadd(x4, y0o, y4o); \ + dct_wadd(x5, y1o, y5o); \ + dct_wadd(x6, y2o, y5o); \ + dct_wadd(x7, y3o, y4o); \ + dct_bfly32o(row0,row7, x0,x7,bias,shift); \ + dct_bfly32o(row1,row6, x1,x6,bias,shift); \ + dct_bfly32o(row2,row5, x2,x5,bias,shift); \ + dct_bfly32o(row3,row4, x3,x4,bias,shift); \ + } + + __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); + __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); + __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); + __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); + __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); + __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); + __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); + __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); + + // rounding biases in column/row passes, see stbi__idct_block for explanation. + __m128i bias_0 = _mm_set1_epi32(512); + __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); + + // load + row0 = _mm_load_si128((const __m128i *) (data + 0*8)); + row1 = _mm_load_si128((const __m128i *) (data + 1*8)); + row2 = _mm_load_si128((const __m128i *) (data + 2*8)); + row3 = _mm_load_si128((const __m128i *) (data + 3*8)); + row4 = _mm_load_si128((const __m128i *) (data + 4*8)); + row5 = _mm_load_si128((const __m128i *) (data + 5*8)); + row6 = _mm_load_si128((const __m128i *) (data + 6*8)); + row7 = _mm_load_si128((const __m128i *) (data + 7*8)); + + // column pass + dct_pass(bias_0, 10); + + { + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); + + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); + + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); + } + + // row pass + dct_pass(bias_1, 17); + + { + // pack + __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); + + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2); // a0e0a1e1... + dct_interleave8(p1, p3); // c0g0c1g1... + + // transpose pass 2 + dct_interleave8(p0, p1); // a0c0e0g0... + dct_interleave8(p2, p3); // b0d0f0h0... + + // transpose pass 3 + dct_interleave8(p0, p2); // a0b0c0d0... + dct_interleave8(p1, p3); // a4b4c4d4... + + // store + _mm_storel_epi64((__m128i *) out, p0); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p2); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p1); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p3); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); + } + +#undef dct_const +#undef dct_rot +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_interleave8 +#undef dct_interleave16 +#undef dct_pass +} + +#endif // STBI_SSE2 + +#ifdef STBI_NEON + +// NEON integer IDCT. should produce bit-identical +// results to the generic C version. +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; + + int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); + int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); + int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); + int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); + int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); + int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); + int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); + int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); + int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); + int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); + int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); + int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); + +#define dct_long_mul(out, inq, coeff) \ + int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) + +#define dct_long_mac(out, acc, inq, coeff) \ + int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) + +#define dct_widen(out, inq) \ + int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ + int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) + +// wide add +#define dct_wadd(out, a, b) \ + int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vaddq_s32(a##_h, b##_h) + +// wide sub +#define dct_wsub(out, a, b) \ + int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vsubq_s32(a##_h, b##_h) + +// butterfly a/b, then shift using "shiftop" by "s" and pack +#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ + { \ + dct_wadd(sum, a, b); \ + dct_wsub(dif, a, b); \ + out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ + out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ + } + +#define dct_pass(shiftop, shift) \ + { \ + /* even part */ \ + int16x8_t sum26 = vaddq_s16(row2, row6); \ + dct_long_mul(p1e, sum26, rot0_0); \ + dct_long_mac(t2e, p1e, row6, rot0_1); \ + dct_long_mac(t3e, p1e, row2, rot0_2); \ + int16x8_t sum04 = vaddq_s16(row0, row4); \ + int16x8_t dif04 = vsubq_s16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + int16x8_t sum15 = vaddq_s16(row1, row5); \ + int16x8_t sum17 = vaddq_s16(row1, row7); \ + int16x8_t sum35 = vaddq_s16(row3, row5); \ + int16x8_t sum37 = vaddq_s16(row3, row7); \ + int16x8_t sumodd = vaddq_s16(sum17, sum35); \ + dct_long_mul(p5o, sumodd, rot1_0); \ + dct_long_mac(p1o, p5o, sum17, rot1_1); \ + dct_long_mac(p2o, p5o, sum35, rot1_2); \ + dct_long_mul(p3o, sum37, rot2_0); \ + dct_long_mul(p4o, sum15, rot2_1); \ + dct_wadd(sump13o, p1o, p3o); \ + dct_wadd(sump24o, p2o, p4o); \ + dct_wadd(sump23o, p2o, p3o); \ + dct_wadd(sump14o, p1o, p4o); \ + dct_long_mac(x4, sump13o, row7, rot3_0); \ + dct_long_mac(x5, sump24o, row5, rot3_1); \ + dct_long_mac(x6, sump23o, row3, rot3_2); \ + dct_long_mac(x7, sump14o, row1, rot3_3); \ + dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ + dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ + dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ + dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ + } + + // load + row0 = vld1q_s16(data + 0*8); + row1 = vld1q_s16(data + 1*8); + row2 = vld1q_s16(data + 2*8); + row3 = vld1q_s16(data + 3*8); + row4 = vld1q_s16(data + 4*8); + row5 = vld1q_s16(data + 5*8); + row6 = vld1q_s16(data + 6*8); + row7 = vld1q_s16(data + 7*8); + + // add DC bias + row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); + + // column pass + dct_pass(vrshrn_n_s32, 10); + + // 16bit 8x8 transpose + { +// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. +// whether compilers actually get this is another story, sadly. +#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } +#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } + + // pass 1 + dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); + + // pass 2 + dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); + + // pass 3 + dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); + +#undef dct_trn16 +#undef dct_trn32 +#undef dct_trn64 + } + + // row pass + // vrshrn_n_s32 only supports shifts up to 16, we need + // 17. so do a non-rounding shift of 16 first then follow + // up with a rounding shift by 1. + dct_pass(vshrn_n_s32, 16); + + { + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + + // again, these can translate into one instruction, but often don't. +#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } +#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } + + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! + + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); + + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); + + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); + + // store + vst1_u8(out, p0); out += out_stride; + vst1_u8(out, p1); out += out_stride; + vst1_u8(out, p2); out += out_stride; + vst1_u8(out, p3); out += out_stride; + vst1_u8(out, p4); out += out_stride; + vst1_u8(out, p5); out += out_stride; + vst1_u8(out, p6); out += out_stride; + vst1_u8(out, p7); + +#undef dct_trn8_8 +#undef dct_trn8_16 +#undef dct_trn8_32 + } + +#undef dct_long_mul +#undef dct_long_mac +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_pass +} + +#endif // STBI_NEON + +#define STBI__MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static stbi_uc stbi__get_marker(stbi__jpeg *j) +{ + stbi_uc x; + if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } + x = stbi__get8(j->s); + if (x != 0xff) return STBI__MARKER_none; + while (x == 0xff) + x = stbi__get8(j->s); // consume repeated 0xff fill bytes + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, stbi__jpeg_reset the entropy decoder and +// the dc prediction +static void stbi__jpeg_reset(stbi__jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; + j->marker = STBI__MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + j->eob_run = 0; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int stbi__parse_entropy_coded_data(stbi__jpeg *z) +{ + stbi__jpeg_reset(z); + if (!z->progressive) { + if (z->scan_n == 1) { + int i,j; + STBI_SIMD_ALIGN(short, data[64]); + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + STBI_SIMD_ALIGN(short, data[64]); + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } else { + if (z->scan_n == 1) { + int i,j; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + if (z->spec_start == 0) { + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x); + int y2 = (j*z->img_comp[n].v + y); + short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } +} + +static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) +{ + int i; + for (i=0; i < 64; ++i) + data[i] *= dequant[i]; +} + +static void stbi__jpeg_finish(stbi__jpeg *z) +{ + if (z->progressive) { + // dequantize and idct the data + int i,j,n; + for (n=0; n < z->s->img_n; ++n) { + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + } + } + } + } +} + +static int stbi__process_marker(stbi__jpeg *z, int m) +{ + int L; + switch (m) { + case STBI__MARKER_none: // no marker found + return stbi__err("expected marker","Corrupt JPEG"); + + case 0xDD: // DRI - specify restart interval + if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = stbi__get16be(z->s)-2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4, sixteen = (p != 0); + int t = q & 15,i; + if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); + if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + + for (i=0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); + L -= (sixteen ? 129 : 65); + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = stbi__get16be(z->s)-2; + while (L > 0) { + stbi_uc *v; + int sizes[16],i,n=0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + if(n > 256) return stbi__err("bad DHT header","Corrupt JPEG"); // Loop over i < n would write past end of values! + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L==0; + } + + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + L = stbi__get16be(z->s); + if (L < 2) { + if (m == 0xFE) + return stbi__err("bad COM len","Corrupt JPEG"); + else + return stbi__err("bad APP len","Corrupt JPEG"); + } + L -= 2; + + if (m == 0xE0 && L >= 5) { // JFIF APP0 segment + static const unsigned char tag[5] = {'J','F','I','F','\0'}; + int ok = 1; + int i; + for (i=0; i < 5; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 5; + if (ok) + z->jfif = 1; + } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment + static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; + int ok = 1; + int i; + for (i=0; i < 6; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 6; + if (ok) { + stbi__get8(z->s); // version + stbi__get16be(z->s); // flags0 + stbi__get16be(z->s); // flags1 + z->app14_color_transform = stbi__get8(z->s); // color transform + L -= 6; + } + } + + stbi__skip(z->s, L); + return 1; + } + + return stbi__err("unknown marker","Corrupt JPEG"); +} + +// after we see SOS +static int stbi__process_scan_header(stbi__jpeg *z) +{ + int i; + int Ls = stbi__get16be(z->s); + z->scan_n = stbi__get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = stbi__get8(z->s), which; + int q = stbi__get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; // no match + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + + { + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); + z->spec_end = 63; + } + } + + return 1; +} + +static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) +{ + int i; + for (i=0; i < ncomp; ++i) { + if (z->img_comp[i].raw_data) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + z->img_comp[i].data = NULL; + } + if (z->img_comp[i].raw_coeff) { + STBI_FREE(z->img_comp[i].raw_coeff); + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].coeff = 0; + } + if (z->img_comp[i].linebuf) { + STBI_FREE(z->img_comp[i].linebuf); + z->img_comp[i].linebuf = NULL; + } + } + return why; +} + +static int stbi__process_frame_header(stbi__jpeg *z, int scan) +{ + stbi__context *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG + p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + c = stbi__get8(s); + if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); + + z->rgb = 0; + for (i=0; i < s->img_n; ++i) { + static const unsigned char rgb[3] = { 'R', 'G', 'B' }; + z->img_comp[i].id = stbi__get8(s); + if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) + ++z->rgb; + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); + } + + if (scan != STBI__SCAN_load) return 1; + + if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios + // and I've never seen a non-corrupted JPEG file actually use them + for (i=0; i < s->img_n; ++i) { + if (h_max % z->img_comp[i].h != 0) return stbi__err("bad H","Corrupt JPEG"); + if (v_max % z->img_comp[i].v != 0) return stbi__err("bad V","Corrupt JPEG"); + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + // these sizes can't be more than 17 bits + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + // + // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) + // so these muls can't overflow with 32-bit ints (which we require) + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].linebuf = NULL; + z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); + if (z->img_comp[i].raw_data == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + if (z->progressive) { + // w2, h2 are multiples of 8 (see above) + z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; + z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; + z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); + if (z->img_comp[i].raw_coeff == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define stbi__DNL(x) ((x) == 0xdc) +#define stbi__SOI(x) ((x) == 0xd8) +#define stbi__EOI(x) ((x) == 0xd9) +#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) +#define stbi__SOS(x) ((x) == 0xda) + +#define stbi__SOF_progressive(x) ((x) == 0xc2) + +static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) +{ + int m; + z->jfif = 0; + z->app14_color_transform = -1; // valid values are 0,1,2 + z->marker = STBI__MARKER_none; // initialize cached marker to empty + m = stbi__get_marker(z); + if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); + if (scan == STBI__SCAN_type) return 1; + m = stbi__get_marker(z); + while (!stbi__SOF(m)) { + if (!stbi__process_marker(z,m)) return 0; + m = stbi__get_marker(z); + while (m == STBI__MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); + m = stbi__get_marker(z); + } + } + z->progressive = stbi__SOF_progressive(m); + if (!stbi__process_frame_header(z, scan)) return 0; + return 1; +} + +static stbi_uc stbi__skip_jpeg_junk_at_end(stbi__jpeg *j) +{ + // some JPEGs have junk at end, skip over it but if we find what looks + // like a valid marker, resume there + while (!stbi__at_eof(j->s)) { + stbi_uc x = stbi__get8(j->s); + while (x == 255) { // might be a marker + if (stbi__at_eof(j->s)) return STBI__MARKER_none; + x = stbi__get8(j->s); + if (x != 0x00 && x != 0xff) { + // not a stuffed zero or lead-in to another marker, looks + // like an actual marker, return it + return x; + } + // stuffed zero has x=0 now which ends the loop, meaning we go + // back to regular scan loop. + // repeated 0xff keeps trying to read the next byte of the marker. + } + } + return STBI__MARKER_none; +} + +// decode image to YCbCr format +static int stbi__decode_jpeg_image(stbi__jpeg *j) +{ + int m; + for (m = 0; m < 4; m++) { + j->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; + } + j->restart_interval = 0; + if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; + m = stbi__get_marker(j); + while (!stbi__EOI(m)) { + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) return 0; + if (!stbi__parse_entropy_coded_data(j)) return 0; + if (j->marker == STBI__MARKER_none ) { + j->marker = stbi__skip_jpeg_junk_at_end(j); + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } + m = stbi__get_marker(j); + if (STBI__RESTART(m)) + m = stbi__get_marker(j); + } else if (stbi__DNL(m)) { + int Ld = stbi__get16be(j->s); + stbi__uint32 NL = stbi__get16be(j->s); + if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); + if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); + m = stbi__get_marker(j); + } else { + if (!stbi__process_marker(j, m)) return 1; + m = stbi__get_marker(j); + } + } + if (j->progressive) + stbi__jpeg_finish(j); + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, + int w, int hs); + +#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) + +static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + stbi_uc *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = stbi__div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = stbi__div4(n+input[i-1]); + out[i*2+1] = stbi__div4(n+input[i+1]); + } + out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) + +static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = stbi__div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i=0,t0,t1; + + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + // process groups of 8 pixels for as long as we can. + // note we can't handle the last pixel in a row in this loop + // because we need to handle the filter boundary conditions. + for (; i < ((w-1) & ~7); i += 8) { +#if defined(STBI_SSE2) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); + + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); + + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *) (out + i*2), outv); +#elif defined(STBI_NEON) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); + + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i*2, o); +#endif + + // "previous" value for next iter + t1 = 3*in_near[i+7] + in_far[i+7]; + } + + t0 = t1; + t1 = 3*in_near[i] + in_far[i]; + out[i*2] = stbi__div16(3*t1 + t0 + 8); + + for (++i; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} +#endif + +static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + STBI_NOTUSED(in_far); + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +// this is a reduced-precision calculation of YCbCr-to-RGB introduced +// to make sure the code produces the same results in both SIMD and scalar +#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) +{ + int i = 0; + +#ifdef STBI_SSE2 + // step == 3 is pretty ugly on the final interleave, and i'm not convinced + // it's useful in practice (you wouldn't use it for textures, for example). + // so just accelerate step == 4 case. + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); + __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); + __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); + __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); + __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); + __m128i xw = _mm_set1_epi16(255); // alpha channel + + for (; i+7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); + + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); + + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); + + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); + + // store + _mm_storeu_si128((__m128i *) (out + 0), o0); + _mm_storeu_si128((__m128i *) (out + 16), o1); + out += 32; + } + } +#endif + +#ifdef STBI_NEON + // in this version, step=3 support would be easy to add. but is there demand? + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); + int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); + int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); + int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); + + for (; i+7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); + + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); + + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); + + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8*4; + } + } +#endif + + for (; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +// set up the kernels +static void stbi__setup_jpeg(stbi__jpeg *j) +{ + j->idct_block_kernel = stbi__idct_block; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; + +#ifdef STBI_SSE2 + if (stbi__sse2_available()) { + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + } +#endif + +#ifdef STBI_NEON + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; +#endif +} + +// clean up the temporary component buffers +static void stbi__cleanup_jpeg(stbi__jpeg *j) +{ + stbi__free_jpeg_components(j, j->s->img_n, 0); +} + +typedef struct +{ + resample_row_func resample; + stbi_uc *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi__resample; + +// fast 0..255 * 0..255 => 0..255 rounded multiplication +static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) +{ + unsigned int t = x*y + 128; + return (stbi_uc) ((t + (t >>8)) >> 8); +} + +static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n, is_rgb; + z->s->img_n = 0; // make stbi__cleanup_jpeg safe + + // validate req_comp + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + + // load a jpeg image from whichever source, but leave in YCbCr format + if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; + + is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); + + if (z->s->img_n == 3 && n < 3 && !is_rgb) + decode_n = 1; + else + decode_n = z->s->img_n; + + // nothing to do if no components requested; check this now to avoid + // accessing uninitialized coutput[0] later + if (decode_n <= 0) { stbi__cleanup_jpeg(z); return NULL; } + + // resample and color-convert + { + int k; + unsigned int i,j; + stbi_uc *output; + stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; + + stbi__resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; + else r->resample = stbi__resample_row_generic; + } + + // can't error after this so, this is safe + output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); + if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + stbi_uc *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + if (is_rgb) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = y[i]; + out[1] = coutput[1][i]; + out[2] = coutput[2][i]; + out[3] = 255; + out += n; + } + } else { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else if (z->s->img_n == 4) { + if (z->app14_color_transform == 0) { // CMYK + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(coutput[0][i], m); + out[1] = stbi__blinn_8x8(coutput[1][i], m); + out[2] = stbi__blinn_8x8(coutput[2][i], m); + out[3] = 255; + out += n; + } + } else if (z->app14_color_transform == 2) { // YCCK + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(255 - out[0], m); + out[1] = stbi__blinn_8x8(255 - out[1], m); + out[2] = stbi__blinn_8x8(255 - out[2], m); + out += n; + } + } else { // YCbCr + alpha? Ignore the fourth channel for now + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + if (is_rgb) { + if (n == 1) + for (i=0; i < z->s->img_x; ++i) + *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + else { + for (i=0; i < z->s->img_x; ++i, out += 2) { + out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + out[1] = 255; + } + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); + stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); + stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); + out[0] = stbi__compute_y(r, g, b); + out[1] = 255; + out += n; + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); + out[1] = 255; + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } + } + } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output + return output; + } +} + +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + unsigned char* result; + stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__errpuc("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); + STBI_NOTUSED(ri); + j->s = s; + stbi__setup_jpeg(j); + result = load_jpeg_image(j, x,y,comp,req_comp); + STBI_FREE(j); + return result; +} + +static int stbi__jpeg_test(stbi__context *s) +{ + int r; + stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__err("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); + j->s = s; + stbi__setup_jpeg(j); + r = stbi__decode_jpeg_header(j, STBI__SCAN_type); + stbi__rewind(s); + STBI_FREE(j); + return r; +} + +static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) +{ + if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { + stbi__rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; + return 1; +} + +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) +{ + int result; + stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); + if (!j) return stbi__err("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); + j->s = s; + result = stbi__jpeg_info_raw(j, x, y, comp); + STBI_FREE(j); + return result; +} +#endif + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +#ifndef STBI_NO_ZLIB + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) +#define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi_uc size[STBI__ZNSYMS]; + stbi__uint16 value[STBI__ZNSYMS]; +} stbi__zhuffman; + +stbi_inline static int stbi__bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int stbi__bit_reverse(int v, int bits) +{ + STBI_ASSERT(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16-bits); +} + +static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); + z->size [c] = (stbi_uc ) s; + z->value[c] = (stbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + stbi_uc *zbuffer, *zbuffer_end; + int num_bits; + int hit_zeof_once; + stbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + stbi__zhuffman z_length, z_distance; +} stbi__zbuf; + +stbi_inline static int stbi__zeof(stbi__zbuf *z) +{ + return (z->zbuffer >= z->zbuffer_end); +} + +stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) +{ + return stbi__zeof(z) ? 0 : *z->zbuffer++; +} + +static void stbi__fill_bits(stbi__zbuf *z) +{ + do { + if (z->code_buffer >= (1U << z->num_bits)) { + z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ + return; + } + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s,k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s >= 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + if (b >= STBI__ZNSYMS) return -1; // some data was corrupt somewhere! + if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) { + if (stbi__zeof(a)) { + if (!a->hit_zeof_once) { + // This is the first time we hit eof, insert 16 extra padding btis + // to allow us to keep going; if we actually consume any of them + // though, that is invalid data. This is caught later. + a->hit_zeof_once = 1; + a->num_bits += 16; // add 16 implicit zero bits + } else { + // We already inserted our extra 16 padding bits and are again + // out, this stream is actually prematurely terminated. + return -1; + } + } else { + stbi__fill_bits(a); + } + } + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); +} + +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +{ + char *q; + unsigned int cur, limit, old_limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); + cur = (unsigned int) (z->zout - z->zout_start); + limit = old_limit = (unsigned) (z->zout_end - z->zout_start); + if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); + while (cur + n > limit) { + if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); + limit *= 2; + } + q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + STBI_NOTUSED(old_limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static const int stbi__zlength_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static const int stbi__zlength_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static const int stbi__zdist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int stbi__parse_huffman_block(stbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + stbi_uc *p; + int len,dist; + if (z == 256) { + a->zout = zout; + if (a->hit_zeof_once && a->num_bits < 16) { + // The first time we hit zeof, we inserted 16 extra zero bits into our bit + // buffer so the decoder can just do its speculative decoding. But if we + // actually consumed any of those bits (which is the case when num_bits < 16), + // the stream actually read past the end so it is malformed. + return stbi__err("unexpected end","Corrupt PNG"); + } + return 1; + } + if (z >= 286) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0 || z >= 30) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, distance codes 30 and 31 must not appear in compressed data + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); + if (len > a->zout_end - zout) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (stbi_uc *) (zout - dist); + if (dist == 1) { // run of one byte; common in images. + stbi_uc v = *p; + if (len) { do *zout++ = v; while (--len); } + } else { + if (len) { do *zout++ = *p++; while (--len); } + } + } + } +} + +static int stbi__compute_huffman_codes(stbi__zbuf *a) +{ + static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + stbi__zhuffman z_codelength; + stbi_uc lencodes[286+32+137];//padding for maximum single op + stbi_uc codelength_sizes[19]; + int i,n; + + int hlit = stbi__zreceive(a,5) + 257; + int hdist = stbi__zreceive(a,5) + 1; + int hclen = stbi__zreceive(a,4) + 4; + int ntot = hlit + hdist; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < ntot) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (stbi_uc) c; + else { + stbi_uc fill = 0; + if (c == 16) { + c = stbi__zreceive(a,2)+3; + if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); + fill = lencodes[n-1]; + } else if (c == 17) { + c = stbi__zreceive(a,3)+3; + } else if (c == 18) { + c = stbi__zreceive(a,7)+11; + } else { + return stbi__err("bad codelengths", "Corrupt PNG"); + } + if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); + memset(lencodes+n, fill, c); + n += c; + } + } + if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int stbi__parse_uncompressed_block(stbi__zbuf *a) +{ + stbi_uc header[4]; + int len,nlen,k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int stbi__parse_zlib_header(stbi__zbuf *a) +{ + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +static const stbi_uc stbi__zdefault_length[STBI__ZNSYMS] = +{ + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 +}; +static const stbi_uc stbi__zdefault_distance[32] = +{ + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +}; +/* +Init algorithm: +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; +} +*/ + +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + a->hit_zeof_once = 0; + do { + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , STBI__ZNSYMS)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return stbi__parse_zlib(a, parse_header); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer+len; + if (stbi__do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} +#endif + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + +#ifndef STBI_NO_PNG +typedef struct +{ + stbi__uint32 length; + stbi__uint32 type; +} stbi__pngchunk; + +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) +{ + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; +} + +static int stbi__check_png_header(stbi__context *s) +{ + static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi__context *s; + stbi_uc *idata, *expanded, *out; + int depth; +} stbi__png; + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + // synthetic filters used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static stbi_uc first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static int stbi__paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +// create the png data from post-deflated data +static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) +{ + int bytes = (depth == 16? 2 : 1); + stbi__context *s = a->s; + stbi__uint32 i,j,stride = x*out_n*bytes; + stbi__uint32 img_len, img_width_bytes; + int k; + int img_n = s->img_n; // copy it into a local for later + + int output_bytes = out_n*bytes; + int filter_bytes = img_n*bytes; + int width = x; + + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); + a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into + if (!a->out) return stbi__err("outofmem", "Out of memory"); + + if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + img_len = (img_width_bytes + 1) * y; + + // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, + // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), + // so just check for raw_len < img_len always. + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *prior; + int filter = *raw++; + + if (filter > 4) + return stbi__err("invalid filter","Corrupt PNG"); + + if (depth < 8) { + if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG"); + cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place + filter_bytes = 1; + width = img_width_bytes; + } + prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above + + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + + // handle first byte explicitly + for (k=0; k < filter_bytes; ++k) { + switch (filter) { + case STBI__F_none : cur[k] = raw[k]; break; + case STBI__F_sub : cur[k] = raw[k]; break; + case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; + case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; + case STBI__F_avg_first : cur[k] = raw[k]; break; + case STBI__F_paeth_first: cur[k] = raw[k]; break; + } + } + + if (depth == 8) { + if (img_n != out_n) + cur[img_n] = 255; // first pixel + raw += img_n; + cur += out_n; + prior += out_n; + } else if (depth == 16) { + if (img_n != out_n) { + cur[filter_bytes] = 255; // first pixel top byte + cur[filter_bytes+1] = 255; // first pixel bottom byte + } + raw += filter_bytes; + cur += output_bytes; + prior += output_bytes; + } else { + raw += 1; + cur += 1; + prior += 1; + } + + // this is a little gross, so that we don't switch per-pixel or per-component + if (depth < 8 || img_n == out_n) { + int nk = (width - 1)*filter_bytes; + #define STBI__CASE(f) \ + case f: \ + for (k=0; k < nk; ++k) + switch (filter) { + // "none" filter turns into a memcpy here; make that explicit. + case STBI__F_none: memcpy(cur, raw, nk); break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; + } + #undef STBI__CASE + raw += nk; + } else { + STBI_ASSERT(img_n+1 == out_n); + #define STBI__CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ + for (k=0; k < filter_bytes; ++k) + switch (filter) { + STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; + } + #undef STBI__CASE + + // the loop above sets the high byte of the pixels' alpha, but for + // 16 bit png files we also need the low byte set. we'll do that here. + if (depth == 16) { + cur = a->out + stride*j; // start at the beginning of the row again + for (i=0; i < x; ++i,cur+=output_bytes) { + cur[filter_bytes+1] = 255; + } + } + } + } + + // we make a separate pass to expand bits to pixels; for performance, + // this could run two scanlines behind the above code, so it won't + // intefere with filtering but will still be in the cache. + if (depth < 8) { + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; + // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit + // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + + // note that the final byte might overshoot and write more data than desired. + // we can allocate enough data that this never writes out of memory, but it + // could also overwrite the next scanline. can it overwrite non-empty data + // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. + // so we need to explicitly clamp the final ones + + if (depth == 4) { + for (k=x*img_n; k >= 2; k-=2, ++in) { + *cur++ = scale * ((*in >> 4) ); + *cur++ = scale * ((*in ) & 0x0f); + } + if (k > 0) *cur++ = scale * ((*in >> 4) ); + } else if (depth == 2) { + for (k=x*img_n; k >= 4; k-=4, ++in) { + *cur++ = scale * ((*in >> 6) ); + *cur++ = scale * ((*in >> 4) & 0x03); + *cur++ = scale * ((*in >> 2) & 0x03); + *cur++ = scale * ((*in ) & 0x03); + } + if (k > 0) *cur++ = scale * ((*in >> 6) ); + if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); + if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); + } else if (depth == 1) { + for (k=x*img_n; k >= 8; k-=8, ++in) { + *cur++ = scale * ((*in >> 7) ); + *cur++ = scale * ((*in >> 6) & 0x01); + *cur++ = scale * ((*in >> 5) & 0x01); + *cur++ = scale * ((*in >> 4) & 0x01); + *cur++ = scale * ((*in >> 3) & 0x01); + *cur++ = scale * ((*in >> 2) & 0x01); + *cur++ = scale * ((*in >> 1) & 0x01); + *cur++ = scale * ((*in ) & 0x01); + } + if (k > 0) *cur++ = scale * ((*in >> 7) ); + if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); + if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); + if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); + if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); + if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); + if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); + } + if (img_n != out_n) { + int q; + // insert alpha = 255 + cur = a->out + stride*j; + if (img_n == 1) { + for (q=x-1; q >= 0; --q) { + cur[q*2+1] = 255; + cur[q*2+0] = cur[q]; + } + } else { + STBI_ASSERT(img_n == 3); + for (q=x-1; q >= 0; --q) { + cur[q*4+3] = 255; + cur[q*4+2] = cur[q*3+2]; + cur[q*4+1] = cur[q*3+1]; + cur[q*4+0] = cur[q*3+0]; + } + } + } + } + } else if (depth == 16) { + // force the image data from big-endian to platform-native. + // this is done in a separate pass due to the decoding relying + // on the data being untouched, but could probably be done + // per-line during decode if care is taken. + stbi_uc *cur = a->out; + stbi__uint16 *cur16 = (stbi__uint16*)cur; + + for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { + *cur16 = (cur[0] << 8) | cur[1]; + } + } + + return 1; +} + +static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) +{ + int bytes = (depth == 16 ? 2 : 1); + int out_bytes = out_n * bytes; + stbi_uc *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + // de-interlacing + final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + if (!final) return stbi__err("outofmem", "Out of memory"); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, + a->out + (j*x+i)*out_bytes, out_bytes); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi__uint16 *p = (stbi__uint16*) z->out; + + // compute color-based transparency, assuming we've + // already got 65535 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 65535); + p += 2; + } + } else { + for (i = 0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) +{ + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi_uc *p, *temp_out, *orig = a->out; + + p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi__unpremultiply_on_load_global = 0; +static int stbi__de_iphone_flag_global = 0; + +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag_global = flag_true_if_should_convert; +} + +#ifndef STBI_THREAD_LOCAL +#define stbi__unpremultiply_on_load stbi__unpremultiply_on_load_global +#define stbi__de_iphone_flag stbi__de_iphone_flag_global +#else +static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set; +static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set; + +STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply; + stbi__unpremultiply_on_load_set = 1; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag_local = flag_true_if_should_convert; + stbi__de_iphone_flag_set = 1; +} + +#define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \ + ? stbi__unpremultiply_on_load_local \ + : stbi__unpremultiply_on_load_global) +#define stbi__de_iphone_flag (stbi__de_iphone_flag_set \ + ? stbi__de_iphone_flag_local \ + : stbi__de_iphone_flag_global) +#endif // STBI_THREAD_LOCAL + +static void stbi__de_iphone(stbi__png *z) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + stbi_uc half = a / 2; + p[0] = (p[2] * 255 + half) / a; + p[1] = (p[1] * 255 + half) / a; + p[2] = ( t * 255 + half) / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) + +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) +{ + stbi_uc palette[1024], pal_img_n=0; + stbi_uc has_trans=0, tc[3]={0}; + stbi__uint16 tc16[3]; + stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0, is_iphone=0; + stbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!stbi__check_png_header(s)) return 0; + + if (scan == STBI__SCAN_type) return 1; + + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); + s->img_y = stbi__get32be(s); + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); + } + // even with SCAN_header, have to scan to see if we have a tRNS + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + // non-paletted with tRNS = constant alpha. if header-scanning, we can stop now. + if (scan == STBI__SCAN_header) { ++s->img_n; return 1; } + if (z->depth == 16) { + for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is + } else { + for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger + } + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { + // header scan definitely stops at first IDAT + if (pal_img_n) + s->img_n = pal_img_n; + return 1; + } + if (c.length > (1u << 30)) return stbi__err("IDAT size limit", "IDAT section larger than 2^30 bytes"); + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; + stbi_uc *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + STBI_NOTUSED(idata_limit_old); + p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0; // zlib should set error + STBI_FREE(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; + if (has_trans) { + if (z->depth == 16) { + if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; + } else { + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + } + } + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } else if (has_trans) { + // non-paletted image with tRNS -> source image has (constant) alpha + ++s->img_n; + } + STBI_FREE(z->expanded); z->expanded = NULL; + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + return 1; + } + + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); + #endif + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } +} + +static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) +{ + void *result=NULL; + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + if (p->depth <= 8) + ri->bits_per_channel = 8; + else if (p->depth == 16) + ri->bits_per_channel = 16; + else + return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + if (ri->bits_per_channel == 8) + result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + else + result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi__png p; + p.s = s; + return stbi__do_png(&p, x,y,comp,req_comp, ri); +} + +static int stbi__png_test(stbi__context *s) +{ + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; +} + +static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) +{ + if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { + stbi__rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__png p; + p.s = s; + return stbi__png_info_raw(&p, x, y, comp); +} + +static int stbi__png_is16(stbi__context *s) +{ + stbi__png p; + p.s = s; + if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) + return 0; + if (p.depth != 16) { + stbi__rewind(p.s); + return 0; + } + return 1; +} +#endif + +// Microsoft/Windows BMP image + +#ifndef STBI_NO_BMP +static int stbi__bmp_test_raw(stbi__context *s) +{ + int r; + int sz; + if (stbi__get8(s) != 'B') return 0; + if (stbi__get8(s) != 'M') return 0; + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + stbi__get32le(s); // discard data offset + sz = stbi__get32le(s); + r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); + return r; +} + +static int stbi__bmp_test(stbi__context *s) +{ + int r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int stbi__high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) { n += 16; z >>= 16; } + if (z >= 0x00100) { n += 8; z >>= 8; } + if (z >= 0x00010) { n += 4; z >>= 4; } + if (z >= 0x00004) { n += 2; z >>= 2; } + if (z >= 0x00002) { n += 1;/* >>= 1;*/ } + return n; +} + +static int stbi__bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +// extract an arbitrarily-aligned N-bit value (N=bits) +// from v, and then make it 8-bits long and fractionally +// extend it to full full range. +static int stbi__shiftsigned(unsigned int v, int shift, int bits) +{ + static unsigned int mul_table[9] = { + 0, + 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, + 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, + }; + static unsigned int shift_table[9] = { + 0, 0,0,1,0,2,4,6,0, + }; + if (shift < 0) + v <<= -shift; + else + v >>= shift; + STBI_ASSERT(v < 256); + v >>= (8-bits); + STBI_ASSERT(bits >= 0 && bits <= 8); + return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; +} + +typedef struct +{ + int bpp, offset, hsz; + unsigned int mr,mg,mb,ma, all_a; + int extra_read; +} stbi__bmp_data; + +static int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress) +{ + // BI_BITFIELDS specifies masks explicitly, don't override + if (compress == 3) + return 1; + + if (compress == 0) { + if (info->bpp == 16) { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } else if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + // otherwise, use defaults, which is all-0 + info->mr = info->mg = info->mb = info->ma = 0; + } + return 1; + } + return 0; // error +} + +static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) +{ + int hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + info->offset = stbi__get32le(s); + info->hsz = hsz = stbi__get32le(s); + info->mr = info->mg = info->mb = info->ma = 0; + info->extra_read = 14; + + if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); + + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = stbi__get16le(s); + s->img_y = stbi__get16le(s); + } else { + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); + info->bpp = stbi__get16le(s); + if (hsz != 12) { + int compress = stbi__get32le(s); + if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes + if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + if (info->bpp == 16 || info->bpp == 32) { + if (compress == 0) { + stbi__bmp_set_mask_defaults(info, compress); + } else if (compress == 3) { + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->extra_read += 12; + // not documented, but generated by photoshop and handled by mspaint + if (info->mr == info->mg && info->mg == info->mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else { + // V4/V5 header + int i; + if (hsz != 108 && hsz != 124) + return stbi__errpuc("bad BMP", "bad BMP"); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->ma = stbi__get32le(s); + if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs + stbi__bmp_set_mask_defaults(info, compress); + stbi__get32le(s); // discard color space + for (i=0; i < 12; ++i) + stbi__get32le(s); // discard color space parameters + if (hsz == 124) { + stbi__get32le(s); // discard rendering intent + stbi__get32le(s); // discard offset of profile data + stbi__get32le(s); // discard size of profile data + stbi__get32le(s); // discard reserved + } + } + } + return (void *) 1; +} + + +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + unsigned int mr=0,mg=0,mb=0,ma=0, all_a; + stbi_uc pal[256][4]; + int psize=0,i,j,width; + int flip_vertically, pad, target; + stbi__bmp_data info; + STBI_NOTUSED(ri); + + info.all_a = 255; + if (stbi__bmp_parse_header(s, &info) == NULL) + return NULL; // error code already set + + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + mr = info.mr; + mg = info.mg; + mb = info.mb; + ma = info.ma; + all_a = info.all_a; + + if (info.hsz == 12) { + if (info.bpp < 24) + psize = (info.offset - info.extra_read - 24) / 3; + } else { + if (info.bpp < 16) + psize = (info.offset - info.extra_read - info.hsz) >> 2; + } + if (psize == 0) { + // accept some number of extra bytes after the header, but if the offset points either to before + // the header ends or implies a large amount of extra data, reject the file as malformed + int bytes_read_so_far = s->callback_already_read + (int)(s->img_buffer - s->img_buffer_original); + int header_limit = 1024; // max we actually read is below 256 bytes currently. + int extra_data_limit = 256*4; // what ordinarily goes here is a palette; 256 entries*4 bytes is its max size. + if (bytes_read_so_far <= 0 || bytes_read_so_far > header_limit) { + return stbi__errpuc("bad header", "Corrupt BMP"); + } + // we established that bytes_read_so_far is positive and sensible. + // the first half of this test rejects offsets that are either too small positives, or + // negative, and guarantees that info.offset >= bytes_read_so_far > 0. this in turn + // ensures the number computed in the second half of the test can't overflow. + if (info.offset < bytes_read_so_far || info.offset - bytes_read_so_far > extra_data_limit) { + return stbi__errpuc("bad offset", "Corrupt BMP"); + } else { + stbi__skip(s, info.offset - bytes_read_so_far); + } + } + + if (info.bpp == 24 && ma == 0xff000000) + s->img_n = 3; + else + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + + // sanity-check size + if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "Corrupt BMP"); + + out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (info.bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (info.hsz != 12) stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 1) width = (s->img_x + 7) >> 3; + else if (info.bpp == 4) width = (s->img_x + 1) >> 1; + else if (info.bpp == 8) width = s->img_x; + else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + if (info.bpp == 1) { + for (j=0; j < (int) s->img_y; ++j) { + int bit_offset = 7, v = stbi__get8(s); + for (i=0; i < (int) s->img_x; ++i) { + int color = (v>>bit_offset)&0x1; + out[z++] = pal[color][0]; + out[z++] = pal[color][1]; + out[z++] = pal[color][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + if((--bit_offset) < 0) { + bit_offset = 7; + v = stbi__get8(s); + } + } + stbi__skip(s, pad); + } + } else { + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); + } + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + stbi__skip(s, info.offset - info.extra_read - info.hsz); + if (info.bpp == 24) width = 3 * s->img_x; + else if (info.bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (info.bpp == 24) { + easy = 1; + } else if (info.bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + unsigned char a; + out[z+2] = stbi__get8(s); + out[z+1] = stbi__get8(s); + out[z+0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; + if (target == 4) out[z++] = a; + } + } else { + int bpp = info.bpp; + for (i=0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); + unsigned int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; + if (target == 4) out[z++] = STBI__BYTECAST(a); + } + } + stbi__skip(s, pad); + } + } + + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) + out[i] = 255; + + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i]; p1[i] = p2[i]; p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} +#endif + +// Targa Truevision - TGA +// by Jonathan Dummer +#ifndef STBI_NO_TGA +// returns STBI_rgb or whatever, 0 on error +static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) +{ + // only RGB or RGBA (incl. 16bit) or grey allowed + if (is_rgb16) *is_rgb16 = 0; + switch(bits_per_pixel) { + case 8: return STBI_grey; + case 16: if(is_grey) return STBI_grey_alpha; + // fallthrough + case 15: if(is_rgb16) *is_rgb16 = 1; + return STBI_rgb; + case 24: // fallthrough + case 32: return bits_per_pixel/8; + default: return 0; + } +} + +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; + int sz, tga_colormap_type; + stbi__get8(s); // discard Offset + tga_colormap_type = stbi__get8(s); // colormap type + if( tga_colormap_type > 1 ) { + stbi__rewind(s); + return 0; // only RGB or indexed allowed + } + tga_image_type = stbi__get8(s); // image type + if ( tga_colormap_type == 1 ) { // colormapped (paletted) image + if (tga_image_type != 1 && tga_image_type != 9) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip image x and y origin + tga_colormap_bpp = sz; + } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE + if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { + stbi__rewind(s); + return 0; // only RGB or grey allowed, +/- RLE + } + stbi__skip(s,9); // skip colormap specification and image x/y origin + tga_colormap_bpp = 0; + } + tga_w = stbi__get16le(s); + if( tga_w < 1 ) { + stbi__rewind(s); + return 0; // test width + } + tga_h = stbi__get16le(s); + if( tga_h < 1 ) { + stbi__rewind(s); + return 0; // test height + } + tga_bits_per_pixel = stbi__get8(s); // bits per pixel + stbi__get8(s); // ignore alpha bits + if (tga_colormap_bpp != 0) { + if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { + // when using a colormap, tga_bits_per_pixel is the size of the indexes + // I don't think anything but 8 or 16bit indexes makes sense + stbi__rewind(s); + return 0; + } + tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); + } else { + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); + } + if(!tga_comp) { + stbi__rewind(s); + return 0; + } + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp; + return 1; // seems to have passed everything +} + +static int stbi__tga_test(stbi__context *s) +{ + int res = 0; + int sz, tga_color_type; + stbi__get8(s); // discard Offset + tga_color_type = stbi__get8(s); // color type + if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed + sz = stbi__get8(s); // image type + if ( tga_color_type == 1 ) { // colormapped (paletted) image + if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + stbi__skip(s,4); // skip image x and y origin + } else { // "normal" image w/o colormap + if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE + stbi__skip(s,9); // skip colormap specification and image x/y origin + } + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height + sz = stbi__get8(s); // bits per pixel + if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + + res = 1; // if we got this far, everything's good and we can return 1 instead of 0 + +errorEnd: + stbi__rewind(s); + return res; +} + +// read 16bit value and convert to 24bit RGB +static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) +{ + stbi__uint16 px = (stbi__uint16)stbi__get16le(s); + stbi__uint16 fiveBitMask = 31; + // we have 3 channels with 5bits each + int r = (px >> 10) & fiveBitMask; + int g = (px >> 5) & fiveBitMask; + int b = px & fiveBitMask; + // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later + out[0] = (stbi_uc)((r * 255)/31); + out[1] = (stbi_uc)((g * 255)/31); + out[2] = (stbi_uc)((b * 255)/31); + + // some people claim that the most significant bit might be used for alpha + // (possibly if an alpha-bit is set in the "image descriptor byte") + // but that only made 16bit test images completely translucent.. + // so let's treat all 15 and 16bit TGAs as RGB with no alpha. +} + +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + // read in the TGA header stuff + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + int tga_is_RLE = 0; + int tga_palette_start = stbi__get16le(s); + int tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + int tga_x_origin = stbi__get16le(s); + int tga_y_origin = stbi__get16le(s); + int tga_width = stbi__get16le(s); + int tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + int tga_comp, tga_rgb16=0; + int tga_inverted = stbi__get8(s); + // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4] = {0}; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + STBI_NOTUSED(ri); + STBI_NOTUSED(tga_x_origin); // @TODO + STBI_NOTUSED(tga_y_origin); // @TODO + + if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); + else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); + + if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency + return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); + + // tga info + *x = tga_width; + *y = tga_height; + if (comp) *comp = tga_comp; + + if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) + return stbi__errpuc("too large", "Corrupt TGA"); + + tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); + if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + stbi__skip(s, tga_offset ); + + if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { + for (i=0; i < tga_height; ++i) { + int row = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if ( tga_indexed) + { + if (tga_palette_len == 0) { /* you have to have at least one entry! */ + STBI_FREE(tga_data); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (tga_rgb16) { + stbi_uc *pal_entry = tga_palette; + STBI_ASSERT(tga_comp == STBI_rgb); + for (i=0; i < tga_palette_len; ++i) { + stbi__tga_read_rgb16(s, pal_entry); + pal_entry += tga_comp; + } + } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in index, then perform the lookup + int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); + if ( pal_idx >= tga_palette_len ) { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_comp; + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else if(tga_rgb16) { + STBI_ASSERT(tga_comp == STBI_rgb); + stbi__tga_read_rgb16(s, raw_data); + } else { + // read in the data raw + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i*tga_comp+j] = raw_data[j]; + + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + STBI_FREE( tga_palette ); + } + } + + // swap RGB - if the source data was RGB16, it already is in the right order + if (tga_comp >= 3 && !tga_rgb16) + { + unsigned char* tga_pixel = tga_data; + for (i=0; i < tga_width * tga_height; ++i) + { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } + + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); + + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + STBI_NOTUSED(tga_palette_start); + // OK, done + return tga_data; +} +#endif + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s) +{ + int r = (stbi__get32be(s) == 0x38425053); + stbi__rewind(s); + return r; +} + +static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) +{ + int count, nleft, len; + + count = 0; + while ((nleft = pixelCount - count) > 0) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + if (len > nleft) return 0; // corrupt data + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len = 257 - len; + if (len > nleft) return 0; // corrupt data + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + + return 1; +} + +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + int pixelCount; + int channelCount, compression; + int channel, i; + int bitdepth; + int w,h; + stbi_uc *out; + STBI_NOTUSED(ri); + + // Check identifier + if (stbi__get32be(s) != 0x38425053) // "8BPS" + return stbi__errpuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (stbi__get16be(s) != 1) + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + stbi__skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = stbi__get32be(s); + w = stbi__get32be(s); + + if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + // Make sure the depth is 8 bits. + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (stbi__get16be(s) != 3) + return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + stbi__skip(s,stbi__get32be(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + stbi__skip(s, stbi__get32be(s) ); + + // Skip the reserved data. + stbi__skip(s, stbi__get32be(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = stbi__get16be(s); + if (compression > 1) + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + + // Check size + if (!stbi__mad3sizes_valid(4, w, h, 0)) + return stbi__errpuc("too large", "Corrupt PSD"); + + // Create the destination image. + + if (!compression && bitdepth == 16 && bpc == 16) { + out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); + ri->bits_per_channel = 16; + } else + out = (stbi_uc *) stbi__malloc(4 * w*h); + + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); + } else { + // Read the RLE data. + if (!stbi__psd_decode_rle(s, p, pixelCount)) { + STBI_FREE(out); + return stbi__errpuc("corrupt", "bad RLE data"); + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + if (channel >= channelCount) { + // Fill this channel with default data. + if (bitdepth == 16 && bpc == 16) { + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + stbi__uint16 val = channel == 3 ? 65535 : 0; + for (i = 0; i < pixelCount; i++, q += 4) + *q = val; + } else { + stbi_uc *p = out+channel; + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } + } else { + if (ri->bits_per_channel == 16) { // output bpc + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + for (i = 0; i < pixelCount; i++, q += 4) + *q = (stbi__uint16) stbi__get16be(s); + } else { + stbi_uc *p = out+channel; + if (bitdepth == 16) { // input bpc + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + } + } + + // remove weird white matte from PSD + if (channelCount >= 4) { + if (ri->bits_per_channel == 16) { + for (i=0; i < w*h; ++i) { + stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; + if (pixel[3] != 0 && pixel[3] != 65535) { + float a = pixel[3] / 65535.0f; + float ra = 1.0f / a; + float inv_a = 65535.0f * (1 - ra); + pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); + pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); + pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); + } + } + } else { + for (i=0; i < w*h; ++i) { + unsigned char *pixel = out + 4*i; + if (pixel[3] != 0 && pixel[3] != 255) { + float a = pixel[3] / 255.0f; + float ra = 1.0f / a; + float inv_a = 255.0f * (1 - ra); + pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); + pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); + pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); + } + } + } + } + + // convert to desired output format + if (req_comp && req_comp != 4) { + if (ri->bits_per_channel == 16) + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); + else + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + if (comp) *comp = 4; + *y = h; + *x = w; + + return out; +} +#endif + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +#ifndef STBI_NO_PIC +static int stbi__pic_is4(stbi__context *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (stbi__get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int stbi__pic_test_core(stbi__context *s) +{ + int i; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + stbi__get8(s); + + if (!stbi__pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} stbi__pic_packet; + +static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); + dest[i]=stbi__get8(s); + } + } + + return dest; +} + +static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + stbi__pic_packet packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return stbi__errpuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + + act_comp |= packet->channel; + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return stbi__errpuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=stbi__get8(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (stbi_uc) left; + + if (!stbi__readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = stbi__get8(s), i; + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + + if (count==128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file","scanline overrun"); + + if (!stbi__readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return stbi__errpuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) +{ + stbi_uc *result; + int i, x,y, internal_comp; + STBI_NOTUSED(ri); + + if (!comp) comp = &internal_comp; + + for (i=0; i<92; ++i) + stbi__get8(s); + + x = stbi__get16be(s); + y = stbi__get16be(s); + + if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); + if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); + + stbi__get32be(s); //skip `ratio' + stbi__get16be(s); //skip `fields' + stbi__get16be(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); + if (!result) return stbi__errpuc("outofmem", "Out of memory"); + memset(result, 0xff, x*y*4); + + if (!stbi__pic_load_core(s,x,y,comp, result)) { + STBI_FREE(result); + return 0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=stbi__convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi__pic_test(stbi__context *s) +{ + int r = stbi__pic_test_core(s); + stbi__rewind(s); + return r; +} +#endif + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb + +#ifndef STBI_NO_GIF +typedef struct +{ + stbi__int16 prefix; + stbi_uc first; + stbi_uc suffix; +} stbi__gif_lzw; + +typedef struct +{ + int w,h; + stbi_uc *out; // output buffer (always 4 components) + stbi_uc *background; // The current "background" as far as a gif is concerned + stbi_uc *history; + int flags, bgindex, ratio, transparent, eflags; + stbi_uc pal[256][4]; + stbi_uc lpal[256][4]; + stbi__gif_lzw codes[8192]; + stbi_uc *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; + int delay; +} stbi__gif; + +static int stbi__gif_test_raw(stbi__context *s) +{ + int sz; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; + sz = stbi__get8(s); + if (sz != '9' && sz != '7') return 0; + if (stbi__get8(s) != 'a') return 0; + return 1; +} + +static int stbi__gif_test(stbi__context *s) +{ + int r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; +} + +static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = transp == i ? 0 : 255; + } +} + +static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) +{ + stbi_uc version; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return stbi__err("not GIF", "Corrupt GIF"); + + version = stbi__get8(s); + if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); + if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); + + stbi__g_failure_reason = ""; + g->w = stbi__get16le(s); + g->h = stbi__get16le(s); + g->flags = stbi__get8(s); + g->bgindex = stbi__get8(s); + g->ratio = stbi__get8(s); + g->transparent = -1; + + if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + if (!g) return stbi__err("outofmem", "Out of memory"); + if (!stbi__gif_header(s, g, comp, 1)) { + STBI_FREE(g); + stbi__rewind( s ); + return 0; + } + if (x) *x = g->w; + if (y) *y = g->h; + STBI_FREE(g); + return 1; +} + +static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) +{ + stbi_uc *p, *c; + int idx; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi__out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + idx = g->cur_x + g->cur_y; + p = &g->out[idx]; + g->history[idx / 4] = 1; + + c = &g->color_table[g->codes[code].suffix * 4]; + if (c[3] > 128) { // don't render transparent pixels; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) +{ + stbi_uc lzw_cs; + stbi__int32 len, init_code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi__gif_lzw *p; + + lzw_cs = stbi__get8(s); + if (lzw_cs > 12) return NULL; + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc) init_code; + g->codes[init_code].suffix = (stbi_uc) init_code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) { + return stbi__errpuc("no clear code", "Corrupt GIF"); + } + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 8192) { + return stbi__errpuc("too many codes", "Corrupt GIF"); + } + + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + + stbi__out_gif_code(g, (stbi__uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +// two back is the image from two frames ago, used for a very specific disposal format +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) +{ + int dispose; + int first_frame; + int pi; + int pcount; + STBI_NOTUSED(req_comp); + + // on first frame, any non-written pixels get the background colour (non-transparent) + first_frame = 0; + if (g->out == 0) { + if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header + if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) + return stbi__errpuc("too large", "GIF image is too large"); + pcount = g->w * g->h; + g->out = (stbi_uc *) stbi__malloc(4 * pcount); + g->background = (stbi_uc *) stbi__malloc(4 * pcount); + g->history = (stbi_uc *) stbi__malloc(pcount); + if (!g->out || !g->background || !g->history) + return stbi__errpuc("outofmem", "Out of memory"); + + // image is treated as "transparent" at the start - ie, nothing overwrites the current background; + // background colour is only used for pixels that are not rendered first frame, after that "background" + // color refers to the color that was there the previous frame. + memset(g->out, 0x00, 4 * pcount); + memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) + memset(g->history, 0x00, pcount); // pixels that were affected previous frame + first_frame = 1; + } else { + // second frame - how do we dispose of the previous one? + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; + + if ((dispose == 3) && (two_back == 0)) { + dispose = 2; // if I don't have an image to revert back to, default to the old background + } + + if (dispose == 3) { // use previous graphic + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); + } + } + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); + } + } + } else { + // This is a non-disposal case eithe way, so just + // leave the pixels as is, and they will become the new background + // 1: do not dispose + // 0: not specified. + } + + // background is what out is after the undoing of the previou frame; + memcpy( g->background, g->out, 4 * g->w * g->h ); + } + + // clear my history; + memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame + + for (;;) { + int tag = stbi__get8(s); + switch (tag) { + case 0x2C: /* Image Descriptor */ + { + stbi__int32 x, y, w, h; + stbi_uc *o; + + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + // if the width of the specified rectangle is 0, that means + // we may not see *any* pixels or the image is malformed; + // to make sure this is caught, move the current y down to + // max_y (which is what out_gif_code checks). + if (w == 0) + g->cur_y = g->max_y; + + g->lflags = stbi__get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi_uc *) g->lpal; + } else if (g->flags & 0x80) { + g->color_table = (stbi_uc *) g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); + + o = stbi__process_gif_raster(s, g); + if (!o) return NULL; + + // if this was the first frame, + pcount = g->w * g->h; + if (first_frame && (g->bgindex > 0)) { + // if first frame, any pixel not drawn to gets the background color + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi] == 0) { + g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); + } + } + } + + return o; + } + + case 0x21: // Comment Extension. + { + int len; + int ext = stbi__get8(s); + if (ext == 0xF9) { // Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. + + // unset old transparent + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 255; + } + if (g->eflags & 0x01) { + g->transparent = stbi__get8(s); + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 0; + } + } else { + // don't need transparent + stbi__skip(s, 1); + g->transparent = -1; + } + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) { + stbi__skip(s, len); + } + break; + } + + case 0x3B: // gif stream termination code + return (stbi_uc *) s; // using '1' causes warning on some compilers + + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } + } +} + +static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays) +{ + STBI_FREE(g->out); + STBI_FREE(g->history); + STBI_FREE(g->background); + + if (out) STBI_FREE(out); + if (delays && *delays) STBI_FREE(*delays); + return stbi__errpuc("outofmem", "Out of memory"); +} + +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + if (stbi__gif_test(s)) { + int layers = 0; + stbi_uc *u = 0; + stbi_uc *out = 0; + stbi_uc *two_back = 0; + stbi__gif g; + int stride; + int out_size = 0; + int delays_size = 0; + + STBI_NOTUSED(out_size); + STBI_NOTUSED(delays_size); + + memset(&g, 0, sizeof(g)); + if (delays) { + *delays = 0; + } + + do { + u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + + if (u) { + *x = g.w; + *y = g.h; + ++layers; + stride = g.w * g.h * 4; + + if (out) { + void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); + if (!tmp) + return stbi__load_gif_main_outofmem(&g, out, delays); + else { + out = (stbi_uc*) tmp; + out_size = layers * stride; + } + + if (delays) { + int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); + if (!new_delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + *delays = new_delays; + delays_size = layers * sizeof(int); + } + } else { + out = (stbi_uc*)stbi__malloc( layers * stride ); + if (!out) + return stbi__load_gif_main_outofmem(&g, out, delays); + out_size = layers * stride; + if (delays) { + *delays = (int*) stbi__malloc( layers * sizeof(int) ); + if (!*delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + delays_size = layers * sizeof(int); + } + } + memcpy( out + ((layers - 1) * stride), u, stride ); + if (layers >= 2) { + two_back = out - 2 * stride; + } + + if (delays) { + (*delays)[layers - 1U] = g.delay; + } + } + } while (u != 0); + + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); + + // do the final conversion after loading everything; + if (req_comp && req_comp != 4) + out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); + + *z = layers; + return out; + } else { + return stbi__errpuc("not GIF", "Image was not as a gif type."); + } +} + +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *u = 0; + stbi__gif g; + memset(&g, 0, sizeof(g)); + STBI_NOTUSED(ri); + + u = stbi__gif_load_next(s, &g, comp, req_comp, 0); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + + // moved conversion to after successful load so that the same + // can be done for multiple frames. + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g.w, g.h); + } else if (g.out) { + // if there was an error and we allocated an image buffer, free it! + STBI_FREE(g.out); + } + + // free buffers needed for multiple frame loading; + STBI_FREE(g.history); + STBI_FREE(g.background); + + return u; +} + +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) +{ + return stbi__gif_info_raw(s,x,y,comp); +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int stbi__hdr_test_core(stbi__context *s, const char *signature) +{ + int i; + for (i=0; signature[i]; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + stbi__rewind(s); + return 1; +} + +static int stbi__hdr_test(stbi__context* s) +{ + int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); + stbi__rewind(s); + if(!r) { + r = stbi__hdr_test_core(s, "#?RGBE\n"); + stbi__rewind(s); + } + return r; +} + +#define STBI__HDR_BUFLEN 1024 +static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) stbi__get8(z); + + while (!stbi__at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == STBI__HDR_BUFLEN-1) { + // flush to end of line + while (!stbi__at_eof(z) && stbi__get8(z) != '\n') + ; + break; + } + c = (char) stbi__get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + const char *headerToken; + STBI_NOTUSED(ri); + + // Check identifier + headerToken = stbi__hdr_gettoken(s,buffer); + if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) + return stbi__errpf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int) strtol(token, NULL, 10); + + if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + + *x = width; + *y = height; + + if (comp) *comp = 3; + if (req_comp == 0) req_comp = 3; + + if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) + return stbi__errpf("too large", "HDR image is too large"); + + // Read data + hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); + if (!hdr_data) + return stbi__errpf("outofmem", "Out of memory"); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc) c1; + rgbe[1] = (stbi_uc) c2; + rgbe[2] = (stbi_uc) len; + rgbe[3] = (stbi_uc) stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) { + scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); + if (!scanline) { + STBI_FREE(hdr_data); + return stbi__errpf("outofmem", "Out of memory"); + } + } + + for (k = 0; k < 4; ++k) { + int nleft; + i = 0; + while ((nleft = width - i) > 0) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i=0; i < width; ++i) + stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + if (scanline) + STBI_FREE(scanline); + } + + return hdr_data; +} + +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int dummy; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (stbi__hdr_test(s) == 0) { + stbi__rewind( s ); + return 0; + } + + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi__rewind( s ); + return 0; + } + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *y = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *x = (int) strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +#ifndef STBI_NO_BMP +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) +{ + void *p; + stbi__bmp_data info; + + info.all_a = 255; + p = stbi__bmp_parse_header(s, &info); + if (p == NULL) { + stbi__rewind( s ); + return 0; + } + if (x) *x = s->img_x; + if (y) *y = s->img_y; + if (comp) { + if (info.bpp == 24 && info.ma == 0xff000000) + *comp = 3; + else + *comp = info.ma ? 4 : 3; + } + return 1; +} +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) +{ + int channelCount, dummy, depth; + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + *y = stbi__get32be(s); + *x = stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 8 && depth != 16) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 3) { + stbi__rewind( s ); + return 0; + } + *comp = 4; + return 1; +} + +static int stbi__psd_is16(stbi__context *s) +{ + int channelCount, depth; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + STBI_NOTUSED(stbi__get32be(s)); + STBI_NOTUSED(stbi__get32be(s)); + depth = stbi__get16be(s); + if (depth != 16) { + stbi__rewind( s ); + return 0; + } + return 1; +} +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained,dummy; + stbi__pic_packet packets[10]; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 88); + + *x = stbi__get16be(s); + *y = stbi__get16be(s); + if (stbi__at_eof(s)) { + stbi__rewind( s); + return 0; + } + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi__rewind( s ); + return 0; + } + + stbi__skip(s, 8); + + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; + + if (stbi__at_eof(s)) { + stbi__rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi__rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} +#endif + +// ************************************************************************************************* +// Portable Gray Map and Portable Pixel Map loader +// by Ken Miller +// +// PGM: http://netpbm.sourceforge.net/doc/pgm.html +// PPM: http://netpbm.sourceforge.net/doc/ppm.html +// +// Known limitations: +// Does not support comments in the header section +// Does not support ASCII image data (formats P2 and P3) + +#ifndef STBI_NO_PNM + +static int stbi__pnm_test(stbi__context *s) +{ + char p, t; + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + return 1; +} + +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + STBI_NOTUSED(ri); + + ri->bits_per_channel = stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n); + if (ri->bits_per_channel == 0) + return 0; + + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + + if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0)) + return stbi__errpuc("too large", "PNM too large"); + + out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (!stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) { + STBI_FREE(out); + return stbi__errpuc("bad PNM", "PNM file truncated"); + } + + if (req_comp && req_comp != s->img_n) { + if (ri->bits_per_channel == 16) { + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, s->img_n, req_comp, s->img_x, s->img_y); + } else { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + } + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + return out; +} + +static int stbi__pnm_isspace(char c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +} + +static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) +{ + for (;;) { + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); + + if (stbi__at_eof(s) || *c != '#') + break; + + while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) + *c = (char) stbi__get8(s); + } +} + +static int stbi__pnm_isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static int stbi__pnm_getinteger(stbi__context *s, char *c) +{ + int value = 0; + + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value*10 + (*c - '0'); + *c = (char) stbi__get8(s); + if((value > 214748364) || (value == 214748364 && *c > '7')) + return stbi__err("integer parse overflow", "Parsing an integer in the PPM header overflowed a 32-bit int"); + } + + return value; +} + +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) +{ + int maxv, dummy; + char c, p, t; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + stbi__rewind(s); + + // Get identifier + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind(s); + return 0; + } + + *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm + + c = (char) stbi__get8(s); + stbi__pnm_skip_whitespace(s, &c); + + *x = stbi__pnm_getinteger(s, &c); // read width + if(*x == 0) + return stbi__err("invalid width", "PPM image header had zero or overflowing width"); + stbi__pnm_skip_whitespace(s, &c); + + *y = stbi__pnm_getinteger(s, &c); // read height + if (*y == 0) + return stbi__err("invalid width", "PPM image header had zero or overflowing width"); + stbi__pnm_skip_whitespace(s, &c); + + maxv = stbi__pnm_getinteger(s, &c); // read max value + if (maxv > 65535) + return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images"); + else if (maxv > 255) + return 16; + else + return 8; +} + +static int stbi__pnm_is16(stbi__context *s) +{ + if (stbi__pnm_info(s, NULL, NULL, NULL) == 16) + return 1; + return 0; +} +#endif + +static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNG + if (stbi__png_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_GIF + if (stbi__gif_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_BMP + if (stbi__bmp_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PIC + if (stbi__pic_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_info(s, x, y, comp)) return 1; + #endif + + // test tga last because it's a crappy test! + #ifndef STBI_NO_TGA + if (stbi__tga_info(s, x, y, comp)) + return 1; + #endif + return stbi__err("unknown image type", "Image not of any known type, or corrupt"); +} + +static int stbi__is_16_main(stbi__context *s) +{ + #ifndef STBI_NO_PNG + if (stbi__png_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_is16(s)) return 1; + #endif + return 0; +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} + +STBIDEF int stbi_is_16_bit(char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_is_16_bit_from_file(f); + fclose(f); + return result; +} + +STBIDEF int stbi_is_16_bit_from_file(FILE *f) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__is_16_main(&s); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__is_16_main(&s); +} + +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__is_16_main(&s); +} + +#endif // STB_IMAGE_IMPLEMENTATION + +/* + revision history: + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug + 1-bit BMP + *_is_16_bit api + avoid warnings + 2.16 (2017-07-23) all functions have 16-bit variants; + STBI_NO_STDIO works again; + compilation fixes; + fix rounding in unpremultiply; + optimize vertical flip; + disable raw_len validation; + documentation fixes + 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; + warning fixes; disable run-time SSE detection on gcc; + uniform handling of optional "return" values; + thread-safe initialization of zlib tables + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) allocate large structures on the stack + remove white matting for transparent PSD + fix reported channel count for PNG & BMP + re-enable SSE2 in non-gcc 64-bit + support RGB-formatted JPEG + read 16-bit PNGs (only as 8-bit) + 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED + 2.09 (2016-01-16) allow comments in PNM files + 16-bit-per-pixel TGA (not bit-per-component) + info() for TGA could break due to .hdr handling + info() for BMP to shares code instead of sloppy parse + can use STBI_REALLOC_SIZED if allocator doesn't support realloc + code cleanup + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) fix compiler warnings + partial animated GIF support + limited 16-bpc PSD support + #ifdef unused functions + bug with < 92 byte PIC,PNM,HDR,TGA + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) extra corruption checking (mmozeiko) + stbi_set_flip_vertically_on_load (nguillemot) + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) + progressive JPEG (stb) + PGM/PPM support (Ken Miller) + STBI_MALLOC,STBI_REALLOC,STBI_FREE + GIF bugfix -- seemingly never worked + STBI_NO_*, STBI_ONLY_* + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) + optimize PNG (ryg) + fix bug in interlaced PNG with user-specified channel count (stb) + 1.46 (2014-08-26) + fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG + 1.45 (2014-08-16) + fix MSVC-ARM internal compiler error by wrapping malloc + 1.44 (2014-08-07) + various warning fixes from Ronny Chevalier + 1.43 (2014-07-15) + fix MSVC-only compiler problem in code changed in 1.42 + 1.42 (2014-07-09) + don't define _CRT_SECURE_NO_WARNINGS (affects user code) + fixes to stbi__cleanup_jpeg path + added STBI_ASSERT to avoid requiring assert.h + 1.41 (2014-06-25) + fix search&replace from 1.36 that messed up comments/error messages + 1.40 (2014-06-22) + fix gcc struct-initialization warning + 1.39 (2014-06-15) + fix to TGA optimization when req_comp != number of components in TGA; + fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) + add support for BMP version 5 (more ignored fields) + 1.38 (2014-06-06) + suppress MSVC warnings on integer casts truncating values + fix accidental rename of 'skip' field of I/O + 1.37 (2014-06-04) + remove duplicate typedef + 1.36 (2014-06-03) + convert to header file single-file library + if de-iphone isn't set, load iphone images color-swapped instead of returning NULL + 1.35 (2014-05-27) + various warnings + fix broken STBI_SIMD path + fix bug where stbi_load_from_file no longer left file pointer in correct place + fix broken non-easy path for 32-bit BMP (possibly never used) + TGA optimization by Arseny Kapoulkine + 1.34 (unknown) + use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-stbi_uc to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) + 1.21 fix use of 'stbi_uc' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 (2008-08-02) + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - stbi__convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 (2006-11-19) + first released version +*/ + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/samples/readback/third_party/stb_image_write.h b/samples/readback/third_party/stb_image_write.h new file mode 100644 index 0000000..151eaca --- /dev/null +++ b/samples/readback/third_party/stb_image_write.h @@ -0,0 +1,1724 @@ +/* stb_image_write - v1.16 - public domain - http://nothings.org/stb + writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 + no warranty implied; use at your own risk + + Before #including, + + #define STB_IMAGE_WRITE_IMPLEMENTATION + + in the file that you want to have the implementation. + + Will probably not work correctly with strict-aliasing optimizations. + +ABOUT: + + This header file is a library for writing images to C stdio or a callback. + + The PNG output is not optimal; it is 20-50% larger than the file + written by a decent optimizing implementation; though providing a custom + zlib compress function (see STBIW_ZLIB_COMPRESS) can mitigate that. + This library is designed for source code compactness and simplicity, + not optimal image file size or run-time performance. + +BUILDING: + + You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. + You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace + malloc,realloc,free. + You can #define STBIW_MEMMOVE() to replace memmove() + You can #define STBIW_ZLIB_COMPRESS to use a custom zlib-style compress function + for PNG compression (instead of the builtin one), it must have the following signature: + unsigned char * my_compress(unsigned char *data, int data_len, int *out_len, int quality); + The returned data will be freed with STBIW_FREE() (free() by default), + so it must be heap allocated with STBIW_MALLOC() (malloc() by default), + +UNICODE: + + If compiling for Windows and you wish to use Unicode filenames, compile + with + #define STBIW_WINDOWS_UTF8 + and pass utf8-encoded filenames. Call stbiw_convert_wchar_to_utf8 to convert + Windows wchar_t filenames to utf8. + +USAGE: + + There are five functions, one for each image file format: + + int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_jpg(char const *filename, int w, int h, int comp, const void *data, int quality); + int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); + + void stbi_flip_vertically_on_write(int flag); // flag is non-zero to flip data vertically + + There are also five equivalent functions that use an arbitrary write function. You are + expected to open/close your file-equivalent before and after calling these: + + int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); + int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); + + where the callback is: + void stbi_write_func(void *context, void *data, int size); + + You can configure it with these global variables: + int stbi_write_tga_with_rle; // defaults to true; set to 0 to disable RLE + int stbi_write_png_compression_level; // defaults to 8; set to higher for more compression + int stbi_write_force_png_filter; // defaults to -1; set to 0..5 to force a filter mode + + + You can define STBI_WRITE_NO_STDIO to disable the file variant of these + functions, so the library will not use stdio.h at all. However, this will + also disable HDR writing, because it requires stdio for formatted output. + + Each function returns 0 on failure and non-0 on success. + + The functions create an image file defined by the parameters. The image + is a rectangle of pixels stored from left-to-right, top-to-bottom. + Each pixel contains 'comp' channels of data stored interleaved with 8-bits + per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is + monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. + The *data pointer points to the first byte of the top-left-most pixel. + For PNG, "stride_in_bytes" is the distance in bytes from the first byte of + a row of pixels to the first byte of the next row of pixels. + + PNG creates output files with the same number of components as the input. + The BMP format expands Y to RGB in the file format and does not + output alpha. + + PNG supports writing rectangles of data even when the bytes storing rows of + data are not consecutive in memory (e.g. sub-rectangles of a larger image), + by supplying the stride between the beginning of adjacent rows. The other + formats do not. (Thus you cannot write a native-format BMP through the BMP + writer, both because it is in BGR order and because it may have padding + at the end of the line.) + + PNG allows you to set the deflate compression level by setting the global + variable 'stbi_write_png_compression_level' (it defaults to 8). + + HDR expects linear float data. Since the format is always 32-bit rgb(e) + data, alpha (if provided) is discarded, and for monochrome data it is + replicated across all three channels. + + TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed + data, set the global variable 'stbi_write_tga_with_rle' to 0. + + JPEG does ignore alpha channels in input data; quality is between 1 and 100. + Higher quality looks better but results in a bigger image. + JPEG baseline (no JPEG progressive). + +CREDITS: + + + Sean Barrett - PNG/BMP/TGA + Baldur Karlsson - HDR + Jean-Sebastien Guay - TGA monochrome + Tim Kelsey - misc enhancements + Alan Hickman - TGA RLE + Emmanuel Julien - initial file IO callback implementation + Jon Olick - original jo_jpeg.cpp code + Daniel Gibson - integrate JPEG, allow external zlib + Aarni Koskela - allow choosing PNG filter + + bugfixes: + github:Chribba + Guillaume Chereau + github:jry2 + github:romigrou + Sergio Gonzalez + Jonas Karlsson + Filip Wasil + Thatcher Ulrich + github:poppolopoppo + Patrick Boettcher + github:xeekworx + Cap Petschulat + Simon Rodriguez + Ivan Tikhonov + github:ignotion + Adam Schackart + Andrew Kensler + +LICENSE + + See end of file for license information. + +*/ + +#ifndef INCLUDE_STB_IMAGE_WRITE_H +#define INCLUDE_STB_IMAGE_WRITE_H + +#include + +// if STB_IMAGE_WRITE_STATIC causes problems, try defining STBIWDEF to 'inline' or 'static inline' +#ifndef STBIWDEF +#ifdef STB_IMAGE_WRITE_STATIC +#define STBIWDEF static +#else +#ifdef __cplusplus +#define STBIWDEF extern "C" +#else +#define STBIWDEF extern +#endif +#endif +#endif + +#ifndef STB_IMAGE_WRITE_STATIC // C++ forbids static forward declarations +STBIWDEF int stbi_write_tga_with_rle; +STBIWDEF int stbi_write_png_compression_level; +STBIWDEF int stbi_write_force_png_filter; +#endif + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); +STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality); + +#ifdef STBIW_WINDOWS_UTF8 +STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif +#endif + +typedef void stbi_write_func(void *context, void *data, int size); + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); +STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); + +STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean); + +#endif//INCLUDE_STB_IMAGE_WRITE_H + +#ifdef STB_IMAGE_WRITE_IMPLEMENTATION + +#ifdef _WIN32 + #ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS + #endif + #ifndef _CRT_NONSTDC_NO_DEPRECATE + #define _CRT_NONSTDC_NO_DEPRECATE + #endif +#endif + +#ifndef STBI_WRITE_NO_STDIO +#include +#endif // STBI_WRITE_NO_STDIO + +#include +#include +#include +#include + +#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) +// ok +#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." +#endif + +#ifndef STBIW_MALLOC +#define STBIW_MALLOC(sz) malloc(sz) +#define STBIW_REALLOC(p,newsz) realloc(p,newsz) +#define STBIW_FREE(p) free(p) +#endif + +#ifndef STBIW_REALLOC_SIZED +#define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) +#endif + + +#ifndef STBIW_MEMMOVE +#define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) +#endif + + +#ifndef STBIW_ASSERT +#include +#define STBIW_ASSERT(x) assert(x) +#endif + +#define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) + +#ifdef STB_IMAGE_WRITE_STATIC +static int stbi_write_png_compression_level = 8; +static int stbi_write_tga_with_rle = 1; +static int stbi_write_force_png_filter = -1; +#else +int stbi_write_png_compression_level = 8; +int stbi_write_tga_with_rle = 1; +int stbi_write_force_png_filter = -1; +#endif + +static int stbi__flip_vertically_on_write = 0; + +STBIWDEF void stbi_flip_vertically_on_write(int flag) +{ + stbi__flip_vertically_on_write = flag; +} + +typedef struct +{ + stbi_write_func *func; + void *context; + unsigned char buffer[64]; + int buf_used; +} stbi__write_context; + +// initialize a callback-based context +static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) +{ + s->func = c; + s->context = context; +} + +#ifndef STBI_WRITE_NO_STDIO + +static void stbi__stdio_write(void *context, void *data, int size) +{ + fwrite(data,1,size,(FILE*) context); +} + +#if defined(_WIN32) && defined(STBIW_WINDOWS_UTF8) +#ifdef __cplusplus +#define STBIW_EXTERN extern "C" +#else +#define STBIW_EXTERN extern +#endif +STBIW_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBIW_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); + +STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + +static FILE *stbiw__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_WIN32) && defined(STBIW_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) + return 0; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + +static int stbi__start_write_file(stbi__write_context *s, const char *filename) +{ + FILE *f = stbiw__fopen(filename, "wb"); + stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); + return f != NULL; +} + +static void stbi__end_write_file(stbi__write_context *s) +{ + fclose((FILE *)s->context); +} + +#endif // !STBI_WRITE_NO_STDIO + +typedef unsigned int stbiw_uint32; +typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; + +static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) +{ + while (*fmt) { + switch (*fmt++) { + case ' ': break; + case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int)); + s->func(s->context,&x,1); + break; } + case '2': { int x = va_arg(v,int); + unsigned char b[2]; + b[0] = STBIW_UCHAR(x); + b[1] = STBIW_UCHAR(x>>8); + s->func(s->context,b,2); + break; } + case '4': { stbiw_uint32 x = va_arg(v,int); + unsigned char b[4]; + b[0]=STBIW_UCHAR(x); + b[1]=STBIW_UCHAR(x>>8); + b[2]=STBIW_UCHAR(x>>16); + b[3]=STBIW_UCHAR(x>>24); + s->func(s->context,b,4); + break; } + default: + STBIW_ASSERT(0); + return; + } + } +} + +static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) +{ + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); +} + +static void stbiw__write_flush(stbi__write_context *s) +{ + if (s->buf_used) { + s->func(s->context, &s->buffer, s->buf_used); + s->buf_used = 0; + } +} + +static void stbiw__putc(stbi__write_context *s, unsigned char c) +{ + s->func(s->context, &c, 1); +} + +static void stbiw__write1(stbi__write_context *s, unsigned char a) +{ + if ((size_t)s->buf_used + 1 > sizeof(s->buffer)) + stbiw__write_flush(s); + s->buffer[s->buf_used++] = a; +} + +static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) +{ + int n; + if ((size_t)s->buf_used + 3 > sizeof(s->buffer)) + stbiw__write_flush(s); + n = s->buf_used; + s->buf_used = n+3; + s->buffer[n+0] = a; + s->buffer[n+1] = b; + s->buffer[n+2] = c; +} + +static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) +{ + unsigned char bg[3] = { 255, 0, 255}, px[3]; + int k; + + if (write_alpha < 0) + stbiw__write1(s, d[comp - 1]); + + switch (comp) { + case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case + case 1: + if (expand_mono) + stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp + else + stbiw__write1(s, d[0]); // monochrome TGA + break; + case 4: + if (!write_alpha) { + // composite against pink background + for (k = 0; k < 3; ++k) + px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; + stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); + break; + } + /* FALLTHROUGH */ + case 3: + stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); + break; + } + if (write_alpha > 0) + stbiw__write1(s, d[comp - 1]); +} + +static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) +{ + stbiw_uint32 zero = 0; + int i,j, j_end; + + if (y <= 0) + return; + + if (stbi__flip_vertically_on_write) + vdir *= -1; + + if (vdir < 0) { + j_end = -1; j = y-1; + } else { + j_end = y; j = 0; + } + + for (; j != j_end; j += vdir) { + for (i=0; i < x; ++i) { + unsigned char *d = (unsigned char *) data + (j*x+i)*comp; + stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); + } + stbiw__write_flush(s); + s->func(s->context, &zero, scanline_pad); + } +} + +static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) +{ + if (y < 0 || x < 0) { + return 0; + } else { + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); + stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); + return 1; + } +} + +static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) +{ + if (comp != 4) { + // write RGB bitmap + int pad = (-x*3) & 3; + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, + "11 4 22 4" "4 44 22 444444", + 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header + 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header + } else { + // RGBA bitmaps need a v4 header + // use BI_BITFIELDS mode with 32bpp and alpha mask + // (straight BI_RGB with alpha mask doesn't work in most readers) + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *)data,1,0, + "11 4 22 4" "4 44 22 444444 4444 4 444 444 444 444", + 'B', 'M', 14+108+x*y*4, 0, 0, 14+108, // file header + 108, x,y, 1,32, 3,0,0,0,0,0, 0xff0000,0xff00,0xff,0xff000000u, 0, 0,0,0, 0,0,0, 0,0,0, 0,0,0); // bitmap V4 header + } +} + +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s = { 0 }; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_bmp_core(&s, x, y, comp, data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s = { 0 }; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_bmp_core(&s, x, y, comp, data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif //!STBI_WRITE_NO_STDIO + +static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) +{ + int has_alpha = (comp == 2 || comp == 4); + int colorbytes = has_alpha ? comp-1 : comp; + int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 + + if (y < 0 || x < 0) + return 0; + + if (!stbi_write_tga_with_rle) { + return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, + "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); + } else { + int i,j,k; + int jend, jdir; + + stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); + + if (stbi__flip_vertically_on_write) { + j = 0; + jend = y; + jdir = 1; + } else { + j = y-1; + jend = -1; + jdir = -1; + } + for (; j != jend; j += jdir) { + unsigned char *row = (unsigned char *) data + j * x * comp; + int len; + + for (i = 0; i < x; i += len) { + unsigned char *begin = row + i * comp; + int diff = 1; + len = 1; + + if (i < x - 1) { + ++len; + diff = memcmp(begin, row + (i + 1) * comp, comp); + if (diff) { + const unsigned char *prev = begin; + for (k = i + 2; k < x && len < 128; ++k) { + if (memcmp(prev, row + k * comp, comp)) { + prev += comp; + ++len; + } else { + --len; + break; + } + } + } else { + for (k = i + 2; k < x && len < 128; ++k) { + if (!memcmp(begin, row + k * comp, comp)) { + ++len; + } else { + break; + } + } + } + } + + if (diff) { + unsigned char header = STBIW_UCHAR(len - 1); + stbiw__write1(s, header); + for (k = 0; k < len; ++k) { + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); + } + } else { + unsigned char header = STBIW_UCHAR(len - 129); + stbiw__write1(s, header); + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); + } + } + } + stbiw__write_flush(s); + } + return 1; +} + +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s = { 0 }; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_tga_core(&s, x, y, comp, (void *) data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s = { 0 }; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR writer +// by Baldur Karlsson + +#define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) + +#ifndef STBI_WRITE_NO_STDIO + +static void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) +{ + int exponent; + float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); + + if (maxcomp < 1e-32f) { + rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; + } else { + float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; + + rgbe[0] = (unsigned char)(linear[0] * normalize); + rgbe[1] = (unsigned char)(linear[1] * normalize); + rgbe[2] = (unsigned char)(linear[2] * normalize); + rgbe[3] = (unsigned char)(exponent + 128); + } +} + +static void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) +{ + unsigned char lengthbyte = STBIW_UCHAR(length+128); + STBIW_ASSERT(length+128 <= 255); + s->func(s->context, &lengthbyte, 1); + s->func(s->context, &databyte, 1); +} + +static void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) +{ + unsigned char lengthbyte = STBIW_UCHAR(length); + STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code + s->func(s->context, &lengthbyte, 1); + s->func(s->context, data, length); +} + +static void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) +{ + unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; + unsigned char rgbe[4]; + float linear[3]; + int x; + + scanlineheader[2] = (width&0xff00)>>8; + scanlineheader[3] = (width&0x00ff); + + /* skip RLE for images too small or large */ + if (width < 8 || width >= 32768) { + for (x=0; x < width; x++) { + switch (ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + s->func(s->context, rgbe, 4); + } + } else { + int c,r; + /* encode into scratch buffer */ + for (x=0; x < width; x++) { + switch(ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + scratch[x + width*0] = rgbe[0]; + scratch[x + width*1] = rgbe[1]; + scratch[x + width*2] = rgbe[2]; + scratch[x + width*3] = rgbe[3]; + } + + s->func(s->context, scanlineheader, 4); + + /* RLE each component separately */ + for (c=0; c < 4; c++) { + unsigned char *comp = &scratch[width*c]; + + x = 0; + while (x < width) { + // find first run + r = x; + while (r+2 < width) { + if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) + break; + ++r; + } + if (r+2 >= width) + r = width; + // dump up to first run + while (x < r) { + int len = r-x; + if (len > 128) len = 128; + stbiw__write_dump_data(s, len, &comp[x]); + x += len; + } + // if there's a run, output it + if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd + // find next byte after run + while (r < width && comp[r] == comp[x]) + ++r; + // output run up to r + while (x < r) { + int len = r-x; + if (len > 127) len = 127; + stbiw__write_run_data(s, len, comp[x]); + x += len; + } + } + } + } + } +} + +static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) +{ + if (y <= 0 || x <= 0 || data == NULL) + return 0; + else { + // Each component is stored separately. Allocate scratch space for full output scanline. + unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); + int i, len; + char buffer[128]; + char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; + s->func(s->context, header, sizeof(header)-1); + +#ifdef __STDC_LIB_EXT1__ + len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); +#else + len = snprintf(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); +#endif + s->func(s->context, buffer, len); + + for(i=0; i < y; i++) + stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i)); + STBIW_FREE(scratch); + return 1; + } +} + +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) +{ + stbi__write_context s = { 0 }; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_hdr_core(&s, x, y, comp, (float *) data); +} + +STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) +{ + stbi__write_context s = { 0 }; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif // STBI_WRITE_NO_STDIO + + +////////////////////////////////////////////////////////////////////////////// +// +// PNG writer +// + +#ifndef STBIW_ZLIB_COMPRESS +// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() +#define stbiw__sbraw(a) ((int *) (void *) (a) - 2) +#define stbiw__sbm(a) stbiw__sbraw(a)[0] +#define stbiw__sbn(a) stbiw__sbraw(a)[1] + +#define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) +#define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) +#define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) + +#define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) +#define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) +#define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) + +static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) +{ + int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; + void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); + STBIW_ASSERT(p); + if (p) { + if (!*arr) ((int *) p)[1] = 0; + *arr = (void *) ((int *) p + 2); + stbiw__sbm(*arr) = m; + } + return *arr; +} + +static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) +{ + while (*bitcount >= 8) { + stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); + *bitbuffer >>= 8; + *bitcount -= 8; + } + return data; +} + +static int stbiw__zlib_bitrev(int code, int codebits) +{ + int res=0; + while (codebits--) { + res = (res << 1) | (code & 1); + code >>= 1; + } + return res; +} + +static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) +{ + int i; + for (i=0; i < limit && i < 258; ++i) + if (a[i] != b[i]) break; + return i; +} + +static unsigned int stbiw__zhash(unsigned char *data) +{ + stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +#define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) +#define stbiw__zlib_add(code,codebits) \ + (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) +#define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) +// default huffman tables +#define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) +#define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) +#define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) +#define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) +#define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n)) +#define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) + +#define stbiw__ZHASH 16384 + +#endif // STBIW_ZLIB_COMPRESS + +STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) +{ +#ifdef STBIW_ZLIB_COMPRESS + // user provided a zlib compress implementation, use that + return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality); +#else // use builtin + static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; + static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; + static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; + static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; + unsigned int bitbuf=0; + int i,j, bitcount=0; + unsigned char *out = NULL; + unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(unsigned char**)); + if (hash_table == NULL) + return NULL; + if (quality < 5) quality = 5; + + stbiw__sbpush(out, 0x78); // DEFLATE 32K window + stbiw__sbpush(out, 0x5e); // FLEVEL = 1 + stbiw__zlib_add(1,1); // BFINAL = 1 + stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman + + for (i=0; i < stbiw__ZHASH; ++i) + hash_table[i] = NULL; + + i=0; + while (i < data_len-3) { + // hash next 3 bytes of data to be compressed + int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; + unsigned char *bestloc = 0; + unsigned char **hlist = hash_table[h]; + int n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32768) { // if entry lies within window + int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); + if (d >= best) { best=d; bestloc=hlist[j]; } + } + } + // when hash table entry is too long, delete half the entries + if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { + STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); + stbiw__sbn(hash_table[h]) = quality; + } + stbiw__sbpush(hash_table[h],data+i); + + if (bestloc) { + // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal + h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); + hlist = hash_table[h]; + n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32767) { + int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); + if (e > best) { // if next match is better, bail on current match + bestloc = NULL; + break; + } + } + } + } + + if (bestloc) { + int d = (int) (data+i - bestloc); // distance back + STBIW_ASSERT(d <= 32767 && best <= 258); + for (j=0; best > lengthc[j+1]-1; ++j); + stbiw__zlib_huff(j+257); + if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); + for (j=0; d > distc[j+1]-1; ++j); + stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); + if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); + i += best; + } else { + stbiw__zlib_huffb(data[i]); + ++i; + } + } + // write out final bytes + for (;i < data_len; ++i) + stbiw__zlib_huffb(data[i]); + stbiw__zlib_huff(256); // end of block + // pad with 0 bits to byte boundary + while (bitcount) + stbiw__zlib_add(0,1); + + for (i=0; i < stbiw__ZHASH; ++i) + (void) stbiw__sbfree(hash_table[i]); + STBIW_FREE(hash_table); + + // store uncompressed instead if compression was worse + if (stbiw__sbn(out) > data_len + 2 + ((data_len+32766)/32767)*5) { + stbiw__sbn(out) = 2; // truncate to DEFLATE 32K window and FLEVEL = 1 + for (j = 0; j < data_len;) { + int blocklen = data_len - j; + if (blocklen > 32767) blocklen = 32767; + stbiw__sbpush(out, data_len - j == blocklen); // BFINAL = ?, BTYPE = 0 -- no compression + stbiw__sbpush(out, STBIW_UCHAR(blocklen)); // LEN + stbiw__sbpush(out, STBIW_UCHAR(blocklen >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(~blocklen)); // NLEN + stbiw__sbpush(out, STBIW_UCHAR(~blocklen >> 8)); + memcpy(out+stbiw__sbn(out), data+j, blocklen); + stbiw__sbn(out) += blocklen; + j += blocklen; + } + } + + { + // compute adler32 on input + unsigned int s1=1, s2=0; + int blocklen = (int) (data_len % 5552); + j=0; + while (j < data_len) { + for (i=0; i < blocklen; ++i) { s1 += data[j+i]; s2 += s1; } + s1 %= 65521; s2 %= 65521; + j += blocklen; + blocklen = 5552; + } + stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s2)); + stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s1)); + } + *out_len = stbiw__sbn(out); + // make returned pointer freeable + STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); + return (unsigned char *) stbiw__sbraw(out); +#endif // STBIW_ZLIB_COMPRESS +} + +static unsigned int stbiw__crc32(unsigned char *buffer, int len) +{ +#ifdef STBIW_CRC32 + return STBIW_CRC32(buffer, len); +#else + static unsigned int crc_table[256] = + { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + + unsigned int crc = ~0u; + int i; + for (i=0; i < len; ++i) + crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; + return ~crc; +#endif +} + +#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) +#define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); +#define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) + +static void stbiw__wpcrc(unsigned char **data, int len) +{ + unsigned int crc = stbiw__crc32(*data - len - 4, len+4); + stbiw__wp32(*data, crc); +} + +static unsigned char stbiw__paeth(int a, int b, int c) +{ + int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); + if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); + if (pb <= pc) return STBIW_UCHAR(b); + return STBIW_UCHAR(c); +} + +// @OPTIMIZE: provide an option that always forces left-predict or paeth predict +static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char *line_buffer) +{ + static int mapping[] = { 0,1,2,3,4 }; + static int firstmap[] = { 0,1,0,5,6 }; + int *mymap = (y != 0) ? mapping : firstmap; + int i; + int type = mymap[filter_type]; + unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y); + int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes; + + if (type==0) { + memcpy(line_buffer, z, width*n); + return; + } + + // first loop isn't optimized since it's just one pixel + for (i = 0; i < n; ++i) { + switch (type) { + case 1: line_buffer[i] = z[i]; break; + case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break; + case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break; + case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break; + case 5: line_buffer[i] = z[i]; break; + case 6: line_buffer[i] = z[i]; break; + } + } + switch (type) { + case 1: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-n]; break; + case 2: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-signed_stride]; break; + case 3: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break; + case 4: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break; + case 5: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - (z[i-n]>>1); break; + case 6: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; + } +} + +STBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) +{ + int force_filter = stbi_write_force_png_filter; + int ctype[5] = { -1, 0, 4, 2, 6 }; + unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; + unsigned char *out,*o, *filt, *zlib; + signed char *line_buffer; + int j,zlen; + + if (stride_bytes == 0) + stride_bytes = x * n; + + if (force_filter >= 5) { + force_filter = -1; + } + + filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; + line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } + for (j=0; j < y; ++j) { + int filter_type; + if (force_filter > -1) { + filter_type = force_filter; + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, force_filter, line_buffer); + } else { // Estimate the best filter by running through all of them: + int best_filter = 0, best_filter_val = 0x7fffffff, est, i; + for (filter_type = 0; filter_type < 5; filter_type++) { + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, filter_type, line_buffer); + + // Estimate the entropy of the line using this filter; the less, the better. + est = 0; + for (i = 0; i < x*n; ++i) { + est += abs((signed char) line_buffer[i]); + } + if (est < best_filter_val) { + best_filter_val = est; + best_filter = filter_type; + } + } + if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, best_filter, line_buffer); + filter_type = best_filter; + } + } + // when we get here, filter_type contains the filter type, and line_buffer contains the data + filt[j*(x*n+1)] = (unsigned char) filter_type; + STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); + } + STBIW_FREE(line_buffer); + zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_compression_level); + STBIW_FREE(filt); + if (!zlib) return 0; + + // each tag requires 12 bytes of overhead + out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); + if (!out) return 0; + *out_len = 8 + 12+13 + 12+zlen + 12; + + o=out; + STBIW_MEMMOVE(o,sig,8); o+= 8; + stbiw__wp32(o, 13); // header length + stbiw__wptag(o, "IHDR"); + stbiw__wp32(o, x); + stbiw__wp32(o, y); + *o++ = 8; + *o++ = STBIW_UCHAR(ctype[n]); + *o++ = 0; + *o++ = 0; + *o++ = 0; + stbiw__wpcrc(&o,13); + + stbiw__wp32(o, zlen); + stbiw__wptag(o, "IDAT"); + STBIW_MEMMOVE(o, zlib, zlen); + o += zlen; + STBIW_FREE(zlib); + stbiw__wpcrc(&o, zlen); + + stbiw__wp32(o,0); + stbiw__wptag(o, "IEND"); + stbiw__wpcrc(&o,0); + + STBIW_ASSERT(o == out + *out_len); + + return out; +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) +{ + FILE *f; + int len; + unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + + f = stbiw__fopen(filename, "wb"); + if (!f) { STBIW_FREE(png); return 0; } + fwrite(png, 1, len, f); + fclose(f); + STBIW_FREE(png); + return 1; +} +#endif + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) +{ + int len; + unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + func(context, png, len); + STBIW_FREE(png); + return 1; +} + + +/* *************************************************************************** + * + * JPEG writer + * + * This is based on Jon Olick's jo_jpeg.cpp: + * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html + */ + +static const unsigned char stbiw__jpg_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18, + 24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 }; + +static void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) { + int bitBuf = *bitBufP, bitCnt = *bitCntP; + bitCnt += bs[1]; + bitBuf |= bs[0] << (24 - bitCnt); + while(bitCnt >= 8) { + unsigned char c = (bitBuf >> 16) & 255; + stbiw__putc(s, c); + if(c == 255) { + stbiw__putc(s, 0); + } + bitBuf <<= 8; + bitCnt -= 8; + } + *bitBufP = bitBuf; + *bitCntP = bitCnt; +} + +static void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) { + float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p; + float z1, z2, z3, z4, z5, z11, z13; + + float tmp0 = d0 + d7; + float tmp7 = d0 - d7; + float tmp1 = d1 + d6; + float tmp6 = d1 - d6; + float tmp2 = d2 + d5; + float tmp5 = d2 - d5; + float tmp3 = d3 + d4; + float tmp4 = d3 - d4; + + // Even part + float tmp10 = tmp0 + tmp3; // phase 2 + float tmp13 = tmp0 - tmp3; + float tmp11 = tmp1 + tmp2; + float tmp12 = tmp1 - tmp2; + + d0 = tmp10 + tmp11; // phase 3 + d4 = tmp10 - tmp11; + + z1 = (tmp12 + tmp13) * 0.707106781f; // c4 + d2 = tmp13 + z1; // phase 5 + d6 = tmp13 - z1; + + // Odd part + tmp10 = tmp4 + tmp5; // phase 2 + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + // The rotator is modified from fig 4-8 to avoid extra negations. + z5 = (tmp10 - tmp12) * 0.382683433f; // c6 + z2 = tmp10 * 0.541196100f + z5; // c2-c6 + z4 = tmp12 * 1.306562965f + z5; // c2+c6 + z3 = tmp11 * 0.707106781f; // c4 + + z11 = tmp7 + z3; // phase 5 + z13 = tmp7 - z3; + + *d5p = z13 + z2; // phase 6 + *d3p = z13 - z2; + *d1p = z11 + z4; + *d7p = z11 - z4; + + *d0p = d0; *d2p = d2; *d4p = d4; *d6p = d6; +} + +static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) { + int tmp1 = val < 0 ? -val : val; + val = val < 0 ? val-1 : val; + bits[1] = 1; + while(tmp1 >>= 1) { + ++bits[1]; + } + bits[0] = val & ((1<0)&&(DU[end0pos]==0); --end0pos) { + } + // end0pos = first element in reverse order !=0 + if(end0pos == 0) { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); + return DU[0]; + } + for(i = 1; i <= end0pos; ++i) { + int startpos = i; + int nrzeroes; + unsigned short bits[2]; + for (; DU[i]==0 && i<=end0pos; ++i) { + } + nrzeroes = i-startpos; + if ( nrzeroes >= 16 ) { + int lng = nrzeroes>>4; + int nrmarker; + for (nrmarker=1; nrmarker <= lng; ++nrmarker) + stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes); + nrzeroes &= 15; + } + stbiw__jpg_calcBits(DU[i], bits); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); + } + if(end0pos != 63) { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); + } + return DU[0]; +} + +static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void* data, int quality) { + // Constants that don't pollute global namespace + static const unsigned char std_dc_luminance_nrcodes[] = {0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0}; + static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const unsigned char std_ac_luminance_nrcodes[] = {0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d}; + static const unsigned char std_ac_luminance_values[] = { + 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, + 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, + 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, + 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, + 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, + 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, + 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa + }; + static const unsigned char std_dc_chrominance_nrcodes[] = {0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0}; + static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const unsigned char std_ac_chrominance_nrcodes[] = {0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77}; + static const unsigned char std_ac_chrominance_values[] = { + 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, + 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, + 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, + 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, + 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, + 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, + 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa + }; + // Huffman tables + static const unsigned short YDC_HT[256][2] = { {0,2},{2,3},{3,3},{4,3},{5,3},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9}}; + static const unsigned short UVDC_HT[256][2] = { {0,2},{1,2},{2,2},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9},{1022,10},{2046,11}}; + static const unsigned short YAC_HT[256][2] = { + {10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} + }; + static const unsigned short UVAC_HT[256][2] = { + {0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} + }; + static const int YQT[] = {16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22, + 37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99}; + static const int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99, + 99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99}; + static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f, + 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f }; + + int row, col, i, k, subsample; + float fdtbl_Y[64], fdtbl_UV[64]; + unsigned char YTable[64], UVTable[64]; + + if(!data || !width || !height || comp > 4 || comp < 1) { + return 0; + } + + quality = quality ? quality : 90; + subsample = quality <= 90 ? 1 : 0; + quality = quality < 1 ? 1 : quality > 100 ? 100 : quality; + quality = quality < 50 ? 5000 / quality : 200 - quality * 2; + + for(i = 0; i < 64; ++i) { + int uvti, yti = (YQT[i]*quality+50)/100; + YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti); + uvti = (UVQT[i]*quality+50)/100; + UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti); + } + + for(row = 0, k = 0; row < 8; ++row) { + for(col = 0; col < 8; ++col, ++k) { + fdtbl_Y[k] = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); + fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); + } + } + + // Write Headers + { + static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 }; + static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 }; + const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width), + 3,1,(unsigned char)(subsample?0x22:0x11),0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 }; + s->func(s->context, (void*)head0, sizeof(head0)); + s->func(s->context, (void*)YTable, sizeof(YTable)); + stbiw__putc(s, 1); + s->func(s->context, UVTable, sizeof(UVTable)); + s->func(s->context, (void*)head1, sizeof(head1)); + s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1); + s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values)); + stbiw__putc(s, 0x10); // HTYACinfo + s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1); + s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values)); + stbiw__putc(s, 1); // HTUDCinfo + s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1); + s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values)); + stbiw__putc(s, 0x11); // HTUACinfo + s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1); + s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values)); + s->func(s->context, (void*)head2, sizeof(head2)); + } + + // Encode 8x8 macroblocks + { + static const unsigned short fillBits[] = {0x7F, 7}; + int DCY=0, DCU=0, DCV=0; + int bitBuf=0, bitCnt=0; + // comp == 2 is grey+alpha (alpha is ignored) + int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0; + const unsigned char *dataR = (const unsigned char *)data; + const unsigned char *dataG = dataR + ofsG; + const unsigned char *dataB = dataR + ofsB; + int x, y, pos; + if(subsample) { + for(y = 0; y < height; y += 16) { + for(x = 0; x < width; x += 16) { + float Y[256], U[256], V[256]; + for(row = y, pos = 0; row < y+16; ++row) { + // row >= height => use last input row + int clamped_row = (row < height) ? row : height - 1; + int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp; + for(col = x; col < x+16; ++col, ++pos) { + // if col >= width => use pixel from last input column + int p = base_p + ((col < width) ? col : (width-1))*comp; + float r = dataR[p], g = dataG[p], b = dataB[p]; + Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128; + U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b; + V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b; + } + } + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+0, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+8, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+128, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+136, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + + // subsample U,V + { + float subU[64], subV[64]; + int yy, xx; + for(yy = 0, pos = 0; yy < 8; ++yy) { + for(xx = 0; xx < 8; ++xx, ++pos) { + int j = yy*32+xx*2; + subU[pos] = (U[j+0] + U[j+1] + U[j+16] + U[j+17]) * 0.25f; + subV[pos] = (V[j+0] + V[j+1] + V[j+16] + V[j+17]) * 0.25f; + } + } + DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subU, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); + DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subV, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); + } + } + } + } else { + for(y = 0; y < height; y += 8) { + for(x = 0; x < width; x += 8) { + float Y[64], U[64], V[64]; + for(row = y, pos = 0; row < y+8; ++row) { + // row >= height => use last input row + int clamped_row = (row < height) ? row : height - 1; + int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp; + for(col = x; col < x+8; ++col, ++pos) { + // if col >= width => use pixel from last input column + int p = base_p + ((col < width) ? col : (width-1))*comp; + float r = dataR[p], g = dataG[p], b = dataB[p]; + Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128; + U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b; + V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b; + } + } + + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y, 8, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, U, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); + DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, V, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); + } + } + } + + // Do the bit alignment of the EOI marker + stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits); + } + + // EOI + stbiw__putc(s, 0xFF); + stbiw__putc(s, 0xD9); + + return 1; +} + +STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality) +{ + stbi__write_context s = { 0 }; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality); +} + + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality) +{ + stbi__write_context s = { 0 }; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_jpg_core(&s, x, y, comp, data, quality); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + +#endif // STB_IMAGE_WRITE_IMPLEMENTATION + +/* Revision history + 1.16 (2021-07-11) + make Deflate code emit uncompressed blocks when it would otherwise expand + support writing BMPs with alpha channel + 1.15 (2020-07-13) unknown + 1.14 (2020-02-02) updated JPEG writer to downsample chroma channels + 1.13 + 1.12 + 1.11 (2019-08-11) + + 1.10 (2019-02-07) + support utf8 filenames in Windows; fix warnings and platform ifdefs + 1.09 (2018-02-11) + fix typo in zlib quality API, improve STB_I_W_STATIC in C++ + 1.08 (2018-01-29) + add stbi__flip_vertically_on_write, external zlib, zlib quality, choose PNG filter + 1.07 (2017-07-24) + doc fix + 1.06 (2017-07-23) + writing JPEG (using Jon Olick's code) + 1.05 ??? + 1.04 (2017-03-03) + monochrome BMP expansion + 1.03 ??? + 1.02 (2016-04-02) + avoid allocating large structures on the stack + 1.01 (2016-01-16) + STBIW_REALLOC_SIZED: support allocators with no realloc support + avoid race-condition in crc initialization + minor compile issues + 1.00 (2015-09-14) + installable file IO function + 0.99 (2015-09-13) + warning fixes; TGA rle support + 0.98 (2015-04-08) + added STBIW_MALLOC, STBIW_ASSERT etc + 0.97 (2015-01-18) + fixed HDR asserts, rewrote HDR rle logic + 0.96 (2015-01-17) + add HDR output + fix monochrome BMP + 0.95 (2014-08-17) + add monochrome TGA output + 0.94 (2014-05-31) + rename private functions to avoid conflicts with stb_image.h + 0.93 (2014-05-27) + warning fixes + 0.92 (2010-08-01) + casts to unsigned char to fix warnings + 0.91 (2010-07-17) + first public release + 0.90 first internal release +*/ + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/samples/rubics_cube/AndroidManifest.xml b/samples/rubics_cube/AndroidManifest.xml new file mode 100644 index 0000000..ae7ad79 --- /dev/null +++ b/samples/rubics_cube/AndroidManifest.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/rubics_cube/CMakeLists.txt b/samples/rubics_cube/CMakeLists.txt new file mode 100644 index 0000000..d9ea98f --- /dev/null +++ b/samples/rubics_cube/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.22) +project(rubics_cube) + +set(SAMPLE_SRCS + ${CMAKE_CURRENT_LIST_DIR}/cpp/rubics_cube.cpp + ${CMAKE_CURRENT_LIST_DIR}/cpp/camera_manager.cpp + ${CMAKE_CURRENT_LIST_DIR}/cpp/color_classifier.cpp + ${CMAKE_CURRENT_LIST_DIR}/cpp/scan_ui.cpp + ${CMAKE_CURRENT_LIST_DIR}/cpp/scan_manager.cpp + ${CMAKE_CURRENT_LIST_DIR}/cpp/cube_model.cpp + ${CMAKE_CURRENT_LIST_DIR}/cpp/solver.cpp + ${CMAKE_CURRENT_LIST_DIR}/cpp/virtual_cube.cpp +) +set(SAMPLE_DIR ${CMAKE_CURRENT_LIST_DIR}/cpp) +set(USE_SECURE_MR_UTILS ON) +set(THIRD_PARTY_DIRS ${CMAKE_CURRENT_LIST_DIR}/third_party) +include(${CMAKE_CURRENT_LIST_DIR}/../../cmake/kociemba.cmake) + +# OpenCV (Android only) +if(ANDROID) + # This creates an imported target named `lib_opencv` and sets include dirs. + include(${CMAKE_CURRENT_LIST_DIR}/../../cmake/opencv.cmake) +endif() + +include(../../base/base.cmake) + +# Link OpenCV to the current sample target (Android only) +if(ANDROID) + target_link_libraries(${PROJECT_NAME} PRIVATE lib_opencv) +endif() + +target_link_libraries(${PROJECT_NAME} PRIVATE kociemba_c) diff --git a/samples/rubics_cube/README.md b/samples/rubics_cube/README.md new file mode 100644 index 0000000..09dc6b5 --- /dev/null +++ b/samples/rubics_cube/README.md @@ -0,0 +1,31 @@ +## Sample: Rubic's Cube + +This sample demonstrates a SecureMR-powered Rubik's Cube scanner and solver for PICO. It guides you through scanning each face via passthrough, classifies sticker colors on-device, computes a solution, and visualizes the turn sequence with an overlaid virtual cube and UI hints. + +### Code walk-through + +The orchestrator lives in `cpp/rubics_cube.cpp` and wires together the subsystems below: + +- **Camera + readback**: `CreateRelaxMrReadbackPipeline` opens the SecureMR VST pipeline to produce stereo RGB tensors. `TensorReadback` continuously copies the left-eye tensor `vstOutputLeftUint8Global` to the CPU for color analysis. +- **Computer vision**: `CameraManager`, `ScanManager`, and `ColorClassifier` process readback frames to detect facelets while `VirtualCube` mirrors the detected state and `CubeModel` tracks geometry. +- **Solving + UI**: Once all faces are captured, `Solver` computes a solution. `ScanUi` renders overlay arrows/labels that guide scanning, and `VirtualCube` animates the resulting turn sequence. + +Pipelines: + +1. `m_secureMrVSTImagePipeline` — captures stereo passthrough RGB and exposes the left-eye tensor (`vstOutputLeftUint8Global`) for CPU-side processing, plus timestamp/camera calibration for pose alignment. +2. A lightweight readback loop (`RunRelaxMrReadbackPipeline`) uses `TensorReadback` to pull the left-eye image at ~20 Hz; all CV runs on the CPU, so no extra inference pipeline is required. + +Rendering and UI run in the OpenXR layer using the custom overlay and virtual cube renderers, driven by the SecureMR camera/readback outputs. + +### How to Build and Run + +1. **Build and install** + Connect your PICO 4 Ultra device and run: + ```bash + ./gradlew :samples:rubics_cube:installDebug + ``` + +2. **Run the app** + Launch the "Rubic's Cube" app. Follow the on-screen instructions to scan each face of your physical cube. + +*** diff --git a/samples/rubics_cube/build.gradle.kts b/samples/rubics_cube/build.gradle.kts new file mode 100644 index 0000000..d4e72b6 --- /dev/null +++ b/samples/rubics_cube/build.gradle.kts @@ -0,0 +1,69 @@ +plugins { + id("com.android.application") + id("org.jetbrains.kotlin.android") +} +android { + compileSdk = 34 + ndkVersion = "26.3.11579264" + namespace = "com.bytedance.pico.secure_mr_demo.rubics_cube" + defaultConfig { + minSdk = 34 + targetSdk = 34 + versionCode = 1 + versionName = "1.0" + applicationId = "com.bytedance.pico.secure_mr_demo.rubics_cube" + externalNativeBuild { + cmake { + arguments.add("-DANDROID_STL=c++_shared") + arguments.add("-DANDROID_USE_LEGACY_TOOLCHAIN_FILE=OFF") + } + ndk { + abiFilters.add("arm64-v8a") + } + } + } + lint { + disable.add("ExpiredTargetSdkVersion") + } + buildFeatures { + prefab = true + viewBinding = true + } + buildTypes { + getByName("debug") { + isDebuggable = true + isJniDebuggable = true + } + getByName("release") { + isDebuggable = false + isJniDebuggable = false + } + } + flavorDimensions += "version" + externalNativeBuild { + cmake { + version = "3.22.1" + path("CMakeLists.txt") + } + } + sourceSets { + getByName("main") { + manifest.srcFile("AndroidManifest.xml") + assets.srcDirs("../../assets/rubics_cube") + java.srcDirs("src/main/java") + } + } + packaging { + jniLibs { + keepDebugSymbols.add("**.so") + } + } + kotlinOptions { + jvmTarget = "1.8" + } +} +dependencies { +// implementation("com.android.support:appcompat-v7:28.0.0") +// implementation("androidx.activity:activity-ktx:1.11.0") + implementation("androidx.appcompat:appcompat:1.7.0") +} diff --git a/samples/rubics_cube/cpp/camera_manager.cpp b/samples/rubics_cube/cpp/camera_manager.cpp new file mode 100644 index 0000000..b1351db --- /dev/null +++ b/samples/rubics_cube/cpp/camera_manager.cpp @@ -0,0 +1,61 @@ +// Copyright (2025) Bytedance Ltd. and/or its affiliates +// Licensed under the Apache License, Version 2.0 + +#include "camera_manager.h" +#include "logger.h" +#include "common.h" + +#include +#include + +namespace SecureMR { + +void CameraManager::OnReadbackResult(TensorReadbackResult&& result) { + if (result.data.empty() || result.dimensions.empty()) { + Log::Write(Log::Level::Debug, "RubicsCube|CameraManager: empty readback or no dimensions"); + return; + } + Frame frame; + // Conservatively interpret dims: if two dims, treat as HxW; otherwise 1D + if (result.dimensions.size() >= 2) { + frame.height = result.dimensions[0]; + frame.width = result.dimensions[1]; + } else { + frame.height = 1; + frame.width = result.dimensions[0]; + } + const int srcChannels = result.channels; + const int w = frame.width, h = frame.height; + const size_t bytes = result.data.size(); + + // Construct src Mat that views the incoming buffer, then convert to BGR and own it. + if (srcChannels == 3) { + cv::Mat src(h, w, CV_8UC3, const_cast(result.data.data())); + cv::cvtColor(src, frame.mat, cv::COLOR_RGB2BGR); + } else if (srcChannels == 4) { + cv::Mat src(h, w, CV_8UC4, const_cast(result.data.data())); + cv::cvtColor(src, frame.mat, cv::COLOR_RGBA2BGR); + } else { + // Fallback: treat as single-channel and expand to BGR by replication + cv::Mat src(h, w, CV_8UC1, const_cast(result.data.data())); + cv::cvtColor(src, frame.mat, cv::COLOR_GRAY2BGR); + } + frame.channels = frame.mat.channels(); // should be 3 + frame.timestamp = std::chrono::steady_clock::now(); + + { + std::scoped_lock lk(mtx_); + latest_ = std::move(frame); + hasFrame_ = true; + } + + Log::Write(Log::Level::Debug, Fmt("RubicsCube|CameraManager: frame updated %dx%dx%d (BGR), bytes=%zu", w, h, 3, bytes)); +} + +std::optional CameraManager::GetLatestFrame() const { + std::scoped_lock lk(mtx_); + if (!hasFrame_) return std::nullopt; + return latest_; +} + +} // namespace SecureMR diff --git a/samples/rubics_cube/cpp/camera_manager.h b/samples/rubics_cube/cpp/camera_manager.h new file mode 100644 index 0000000..c13ccd4 --- /dev/null +++ b/samples/rubics_cube/cpp/camera_manager.h @@ -0,0 +1,34 @@ +// Copyright (2025) Bytedance Ltd. and/or its affiliates +// Licensed under the Apache License, Version 2.0 + +#pragma once + +#include "pch.h" + +#include +#include + +#include "cube_types.h" +#include "securemr_utils/readback_async.h" + +namespace SecureMR { + +// Lightweight manager that accepts TensorReadbackResult and exposes a CPU-side Frame +class CameraManager { + public: + CameraManager() = default; + ~CameraManager() = default; + + void OnReadbackResult(TensorReadbackResult&& result); + + // Thread-safe snapshot of latest frame + std::optional GetLatestFrame() const; + + private: + mutable std::mutex mtx_; + Frame latest_{}; + bool hasFrame_{false}; +}; + +} // namespace SecureMR + diff --git a/samples/rubics_cube/cpp/color_classifier.cpp b/samples/rubics_cube/cpp/color_classifier.cpp new file mode 100644 index 0000000..7046040 --- /dev/null +++ b/samples/rubics_cube/cpp/color_classifier.cpp @@ -0,0 +1,64 @@ +// Copyright (2025) Bytedance Ltd. and/or its affiliates +// Licensed under the Apache License, Version 2.0 + +#include "color_classifier.h" +#include "logger.h" +#include "common.h" + +#include + +namespace SecureMR { + +static float Dist2(const PatchColor& a, const PatchColor& b) { + const float dr = a.r - b.r; + const float dg = a.g - b.g; + const float db = a.b - b.b; + return dr * dr + dg * dg + db * db; +} + +void ColorClassifier::CalibrateCenter(Face face, const PatchColor& c) { + centers_[face] = c; + Log::Write(Log::Level::Debug, + Fmt("RubicsCube|ColorClassifier: calibrate face %d center rgb=(%.2f,%.2f,%.2f)", static_cast(face), c.r, c.g, + c.b)); +} + +Color ColorClassifier::Classify(const PatchColor& c) const { + if (!centers_.empty()) { + float best = std::numeric_limits::max(); + Face bestFace = Face::U; + for (const auto& kv : centers_) { + float d = Dist2(c, kv.second); + if (d < best) { + best = d; + bestFace = kv.first; + } + } + // Map face centers to fixed colors based on standard scheme + Color out; + switch (bestFace) { + case Face::U: out = Color::W; break; + case Face::D: out = Color::Y; break; + case Face::F: out = Color::G; break; + case Face::B: out = Color::B; break; + case Face::R: out = Color::R; break; + case Face::L: out = Color::O; break; + } + Log::Write(Log::Level::Debug, + Fmt("RubicsCube|ColorClassifier: classify rgb=(%.2f,%.2f,%.2f) -> %s by nearest face %d", c.r, c.g, c.b, + ColorToChar(out).c_str(), static_cast(bestFace))); + return out; + } + + // Fallback heuristic: dominant channel + Color out = Color::Unknown; + if (c.r >= c.g && c.r >= c.b) out = Color::R; + else if (c.g >= c.r && c.g >= c.b) out = Color::G; + else if (c.b >= c.r && c.b >= c.g) out = Color::B; + Log::Write(Log::Level::Debug, + Fmt("RubicsCube|ColorClassifier: fallback classify rgb=(%.2f,%.2f,%.2f) -> %s", c.r, c.g, c.b, + ColorToChar(out).c_str())); + return out; +} + +} // namespace SecureMR diff --git a/samples/rubics_cube/cpp/color_classifier.h b/samples/rubics_cube/cpp/color_classifier.h new file mode 100644 index 0000000..04b17ae --- /dev/null +++ b/samples/rubics_cube/cpp/color_classifier.h @@ -0,0 +1,27 @@ +// Copyright (2025) Bytedance Ltd. and/or its affiliates +// Licensed under the Apache License, Version 2.0 + +#pragma once + +#include "pch.h" + +#include +#include + +#include "cube_types.h" + +namespace SecureMR { + +class ColorClassifier { + public: + ColorClassifier() = default; + + void CalibrateCenter(Face face, const PatchColor& c); + Color Classify(const PatchColor& c) const; + + private: + std::unordered_map centers_; +}; + +} // namespace SecureMR + diff --git a/samples/rubics_cube/cpp/cube_model.cpp b/samples/rubics_cube/cpp/cube_model.cpp new file mode 100644 index 0000000..4445e55 --- /dev/null +++ b/samples/rubics_cube/cpp/cube_model.cpp @@ -0,0 +1,172 @@ +// Copyright (2025) Bytedance Ltd. and/or its affiliates +// Licensed under the Apache License, Version 2.0 + +#include "cube_model.h" +#include "logger.h" +#include "common.h" + +namespace SecureMR { + +CubeModel::CubeModel() { + for (auto& face : stickers_) { + face.fill(Color::Unknown); + } + faceSet_.fill(false); + for (auto& rawFace : faceRawColors_) { + for (auto& patch : rawFace) { + patch = {}; + } + } + faceRawSet_.fill(false); +} + +void CubeModel::SetFaceColors(Face face, const std::array& colors) { + const int idx = static_cast(face); + stickers_[idx] = colors; + faceSet_[idx] = true; + std::string seq; + seq.reserve(9); + for (Color c : colors) seq += ColorToChar(c); + Log::Write(Log::Level::Info, Fmt("RubicsCube|CubeModel: set face %d colors=%s", idx, seq.c_str())); +} + +void CubeModel::SetFaceFromPatches(const ScannedFace& face, const ColorClassifier& classifier) { + std::array c{}; + for (size_t i = 0; i < 9; ++i) { + c[i] = classifier.Classify(face.patches[i]); + } + SetFaceColors(face.face, c); + const int idx = static_cast(face.face); + faceRawColors_[idx] = face.patches; + faceRawSet_[idx] = true; +} + +void CubeModel::SetFaceRawColors(Face face, const std::array& patches) { + const int idx = static_cast(face); + if (idx < 0 || idx >= 6) return; + faceRawColors_[idx] = patches; + faceRawSet_[idx] = true; +} + +bool CubeModel::Validate() const { + // Basic check: every face set, and each color count equals 9 + for (bool s : faceSet_) { + if (!s) return false; + } + std::array cnt{}; + for (const auto& face : stickers_) { + for (Color c : face) { + switch (c) { + case Color::W: cnt[0]++; break; + case Color::Y: cnt[1]++; break; + case Color::R: cnt[2]++; break; + case Color::O: cnt[3]++; break; + case Color::G: cnt[4]++; break; + case Color::B: cnt[5]++; break; + default: return false; + } + } + } + Log::Write(Log::Level::Info, + Fmt("RubicsCube|CubeModel: counts W=%d Y=%d R=%d O=%d G=%d B=%d", cnt[0], cnt[1], cnt[2], cnt[3], cnt[4], cnt[5])); + for (int v : cnt) { + if (v != 9) return false; + } + return true; +} + +std::string CubeModel::ToKociembaCode() const { + // Order: U, R, F, D, L, B; each face left-to-right top-to-bottom + std::string s; + s.reserve(54); + auto appendFace = [&s](const std::array& f) { + for (Color c : f) s += ColorToChar(c); + }; + appendFace(stickers_[static_cast(Face::U)]); + appendFace(stickers_[static_cast(Face::R)]); + appendFace(stickers_[static_cast(Face::F)]); + appendFace(stickers_[static_cast(Face::D)]); + appendFace(stickers_[static_cast(Face::L)]); + appendFace(stickers_[static_cast(Face::B)]); + Log::Write(Log::Level::Info, Fmt("RubicsCube|CubeModel: Kociemba code=%s", s.c_str())); + return s; +} + +std::string CubeModel::ToKociembaFacelets() const { + // Map each Color to one of URFDLB letters based on center stickers of faces + // Center index = 4 in each 3x3 face array + char colorToFace[6]; + for (int i = 0; i < 6; ++i) colorToFace[i] = '?'; + auto colorIndex = [](Color c) -> int { + switch (c) { + case Color::W: return 0; + case Color::Y: return 1; + case Color::R: return 2; + case Color::O: return 3; + case Color::G: return 4; + case Color::B: return 5; + default: return -1; + } + }; + const char faceLetters[6] = {'U','R','F','D','L','B'}; + for (int f = 0; f < 6; ++f) { + Color center = stickers_[f][4]; + int idx = colorIndex(center); + if (idx >= 0) colorToFace[idx] = faceLetters[f]; + } + // Build facelets string in order U,R,F,D,L,B each 9 stickers + std::string s; + s.reserve(54); + auto appendFace = [&](int fIndex) { + for (int i = 0; i < 9; ++i) { + Color c = stickers_[fIndex][i]; + int ci = colorIndex(c); + char ch = (ci >= 0) ? colorToFace[ci] : '?'; + s += ch; + } + }; + appendFace(static_cast(Face::U)); + appendFace(static_cast(Face::R)); + appendFace(static_cast(Face::F)); + appendFace(static_cast(Face::D)); + appendFace(static_cast(Face::L)); + appendFace(static_cast(Face::B)); + Log::Write(Log::Level::Info, Fmt("RubicsCube|CubeModel: facelets=%s", s.c_str())); + return s; +} + +void CubeModel::Clear() { + for (auto& face : stickers_) { + face.fill(Color::Unknown); + } + faceSet_.fill(false); + for (auto& rawFace : faceRawColors_) { + for (auto& patch : rawFace) { + patch = {}; + } + } + faceRawSet_.fill(false); + Log::Write(Log::Level::Info, "RubicsCube|CubeModel: cleared all faces"); +} + +int CubeModel::GetScannedCount() const { + int cnt = 0; + for (bool s : faceSet_) cnt += s ? 1 : 0; + return cnt; +} + +std::optional> CubeModel::GetFaceColors(Face face) const { + const int idx = static_cast(face); + if (idx < 0 || idx >= 6) return std::nullopt; + if (!faceSet_[idx]) return std::nullopt; + return stickers_[idx]; +} + +std::optional> CubeModel::GetFaceRawColors(Face face) const { + const int idx = static_cast(face); + if (idx < 0 || idx >= 6) return std::nullopt; + if (!faceRawSet_[idx]) return std::nullopt; + return faceRawColors_[idx]; +} + +} // namespace SecureMR diff --git a/samples/rubics_cube/cpp/cube_model.h b/samples/rubics_cube/cpp/cube_model.h new file mode 100644 index 0000000..8261500 --- /dev/null +++ b/samples/rubics_cube/cpp/cube_model.h @@ -0,0 +1,49 @@ +// Copyright (2025) Bytedance Ltd. and/or its affiliates +// Licensed under the Apache License, Version 2.0 + +#pragma once + +#include "pch.h" + +#include +#include + +#include "color_classifier.h" +#include "cube_types.h" + +namespace SecureMR { + +class CubeModel { + public: + CubeModel(); + + // Set colors for a face directly + void SetFaceColors(Face face, const std::array& colors); + // Convenience: classify from patches via classifier + void SetFaceFromPatches(const ScannedFace& face, const ColorClassifier& classifier); + void SetFaceRawColors(Face face, const std::array& patches); + + bool Validate() const; // basic count check + std::string ToKociembaCode() const; + // Produce facelet string in URFDLB letters for kociemba + std::string ToKociembaFacelets() const; + + // Reset all faces to Unknown and clear set flags + void Clear(); + // Count how many faces have been set + int GetScannedCount() const; + // Get face colors if set + std::optional> GetFaceColors(Face face) const; + std::optional> GetFaceRawColors(Face face) const; + + void ApplyMove(const Move& /*move*/) {}; // stub for now + + private: + // Stored as 6 faces x 9 stickers, face index matches Face enum order + std::array, 6> stickers_; + std::array faceSet_{}; + std::array, 6> faceRawColors_{}; + std::array faceRawSet_{}; +}; + +} // namespace SecureMR diff --git a/samples/rubics_cube/cpp/cube_types.h b/samples/rubics_cube/cpp/cube_types.h new file mode 100644 index 0000000..815a81c --- /dev/null +++ b/samples/rubics_cube/cpp/cube_types.h @@ -0,0 +1,67 @@ +// Copyright (2025) Bytedance Ltd. and/or its affiliates +// Licensed under the Apache License, Version 2.0 + +#pragma once + +#include "pch.h" + +#include +#include +#include +#include +#include + +#include + +namespace SecureMR { + +// Face order follows design: U, R, F, D, L, B +enum class Face { U = 0, R = 1, F = 2, D = 3, L = 4, B = 5 }; + +// Discrete cube colors +enum class Color { W = 0, Y, R, O, G, B, Unknown }; + +// Basic cube move types +enum class MoveType { U, R, F, D, L, B }; + +// Quarter-turn metric: clockwise, counter-clockwise, double +enum class Turn { CW = 1, CCW = -1, Double = 2 }; + +struct Move { + MoveType type{MoveType::U}; + Turn turn{Turn::CW}; +}; + +// Frame captured from camera tensor +struct Frame { + int width{0}; + int height{0}; + int channels{0}; + // BGR image buffer + cv::Mat mat; // CV_8UC3 (BGR) + std::chrono::steady_clock::time_point timestamp{}; +}; + +// Average color of a grid patch +struct PatchColor { + float r{0.0f}, g{0.0f}, b{0.0f}; +}; + +struct ScannedFace { + Face face{Face::U}; + std::array patches{}; // row-major 3x3 +}; + +inline std::string ColorToChar(Color c) { + switch (c) { + case Color::W: return "W"; + case Color::Y: return "Y"; + case Color::R: return "R"; + case Color::O: return "O"; + case Color::G: return "G"; + case Color::B: return "B"; + default: return "?"; + } +} + +} // namespace SecureMR diff --git a/samples/rubics_cube/cpp/rubics_cube.cpp b/samples/rubics_cube/cpp/rubics_cube.cpp new file mode 100644 index 0000000..01edbe2 --- /dev/null +++ b/samples/rubics_cube/cpp/rubics_cube.cpp @@ -0,0 +1,918 @@ +// Copyright (2025) Bytedance Ltd. and/or its affiliates +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "rubics_cube.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "common.h" +#include "camera_manager.h" +#include "color_classifier.h" +#include "cube_model.h" +#include "logger.h" +#include "scan_manager.h" +#include "solver.h" +#include "scan_ui.h" +#include "virtual_cube.h" +#include "xr_linear.h" + +namespace SecureMR { + +extern AAssetManager* g_assetManager; + +struct android_app* RubicsCube::gapp = nullptr; + +namespace { +constexpr auto kReadbackInterval = std::chrono::milliseconds(50); +constexpr float kCubeForwardOffsetM = 0.85f; +constexpr float kCubeLeftOffsetM = 0.25f; + +ScanUiState::Arrow ArrowForStep(int step) { + static const ScanUiState::Arrow seq[6] = {ScanUiState::Arrow::Down, ScanUiState::Arrow::Right, + ScanUiState::Arrow::Down, ScanUiState::Arrow::Right, + ScanUiState::Arrow::Down, ScanUiState::Arrow::Right}; + if (step <= 0) return ScanUiState::Arrow::None; + int idx = step - 1; + if (idx >= 0 && idx < 6) return seq[idx]; + return ScanUiState::Arrow::None; +} + +std::optional TurnForArrow(ScanUiState::Arrow arrow) { + switch (arrow) { + case ScanUiState::Arrow::Right: return VirtualCube::CubeTurnOp::Right; + case ScanUiState::Arrow::Down: return VirtualCube::CubeTurnOp::Down; + default: return std::nullopt; + } +} + +template +std::array RemapGrid(const std::array& input, const std::array& mapping) { + std::array output{}; + for (int i = 0; i < 9; ++i) { + int target = mapping[i]; + if (target >= 0 && target < 9) { + output[target] = input[i]; + } + } + return output; +} + +Color LetterToColor(char letter) { + switch (letter) { + case 'U': return Color::W; + case 'R': return Color::R; + case 'F': return Color::G; + case 'D': return Color::Y; + case 'L': return Color::O; + case 'B': return Color::B; + default: return Color::Unknown; + } +} + +PatchColor ColorToPatch(Color color) { + switch (color) { + case Color::W: return {1.0f, 1.0f, 1.0f}; + case Color::Y: return {1.0f, 1.0f, 0.0f}; + case Color::R: return {1.0f, 0.0f, 0.0f}; + case Color::O: return {1.0f, 0.55f, 0.0f}; + case Color::G: return {0.0f, 1.0f, 0.0f}; + case Color::B: return {0.0f, 0.0f, 1.0f}; + default: return {0.3f, 0.3f, 0.3f}; + } +} +} // namespace + +RubicsCube::RubicsCube(const XrInstance& instance, const XrSession& session) + : instance_(instance), session_(session) {} + +RubicsCube::~RubicsCube() { + if (readback_) { + readback_->Stop(); + } + if (scanUi_) { + scanUi_->StopThread(); + } +} + +void RubicsCube::CreateFramework() { + Log::SetLevel(Log::Level::Debug); + Log::Write(Log::Level::Info, "RubicsCube|Orchestrator: Initializing SecureMR framework session for readback sample"); + const int imgW = overlayW_ > 0 ? overlayW_ : 1024; + const int imgH = overlayH_ > 0 ? overlayH_ : 1024; + frameworkSession_ = std::make_shared(instance_, session_, imgW, imgH); + Log::Write(Log::Level::Info, "RubicsCube|Orchestrator: Framework session created"); + + // Initialize components + cameraManager_ = std::make_unique(); + colorClassifier_ = std::make_shared(); + scanManager_ = std::make_unique(colorClassifier_); + cubeModel_ = std::make_unique(); + solver_ = std::make_unique(); + virtualCube_ = std::make_unique(); + scanUi_ = std::make_unique(); + + if (scanUi_ && scanManager_) { + scanUi_->SetScanManager(scanManager_.get()); + // Start UI composition thread with initial overlay size hint + const int imgW = overlayW_ > 0 ? overlayW_ : 1024; + const int imgH = overlayH_ > 0 ? overlayH_ : 1024; + scanUi_->SetOverlaySize(imgW, imgH); + scanUi_->StartThread(); + } + stage_ = AppStage::Scan; + hasLastAcceptedCenter_ = false; + solutionText_.clear(); + solutionSteps_.clear(); + cachedFacelets_.reset(); + scanIndex_ = 0; + Log::Write(Log::Level::Info, "RubicsCube|Orchestrator: Managers created; entering Scan stage"); + if (virtualCube_ && cubeModel_) virtualCube_->SetCubeModel(cubeModel_.get()); + if (virtualCube_) { + virtualCube_->ResetOrientation(); + } + ClearArrowHold(); +} + +void RubicsCube::CreateGlobalTensors() { + const int imgW = overlayW_ > 0 ? overlayW_ : 1024; + const int imgH = overlayH_ > 0 ? overlayH_ : 1024; + vstOutputLeftUint8Global_ = std::make_shared( + frameworkSession_, + TensorAttribute{.dimensions = {imgW, imgH}, + .channels = 3, + .usage = XR_SECURE_MR_TENSOR_TYPE_MAT_PICO, + .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_UINT8_PICO}); +} + +void RubicsCube::CreateRelaxMrReadbackPipeline() { + relaxMrReadbackPipeline_ = std::make_shared(frameworkSession_); + vstOutputLeftUint8Placeholder_ = + PipelineTensor::PipelinePlaceholderLike(relaxMrReadbackPipeline_, vstOutputLeftUint8Global_); + relaxMrReadbackPipeline_->cameraAccess(nullptr, vstOutputLeftUint8Placeholder_, nullptr, nullptr); +} + +void RubicsCube::CreatePipelines() { + CreateGlobalTensors(); + CreateRelaxMrReadbackPipeline(); + + TensorReadback::Target target{ + .tensor = vstOutputLeftUint8Global_, + .callback = [this](TensorReadbackResult&& result) { HandleReadbackResult(std::move(result)); }, + .name = "vst_output_left_u8"}; + + TensorReadback::Config config{.pollingInterval = std::chrono::milliseconds(33)}; + readback_ = std::make_unique(frameworkSession_, std::vector{std::move(target)}, config); + readback_->Start(); + + pipelinesInitialized_ = true; + Log::Write(Log::Level::Info, "RubicsCube|Orchestrator: Pipelines and readback utilities initialized"); +} + +void RubicsCube::RunRelaxMrReadbackPipeline() { + if (!relaxMrReadbackPipeline_) { + return; + } + relaxMrReadbackPipeline_->submit({{vstOutputLeftUint8Placeholder_, vstOutputLeftUint8Global_}}, XR_NULL_HANDLE, nullptr); +} + +void RubicsCube::Tick() { + if (!pipelinesInitialized_) { + return; + } + + const auto now = std::chrono::steady_clock::now(); + if (stage_ != AppStage::Scan && arrowHoldActive_) { + ClearArrowHold(); + } else if (arrowHoldActive_) { + if (now - arrowHoldStart_ >= arrowHoldDuration_) { + ClearArrowHold(); + } + } + // Only submit readback during scanning to save bandwidth/CPU in later stages + if (stage_ == AppStage::Scan && now - lastRelaxReadbackRunTime_ >= kReadbackInterval) { + RunRelaxMrReadbackPipeline(); + lastRelaxReadbackRunTime_ = now; + } + if (awaitingValidate_) { + auto elapsed = std::chrono::duration_cast(now - scanCompleteTime_); + if (elapsed >= std::chrono::seconds(1)) { + awaitingValidate_ = false; + stage_ = AppStage::Validate; + solutionText_.clear(); + solutionSteps_.clear(); + Log::Write(Log::Level::Info, "RubicsCube|Orchestrator: delay elapsed; entering Validate stage"); + ClearArrowHold(); + } + } + if (now - lastTickLog_ >= std::chrono::seconds(1)) { + auto stageName = [](AppStage s) -> const char* { + switch (s) { + case AppStage::Idle: return "Idle"; + case AppStage::Scan: return "Scan"; + case AppStage::Validate: return "Validate"; + case AppStage::Solve: return "Solve"; + case AppStage::Animate: return "Animate"; + case AppStage::Done: return "Done"; + default: return "Unknown"; + } + }; + Log::Write(Log::Level::Info, + Fmt("RubicsCube|TickFlow: stage=%s scanIndex=%d awaitingValidate=%d arrowHold=%d lastCenter=%d cachedFacelets=%d headPose=%d", + stageName(stage_), + scanIndex_, + awaitingValidate_ ? 1 : 0, + arrowHoldActive_ ? 1 : 0, + hasLastAcceptedCenter_ ? 1 : 0, + cachedFacelets_.has_value() ? 1 : 0, + hasHeadPose_ ? 1 : 0)); + lastTickLog_ = now; + } + // continue into scanning state machine + + // Lightweight state machine driver + if (stage_ == AppStage::Scan && cameraManager_ && scanManager_) { + if (scanIndex_ >= static_cast(scanOrder_.size())) { + // All faces have been scanned; wait for the delayed Validate transition without touching scanOrder_. + return; + } + auto frameOpt = cameraManager_->GetLatestFrame(); + if (frameOpt) { + Face target = scanOrder_[scanIndex_]; + Face previewFace = target; + if (auto facing = GetFacingFaceFromHeadPose()) { + previewFace = *facing; + } + const bool allowFaceOps = !arrowHoldActive_; + if (allowFaceOps && virtualCube_) { + auto patches = scanManager_->GetPatchColors9U8(); + std::array preview{}; + std::array previewDisplay{}; + auto classifyColor = [&](uint8_t r8, uint8_t g8, uint8_t b8) { + PatchColor pc{r8 / 255.0f, g8 / 255.0f, b8 / 255.0f}; + if (colorClassifier_) { + return colorClassifier_->Classify(pc); + } + if (r8 >= g8 && r8 >= b8) return Color::R; + if (g8 >= r8 && g8 >= b8) return Color::G; + if (b8 >= r8 && b8 >= g8) return Color::B; + return Color::Unknown; + }; + for (int i = 0; i < 9; ++i) { + preview[i] = classifyColor(patches[i][0], patches[i][1], patches[i][2]); + previewDisplay[i] = {patches[i][0] / 255.0f, patches[i][1] / 255.0f, patches[i][2] / 255.0f}; + } + if (hasHeadPose_) { + std::array mapping{}; + if (virtualCube_->MapScreenGridToFaceIndices(previewFace, lastHeadPose_.orientation, mapping)) { + preview = RemapGrid(preview, mapping); + previewDisplay = RemapGrid(previewDisplay, mapping); + } + } + virtualCube_->PreviewFace(previewFace, preview, previewDisplay); + } + Log::Write(Log::Level::Debug, + Fmt("RubicsCube|Orchestrator: Tick scanning index=%d targetFace=%d", scanIndex_, static_cast(target))); + if (allowFaceOps && scanManager_->TryLockFace(target, *frameOpt)) { + auto locked = scanManager_->GetLockedFace(); + if (locked && cubeModel_ && colorClassifier_) { + Face facingFace = target; + if (auto facing = GetFacingFaceFromHeadPose()) { + facingFace = *facing; + } + locked->face = facingFace; + if (hasHeadPose_ && virtualCube_) { + std::array mapping{}; + if (virtualCube_->MapScreenGridToFaceIndices(facingFace, lastHeadPose_.orientation, mapping)) { + auto reordered = RemapGrid(locked->patches, mapping); + locked->patches = reordered; + } + } + // Compute 8-bit center color of the newly locked face + const auto& pc = locked->patches[4]; + auto clampU8 = [](int v) { return static_cast(std::min(std::max(v, 0), 255)); }; + const uint8_t cr = clampU8(static_cast(std::round(pc.r * 255.0f))); + const uint8_t cg = clampU8(static_cast(std::round(pc.g * 255.0f))); + const uint8_t cb = clampU8(static_cast(std::round(pc.b * 255.0f))); + + bool accept = true; + if (hasLastAcceptedCenter_) { + const int tol = 20; // per-channel tolerance + const int dr = std::abs((int)cr - (int)lastAcceptedCenterU8_[0]); + const int dg = std::abs((int)cg - (int)lastAcceptedCenterU8_[1]); + const int db = std::abs((int)cb - (int)lastAcceptedCenterU8_[2]); + if (dr <= tol && dg <= tol && db <= tol) { + accept = false; // same center color as previous accepted face -> do not advance + } + } + + if (!accept) { + Log::Write(Log::Level::Warning, + Fmt("RubicsCube|Orchestrator: repeated face center color; clearing cube model and restarting scan")); + if (virtualCube_) { + virtualCube_->ClearPreviewFace(facingFace); + virtualCube_->ClearPreview(); + } + if (cubeModel_) cubeModel_->Clear(); + scanIndex_ = 0; + hasLastAcceptedCenter_ = false; + solutionText_.clear(); + solutionSteps_.clear(); + cachedFacelets_.reset(); + stage_ = AppStage::Scan; + if (virtualCube_) { + virtualCube_->SetCubeModel(cubeModel_.get()); + virtualCube_->ClearPreviewFace(scanOrder_[0]); + virtualCube_->ResetOrientation(); + } + awaitingValidate_ = false; + ClearArrowHold(); + } else { + cubeModel_->SetFaceFromPatches(*locked, *colorClassifier_); + if (virtualCube_) { + virtualCube_->ClearPreviewFace(facingFace); + virtualCube_->SetCubeModel(cubeModel_.get()); + } + cachedFacelets_.reset(); + lastAcceptedCenterU8_ = {cr, cg, cb}; + hasLastAcceptedCenter_ = true; + scanIndex_++; + Log::Write(Log::Level::Info, Fmt("RubicsCube|Orchestrator: face %d captured; progress %d/6", + static_cast(target), scanIndex_)); + // Pause scanning for 500ms to avoid cross-face crosstalk + if (scanManager_) scanManager_->SetPauseForMs(500); + if (scanIndex_ >= 6 && !awaitingValidate_) { + awaitingValidate_ = true; + scanCompleteTime_ = std::chrono::steady_clock::now(); + Log::Write(Log::Level::Info, + "RubicsCube|Orchestrator: all faces scanned; holding for 1s before Validate"); + if (virtualCube_) virtualCube_->ClearPreview(); + } + if (virtualCube_) { + auto nextArrow = ArrowForStep(scanIndex_); + if (auto turn = TurnForArrow(nextArrow)) { + StartArrowHold(nextArrow, now); + if (hasHeadPose_) { + virtualCube_->QueueOrientationOpRelative(*turn, lastHeadPose_.orientation); + } else { + virtualCube_->QueueOrientationOp(*turn); + } + } else { + ClearArrowHold(); + } + } + } + } + } + } + } + + // Update overlay Scan UI each tick (static dashed box + arrow + center color) + if ((stage_ == AppStage::Scan || stage_ == AppStage::Validate || stage_ == AppStage::Solve) && scanUi_) { + const auto now2 = std::chrono::steady_clock::now(); + float dt = 0.016f; + if (lastUiTick_.time_since_epoch().count() != 0) { + dt = std::chrono::duration_cast(now2 - lastUiTick_).count() / 1000.0f; + } + lastUiTick_ = now2; + if (virtualCube_) { + if (stage_ == AppStage::Solve) { + virtualCube_->Solving(); + } else { + virtualCube_->Update(dt); + } + } + + ScanUiState ui{}; + ui.scannedCount = cubeModel_ ? cubeModel_->GetScannedCount() : 0; + ui.activeSolveIndex = -1; + ui.solveSteps.clear(); + // Arrow guidance only during Scan + if (stage_ == AppStage::Scan && arrowHoldActive_) { + ui.nextArrow = arrowHoldArrow_; + } else { + ui.nextArrow = ScanUiState::Arrow::None; + } + + // Phase and bottom text + if (stage_ == AppStage::Scan) { + ui.phase = ScanUiState::Phase::Stabilizing; + bool showMotion = false; + if (lastMotionReset_.time_since_epoch().count() != 0) { + showMotion = (now2 - lastMotionReset_) < std::chrono::milliseconds(1500); + } + if (showMotion) { + ui.bottomText = std::string("Motion detected, restarting scan ..."); + } else if (ui.scannedCount <= 0) { + ui.bottomText = std::string("Please place cube inside box."); + } else { + ui.bottomText = Fmt("Scanned %d/6 ...", ui.scannedCount); + } + } else if (stage_ == AppStage::Validate) { + ui.phase = ScanUiState::Phase::Validate; + ui.bottomText = std::string("Hold steady for solution..."); + } else if (stage_ == AppStage::Solve) { + ui.phase = ScanUiState::Phase::Solve; + ui.bottomText = solutionText_.empty() ? std::string("Solving ...") : solutionText_; + ui.solveSteps = solutionSteps_; + if (virtualCube_) { + if (auto idx = virtualCube_->ActiveMoveIndex()) { + if (*idx >= 0 && *idx < static_cast(solutionSteps_.size())) { + ui.activeSolveIndex = *idx; + } + } + } + } + + // Provide hint colors (first face) for Validate/Solve + if (stage_ == AppStage::Validate || stage_ == AppStage::Solve) { + auto toRgb = [](Color c) -> std::array { + switch (c) { + case Color::W: return {255, 255, 255}; + case Color::Y: return {255, 255, 0}; + case Color::R: return {255, 0, 0}; + case Color::O: return {255, 140, 0}; + case Color::G: return {0, 200, 0}; + case Color::B: return {0, 100, 255}; + default: return {80, 80, 80}; + } + }; + if (cubeModel_) { + auto faceColorsOpt = cubeModel_->GetFaceColors(Face::U); + if (faceColorsOpt) { + for (int i = 0; i < 9; ++i) ui.hintColors9[i] = toRgb((*faceColorsOpt)[i]); + } + } + } + auto frameOpt2 = cameraManager_ ? cameraManager_->GetLatestFrame() : std::optional{}; + scanUi_->Update(frameOpt2 ? &*frameOpt2 : nullptr, ui, dt); + } + + if (stage_ == AppStage::Validate) { + if (!cachedFacelets_) { + cachedFacelets_ = BuildFaceletsFromVirtualCube(); + if (cachedFacelets_) { + if (!UpdateCubeModelFromFacelets(*cachedFacelets_)) { + Log::Write(Log::Level::Warning, "VirtualCube: failed to apply cube model from rebuilt facelets during Validate"); + } + } else { + Log::Write(Log::Level::Warning, "VirtualCube: BuildFaceletsFromVirtualCube() returned empty result during Validate"); + } + } + if (cachedFacelets_ && ValidateFacelets(*cachedFacelets_)) { + stage_ = AppStage::Solve; + solutionText_.clear(); + solutionSteps_.clear(); + Log::Write(Log::Level::Info, "RubicsCube|Orchestrator: validation passed; entering Solve stage"); + ClearArrowHold(); + if (virtualCube_) virtualCube_->ClearPreview(); + } else { + // If invalid, reset to rescan + if (virtualCube_) { + if (scanIndex_ >= 0 && scanIndex_ < static_cast(scanOrder_.size())) { + virtualCube_->ClearPreviewFace(scanOrder_[scanIndex_]); + } + virtualCube_->ClearPreview(); + } + if (cubeModel_) cubeModel_->Clear(); + scanIndex_ = 0; + hasLastAcceptedCenter_ = false; + solutionText_.clear(); + solutionSteps_.clear(); + cachedFacelets_.reset(); + stage_ = AppStage::Scan; + Log::Write(Log::Level::Warning, "RubicsCube|Orchestrator: validation failed; restarting scan"); + if (virtualCube_) { + virtualCube_->SetCubeModel(cubeModel_.get()); + virtualCube_->ClearPreviewFace(scanOrder_[0]); + virtualCube_->ClearPreview(); + virtualCube_->ResetOrientation(); + } + awaitingValidate_ = false; + ClearArrowHold(); + } + } + + if (stage_ == AppStage::Solve && cubeModel_ && solver_) { + if (solutionText_.empty()) { + if (cachedFacelets_) { + Log::Write(Log::Level::Info, + Fmt("VirtualCube: cached facelets ready for Solve (len=%zu)", cachedFacelets_->size())); + if (!UpdateCubeModelFromFacelets(*cachedFacelets_)) { + Log::Write(Log::Level::Warning, "VirtualCube: failed to refresh cube model from cached facelets prior to Solve"); + } + } else { + Log::Write(Log::Level::Warning, "VirtualCube: Solve entered without cached facelets; using CubeModel snapshot"); + } + const std::string code = cachedFacelets_.value_or(cubeModel_->ToKociembaFacelets()); + Log::Write(Log::Level::Info, Fmt("RubicsCube|Orchestrator: invoking solver with facelets=%s", code.c_str())); + solutionText_ = solver_->Solve(code); + solutionSteps_.clear(); + if (solutionText_.empty()) { + solutionText_ = std::string("Solve failed (invalid cube or timeout)"); + Log::Write(Log::Level::Warning, "RubicsCube|Orchestrator: solver returned empty result; staying in Solve stage"); + } else { + Log::Write(Log::Level::Info, Fmt("RubicsCube|Orchestrator: solution steps: %s", solutionText_.c_str())); + // Parse and queue moves to the renderer + if (virtualCube_) { + std::vector moves; + moves.reserve(64); + auto tokenize = [](const std::string& s) { + std::vector toks; std::string cur; + for (char ch : s) { + if (ch == ' ' || ch == '\n' || ch == '\t' || ch == '\r') { if (!cur.empty()) { toks.push_back(cur); cur.clear(); } } + else { cur.push_back(ch); } + } + if (!cur.empty()) toks.push_back(cur); + return toks; + }; + auto toks = tokenize(solutionText_); + solutionSteps_ = toks; + for (auto& t : toks) { + if (t.empty()) continue; + Move m{}; + switch (t[0]) { + case 'U': m.type = MoveType::U; break; + case 'D': m.type = MoveType::D; break; + case 'F': m.type = MoveType::F; break; + case 'B': m.type = MoveType::B; break; + case 'R': m.type = MoveType::L; break; // swap L and R for virtual cube + case 'L': m.type = MoveType::R; break; + default: continue; + } + if (t.size() >= 2) { + if (t[1] == '\'') { m.turn = Turn::CCW; } + else if (t[1] == '2') { m.turn = Turn::Double; } + } + + // Fix B/F + if (m.type == MoveType::B || + m.type == MoveType::F + ) { + if (m.turn == Turn::CW) m.turn = Turn::CCW; + else if (m.turn == Turn::CCW) m.turn = Turn::CW; + } + + moves.push_back(m); + } + virtualCube_->QueueFaceMoves(moves); + } + } + } + // Stay in Solve stage to display steps + } +} + +void RubicsCube::HandleReadbackResult(TensorReadbackResult&& result) { + if (result.data.empty()) { + Log::Write(Log::Level::Info, + Fmt("RubicsCube|Readback: target %.*s produced an empty buffer", + static_cast(result.name.size()), result.name.data())); + return; + } + if (cameraManager_) { + cameraManager_->OnReadbackResult(std::move(result)); + } +} + +void RubicsCube::DebugFacesColor(const std::array, 6>& faces) { + constexpr int canvasSize = 200; + constexpr int cellSize = canvasSize / 3; + const std::filesystem::path baseDir("/sdcard/Android/data/com.bytedance.pico.secure_mr_demo.rubics_cube/files"); + std::error_code ec; + std::filesystem::create_directories(baseDir, ec); + if (ec) { + Log::Write(Log::Level::Warning, + Fmt("RubicsCube|DebugFacesColor: failed to create %s (%s)", + baseDir.string().c_str(), + ec.message().c_str())); + } + auto clamp01 = [](float v) { + return std::max(0.0f, std::min(1.0f, v)); + }; + for (int faceIdx = 0; faceIdx < 6; ++faceIdx) { + cv::Mat canvas(canvasSize, canvasSize, CV_8UC3, cv::Scalar(0, 0, 0)); + for (int p = 0; p < 9; ++p) { + int row = p / 3; + int col = p % 3; + int x0 = col * cellSize; + int y0 = row * cellSize; + int x1 = (col == 2) ? canvasSize : x0 + cellSize; + int y1 = (row == 2) ? canvasSize : y0 + cellSize; + const XrVector3f& color = faces[faceIdx][p]; + const uint8_t r = static_cast(clamp01(color.x) * 255.0f + 0.5f); + const uint8_t g = static_cast(clamp01(color.y) * 255.0f + 0.5f); + const uint8_t b = static_cast(clamp01(color.z) * 255.0f + 0.5f); + cv::rectangle(canvas, cv::Point(x0, y0), cv::Point(x1, y1), cv::Scalar(b, g, r), cv::FILLED); + cv::rectangle(canvas, cv::Point(x0, y0), cv::Point(x1, y1), cv::Scalar(40, 40, 40), 2); + } + auto filePath = baseDir / Fmt("debug_%d.png", faceIdx); + if (cv::imwrite(filePath.string(), canvas)) { + Log::Write(Log::Level::Info, + Fmt("RubicsCube|DebugFacesColor: wrote %s", filePath.string().c_str())); + } else { + Log::Write(Log::Level::Warning, + Fmt("RubicsCube|DebugFacesColor: failed to write %s", filePath.string().c_str())); + } + } +} + +std::optional RubicsCube::BuildFaceletsFromVirtualCube() { + if (!virtualCube_) return std::nullopt; + // Ensure cube pose matches initial scan pose: first face toward user, second face up. + virtualCube_->ResetOrientation(); + ResetVirtualCubeAnchorToHead(); + std::array, 6> faces{}; + for (int i = 0; i < 6; ++i) { + auto faceColors = virtualCube_->GetFaceDisplayColors(static_cast(i)); + if (!faceColors) { + Log::Write(Log::Level::Warning, + Fmt("RubicsCube|Orchestrator: missing display colors for face %d while rebuilding facelets", i)); + return std::nullopt; + } + faces[i] = *faceColors; + } + // faces order is Red, Blue, White, Orange, Green, Yellow + // U, L, F, D, R, B + // DebugFacesColor(faces); + + std::array centers{}; + for (int i = 0; i < 6; ++i) centers[i] = faces[i][4]; + + auto dist2 = [](const XrVector3f& a, const XrVector3f& b) { + float dr = a.x - b.x; + float dg = a.y - b.y; + float db = a.z - b.z; + return dr * dr + dg * dg + db * db; + }; + const Face faceOrder[6] = {Face::U, Face::L, Face::F, Face::D, Face::R, Face::B}; + const char faceLetter[6] = {'U', 'L', 'F', 'D', 'R', 'B'}; + + std::array perFace{}; // U R F D L B order + for (auto& s : perFace) s.reserve(9); + + constexpr int orderedPatchIdx[9] = {2, 1, 0, 5, 4, 3, 8, 7, 6}; // remap 123456789 -> 321654987 + constexpr int orderedPatchIdx_Up_Down[9] = {8, 7, 6, 5, 4, 3, 2, 1, 0}; // remap 123456789 -> 321654987 + for (Face f : faceOrder) { + int faceIdx = static_cast(f); + for (int k = 0; k < 9; ++k) { + int p = orderedPatchIdx[k]; + if (f == Face::D || f == Face::U) p = orderedPatchIdx_Up_Down[k]; + const XrVector3f& color = faces[faceIdx][p]; + + // Find closed center color, index is {Red, Blue, White, Orange, Green, Yellow} + int best = faceIdx; + float bestDist = dist2(color, centers[faceIdx]); + for (int c = 0; c < 6; ++c) { + float d = dist2(color, centers[c]); + if (d < bestDist) { + bestDist = d; + best = c; + } + } + const char ch = faceLetter[best]; + perFace[faceIdx].push_back(ch); + } + } + + // swap L and R order, to alias with cubeString order + std::swap(perFace[1], perFace[4]); + + std::string out; + out.reserve(54); + for (const auto& s : perFace) out += s; + + for (int i = 0; i < 6; ++i) { + Face f = faceOrder[i]; + Log::Write(Log::Level::Info, + Fmt("RubicsCube|Orchestrator: face %s facelets=%s", + (f == Face::F ? "F" : f == Face::U ? "U" : f == Face::L ? "L" : f == Face::B ? "B" : f == Face::D ? "D" : "R"), + perFace[i].c_str())); + } + Log::Write(Log::Level::Info, Fmt("RubicsCube|Orchestrator: rebuilt consolidated facelets=%s", out.c_str())); + return out; +} + +bool RubicsCube::ValidateFacelets(const std::string& code) const { + if (code.size() != 54) return false; + std::array counts{}; + for (char ch : code) { + int idx = -1; + switch (ch) { + case 'U': idx = 0; break; + case 'L': idx = 1; break; + case 'F': idx = 2; break; + case 'D': idx = 3; break; + case 'R': idx = 4; break; + case 'B': idx = 5; break; + default: return false; + } + counts[idx]++; + } + for (int c : counts) { + if (c != 9) return false; + } + return true; +} + +bool RubicsCube::UpdateCubeModelFromFacelets(const std::string& facelets) { + if (!cubeModel_) { + Log::Write(Log::Level::Warning, "VirtualCube: UpdateCubeModelFromFacelets called without cubeModel"); + return false; + } + if (facelets.size() != 54) { + Log::Write(Log::Level::Warning, + Fmt("VirtualCube: UpdateCubeModelFromFacelets received invalid length=%zu", facelets.size())); + return false; + } + // Face order aligns with scanning sequence: F, U, L, B, D, R + const Face faceOrder[6] = {Face::F, Face::U, Face::L, Face::B, Face::D, Face::R}; + size_t offset = 0; + int unknownRaw = 0; + for (Face f : faceOrder) { + std::array colors{}; + for (int i = 0; i < 9 && offset < facelets.size(); ++i) { + colors[i] = LetterToColor(facelets[offset++]); + } + cubeModel_->SetFaceColors(f, colors); + if (cubeModel_) { + std::array raw{}; + bool haveRaw = false; + if (virtualCube_) { + auto display = virtualCube_->GetFaceDisplayColors(f); + if (display) { + for (int i = 0; i < 9; ++i) { + raw[i] = {(*display)[i].x, (*display)[i].y, (*display)[i].z}; + } + haveRaw = true; + } + } + if (!haveRaw) { + for (int i = 0; i < 9; ++i) raw[i] = ColorToPatch(colors[i]); + } + cubeModel_->SetFaceRawColors(f, raw); + for (const auto& patch : raw) { + if (patch.r == 0.f && patch.g == 0.f && patch.b == 0.f) { + ++unknownRaw; + } + } + } + } + if (virtualCube_) { + virtualCube_->SetCubeModel(cubeModel_.get()); + } + Log::Write(Log::Level::Info, + Fmt("VirtualCube: applied facelets to cube model (unknownRawRGB=%d)", unknownRaw)); + return true; +} + +void RubicsCube::RequestPermission(struct android_app* app) { + Log::Write(Log::Level::Info, "RubicsCube|Orchestrator: Requesting camera permission from activity"); + RubicsCube::gapp = app; + if (app == nullptr || app->activity == nullptr || app->activity->vm == nullptr) { + Log::Write(Log::Level::Error, "RubicsCube|Orchestrator: Invalid android_app passed to RequestPermission"); + return; + } + + JNIEnv* env = nullptr; + app->activity->vm->AttachCurrentThread(&env, nullptr); + if (env == nullptr) { + Log::Write(Log::Level::Error, "RubicsCube|Orchestrator: Failed to attach JNI environment while requesting permission"); + return; + } + jobject activity = app->activity->clazz; + jclass cls = env->GetObjectClass(activity); + jmethodID requestCamera = env->GetMethodID(cls, "requestCameraFromNative", "()V"); + env->CallVoidMethod(activity, requestCamera); +} + +bool RubicsCube::UpdateOverlayRgba(int width, int height, std::vector& outRgba) { + if (!scanUi_) return false; + // Keep UI thread aware of overlay size + scanUi_->SetOverlaySize(width, height); + const bool changed = scanUi_->TryConsumeOverlayRgba(outRgba); + if (changed) { + Log::Write(Log::Level::Info, Fmt("RubicsCube|Overlay: composed UI RGBA (w=%d h=%d)", width, height)); + } + return changed; +} + +void RubicsCube::SetOverlaySize(int width, int height) { + overlayW_ = width; + overlayH_ = height; + if (scanUi_) scanUi_->SetOverlaySize(width, height); + Log::Write(Log::Level::Info, Fmt("RubicsCube|Orchestrator: overlay size set to %dx%d", width, height)); +} + +std::shared_ptr CreateSecureMrProgram(const XrInstance& instance, const XrSession& session) { + return std::make_shared(instance, session); +} + +void RubicsCube::UpdateHeadPose(const XrPosef& pose) { + lastHeadPose_ = pose; + hasHeadPose_ = true; +} + +void RubicsCube::ResetVirtualCubeAnchorToHead() { + if (!virtualCube_ || !hasHeadPose_) return; + const XrVector3f forwardLocal{0.f, 0.f, -1.f}; + const XrVector3f leftLocal{-1.f, 0.f, 0.f}; + XrVector3f forwardWorld{}; + XrVector3f leftWorld{}; + XrQuaternionf_RotateVector3f(&forwardWorld, &lastHeadPose_.orientation, &forwardLocal); + XrQuaternionf_RotateVector3f(&leftWorld, &lastHeadPose_.orientation, &leftLocal); + XrVector3f anchor = lastHeadPose_.position; + anchor.x += forwardWorld.x * kCubeForwardOffsetM + leftWorld.x * kCubeLeftOffsetM; + anchor.y += forwardWorld.y * kCubeForwardOffsetM + leftWorld.y * kCubeLeftOffsetM; + anchor.z += forwardWorld.z * kCubeForwardOffsetM + leftWorld.z * kCubeLeftOffsetM; + virtualCube_->SetAnchorPosition(anchor); +} + +std::optional RubicsCube::GetFacingFaceFromHeadPose() const { + if (!virtualCube_ || !hasHeadPose_) return std::nullopt; + return virtualCube_->GetFacingFace(lastHeadPose_.orientation); +} + +void RubicsCube::StartArrowHold(ScanUiState::Arrow arrow, const std::chrono::steady_clock::time_point& now) { + if (arrow == ScanUiState::Arrow::None) return; + arrowHoldActive_ = true; + arrowHoldArrow_ = arrow; + arrowHoldStart_ = now; +} + +void RubicsCube::ClearArrowHold() { + arrowHoldActive_ = false; + arrowHoldArrow_ = ScanUiState::Arrow::None; + arrowHoldStart_ = {}; +} + +void RubicsCube::UpdateHeadMotion(float linearDeltaM, float angularDeltaRad) { + // Thresholds: linear 0.15m per frame or angular 0.35rad per frame + const float linThresh = 0.10f; + const float angThresh = 0.20f; + if (linearDeltaM < linThresh && angularDeltaRad < angThresh) return; + // Cooldown to avoid rapid thrash + const auto now = std::chrono::steady_clock::now(); + if (lastMotionReset_.time_since_epoch().count() != 0) { + if (now - lastMotionReset_ < std::chrono::milliseconds(800)) return; + } + lastMotionReset_ = now; + + Log::Write(Log::Level::Warning, Fmt("RubicsCube|Orchestrator: head motion detected (lin=%.3f, ang=%.3f), resetting scan", linearDeltaM, angularDeltaRad)); + // Reset scanning state + if (cubeModel_) cubeModel_->Clear(); + scanIndex_ = 0; + hasLastAcceptedCenter_ = false; + solutionText_.clear(); + solutionSteps_.clear(); + cachedFacelets_.reset(); + stage_ = AppStage::Scan; + if (virtualCube_) { + // Clear any queued face moves so Solve animations stop + virtualCube_->ClearFaceMoves(); + } + if (scanManager_) scanManager_->ResetStabilizer(); + if (virtualCube_) { + virtualCube_->SetCubeModel(cubeModel_.get()); + virtualCube_->ClearPreviewFace(scanOrder_[0]); + virtualCube_->ClearPreview(); + virtualCube_->ResetOrientation(); + ResetVirtualCubeAnchorToHead(); + } + awaitingValidate_ = false; + ClearArrowHold(); +} + +bool RubicsCube::UpdateUserMesh(std::vector& outVerts, std::vector& outIdx, + XrPosef& outWorldPose) { + if (!virtualCube_) return false; + return virtualCube_->BuildMesh(outVerts, outIdx, outWorldPose); +} + +void RubicsCube::QueueWarmupAnimation() { + if (virtualCube_) virtualCube_->QueueWarmupAnimation(); +} + +} // namespace SecureMR diff --git a/samples/rubics_cube/cpp/rubics_cube.h b/samples/rubics_cube/cpp/rubics_cube.h new file mode 100644 index 0000000..67962a2 --- /dev/null +++ b/samples/rubics_cube/cpp/rubics_cube.h @@ -0,0 +1,141 @@ +// Copyright (2025) Bytedance Ltd. and/or its affiliates +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "pch.h" + +#include +#include +#include +#include + +#include "securemr_base.h" +#include "securemr_utils/pipeline.h" +#include "securemr_utils/readback_async.h" +#include "securemr_utils/session.h" +#include "securemr_utils/tensor.h" + +// Local modules +#include "cube_types.h" +#include "geometry.h" +#include "scan_ui.h" +#include "virtual_cube.h" + +struct android_app; + +namespace SecureMR { + +class RubicsCube : public ISecureMR { + public: + static struct android_app* gapp; + + RubicsCube(const XrInstance& instance, const XrSession& session); + ~RubicsCube() override; + + void CreateFramework() override; + void CreatePipelines() override; + void RunPipelines() override {} + void Tick() override; + void RequestPermission(struct android_app* app) override; + + [[nodiscard]] bool LoadingFinished() const override { return pipelinesInitialized_; } + [[nodiscard]] bool WantsScanOverlay() const override { + return stage_ == AppStage::Scan || stage_ == AppStage::Validate || stage_ == AppStage::Solve; + } + bool UpdateOverlayRgba(int width, int height, std::vector& outRgba) override; + void SetOverlaySize(int width, int height) override; + + // Native mesh (Vulkan overlay) + [[nodiscard]] bool WantsUserMesh() const override { return true; } + bool UpdateUserMesh(std::vector& outVerts, std::vector& outIdx, + XrPosef& outWorldPose) override; + [[nodiscard]] bool WantsControllerVisualization() const override { return false; } + + void UpdateHandPose(const XrVector3f*, const XrVector3f*) override {} + void UpdateHeadPose(const XrPosef& pose) override; + void UpdateHeadMotion(float linearDeltaM, float angularDeltaRad) override; + void QueueWarmupAnimation(); + + private: + void CreateGlobalTensors(); + void CreateRelaxMrReadbackPipeline(); + void RunRelaxMrReadbackPipeline(); + void HandleReadbackResult(TensorReadbackResult&& result); + void ResetVirtualCubeAnchorToHead(); + [[nodiscard]] std::optional GetFacingFaceFromHeadPose() const; + std::optional BuildFaceletsFromVirtualCube(); + void DebugFacesColor(const std::array, 6>& faces); + bool ValidateFacelets(const std::string& code) const; + bool UpdateCubeModelFromFacelets(const std::string& facelets); + void StartArrowHold(ScanUiState::Arrow arrow, const std::chrono::steady_clock::time_point& now); + void ClearArrowHold(); + + XrInstance instance_; + XrSession session_; + std::shared_ptr frameworkSession_; + std::shared_ptr relaxMrReadbackPipeline_; + std::shared_ptr vstOutputLeftUint8Global_; + std::shared_ptr vstOutputLeftUint8Placeholder_; + std::unique_ptr readback_; + + std::chrono::steady_clock::time_point lastRelaxReadbackRunTime_{}; + bool pipelinesInitialized_ = false; + + // Managers + std::unique_ptr cameraManager_; + std::shared_ptr colorClassifier_; + std::unique_ptr scanManager_; + std::unique_ptr cubeModel_; + std::unique_ptr solver_; + std::unique_ptr virtualCube_; + std::unique_ptr scanUi_; + + enum class AppStage { Idle, Scan, Validate, Solve, Animate, Done }; + AppStage stage_{AppStage::Idle}; + std::array scanOrder_{Face::F, Face::U, Face::L, Face::B, Face::D, Face::R}; + int scanIndex_{0}; + bool overlayHookLogged_{false}; + std::chrono::steady_clock::time_point lastUiTick_{}; + std::chrono::steady_clock::time_point lastTickLog_{}; + + int overlayW_{0}; + int overlayH_{0}; + + // Track last accepted face center color to avoid advancing when scene is static + std::array lastAcceptedCenterU8_{0, 0, 0}; + bool hasLastAcceptedCenter_{false}; + + std::string solutionText_; + std::vector solutionSteps_; + std::optional cachedFacelets_; + + // Motion-based reset controls + std::chrono::steady_clock::time_point lastMotionReset_{}; + XrPosef lastHeadPose_{}; + bool hasHeadPose_{false}; + + // Delay before leaving Scan stage + bool awaitingValidate_{false}; + std::chrono::steady_clock::time_point scanCompleteTime_{}; + bool arrowHoldActive_{false}; + ScanUiState::Arrow arrowHoldArrow_{ScanUiState::Arrow::None}; + std::chrono::steady_clock::time_point arrowHoldStart_{}; + std::chrono::milliseconds arrowHoldDuration_{1000}; + +}; + +std::shared_ptr CreateSecureMrProgram(const XrInstance& instance, const XrSession& session); + +} // namespace SecureMR diff --git a/samples/rubics_cube/cpp/scan_manager.cpp b/samples/rubics_cube/cpp/scan_manager.cpp new file mode 100644 index 0000000..35a0e39 --- /dev/null +++ b/samples/rubics_cube/cpp/scan_manager.cpp @@ -0,0 +1,377 @@ +// Copyright (2025) Bytedance Ltd. and/or its affiliates +// Licensed under the Apache License, Version 2.0 + +#include "scan_manager.h" +#include "logger.h" +#include "common.h" + +#include +#include +#include + +#if defined(XR_USE_PLATFORM_ANDROID) +// OpenCV for debugging image dump +#include +#include +#include +// Provided by base/main.cpp on Android +extern std::string g_internalDataPath; +#endif + +namespace SecureMR { + +static PatchColor AverageRegion(const Frame& f, int x0, int y0, int x1, int y1) { + // Clamp to frame bounds + x0 = std::max(0, x0); + y0 = std::max(0, y0); + x1 = std::min(f.width, x1); + y1 = std::min(f.height, y1); + if (x1 <= x0 || y1 <= y0 || f.channels < 3 || f.mat.empty()) return {}; + + // Quantized color histogram (mode). Use 16 bins per channel. + constexpr int kBins = 16; + constexpr int kBins2 = kBins * kBins; + constexpr int kBins3 = kBins * kBins * kBins; + std::array counts{}; + std::array sumR{}; + std::array sumG{}; + std::array sumB{}; + + for (int y = y0; y < y1; ++y) { + const uint8_t* row = f.mat.ptr(y); + for (int x = x0; x < x1; ++x) { + const uint8_t* px = row + x * f.channels; // BGR + const int b = px[0]; + const int g = px[1]; + const int r = px[2]; + int rb = std::min(kBins - 1, (r * kBins) >> 8); + int gb = std::min(kBins - 1, (g * kBins) >> 8); + int bb = std::min(kBins - 1, (b * kBins) >> 8); + const int idx = (rb * kBins2) + (gb * kBins) + bb; + counts[idx] += 1; + sumR[idx] += r; + sumG[idx] += g; + sumB[idx] += b; + } + } + + // Find the dominant bin + int bestIdx = -1; + int bestCount = 0; + for (int i = 0; i < kBins3; ++i) { + if (counts[i] > bestCount) { + bestCount = counts[i]; + bestIdx = i; + } + } + if (bestIdx < 0 || bestCount == 0) return {}; + + const float r = static_cast(sumR[bestIdx] / bestCount) / 255.0f; + const float g = static_cast(sumG[bestIdx] / bestCount) / 255.0f; + const float b = static_cast(sumB[bestIdx] / bestCount) / 255.0f; + return PatchColor{r, g, b}; +} + +bool ScanManager::TryLockFace(Face target, const Frame& frame) { + if (frame.width <= 0 || frame.height <= 0 || frame.channels < 3 || frame.mat.empty()) return false; + + const auto now = std::chrono::steady_clock::now(); + // Global pause to avoid cross-face crosstalk after locking + if (pauseUntil_.time_since_epoch().count() != 0 && now < pauseUntil_) { + return false; + } + static const auto kInterval = std::chrono::milliseconds(200); + if (lastSampleTime_.time_since_epoch().count() != 0) { + const auto since = now - lastSampleTime_; + if (since < kInterval) { + // Not time yet; just keep UI color as last and continue + return false; + } + } + const float dtSec = (lastSampleTime_.time_since_epoch().count() == 0) + ? 0.f + : std::chrono::duration_cast>(now - lastSampleTime_).count(); + lastSampleTime_ = now; + + Log::Write(Log::Level::Debug, + Fmt("RubicsCube|ScanManager: TryLockFace(%d) on frame %dx%dx%d", static_cast(target), frame.width, + frame.height, frame.channels)); + + // Simple grid, alias with XrCompositionLayerQuad rectangle + XrRect2Di rect = GetRoiRectPx(frame.width, frame.height); + const int box_x = rect.extent.width * 0.8 - 25; + const int box_y = rect.extent.width * 0.8; + const int x0 = rect.offset.x + 55 + 10; + const int y0 = rect.offset.y - 7 + 5; + + const int step_x = box_x / 3; + const int step_y = box_y / 3; + + // Dump the central box region to cache for debugging (Android only) +#if defined(XR_USE_PLATFORM_ANDROID) + try { + const int rx0 = std::max(0, x0); + const int ry0 = std::max(0, y0); + const int rw = std::min(box_x, frame.width - rx0); + const int rh = std::min(box_y, frame.height - ry0); + + if (rw > 0 && rh > 0 && !frame.mat.empty()) { + cv::Rect roiRect(rx0, ry0, rw, rh); + cv::Mat bgr = frame.mat(roiRect); + + // Property-gated image saving + namespace fs = std::filesystem; + fs::path cacheDir; + if (!g_internalDataPath.empty()) { + cacheDir = fs::path(g_internalDataPath) / "cache"; + } else { + cacheDir = fs::path("/sdcard/Android/data/com.bytedance.pico.secure_mr_demo.rubics_cube/cache"); + } + std::error_code ec; + fs::create_directories(cacheDir, ec); + char prop[PROP_VALUE_MAX] = {}; + if (__system_property_get("rubicscube_debug_save_image", prop) != 0 && prop[0] == '1') { + const auto now = std::chrono::system_clock::now(); + const auto ts = std::chrono::duration_cast(now.time_since_epoch()).count(); + const std::string fileName = + Fmt("rc_box_face%d_%dx%dx%d_%lld.png", static_cast(target), rw, rh, frame.channels, (long long)ts); + const fs::path filePath = cacheDir / fileName; + + + const bool ok = cv::imwrite(filePath.string(), bgr); + Log::Write(Log::Level::Info, + Fmt("RubicsCube|ScanManager: box ROI %dx%dx%d saved=%s path=%s", rw, rh, frame.channels, + ok ? "true" : "false", filePath.string().c_str())); + } + } else { + Log::Write(Log::Level::Warning, "RubicsCube|ScanManager: ROI not saved due to invalid size/channels"); + } + } catch (const std::exception& ex) { + Log::Write(Log::Level::Error, Fmt("RubicsCube|ScanManager: ROI save exception: %s", ex.what())); + } +#endif + + ScannedFace face{}; + face.face = target; + int idx = 0; + for (int gy = 0; gy < 3; ++gy) { + for (int gx = 0; gx < 3; ++gx) { + const int rx0 = x0 + gx * step_x + step_x / 6; + const int ry0 = y0 + gy * step_y + step_y / 6; + const int rx1 = rx0 + step_x * 2 / 3; + const int ry1 = ry0 + step_y * 2 / 3; + face.patches[idx++] = AverageRegion(frame, rx0, ry0, rx1, ry1); + } + } + + // Calibrate center patch to the face for color prototypes + if (classifier_) { + classifier_->CalibrateCenter(target, face.patches[4]); + } + + // Convert patches to 8-bit for comparison/UI + auto clampU8 = [](int v) { return static_cast(std::min(std::max(v, 0), 255)); }; + std::array, 9> currentColors{}; + for (int i = 0; i < 9; ++i) { + const auto& pc = face.patches[i]; + currentColors[i][0] = clampU8(static_cast(std::round(pc.r * 255.0f))); + currentColors[i][1] = clampU8(static_cast(std::round(pc.g * 255.0f))); + currentColors[i][2] = clampU8(static_cast(std::round(pc.b * 255.0f))); + } + + // Tolerant compare to mitigate sensor noise + bool sameAsPrev = hasPrev_; + const int kTol = 20; // per-channel tolerance in 8-bit + if (hasPrev_) { + for (int i = 0; i < 9 && sameAsPrev; ++i) { + const int dr = std::abs((int)currentColors[i][0] - (int)prevPatchColors9U8_[i][0]); + const int dg = std::abs((int)currentColors[i][1] - (int)prevPatchColors9U8_[i][1]); + const int db = std::abs((int)currentColors[i][2] - (int)prevPatchColors9U8_[i][2]); + if (dr > kTol || dg > kTol || db > kTol) { + sameAsPrev = false; + } + } + } + const int targetSamples = std::max(1, static_cast(std::round(stableThresholdSec_ / 0.2f))); // ~5 for 1s/200ms + if (!hasPrev_ || !sameAsPrev) { + prevPatchColors9U8_ = currentColors; + hasPrev_ = true; + stableAccumSec_ = 0.f; + sampleHistory_.clear(); + sampleHistory_.push_back(currentColors); + sampleSeq_ = 1; + } else { + stableAccumSec_ += dtSec; + if (sampleHistory_.empty() || sampleHistory_.back() != currentColors) { + sampleHistory_.push_back(currentColors); + if (static_cast(sampleHistory_.size()) > targetSamples) { + // Keep bounded history + sampleHistory_.erase(sampleHistory_.begin()); + } + } + sampleSeq_++; + } + + // Debug: if current face seems stable at this 200ms sample, save ROI to cache (Android only) +#if defined(XR_USE_PLATFORM_ANDROID) + try { + char prop_face[PROP_VALUE_MAX] = {}; + if (__system_property_get("rubicscube_debug_save_face", prop_face) != 0 && prop_face[0] == '1') { + if (sameAsPrev) { + const int rx0 = std::max(0, x0); + const int ry0 = std::max(0, y0); + const int rw = std::min(box_x, frame.width - rx0); + const int rh = std::min(box_y, frame.height - ry0); + if (rw > 0 && rh > 0 && !frame.mat.empty()) { + namespace fs = std::filesystem; + fs::path cacheDir; + if (!g_internalDataPath.empty()) { + cacheDir = fs::path(g_internalDataPath) / "cache"; + } else { + cacheDir = fs::path("/sdcard/Android/data/com.bytedance.pico.secure_mr_demo.rubics_cube/cache"); + } + std::error_code ec; + fs::create_directories(cacheDir, ec); + cv::Rect roiRect(rx0, ry0, rw, rh); + cv::Mat roi = frame.mat(roiRect); + // Build 9-char color code using currentColors classification + std::string colorCode; + colorCode.reserve(9); + for (int i = 0; i < 9; ++i) { + PatchColor pc{ currentColors[i][0] / 255.0f, currentColors[i][1] / 255.0f, currentColors[i][2] / 255.0f }; + Color lbl = classifier_ ? classifier_->Classify(pc) : Color::Unknown; + std::string ch = ColorToChar(lbl); + colorCode += ch.empty() ? '?' : ch[0]; + } + const auto ts = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + const fs::path filePath = cacheDir / + Fmt("rc_face_sample_face%d_seq%03d_%s_%dx%dx%d_%lld.png", + static_cast(target), sampleSeq_, colorCode.c_str(), rw, rh, frame.channels, + (long long)ts); + const bool ok = cv::imwrite(filePath.string(), roi); + Log::Write(Log::Level::Debug, + Fmt("RubicsCube|ScanManager: stable face ROI %dx%dx%d saved=%s path=%s (seq=%d code=%s)", rw, rh, + frame.channels, ok ? "true" : "false", filePath.string().c_str(), sampleSeq_, colorCode.c_str())); + } + } + } + } catch (const std::exception& ex) { + Log::Write(Log::Level::Error, Fmt("RubicsCube|ScanManager: stable face save exception: %s", ex.what())); + } +#endif + + // Update UI colors regardless + lastPatchColors9U8_ = currentColors; + const auto& c = face.patches[4]; + const int r8 = static_cast(std::round(c.r * 255.0f)); + const int g8 = static_cast(std::round(c.g * 255.0f)); + const int b8 = static_cast(std::round(c.b * 255.0f)); + lastCenterColorU8_[0] = clampU8(r8); + lastCenterColorU8_[1] = clampU8(g8); + lastCenterColorU8_[2] = clampU8(b8); + + // If stable for threshold, filter samples by majority vote (class labels), then average inliers + if (stableAccumSec_ >= stableThresholdSec_) { + std::array, 9> filtered = currentColors; + if (!sampleHistory_.empty()) { + auto colorToIndex = [](Color c) -> int { + switch (c) { + case Color::W: return 0; + case Color::Y: return 1; + case Color::R: return 2; + case Color::O: return 3; + case Color::G: return 4; + case Color::B: return 5; + default: return 6; // Unknown + } + }; + + for (int i = 0; i < 9; ++i) { + int counts[7] = {0}; + std::vector labels; labels.reserve(sampleHistory_.size()); + for (const auto& s : sampleHistory_) { + PatchColor pc{ s[i][0] / 255.0f, s[i][1] / 255.0f, s[i][2] / 255.0f }; + Color lbl = classifier_ ? classifier_->Classify(pc) : Color::Unknown; + int idx = colorToIndex(lbl); + labels.push_back(idx); + counts[idx]++; + } + int bestIdx = 6; int bestCnt = -1; + for (int k = 0; k < 7; ++k) { if (counts[k] > bestCnt) { bestCnt = counts[k]; bestIdx = k; } } + // Average inliers with the majority label; fallback to last sample if all Unknown + int sr = 0, sg = 0, sb = 0, n = 0; + for (size_t si = 0; si < sampleHistory_.size(); ++si) { + if (labels[si] == bestIdx && bestIdx != 6) { + sr += sampleHistory_[si][i][0]; + sg += sampleHistory_[si][i][1]; + sb += sampleHistory_[si][i][2]; + n++; + } + } + if (n > 0) { + filtered[i][0] = static_cast(sr / n); + filtered[i][1] = static_cast(sg / n); + filtered[i][2] = static_cast(sb / n); + } else { + // Fallback: latest sample + const auto& s = sampleHistory_.back(); + filtered[i][0] = s[i][0]; + filtered[i][1] = s[i][1]; + filtered[i][2] = s[i][2]; + } + } + } + // Update UI colors with filtered result + lastPatchColors9U8_ = filtered; + lastCenterColorU8_ = filtered[4]; + + // Build final locked face from filtered colors (convert to 0..1 floats) + ScannedFace finalFace = face; + for (int i = 0; i < 9; ++i) { + finalFace.patches[i].r = filtered[i][0] / 255.0f; + finalFace.patches[i].g = filtered[i][1] / 255.0f; + finalFace.patches[i].b = filtered[i][2] / 255.0f; + } + if (classifier_) { + classifier_->CalibrateCenter(target, finalFace.patches[4]); + } + locked_ = finalFace; + Log::Write(Log::Level::Info, + Fmt("RubicsCube|ScanManager: locked face %d after %.2fs", static_cast(target), stableAccumSec_)); + // Reset for next face + hasPrev_ = false; + stableAccumSec_ = 0.f; + sampleHistory_.clear(); + sampleSeq_ = 0; + return true; + } + return false; +} + +std::optional ScanManager::GetLockedFace() { return locked_; } + +XrRect2Di ScanManager::GetRoiRectPx(int W, int H) { + // assert W = H = 1024 + const int rectW = 200; + const int rectH = 200; + const int left = static_cast(std::round(W / 2.0f)); + const int top = static_cast(std::round(H / 2.0f)); + + XrRect2Di r{}; + r.offset.x = left; + r.offset.y = top; + r.extent.width = rectW; + r.extent.height = rectH; + return r; +} + +void ScanManager::ResetStabilizer() { + hasPrev_ = false; + stableAccumSec_ = 0.f; + sampleHistory_.clear(); + sampleSeq_ = 0; +} + +} // namespace SecureMR diff --git a/samples/rubics_cube/cpp/scan_manager.h b/samples/rubics_cube/cpp/scan_manager.h new file mode 100644 index 0000000..1ec7060 --- /dev/null +++ b/samples/rubics_cube/cpp/scan_manager.h @@ -0,0 +1,60 @@ +// Copyright (2025) Bytedance Ltd. and/or its affiliates +// Licensed under the Apache License, Version 2.0 + +#pragma once + +#include "pch.h" + +#include +#include +#include +#include + +#include "camera_manager.h" +#include "color_classifier.h" +#include "cube_types.h" + +namespace SecureMR { + +class ScanManager { + public: + explicit ScanManager(std::shared_ptr classifier) : classifier_(std::move(classifier)) {} + + // Returns true if the target face is locked; when locked, call GetLockedFace + bool TryLockFace(Face target, const Frame& frame); + std::optional GetLockedFace(); + // UI helper: last computed center color (boosted) in 8-bit RGB + std::array GetCenterColorU8() const { return lastCenterColorU8_; } + // UI helper: last computed 3x3 patch colors in 8-bit RGB (row-major) + std::array, 9> GetPatchColors9U8() const { return lastPatchColors9U8_; } + // UI helper: stabilization progress in [0,1] + float GetStabilizeProgress01() const { + const float denom = stableThresholdSec_ > 0.f ? stableThresholdSec_ : 1.f; + float p = stableAccumSec_ / denom; + if (p < 0.f) p = 0.f; + if (p > 1.f) p = 1.f; + return p; + } + + static XrRect2Di GetRoiRectPx(int W, int H); + void ResetStabilizer(); + void SetPauseUntil(std::chrono::steady_clock::time_point t) { pauseUntil_ = t; } + void SetPauseForMs(int ms) { pauseUntil_ = std::chrono::steady_clock::now() + std::chrono::milliseconds(ms); } + + private: + std::shared_ptr classifier_; + std::optional locked_; + std::array lastCenterColorU8_{255, 0, 0}; + std::array, 9> lastPatchColors9U8_{}; + std::array, 9> prevPatchColors9U8_{}; + bool hasPrev_{false}; + std::chrono::steady_clock::time_point lastSampleTime_{}; + float stableAccumSec_{0.f}; + float stableThresholdSec_{1.0f}; + // Cache sampled 9-patch colors during stabilization window (200ms interval) + std::vector, 9>> sampleHistory_; + int sampleSeq_{0}; + std::chrono::steady_clock::time_point pauseUntil_{}; +}; + +} // namespace SecureMR diff --git a/samples/rubics_cube/cpp/scan_ui.cpp b/samples/rubics_cube/cpp/scan_ui.cpp new file mode 100644 index 0000000..62ec894 --- /dev/null +++ b/samples/rubics_cube/cpp/scan_ui.cpp @@ -0,0 +1,448 @@ +// Copyright (2025) Bytedance Ltd. and/or its affiliates +// Licensed under the Apache License, Version 2.0 + +#include "scan_ui.h" + +#include +#include +#include +#include + +#include "logger.h" +#include "scan_manager.h" + +#include +#include + +#if defined(XR_USE_PLATFORM_ANDROID) +#include +#include +#include +// Provided by base/main.cpp on Android +extern std::string g_internalDataPath; +#endif + +namespace SecureMR { + +namespace { +inline uint8_t clampU8(int v) { return static_cast(std::min(std::max(v, 0), 255)); } + +static void setPixel(std::vector& img, int W, int H, int x, int y, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + if (x < 0 || x >= W || y < 0 || y >= H) return; + const size_t idx = (static_cast(y) * W + x) * 4; + img[idx + 0] = r; + img[idx + 1] = g; + img[idx + 2] = b; + img[idx + 3] = a; +} + +static void drawDashedHLine(std::vector& img, int W, int H, int x0, int x1, int y, int thickness, + int dash, int gap, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + if (x0 > x1) std::swap(x0, x1); + int period = std::max(1, dash + gap); + for (int x = x0; x <= x1; ++x) { + int seg = (x - x0) % period; + if (seg < dash) { + for (int t = 0; t < thickness; ++t) setPixel(img, W, H, x, y + t, r, g, b, a); + } + } +} + +static void drawDashedVLine(std::vector& img, int W, int H, int x, int y0, int y1, int thickness, int dash, int gap, + uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + if (y0 > y1) std::swap(y0, y1); + int period = std::max(1, dash + gap); + for (int y = y0; y <= y1; ++y) { + int seg = (y - y0) % period; + if (seg < dash) { + for (int t = 0; t < thickness; ++t) setPixel(img, W, H, x + t, y, r, g, b, a); + } + } +} + +static void drawLine(std::vector& img, int W, int H, int x0, int y0, int x1, int y1, int thickness, uint8_t r, + uint8_t g, uint8_t b, uint8_t a) { + // Bresenham-like simple line with thickness + const int dx = std::abs(x1 - x0), sx = x0 < x1 ? 1 : -1; + const int dy = -std::abs(y1 - y0), sy = y0 < y1 ? 1 : -1; + int err = dx + dy, e2; /* error value e_xy */ + while (true) { + for (int tx = -thickness / 2; tx <= thickness / 2; ++tx) { + for (int ty = -thickness / 2; ty <= thickness / 2; ++ty) { + setPixel(img, W, H, x0 + tx, y0 + ty, r, g, b, a); + } + } + if (x0 == x1 && y0 == y1) break; + e2 = 2 * err; + if (e2 >= dy) { + err += dy; + x0 += sx; + } + if (e2 <= dx) { + err += dx; + y0 += sy; + } + } +} + +static void drawRightArrow(std::vector& img, int W, int H, int cx, int cy, int size, uint8_t r, uint8_t g, uint8_t b, + uint8_t a) { + // Horizontal shaft + int half = size / 2; + drawLine(img, W, H, cx - half, cy, cx + half - size / 6, cy, size / 8 + 1, r, g, b, a); + // Arrow head (two lines) + drawLine(img, W, H, cx + half - size / 6, cy, cx + half - size / 3, cy - size / 4, size / 8 + 1, r, g, b, a); + drawLine(img, W, H, cx + half - size / 6, cy, cx + half - size / 3, cy + size / 4, size / 8 + 1, r, g, b, a); +} + +static void drawDownArrow(std::vector& img, int W, int H, int cx, int cy, int size, uint8_t r, uint8_t g, uint8_t b, + uint8_t a) { + int half = size / 2; + drawLine(img, W, H, cx, cy - half, cx, cy + half - size / 6, size / 8 + 1, r, g, b, a); + drawLine(img, W, H, cx, cy + half - size / 6, cx - size / 4, cy + half - size / 3, size / 8 + 1, r, g, b, a); + drawLine(img, W, H, cx, cy + half - size / 6, cx + size / 4, cy + half - size / 3, size / 8 + 1, r, g, b, a); +} + +static void fillRect(std::vector& img, int W, int H, int x0, int y0, int x1, int y1, + uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + if (x0 > x1) std::swap(x0, x1); + if (y0 > y1) std::swap(y0, y1); + const int xs = std::max(0, x0); + const int xe = std::min(W - 1, x1); + const int ys = std::max(0, y0); + const int ye = std::min(H - 1, y1); + for (int y = ys; y <= ye; ++y) { + for (int x = xs; x <= xe; ++x) { + setPixel(img, W, H, x, y, r, g, b, a); + } + } +} + +static void drawDigit7(std::vector& img, int W, int H, int x, int y, int dw, int dh, int t, + uint8_t r, uint8_t g, uint8_t b, uint8_t a, int digit) { + // 7-seg bit order: A,B,C,D,E,F,G -> bits 0..6 + static const int segMap[10] = { + /*0*/ 0b0111111, /*1*/ 0b0000110, /*2*/ 0b1011011, /*3*/ 0b1001111, /*4*/ 0b1100110, + /*5*/ 0b1101101, /*6*/ 0b1111101, /*7*/ 0b0000111, /*8*/ 0b1111111, /*9*/ 0b1101111}; + if (digit < 0 || digit > 9) return; + const int mask = segMap[digit]; + const int mid = y + dh / 2; + // Horizontal segments: A (top), G (middle), D (bottom) + if (mask & 0b0000001) fillRect(img, W, H, x + t, y, x + dw - t - 1, y + t - 1, r, g, b, a); // A + if (mask & 0b1000000) fillRect(img, W, H, x + t, mid - t / 2, x + dw - t - 1, mid + (t - 1) / 2, r, g, b, a); // G + if (mask & 0b0001000) fillRect(img, W, H, x + t, y + dh - t, x + dw - t - 1, y + dh - 1, r, g, b, a); // D + // Vertical segments: F (top-left), E (bottom-left), B (top-right), C (bottom-right) + const int vLenTop = dh / 2 - t; + const int vLenBot = dh / 2 - t; + if (mask & 0b0100000) fillRect(img, W, H, x, y + t, x + t - 1, y + t + vLenTop - 1, r, g, b, a); // F + if (mask & 0b00010000) fillRect(img, W, H, x, mid + t / 2, x + t - 1, mid + t / 2 + vLenBot - 1, r, g, b, a); // E + if (mask & 0b0000010) fillRect(img, W, H, x + dw - t, y + t, x + dw - 1, y + t + vLenTop - 1, r, g, b, a); // B + if (mask & 0b0000100) fillRect(img, W, H, x + dw - t, mid + t / 2, x + dw - 1, mid + t / 2 + vLenBot - 1, r, g, b, a); // C +} + +// Simple 3x5 bitmap font for digits and '/' +static const uint8_t kDigit3x5[10][5] = { + {0b111, 0b101, 0b101, 0b101, 0b111}, // 0 + {0b010, 0b110, 0b010, 0b010, 0b111}, // 1 + {0b111, 0b001, 0b111, 0b100, 0b111}, // 2 + {0b111, 0b001, 0b111, 0b001, 0b111}, // 3 + {0b101, 0b101, 0b111, 0b001, 0b001}, // 4 + {0b111, 0b100, 0b111, 0b001, 0b111}, // 5 + {0b111, 0b100, 0b111, 0b101, 0b111}, // 6 + {0b111, 0b001, 0b010, 0b010, 0b010}, // 7 + {0b111, 0b101, 0b111, 0b101, 0b111}, // 8 + {0b111, 0b101, 0b111, 0b001, 0b111}, // 9 +}; + +static const uint8_t kSlash3x5[5] = {0b001, 0b001, 0b010, 0b010, 0b100}; + +static void drawGlyph3x5(std::vector& img, int W, int H, int x, int y, int scale, + uint8_t r, uint8_t g, uint8_t b, uint8_t a, const uint8_t rows[5]) { + for (int ry = 0; ry < 5; ++ry) { + for (int rx = 0; rx < 3; ++rx) { + if (rows[ry] & (1u << (2 - rx))) { + const int gx = x + rx * scale; + const int gy = y + ry * scale; + fillRect(img, W, H, gx, gy, gx + scale - 1, gy + scale - 1, r, g, b, a); + } + } + } +} +} // namespace + +void ScanUi::Update(const Frame* latestFrame, const ScanUiState& s, float dt) { + std::lock_guard lk(mtx_); + state_ = s; + t_ += dt; + if (manager_) { + boxColor_ = manager_->GetCenterColorU8(); + patchColors9_ = manager_->GetPatchColors9U8(); + } else { + boxColor_ = {255, 0, 0}; + } + hintColors9_ = s.hintColors9; +} + +bool ScanUi::ComposeOverlayRgba(int W, int H, std::vector& outRgba) { + if (W <= 0 || H <= 0) return false; + outRgba.assign(static_cast(W) * H * 4, 0); + + const auto roi = ScanManager::GetRoiRectPx(W, H); + const int left = roi.offset.x; + const int top = roi.offset.y; + const int right = left + roi.extent.width - 1; + const int bottom = top + roi.extent.height - 1; + const int thickness = 6; + const int dash = 12; + const int gap = 8; + + const uint8_t r = boxColor_[0], g = boxColor_[1], b = boxColor_[2]; + + const bool isScanPhase = !(state_.phase == ScanUiState::Phase::Validate || state_.phase == ScanUiState::Phase::Solve); + if (isScanPhase) { + for (int t = 0; t < thickness; ++t) { + drawDashedHLine(outRgba, W, H, left, right, top + t, thickness, dash, gap, r, g, b, 255); + drawDashedHLine(outRgba, W, H, left, right, bottom - t, thickness, dash, gap, r, g, b, 255); + drawDashedVLine(outRgba, W, H, left + t, top, bottom, thickness, dash, gap, r, g, b, 255); + drawDashedVLine(outRgba, W, H, right - t, top, bottom, thickness, dash, gap, r, g, b, 255); + } + } + + // Additional 3x3 grid above ROI: filled opaque cells (only during scanning) + if (isScanPhase) { + const int spacing = 20; // gap between ROI and the top grid + const int gridW = roi.extent.width; + const int gridH = roi.extent.height; + const int cellW = gridW / 3; + const int cellH = gridH / 3; + const int topGridBottom = top - spacing - 1; + const int topGridTop = topGridBottom - gridH + 1; + const int topGridLeft = left; + int pidx = 0; + for (int gy = 0; gy < 3; ++gy) { + for (int gx = 0; gx < 3; ++gx) { + const int x0 = topGridLeft + gx * cellW; + const int y0 = topGridTop + gy * cellH; + const int x1 = (gx == 2) ? (topGridLeft + gridW - 1) : (x0 + cellW - 1); + const int y1 = (gy == 2) ? (topGridTop + gridH - 1) : (y0 + cellH - 1); + const auto& cols = patchColors9_; + const uint8_t pr = cols[pidx][0]; + const uint8_t pg = cols[pidx][1]; + const uint8_t pb = cols[pidx][2]; + fillRect(outRgba, W, H, x0, y0, x1, y1, pr, pg, pb, 255); + ++pidx; + } + } + } + + if (isScanPhase) { + // Arrow + if (state_.nextArrow == ScanUiState::Arrow::Right) { + const int acx = right + 40; // right side of the box + const int acy = (top + bottom) / 2; // vertical middle + const int asz = std::min(120, std::min(W, H) / 6); + drawRightArrow(outRgba, W, H, acx, acy, asz, r, g, b, 255); + } else if (state_.nextArrow == ScanUiState::Arrow::Down) { + const int acx = (left + right) / 2; // horizontal middle + const int acy = bottom + 40; // below the box + const int asz = std::min(120, std::min(W, H) / 6); + drawDownArrow(outRgba, W, H, acx, acy, asz, r, g, b, 255); + } + } + + // Stabilization progress bar (1s) on left side, vertical bottom->top + if (isScanPhase) { + const int spacing = 8; + const int barW = 12; + const int barH = roi.extent.height; + const int barLeft = left - spacing - barW; + const int barRight = barLeft + barW - 1; + const int barTop = top; + const int barBottom = bottom; + // Background (semi-transparent) + fillRect(outRgba, W, H, barLeft, barTop, barRight, barBottom, 30, 30, 30, 180); + // Progress fill (bottom-up) + float progress = 0.f; + if (manager_) progress = manager_->GetStabilizeProgress01(); + progress = std::max(0.f, std::min(1.f, progress)); + const int fillHeight = static_cast(std::round(progress * barH)); + if (fillHeight > 0) { + const int fillTop = barBottom - fillHeight + 1; + fillRect(outRgba, W, H, barLeft, fillTop, barRight, barBottom, r, g, b, 255); + } + } + + // Bottom text box + if (!state_.bottomText.empty() || !state_.solveSteps.empty()) { +#if defined(XR_USE_PLATFORM_ANDROID) + // Draw semi-transparent plate and text using OpenCV + const int basePlateH = 64; + const int margin = 20; + const int innerPad = 24; + const double fontScale = 1.2; + const int thickness = 2; + const int fontFace = cv::FONT_HERSHEY_SIMPLEX; + + const int plateW = W - 2 * margin; + const int contentW = plateW - 2 * innerPad; + struct StyledToken { + std::string text; + cv::Scalar color; + cv::Size size; + }; + const cv::Scalar white(255, 255, 255, 255); + const cv::Scalar red(255, 0, 0, 255); + int baseLineSpace = 0; + const cv::Size spaceSize = cv::getTextSize(" ", fontFace, fontScale, thickness, &baseLineSpace); + (void)baseLineSpace; + auto makeToken = [&](const std::string& t, const cv::Scalar& col) -> StyledToken { + int bl = 0; + cv::Size sz = cv::getTextSize(t, fontFace, fontScale, thickness, &bl); + (void)bl; + return StyledToken{t, col, sz}; + }; + + std::vector tokens; + if (!state_.solveSteps.empty()) { + tokens.reserve(state_.solveSteps.size()); + for (size_t i = 0; i < state_.solveSteps.size(); ++i) { + tokens.push_back(makeToken(state_.solveSteps[i], (static_cast(i) == state_.activeSolveIndex) ? red : white)); + } + } else { + std::istringstream iss(state_.bottomText); + std::string word; + while (iss >> word) { + tokens.push_back(makeToken(word, white)); + } + if (tokens.empty()) { + tokens.push_back(makeToken(state_.bottomText, white)); + } + } + + struct Line { + std::vector toks; + int width{0}; + int height{0}; + }; + std::vector lines; + Line current; + auto finalizeLine = [&]() { + if (!current.toks.empty()) { + lines.push_back(current); + } + current = Line{}; + }; + for (const auto& tok : tokens) { + const int addWidth = tok.size.width + (current.toks.empty() ? 0 : spaceSize.width); + if (!current.toks.empty() && current.width + addWidth > contentW && lines.size() < 1) { + finalizeLine(); + } + if (!current.toks.empty()) current.width += spaceSize.width; + current.width += tok.size.width; + current.height = std::max(current.height, tok.size.height); + current.toks.push_back(tok); + } + finalizeLine(); + if (lines.empty()) { + Line l{}; + l.toks = tokens; + for (size_t i = 0; i < tokens.size(); ++i) { + if (i > 0) l.width += spaceSize.width; + l.width += tokens[i].size.width; + l.height = std::max(l.height, tokens[i].size.height); + } + lines.push_back(l); + } + if (lines.size() > 2) { + // Merge extra lines into second line + Line merged = lines[1]; + for (size_t li = 2; li < lines.size(); ++li) { + if (!merged.toks.empty()) merged.width += spaceSize.width; + merged.toks.insert(merged.toks.end(), lines[li].toks.begin(), lines[li].toks.end()); + } + // Recompute width/height + merged.width = 0; + merged.height = 0; + for (size_t i = 0; i < merged.toks.size(); ++i) { + if (i > 0) merged.width += spaceSize.width; + merged.width += merged.toks[i].size.width; + merged.height = std::max(merged.height, merged.toks[i].size.height); + } + lines.resize(2); + lines[1] = merged; + } + + const int plateH = basePlateH * static_cast(lines.size()); + const int px0 = margin; + const int py0 = H - plateH - margin; + const int px1 = W - margin; + const int py1 = H - margin; + fillRect(outRgba, W, H, px0, py0, px1, py1, 0, 0, 0, 120); + cv::Mat rgba(H, W, CV_8UC4, outRgba.data()); + const int lineGap = 6; + int totalH = 0; + for (const auto& l : lines) { + totalH += l.height; + } + totalH += lineGap * static_cast(lines.size() - 1); + + int ty = py0 + (plateH - totalH) / 2 + lines[0].height; + for (size_t i = 0; i < lines.size(); ++i) { + int tx = px0 + (plateW - lines[i].width) / 2; + bool firstTok = true; + for (const auto& tok : lines[i].toks) { + if (!firstTok) tx += spaceSize.width; + cv::putText(rgba, tok.text, cv::Point(tx, ty), fontFace, fontScale, tok.color, thickness); + tx += tok.size.width; + firstTok = false; + } + ty += lines[i].height + lineGap; + } +#else + // Non-Android: skip text to avoid extra deps +#endif + } + + const bool changed = (last_.size() != outRgba.size()) || (std::memcmp(last_.data(), outRgba.data(), outRgba.size()) != 0); + if (changed) { + last_ = outRgba; + Log::Write(Log::Level::Debug, "ScanUi: overlay RGBA composed (static dashed box + arrow)"); + } + return changed; +} + +void ScanUi::StartThread() { + bool expected = false; + if (!running_.compare_exchange_strong(expected, true)) return; + worker_ = std::thread([this]() { + std::vector buf; + while (running_) { + int W, H; + { + std::lock_guard lk(mtx_); + W = overlayW_; + H = overlayH_; + } + bool changed = false; + { + std::lock_guard lk(mtx_); + changed = ComposeOverlayRgba(W, H, buf); + } + if (changed) { + std::lock_guard lk(mtx_); + composed_ = buf; + dirty_ = true; + } + std::this_thread::sleep_for(std::chrono::milliseconds(33)); + } + }); +} + +void ScanUi::StopThread() { + if (running_) { + running_ = false; + if (worker_.joinable()) worker_.join(); + } +} + +} // namespace SecureMR diff --git a/samples/rubics_cube/cpp/scan_ui.h b/samples/rubics_cube/cpp/scan_ui.h new file mode 100644 index 0000000..d5cd61d --- /dev/null +++ b/samples/rubics_cube/cpp/scan_ui.h @@ -0,0 +1,86 @@ +// Copyright (2025) Bytedance Ltd. and/or its affiliates +// Licensed under the Apache License, Version 2.0 + +#pragma once + +#include "pch.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "cube_types.h" + +namespace SecureMR { + +class ScanManager; + +struct ScanUiState { + enum class Arrow { None, Right, Down }; + enum class Phase { Intro, Aligning, Stabilizing, Locked, PromptRotate, Validate, Solve }; + Phase phase{Phase::Intro}; + float stableSec{0.f}; + Arrow nextArrow{Arrow::None}; + bool showText{false}; + int scannedCount{0}; + std::array, 9> hintColors9{}; // for Validate/Solve top grid + std::string bottomText; + std::vector solveSteps; + int activeSolveIndex{-1}; +}; + +class ScanUi { + public: + ScanUi() = default; + ~ScanUi() { StopThread(); } + + // Update UI model and sample center pixel color from frame (if available) + void Update(const Frame* latestFrame, const ScanUiState& s, float dt); + + // Inject ScanManager to fetch center color from it + void SetScanManager(ScanManager* mgr) { manager_ = mgr; } + + // Compose RGBA for overlay. Return true when outRgba changed and needs upload. + bool ComposeOverlayRgba(int W, int H, std::vector& outRgba); + + // Background thread control for UI composition + void StartThread(); + void StopThread(); + void SetOverlaySize(int W, int H) { + std::lock_guard lk(mtx_); + overlayW_ = W; + overlayH_ = H; + } + bool TryConsumeOverlayRgba(std::vector& outRgba) { + std::lock_guard lk(mtx_); + if (!dirty_) return false; + outRgba = composed_; + dirty_ = false; + return true; + } + + private: + std::array boxColor_{255, 0, 0}; + // Latest 3x3 patch colors in 8-bit RGB, row-major order. + std::array, 9> patchColors9_{}; + std::array, 9> hintColors9_{}; + float t_{0.f}; + ScanUiState state_{}; + std::vector last_; // last composed rgba for change detection + ScanManager* manager_{nullptr}; + + // Threading and buffer + std::thread worker_; + std::atomic running_{false}; + int overlayW_{1024}; + int overlayH_{1024}; + std::mutex mtx_; + std::vector composed_; + bool dirty_{false}; +}; + +} // namespace SecureMR diff --git a/samples/rubics_cube/cpp/solver.cpp b/samples/rubics_cube/cpp/solver.cpp new file mode 100644 index 0000000..615c68d --- /dev/null +++ b/samples/rubics_cube/cpp/solver.cpp @@ -0,0 +1,48 @@ +// Copyright (2025) Bytedance Ltd. and/or its affiliates +// Licensed under the Apache License, Version 2.0 + +#include "solver.h" +#include "logger.h" +#include "common.h" + +extern "C" { +#include "search.h" +} + +#if defined(XR_USE_PLATFORM_ANDROID) +// Provided by base/main.cpp on Android +extern std::string g_internalDataPath; +#endif + +namespace SecureMR { + +std::string Solver::Solve(const std::string& code) { + Log::Write(Log::Level::Info, Fmt("RubicsCube|Solver: received facelets len=%zu", code.size())); + if (code.size() != 54) { + Log::Write(Log::Level::Error, "RubicsCube|Solver: invalid facelets length"); + return std::string(); + } + + // Prepare inputs + std::string facelets = code; // already URFDLB letters + int maxDepth = 21; + long timeoutSec = 5; // safety timeout + int useSeparator = 0; // do not insert '.' +#if defined(XR_USE_PLATFORM_ANDROID) + std::string cache = g_internalDataPath.empty() ? std::string("/sdcard/Android/data/com.bytedance.pico.secure_mr_demo.rubics_cube/cache/kociemba") + : (g_internalDataPath + std::string("/kociemba")); +#else + std::string cache = std::string("cache/kociemba"); +#endif + + char* cstr = solution(facelets.data(), maxDepth, timeoutSec, useSeparator, cache.c_str()); + if (!cstr) { + Log::Write(Log::Level::Warning, "RubicsCube|Solver: no solution returned (invalid cube or timeout)"); + return std::string(); + } + std::string out(cstr); + free(cstr); + return out; +} + +} // namespace SecureMR diff --git a/samples/rubics_cube/cpp/solver.h b/samples/rubics_cube/cpp/solver.h new file mode 100644 index 0000000..c3ff926 --- /dev/null +++ b/samples/rubics_cube/cpp/solver.h @@ -0,0 +1,20 @@ +// Copyright (2025) Bytedance Ltd. and/or its affiliates +// Licensed under the Apache License, Version 2.0 + +#pragma once + +#include "pch.h" + +#include + +namespace SecureMR { + +class Solver { + public: + Solver() = default; + // Returns Kociemba solution string, e.g., "R U R' U'" + std::string Solve(const std::string& code); +}; + +} // namespace SecureMR + diff --git a/samples/rubics_cube/cpp/virtual_cube.cpp b/samples/rubics_cube/cpp/virtual_cube.cpp new file mode 100644 index 0000000..72c749d --- /dev/null +++ b/samples/rubics_cube/cpp/virtual_cube.cpp @@ -0,0 +1,1142 @@ +// Copyright (2025) Bytedance Ltd. and/or its affiliates +// Licensed under the Apache License, Version 2.0 + +#include "virtual_cube.h" + +#include "common.h" +#include "cube_model.h" +#include "logger.h" +#include "xr_linear.h" + +#include +#include +#include +#include +#include +#include + +#ifdef __ANDROID__ +#include +#endif + +namespace SecureMR { + +namespace { +constexpr float kDefaultCubeDistance = -1.2f; +struct FaceAxes { + Face face; + XrVector3f normal; + XrVector3f up; + XrVector3f right; +}; +constexpr std::array kFaceAxes = {{ + {Face::U, {0.f, 1.f, 0.f}, {0.f, 0.f, -1.f}, {1.f, 0.f, 0.f}}, + {Face::R, {1.f, 0.f, 0.f}, {0.f, 1.f, 0.f}, {0.f, 0.f, 1.f}}, + {Face::F, {0.f, 0.f, -1.f}, {0.f, 1.f, 0.f}, {1.f, 0.f, 0.f}}, + {Face::D, {0.f, -1.f, 0.f}, {0.f, 0.f, 1.f}, {1.f, 0.f, 0.f}}, + {Face::L, {-1.f, 0.f, 0.f}, {0.f, 1.f, 0.f}, {0.f, 0.f, -1.f}}, + {Face::B, {0.f, 0.f, 1.f}, {0.f, 1.f, 0.f}, {-1.f, 0.f, 0.f}}, +}}; + +const FaceAxes& AxesForFace(Face face) { + for (const auto& axes : kFaceAxes) { + if (axes.face == face) return axes; + } + return kFaceAxes[0]; +} +} // namespace + +VirtualCube::VirtualCube() { + downQuat_.w = 1.f; + rightQuat_.w = 1.f; + leftQuat_.w = 1.f; + upQuat_.w = 1.f; + anchorPosition_ = {0.f, 0.f, kDefaultCubeDistance}; + SetDefaultOrientation(); +} + +void VirtualCube::SetCubeModel(const CubeModel* model) { + cubeModel_ = model; + initialized_ = false; +} + +void VirtualCube::PreviewFace(Face face, const std::array& colors, + const std::array& displayColors) { + previewFace_ = face; + previewColors_ = colors; + previewDisplayColors_ = displayColors; + initialized_ = false; +} + +void VirtualCube::ClearPreviewFace(Face face) { + if (previewFace_ && *previewFace_ == face) { + previewFace_.reset(); + initialized_ = false; + } +} + +void VirtualCube::ClearPreview() { + if (previewFace_) { + previewFace_.reset(); + initialized_ = false; + } +} + +void VirtualCube::QueueFaceMoves(const std::vector& moves) { + ClearFaceMoves(); + for (const auto& m : moves) { + faceQueue_.push_back(m); + } + totalMoves_ = static_cast(faceQueue_.size()); + Log::Write(Log::Level::Info, + Fmt("VirtualCube: queued %zu face moves (pending=%zu)", moves.size(), faceQueue_.size())); +} + +void VirtualCube::ClearFaceMoves() { + faceQueue_.clear(); + faceMoveActive_ = false; + faceMoveTimer_ = 0.f; + pauseActive_ = false; + pauseTimer_ = 0.f; + totalMoves_ = 0; + movesStarted_ = 0; + currentMoveIdx_.reset(); +} + +void VirtualCube::QueueOrientationOp(CubeTurnOp op) { + EnsureTurnQuats(); + OrientationCommand cmd{}; + cmd.op = op; + cmd.delta = DeltaForOp(op); + orientationOps_.push_back(cmd); + StartNextOrientationOp(); +} + +void VirtualCube::QueueOrientationOpRelative(CubeTurnOp op, const XrQuaternionf& viewerOrientation) { + auto normals = ComputeFaceWorldNormals(); + auto info = ComputeViewerFacingInfo(viewerOrientation, normals); + if (!info) { + QueueOrientationOp(op); + return; + } + Face targetFace = info->front; + switch (op) { + case CubeTurnOp::Down: targetFace = info->up; break; + case CubeTurnOp::Up: targetFace = info->down; break; + case CubeTurnOp::Right: targetFace = info->left; break; + case CubeTurnOp::Left: targetFace = info->right; break; + } + if (targetFace == info->front) { + return; + } + XrQuaternionf delta{}; + if (!BuildDeltaBetweenFaces(info->front, targetFace, normals, delta)) { + QueueOrientationOp(op); + return; + } + OrientationCommand cmd{}; + cmd.op = op; + cmd.delta = delta; + orientationOps_.push_back(cmd); + StartNextOrientationOp(); +} + +void VirtualCube::ResetOrientation() { + SetDefaultOrientation(); + orientationTimer_ = 0.f; + orientationAnimating_ = false; + orientationOps_.clear(); + didWarmup_ = false; + // Default face turn duration for animations + faceMoveDuration_ = 2.0f; +} + +void VirtualCube::QueueWarmupAnimation() { + if (didWarmup_) return; + QueueOrientationOp(CubeTurnOp::Down); + QueueOrientationOp(CubeTurnOp::Right); + didWarmup_ = true; +} + +void VirtualCube::SetAnchorPosition(const XrVector3f& position) { + anchorPosition_ = position; +} + +std::optional VirtualCube::GetFacingFace(const XrQuaternionf& viewerOrientation) const { + XrVector3f viewDirLocal{0.f, 0.f, 1.f}; + XrVector3f viewDir{}; + XrQuaternionf_RotateVector3f(&viewDir, &viewerOrientation, &viewDirLocal); + + float bestDot = -2.f; + std::optional bestFace; + for (const auto& entry : kFaceAxes) { + XrVector3f worldNormal{}; + XrQuaternionf_RotateVector3f(&worldNormal, &cubeOrientation_, &entry.normal); + const float dot = XrVector3f_Dot(&worldNormal, &viewDir); + if (dot > bestDot) { + bestDot = dot; + bestFace = entry.face; + } + } + return bestFace; +} + +std::optional> VirtualCube::GetFaceDisplayColors(Face face) const { + if (cubies_.empty()) return std::nullopt; + std::array out{}; + bool any = false; + const int faceIdx = static_cast(face); + for (const auto& c : cubies_) { + bool boundary = false; + int row = 0; + int col = 0; + switch (face) { + case Face::U: + boundary = (c.y == +1); + row = (c.z + 1); + col = (c.x + 1); + break; + case Face::D: + boundary = (c.y == -1); + row = (-c.z + 1); + col = (c.x + 1); + break; + case Face::F: + boundary = (c.z == -1); + row = (-c.y + 1); + col = (c.x + 1); + break; + case Face::B: + boundary = (c.z == +1); + row = (-c.y + 1); + col = (-c.x + 1); + break; + case Face::R: + boundary = (c.x == +1); + row = (-c.y + 1); + col = (c.z + 1); + break; + case Face::L: + boundary = (c.x == -1); + row = (-c.y + 1); + col = (-c.z + 1); + break; + } + if (!boundary) continue; + int idx = row * 3 + col; + if (idx >= 0 && idx < 9) { + out[idx] = c.displayColors[faceIdx]; + any = true; + } + } + if (!any) return std::nullopt; + return out; +} + +bool VirtualCube::BuildMesh(std::vector& outVerts, std::vector& outIdx, + XrPosef& outPose) { + outVerts.clear(); + outIdx.clear(); + outPose = {}; + outPose.orientation = cubeOrientation_; + outPose.position = anchorPosition_; + + EnsureInitialized(); + + const float half = (cubeSize_ / 3.0f - gap_) * 0.5f; + const float spacing = (cubeSize_ / 3.0f); + + const bool rotatingFace = faceMoveActive_; + const float angle = CurrentFaceAngleRad(); + char rotAxis = 0; + int layerVal = 0; + if (rotatingFace) { + switch (currentFaceMove_.type) { + case MoveType::U: rotAxis = 'y'; layerVal = +1; break; + case MoveType::D: rotAxis = 'y'; layerVal = -1; break; + case MoveType::R: rotAxis = 'x'; layerVal = +1; break; + case MoveType::L: rotAxis = 'x'; layerVal = -1; break; + case MoveType::F: rotAxis = 'z'; layerVal = -1; break; + case MoveType::B: rotAxis = 'z'; layerVal = +1; break; + } + } + + for (const auto& c : cubies_) { + XrVector3f center = {c.x * spacing, c.y * spacing, c.z * spacing}; + bool inLayer = false; + if (rotatingFace) { + if (rotAxis == 'x') inLayer = (c.x == layerVal); + if (rotAxis == 'y') inLayer = (c.y == layerVal); + if (rotAxis == 'z') inLayer = (c.z == layerVal); + } + PushBox(outVerts, outIdx, center, half, rotatingFace && inLayer, rotAxis, angle); + + auto addStickerIf = [&](char faceChar, bool cond, int faceIdx) { + if (!cond) return; + XrVector3f rgb = c.displayColors[faceIdx]; + const bool hasDisplay = (rgb.x != 0.f || rgb.y != 0.f || rgb.z != 0.f); + if (!hasDisplay && c.faces[faceIdx] != Color::Unknown) { + // Regenerate display color if the sticker lost its cached RGB value + rgb = ToDisplayColor(c.faces[faceIdx]); + } else if (!hasDisplay && c.faces[faceIdx] == Color::Unknown) { + rgb = {0.3f, 0.3f, 0.3f}; + } + PushSticker(outVerts, outIdx, center, half, faceChar, rgb, rotatingFace && inLayer, rotAxis, angle); + }; + + addStickerIf('U', c.y == +1, 0); + addStickerIf('R', c.x == +1, 1); + addStickerIf('F', c.z == -1, 2); + addStickerIf('D', c.y == -1, 3); + addStickerIf('L', c.x == -1, 4); + addStickerIf('B', c.z == +1, 5); + } + + return (!outVerts.empty() && !outIdx.empty()); +} + +void VirtualCube::Solving() { + if (!faceMoveActive_ && !pauseActive_ && faceQueue_.empty()) { + currentMoveIdx_.reset(); + } + if (faceMoveActive_) { + faceMoveTimer_ += 0.016f; // 60 FPS + if (faceMoveTimer_ >= faceMoveDuration_) { + // Commit the just-finished face move to sticker positions/colors + ApplyFaceMoveToCubies(currentFaceMove_); + faceMoveActive_ = false; + faceMoveTimer_ = 0.f; + // Start pause after move completes + pauseActive_ = true; + pauseTimer_ = 0.f; + } + } + + if (pauseActive_) { + pauseTimer_ += 0.016f; // 60 FPS + if (pauseTimer_ >= pauseAfterMove_) { + pauseActive_ = false; + pauseTimer_ = 0.f; + } + } + + if (!faceMoveActive_ && !pauseActive_) { + BeginNextFaceMove(); + } +} + +void VirtualCube::Update(float dt) { + EnsureInitialized(); + UpdateOrientation(dt); +} + +void VirtualCube::EnsureInitialized() { + if (initialized_) return; + + if (const char* s = std::getenv("RUBICS_SIZE_M")) { + float v = std::strtof(s, nullptr); + if (v > 0.05f && v < 1.0f) cubeSize_ = v; + } + if (const char* s = std::getenv("RUBICS_GAP_M")) { + float v = std::strtof(s, nullptr); + if (v >= 0.0f && v < 0.02f) gap_ = v; + } + if (const char* s = std::getenv("RUBICS_EPS_M")) { + float v = std::strtof(s, nullptr); + if (v >= 0.0f && v < 0.01f) stickerEps_ = v; + } + if (const char* s = std::getenv("RUBICS_SWAP_RB")) { + swapRB_ = (std::atoi(s) != 0); + } + if (const char* s = std::getenv("RUBICS_TURN_MS")) { + int ms = std::atoi(s); + if (ms >= 50 && ms <= 10000) faceMoveDuration_ = ms / 1000.0f; + } +#ifdef __ANDROID__ + char prop[PROP_VALUE_MAX] = {0}; + if (__system_property_get("rubics_size_m", prop) > 0) { + float v = std::strtof(prop, nullptr); + if (v > 0.05f && v < 1.0f) cubeSize_ = v; + } + if (__system_property_get("rubics_gap_m", prop) > 0) { + float v = std::strtof(prop, nullptr); + if (v >= 0.0f && v < 0.02f) gap_ = v; + } + if (__system_property_get("rubics_eps_m", prop) > 0) { + float v = std::strtof(prop, nullptr); + if (v >= 0.0f && v < 0.01f) stickerEps_ = v; + } + if (__system_property_get("rubics_swap_rb", prop) > 0) { + swapRB_ = (std::atoi(prop) != 0); + } + if (__system_property_get("rubics_turn_ms", prop) > 0) { + int ms = std::atoi(prop); + if (ms >= 50 && ms <= 10000) faceMoveDuration_ = ms / 1000.0f; + } +#endif + + cubies_.clear(); + cubies_.reserve(27); + for (int iy = -1; iy <= 1; ++iy) { + for (int iz = -1; iz <= 1; ++iz) { + for (int ix = -1; ix <= 1; ++ix) { + CubieState c{}; + c.x = ix; + c.y = iy; + c.z = iz; + for (auto& dc : c.displayColors) { + dc = {0.3f, 0.3f, 0.3f}; + } + cubies_.push_back(c); + } + } + } + + if (cubeModel_) { + InitializeFromCubeModel(); + } else { + InitializeDefaultColors(); + } + initialized_ = true; +} + +void VirtualCube::InitializeDefaultColors() { + std::array fill{}; + auto apply = [&](Face face, Color color) { + fill.fill(color); + std::array display{}; + display.fill(ToRgb(color)); + AssignFaceColors(face, fill, &display); + }; + apply(Face::F, Color::G); + apply(Face::U, Color::W); + apply(Face::L, Color::O); + apply(Face::B, Color::B); + apply(Face::D, Color::Y); + apply(Face::R, Color::R); + ApplyPreviewOverride(); +} + +void VirtualCube::InitializeFromCubeModel() { + for (auto& c : cubies_) { + for (int k = 0; k < 6; ++k) c.faces[k] = Color::Unknown; + } + if (cubeModel_) { + auto apply = [&](Face face) { + auto opt = cubeModel_->GetFaceColors(face); + if (!opt) return; + auto raw = cubeModel_->GetFaceRawColors(face); + std::array display{}; + const std::array* displayPtr = nullptr; + if (raw) { + for (int i = 0; i < 9; ++i) { + display[i] = {(*raw)[i].r, (*raw)[i].g, (*raw)[i].b}; + } + displayPtr = &display; + } + AssignFaceColors(face, *opt, displayPtr); + }; + apply(Face::U); + apply(Face::D); + apply(Face::F); + apply(Face::B); + apply(Face::R); + apply(Face::L); + } + ApplyPreviewOverride(); + + // Debug: verify that every boundary sticker now has a resolved color + int boundaryUnknown = 0; + std::array missingPerFace{}; + for (const auto& c : cubies_) { + for (int faceIdx = 0; faceIdx < 6; ++faceIdx) { + bool boundary = false; + switch (static_cast(faceIdx)) { + case Face::U: boundary = (c.y == +1); break; + case Face::D: boundary = (c.y == -1); break; + case Face::F: boundary = (c.z == -1); break; + case Face::B: boundary = (c.z == +1); break; + case Face::R: boundary = (c.x == +1); break; + case Face::L: boundary = (c.x == -1); break; + } + if (!boundary) continue; + if (c.faces[faceIdx] == Color::Unknown) { + ++boundaryUnknown; + missingPerFace[faceIdx]++; + } + } + } + if (boundaryUnknown > 0) { + Log::Write(Log::Level::Warning, + Fmt("VirtualCube: InitializeFromCubeModel missing %d stickers (perFace=%d,%d,%d,%d,%d,%d)", + boundaryUnknown, missingPerFace[0], missingPerFace[1], missingPerFace[2], + missingPerFace[3], missingPerFace[4], missingPerFace[5])); + } else { + Log::Write(Log::Level::Info, "VirtualCube: InitializeFromCubeModel populated all stickers"); + } +} + +void VirtualCube::AssignFaceColors(Face face, const std::array& colors, + const std::array* displayColors) { + const int faceIdx = static_cast(face); + for (auto& c : cubies_) { + bool boundary = false; + int row = 0; + int col = 0; + switch (face) { + case Face::U: + boundary = (c.y == +1); + row = (c.z + 1); + col = (c.x + 1); + break; + case Face::D: + boundary = (c.y == -1); + row = (-c.z + 1); + col = (c.x + 1); + break; + case Face::F: + boundary = (c.z == -1); + row = (-c.y + 1); + col = (c.x + 1); + break; + case Face::B: + boundary = (c.z == +1); + row = (-c.y + 1); + col = (-c.x + 1); + break; + case Face::R: + boundary = (c.x == +1); + row = (-c.y + 1); + col = (c.z + 1); + break; + case Face::L: + boundary = (c.x == -1); + row = (-c.y + 1); + col = (-c.z + 1); + break; + } + if (!boundary) continue; + int idx = row * 3 + col; + if (idx >= 0 && idx < 9) { + c.faces[faceIdx] = colors[idx]; + XrVector3f disp{}; + if (displayColors) { + disp = (*displayColors)[idx]; + } else { + disp = ToDisplayColor(colors[idx]); + } + c.displayColors[faceIdx] = disp; + } + } +} + +void VirtualCube::ApplyPreviewOverride() { + if (previewFace_) { + AssignFaceColors(*previewFace_, previewColors_, &previewDisplayColors_); + } +} + +void VirtualCube::BeginNextFaceMove() { + if (faceQueue_.empty()) return; + EnsureInitialized(); + currentFaceMove_ = faceQueue_.front(); + faceQueue_.pop_front(); + currentMoveIdx_ = movesStarted_; + movesStarted_ = std::min(totalMoves_, movesStarted_ + 1); + WarnIfMissingStickerColors(currentFaceMove_); + faceMoveActive_ = true; + faceMoveTimer_ = 0.f; +} + +void VirtualCube::WarnIfMissingStickerColors(const Move& upcomingMove) const { + if (cubies_.empty()) return; + auto hasSticker = [](const CubieState& c, Face face) -> bool { + switch (face) { + case Face::U: return c.y == +1; + case Face::D: return c.y == -1; + case Face::F: return c.z == -1; + case Face::B: return c.z == +1; + case Face::R: return c.x == +1; + case Face::L: return c.x == -1; + } + return false; + }; + int stickersWithIssues = 0; + int missingRgb = 0; + int unknownColor = 0; + std::array missingPerFace{}; + std::vector samples; + for (const auto& c : cubies_) { + for (int faceIdx = 0; faceIdx < 6; ++faceIdx) { + Face face = static_cast(faceIdx); + if (!hasSticker(c, face)) continue; + const XrVector3f& rgb = c.displayColors[faceIdx]; + bool issue = false; + if (rgb.x == 0.f && rgb.y == 0.f && rgb.z == 0.f) { + ++missingRgb; + issue = true; + } + if (c.faces[faceIdx] == Color::Unknown) { + ++unknownColor; + issue = true; + } + if (issue) { + ++stickersWithIssues; + missingPerFace[faceIdx]++; + if (samples.size() < 4) { + samples.push_back( + Fmt("face=%d xyz=(%d,%d,%d) color=%s", faceIdx, c.x, c.y, c.z, ColorToChar(c.faces[faceIdx]).c_str())); + } + } + } + } + if (stickersWithIssues <= 0) return; + auto moveChar = [](MoveType type) -> char { + switch (type) { + case MoveType::U: return 'U'; + case MoveType::R: return 'R'; + case MoveType::F: return 'F'; + case MoveType::D: return 'D'; + case MoveType::L: return 'L'; + case MoveType::B: return 'B'; + } + return '?'; + }; + const char* suffix = ""; + switch (upcomingMove.turn) { + case Turn::CW: suffix = ""; break; + case Turn::CCW: suffix = "'"; break; + case Turn::Double: suffix = "2"; break; + } + Log::Write(Log::Level::Warning, + Fmt("VirtualCube: %d stickers missing colors before move %c%s (missingRGB=%d unknownColor=%d perFace=%d,%d,%d,%d,%d,%d samples=%s)", + stickersWithIssues, moveChar(upcomingMove.type), suffix, missingRgb, unknownColor, + missingPerFace[0], missingPerFace[1], missingPerFace[2], + missingPerFace[3], missingPerFace[4], missingPerFace[5], + samples.empty() ? "none" : samples[0].c_str())); +} + +bool VirtualCube::IsCubieInActiveLayer(const CubieState& c) const { + if (!faceMoveActive_) return false; + switch (currentFaceMove_.type) { + case MoveType::U: return c.y == +1; + case MoveType::D: return c.y == -1; + case MoveType::R: return c.x == +1; + case MoveType::L: return c.x == -1; + case MoveType::F: return c.z == -1; + case MoveType::B: return c.z == +1; + } + return false; +} + +std::optional VirtualCube::ActiveMoveIndex() const { + if ((faceMoveActive_ || pauseActive_) && currentMoveIdx_) return currentMoveIdx_; + return std::nullopt; +} + +int VirtualCube::SignForCW(MoveType t) { + switch (t) { + case MoveType::U: return -1; + case MoveType::D: return +1; + case MoveType::R: return -1; + case MoveType::L: return +1; + case MoveType::F: return -1; + case MoveType::B: return +1; + } + return 1; +} + +float VirtualCube::CurrentFaceAngleRad() const { + if (!faceMoveActive_) return 0.f; + float base = (currentFaceMove_.turn == Turn::Double) ? static_cast(M_PI) + : static_cast(M_PI) * 0.5f; + float progress = std::clamp(faceMoveTimer_ / std::max(0.001f, faceMoveDuration_), 0.f, 1.f); + float s = (currentFaceMove_.turn == Turn::Double) + ? 1.f + : (currentFaceMove_.turn == Turn::CW ? static_cast(SignForCW(currentFaceMove_.type)) + : -static_cast(SignForCW(currentFaceMove_.type))); + return s * base * progress; +} + +void VirtualCube::RotateCubie(CubieState& c, char axis, int signedQuarterTurns) const { + if (signedQuarterTurns == 0) return; + // Normalize turns to [0,3] with positive meaning +90deg about the +axis (right-hand rule) + int turns = signedQuarterTurns % 4; + if (turns < 0) turns += 4; + auto rotateOnce = [&](CubieState& cc) { + // Rotate position + int x = cc.x, y = cc.y, z = cc.z; + if (axis == 'x') { + cc.y = -z; + cc.z = y; + // Face orientation cycle: U -> B -> D -> F -> U + Color u = cc.faces[static_cast(Face::U)]; + Color f = cc.faces[static_cast(Face::F)]; + Color d = cc.faces[static_cast(Face::D)]; + Color b = cc.faces[static_cast(Face::B)]; + XrVector3f ud = cc.displayColors[static_cast(Face::U)]; + XrVector3f fd = cc.displayColors[static_cast(Face::F)]; + XrVector3f dd = cc.displayColors[static_cast(Face::D)]; + XrVector3f bd = cc.displayColors[static_cast(Face::B)]; + cc.faces[static_cast(Face::U)] = f; + cc.faces[static_cast(Face::B)] = u; + cc.faces[static_cast(Face::D)] = b; + cc.faces[static_cast(Face::F)] = d; + cc.displayColors[static_cast(Face::U)] = fd; + cc.displayColors[static_cast(Face::B)] = ud; + cc.displayColors[static_cast(Face::D)] = bd; + cc.displayColors[static_cast(Face::F)] = dd; + } else if (axis == 'y') { + cc.x = z; + cc.z = -x; + // Face orientation cycle: R -> F -> L -> B -> R + Color r = cc.faces[static_cast(Face::R)]; + Color f = cc.faces[static_cast(Face::F)]; + Color l = cc.faces[static_cast(Face::L)]; + Color b = cc.faces[static_cast(Face::B)]; + XrVector3f rd = cc.displayColors[static_cast(Face::R)]; + XrVector3f fd = cc.displayColors[static_cast(Face::F)]; + XrVector3f ld = cc.displayColors[static_cast(Face::L)]; + XrVector3f bd = cc.displayColors[static_cast(Face::B)]; + cc.faces[static_cast(Face::F)] = r; + cc.faces[static_cast(Face::L)] = f; + cc.faces[static_cast(Face::B)] = l; + cc.faces[static_cast(Face::R)] = b; + cc.displayColors[static_cast(Face::F)] = rd; + cc.displayColors[static_cast(Face::L)] = fd; + cc.displayColors[static_cast(Face::B)] = ld; + cc.displayColors[static_cast(Face::R)] = bd; + } else if (axis == 'z') { + cc.x = -y; + cc.y = x; + // Face orientation cycle: U -> L -> D -> R -> U + Color u = cc.faces[static_cast(Face::U)]; + Color l = cc.faces[static_cast(Face::L)]; + Color d = cc.faces[static_cast(Face::D)]; + Color r = cc.faces[static_cast(Face::R)]; + XrVector3f ud = cc.displayColors[static_cast(Face::U)]; + XrVector3f ld = cc.displayColors[static_cast(Face::L)]; + XrVector3f dd = cc.displayColors[static_cast(Face::D)]; + XrVector3f rd = cc.displayColors[static_cast(Face::R)]; + cc.faces[static_cast(Face::L)] = u; + cc.faces[static_cast(Face::D)] = l; + cc.faces[static_cast(Face::R)] = d; + cc.faces[static_cast(Face::U)] = r; + cc.displayColors[static_cast(Face::L)] = ud; + cc.displayColors[static_cast(Face::D)] = ld; + cc.displayColors[static_cast(Face::R)] = dd; + cc.displayColors[static_cast(Face::U)] = rd; + } + }; + + for (int i = 0; i < turns; ++i) { + rotateOnce(c); + } +} + +void VirtualCube::ApplyFaceMoveToCubies(const Move& move) { + if (cubies_.empty()) return; + + char axis = 'y'; + int layer = 0; + switch (move.type) { + case MoveType::U: axis = 'y'; layer = +1; break; + case MoveType::D: axis = 'y'; layer = -1; break; + case MoveType::R: axis = 'x'; layer = +1; break; + case MoveType::L: axis = 'x'; layer = -1; break; + case MoveType::F: axis = 'z'; layer = -1; break; + case MoveType::B: axis = 'z'; layer = +1; break; + } + + int baseTurns = (move.turn == Turn::Double) ? 2 : 1; + int dir = (move.turn == Turn::Double) ? 1 : (move.turn == Turn::CW ? 1 : -1); + int signedTurns = baseTurns * dir; + if (move.turn != Turn::Double) signedTurns *= SignForCW(move.type); + if ((signedTurns % 4) == 0) return; + + for (auto& c : cubies_) { + bool inLayer = false; + switch (axis) { + case 'x': inLayer = (c.x == layer); break; + case 'y': inLayer = (c.y == layer); break; + case 'z': inLayer = (c.z == layer); break; + } + if (!inLayer) continue; + RotateCubie(c, axis, signedTurns); + } +} + +void VirtualCube::PushTri(std::vector& v, std::vector& i, + const XrVector3f& a, const XrVector3f& b, const XrVector3f& c, + const XrVector3f& color) { + uint16_t base = static_cast(v.size()); + v.push_back(Geometry::Vertex{a, color}); + v.push_back(Geometry::Vertex{b, color}); + v.push_back(Geometry::Vertex{c, color}); + i.push_back(base + 0); + i.push_back(base + 1); + i.push_back(base + 2); +} + +XrVector3f VirtualCube::ToRgb(Color c) { + switch (c) { + case Color::W: return {1.f, 1.f, 1.f}; + case Color::Y: return {1.f, 1.f, 0.f}; + case Color::R: return {1.f, 0.f, 0.f}; + case Color::O: return {1.f, 0.55f, 0.f}; + case Color::G: return {0.f, 1.f, 0.f}; + case Color::B: return {0.f, 0.f, 1.f}; + default: return {0.3f, 0.3f, 0.3f}; + } +} + +XrVector3f VirtualCube::ToDisplayColor(Color c) const { + XrVector3f rgb = ToRgb(c); + if (swapRB_) { + std::swap(rgb.x, rgb.z); + } + return rgb; +} + +void VirtualCube::PushBox(std::vector& v, std::vector& i, + const XrVector3f& center, float half, bool rotate, char axis, float angleRad) { + const XrVector3f color{0.05f, 0.05f, 0.05f}; + XrVector3f LBB{center.x - half, center.y - half, center.z - half}; + XrVector3f LBF{center.x - half, center.y - half, center.z + half}; + XrVector3f LTB{center.x - half, center.y + half, center.z - half}; + XrVector3f LTF{center.x - half, center.y + half, center.z + half}; + XrVector3f RBB{center.x + half, center.y - half, center.z - half}; + XrVector3f RBF{center.x + half, center.y - half, center.z + half}; + XrVector3f RTB{center.x + half, center.y + half, center.z - half}; + XrVector3f RTF{center.x + half, center.y + half, center.z + half}; + + auto rotatePoint = [&](XrVector3f& p) { + if (!rotate || angleRad == 0.f) return; + XrVector3f out = p; + float s = std::sin(angleRad); + float c = std::cos(angleRad); + switch (axis) { + case 'x': + out.y = c * p.y - s * p.z; + out.z = s * p.y + c * p.z; + break; + case 'y': + out.x = c * p.x + s * p.z; + out.z = -s * p.x + c * p.z; + break; + case 'z': + out.x = c * p.x - s * p.y; + out.y = s * p.x + c * p.y; + break; + } + p = out; + }; + + if (rotate) { + rotatePoint(LBB); + rotatePoint(LBF); + rotatePoint(LTB); + rotatePoint(LTF); + rotatePoint(RBB); + rotatePoint(RBF); + rotatePoint(RTB); + rotatePoint(RTF); + } + + PushTri(v, i, LTB, LBF, LBB, color); PushTri(v, i, LTB, LTF, LBF, color); + PushTri(v, i, RTB, RBB, RBF, color); PushTri(v, i, RTB, RBF, RTF, color); + PushTri(v, i, LBB, LBF, RBF, color); PushTri(v, i, LBB, RBF, RBB, color); + PushTri(v, i, LTB, RTB, RTF, color); PushTri(v, i, LTB, RTF, LTF, color); + PushTri(v, i, LBB, RBB, RTB, color); PushTri(v, i, LBB, RTB, LTB, color); + PushTri(v, i, LBF, LTF, RTF, color); PushTri(v, i, LBF, RTF, RBF, color); +} + +void VirtualCube::PushSticker(std::vector& v, std::vector& i, + const XrVector3f& center, float half, char face, + const XrVector3f& color, bool rotate, char axis, float angleRad) { + const float margin = gap_ * 0.4f; + const float offset = stickerEps_; + XrVector3f a{}, b{}, c{}, d{}; + switch (face) { + case 'U': + a = {center.x - half + margin, center.y + half + offset, center.z - half + margin}; + b = {center.x + half - margin, center.y + half + offset, center.z - half + margin}; + c = {center.x + half - margin, center.y + half + offset, center.z + half - margin}; + d = {center.x - half + margin, center.y + half + offset, center.z + half - margin}; + break; + case 'D': + a = {center.x - half + margin, center.y - half - offset, center.z + half - margin}; + b = {center.x + half - margin, center.y - half - offset, center.z + half - margin}; + c = {center.x + half - margin, center.y - half - offset, center.z - half + margin}; + d = {center.x - half + margin, center.y - half - offset, center.z - half + margin}; + break; + case 'R': + a = {center.x + half + offset, center.y + half - margin, center.z - half + margin}; + b = {center.x + half + offset, center.y + half - margin, center.z + half - margin}; + c = {center.x + half + offset, center.y - half + margin, center.z + half - margin}; + d = {center.x + half + offset, center.y - half + margin, center.z - half + margin}; + break; + case 'L': + a = {center.x - half - offset, center.y + half - margin, center.z + half - margin}; + b = {center.x - half - offset, center.y + half - margin, center.z - half + margin}; + c = {center.x - half - offset, center.y - half + margin, center.z - half + margin}; + d = {center.x - half - offset, center.y - half + margin, center.z + half - margin}; + break; + case 'F': + a = {center.x - half + margin, center.y + half - margin, center.z - half - offset}; + b = {center.x + half - margin, center.y + half - margin, center.z - half - offset}; + c = {center.x + half - margin, center.y - half + margin, center.z - half - offset}; + d = {center.x - half + margin, center.y - half + margin, center.z - half - offset}; + break; + case 'B': + a = {center.x + half - margin, center.y + half - margin, center.z + half + offset}; + b = {center.x - half + margin, center.y + half - margin, center.z + half + offset}; + c = {center.x - half + margin, center.y - half + margin, center.z + half + offset}; + d = {center.x + half - margin, center.y - half + margin, center.z + half + offset}; + break; + default: + return; + } + auto rotatePoint = [&](XrVector3f& p) { + if (!rotate || angleRad == 0.f) return; + XrVector3f out = p; + float s = std::sin(angleRad); + float c = std::cos(angleRad); + switch (axis) { + case 'x': + out.y = c * p.y - s * p.z; + out.z = s * p.y + c * p.z; + break; + case 'y': + out.x = c * p.x + s * p.z; + out.z = -s * p.x + c * p.z; + break; + case 'z': + out.x = c * p.x - s * p.y; + out.y = s * p.x + c * p.y; + break; + } + p = out; + }; + rotatePoint(a); + rotatePoint(b); + rotatePoint(c); + rotatePoint(d); + + PushTri(v, i, a, c, b, color); + PushTri(v, i, a, d, c, color); +} + +void VirtualCube::UpdateOrientation(float dt) { + if (!orientationAnimating_) StartNextOrientationOp(); + if (!orientationAnimating_) return; + + orientationTimer_ += std::max(0.f, dt); + float denom = orientationDuration_ > 0.f ? orientationDuration_ : 0.001f; + float s = orientationTimer_ / denom; + if (s >= 1.f) { + cubeOrientation_ = orientationEnd_; + orientationAnimating_ = false; + orientationTimer_ = 0.f; + StartNextOrientationOp(); + return; + } + XrQuaternionf_Lerp(&cubeOrientation_, &orientationStart_, &orientationEnd_, s); + XrQuaternionf_Normalize(&cubeOrientation_); +} + +void VirtualCube::StartNextOrientationOp() { + if (orientationAnimating_ || orientationOps_.empty()) return; + OrientationCommand cmd = orientationOps_.front(); + orientationOps_.pop_front(); + orientationStart_ = cubeOrientation_; + XrQuaternionf_Multiply(&orientationEnd_, &cubeOrientation_, &cmd.delta); + XrQuaternionf_Normalize(&orientationEnd_); + orientationTimer_ = 0.f; + orientationAnimating_ = true; +} + +XrQuaternionf VirtualCube::DeltaForOp(CubeTurnOp op) { + EnsureTurnQuats(); + switch (op) { + case CubeTurnOp::Down: return downQuat_; + case CubeTurnOp::Right: return rightQuat_; + case CubeTurnOp::Left: return leftQuat_; + case CubeTurnOp::Up: return upQuat_; + } + return {}; +} + +void VirtualCube::EnsureTurnQuats() { + if (turnQuatsInit_) return; + const float halfPi = static_cast(M_PI) * 0.5f; + XrVector3f axisX{1.f, 0.f, 0.f}; + XrVector3f axisY{0.f, 1.f, 0.f}; + XrQuaternionf_CreateFromAxisAngle(&upQuat_, &axisX, halfPi); + XrQuaternionf_CreateFromAxisAngle(&downQuat_, &axisX, -halfPi); + XrQuaternionf_CreateFromAxisAngle(&leftQuat_, &axisY, -halfPi); + XrQuaternionf_CreateFromAxisAngle(&rightQuat_, &axisY, halfPi); + turnQuatsInit_ = true; +} + +std::array VirtualCube::ComputeFaceWorldNormals() const { + std::array normals{}; + for (const auto& entry : kFaceAxes) { + XrVector3f world{}; + XrQuaternionf_RotateVector3f(&world, &cubeOrientation_, &entry.normal); + normals[static_cast(entry.face)] = world; + } + return normals; +} + +std::optional VirtualCube::ComputeViewerFacingInfo( + const XrQuaternionf& viewerOrientation, const std::array& normals) const { + XrVector3f viewForward{}; + XrVector3f viewUp{}; + const XrVector3f localForward{0.f, 0.f, -1.f}; + const XrVector3f localUp{0.f, 1.f, 0.f}; + XrQuaternionf_RotateVector3f(&viewForward, &viewerOrientation, &localForward); + XrQuaternionf_RotateVector3f(&viewUp, &viewerOrientation, &localUp); + XrVector3f viewRight{}; + XrVector3f_Cross(&viewRight, &viewForward, &viewUp); + float rightLen = XrVector3f_Length(&viewRight); + if (rightLen < 1e-4f) { + return std::nullopt; + } + XrVector3f_Normalize(&viewRight); + + auto selectFace = [&](const XrVector3f& axis) { + float bestDot = -FLT_MAX; + Face bestFace = Face::F; + for (int i = 0; i < 6; ++i) { + float dot = XrVector3f_Dot(&normals[i], &axis); + if (dot > bestDot) { + bestDot = dot; + bestFace = static_cast(i); + } + } + return bestFace; + }; + + ViewerFacingInfo info{}; + info.front = selectFace(viewForward); + XrVector3f negForward{-viewForward.x, -viewForward.y, -viewForward.z}; + info.back = selectFace(negForward); + info.up = selectFace(viewUp); + XrVector3f negUp{-viewUp.x, -viewUp.y, -viewUp.z}; + info.down = selectFace(negUp); + info.right = selectFace(viewRight); + XrVector3f negRight{-viewRight.x, -viewRight.y, -viewRight.z}; + info.left = selectFace(negRight); + return info; +} + +bool VirtualCube::BuildDeltaBetweenFaces(Face from, Face to, const std::array& normals, + XrQuaternionf& outDelta) const { + if (from == to) return false; + const XrVector3f& fromDir = normals[static_cast(from)]; + const XrVector3f& toDir = normals[static_cast(to)]; + XrVector3f axis{}; + XrVector3f_Cross(&axis, &fromDir, &toDir); + float axisLen = XrVector3f_Length(&axis); + float dot = XrVector3f_Dot(&fromDir, &toDir); + dot = std::clamp(dot, -1.0f, 1.0f); + if (axisLen < 1e-4f) { + // Faces are parallel or opposite; fall back to default behavior + return false; + } + XrVector3f_Normalize(&axis); + float angle = std::acos(dot); + XrQuaternionf_CreateFromAxisAngle(&outDelta, &axis, angle); + return true; +} + +bool VirtualCube::MapScreenGridToFaceIndices(Face face, const XrQuaternionf& viewerOrientation, + std::array& outMapping) const { + const auto& axes = AxesForFace(face); + XrVector3f normal{}; + XrVector3f faceUp{}; + XrVector3f faceRight{}; + XrQuaternionf_RotateVector3f(&normal, &cubeOrientation_, &axes.normal); + XrQuaternionf_RotateVector3f(&faceUp, &cubeOrientation_, &axes.up); + XrQuaternionf_RotateVector3f(&faceRight, &cubeOrientation_, &axes.right); + const XrVector3f localRight{1.f, 0.f, 0.f}; + const XrVector3f localDown{0.f, -1.f, 0.f}; + XrVector3f viewRight{}; + XrVector3f viewDown{}; + XrQuaternionf_RotateVector3f(&viewRight, &viewerOrientation, &localRight); + XrQuaternionf_RotateVector3f(&viewDown, &viewerOrientation, &localDown); + + auto projectPlane = [&](XrVector3f& v) { + float d = XrVector3f_Dot(&v, &normal); + v.x -= d * normal.x; + v.y -= d * normal.y; + v.z -= d * normal.z; + float len = XrVector3f_Length(&v); + if (len < 1e-4f) return false; + XrVector3f_Scale(&v, &v, 1.0f / len); + return true; + }; + if (!projectPlane(viewRight)) return false; + if (!projectPlane(viewDown)) { + XrVector3f cross{}; + XrVector3f_Cross(&cross, &normal, &viewRight); + if (!projectPlane(cross)) return false; + viewDown = cross; + } else { + // Orthonormalize + const float dot = XrVector3f_Dot(&viewDown, &viewRight); + viewDown.x -= dot * viewRight.x; + viewDown.y -= dot * viewRight.y; + viewDown.z -= dot * viewRight.z; + if (!projectPlane(viewDown)) { + XrVector3f cross{}; + XrVector3f_Cross(&cross, &normal, &viewRight); + if (!projectPlane(cross)) return false; + viewDown = cross; + } + } + + auto quantize = [](float v) -> int { + if (v > 0.5f) return 1; + if (v < -0.5f) return -1; + return 0; + }; + int srR = quantize(XrVector3f_Dot(&viewRight, &faceRight)); + int srU = quantize(XrVector3f_Dot(&viewRight, &faceUp)); + int sdR = quantize(XrVector3f_Dot(&viewDown, &faceRight)); + int sdU = quantize(XrVector3f_Dot(&viewDown, &faceUp)); + if (srR == 0 && srU == 0) return false; + if (sdR == 0 && sdU == 0) return false; + if (srR * sdR + srU * sdU != 0) return false; + int det = srR * sdU - srU * sdR; + if (det == 0) return false; + + auto clampCoord = [](int v) -> bool { return v >= -1 && v <= 1; }; + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) { + int relCol = col - 1; + int relRow = row - 1; + int x = relCol * srR + relRow * sdR; + int y = relCol * srU + relRow * sdU; + if (!clampCoord(x) || !clampCoord(y)) return false; + int colIdx = x + 1; + int rowIdx = 1 - y; + if (colIdx < 0 || colIdx > 2 || rowIdx < 0 || rowIdx > 2) return false; + outMapping[row * 3 + col] = rowIdx * 3 + colIdx; + } + } + return true; +} + +void VirtualCube::SetDefaultOrientation() { + cubeOrientation_ = {}; + orientationStart_ = {}; + orientationEnd_ = {}; + XrVector3f axis{0.f, 1.f, 0.f}; + XrQuaternionf_CreateFromAxisAngle(&cubeOrientation_, &axis, static_cast(M_PI)); + orientationStart_ = cubeOrientation_; + orientationEnd_ = cubeOrientation_; +} + +} // namespace SecureMR diff --git a/samples/rubics_cube/cpp/virtual_cube.h b/samples/rubics_cube/cpp/virtual_cube.h new file mode 100644 index 0000000..7dd3ce0 --- /dev/null +++ b/samples/rubics_cube/cpp/virtual_cube.h @@ -0,0 +1,150 @@ +// Copyright (2025) Bytedance Ltd. and/or its affiliates +// Licensed under the Apache License, Version 2.0 + +#pragma once + +#include "pch.h" + +#include +#include +#include +#include + +#include "cube_types.h" +#include "geometry.h" + +namespace SecureMR { + +class CubeModel; + +class VirtualCube { + public: + enum class CubeTurnOp { Down, Right, Left, Up }; + + VirtualCube(); + + void Update(float dt); + void SetCubeModel(const CubeModel* model); + void PreviewFace(Face face, const std::array& colors, + const std::array& displayColors); + void ClearPreviewFace(Face face); + void ClearPreview(); + void QueueFaceMoves(const std::vector& moves); + void ClearFaceMoves(); + void QueueOrientationOp(CubeTurnOp op); + void QueueOrientationOpRelative(CubeTurnOp op, const XrQuaternionf& viewerOrientation); + void ResetOrientation(); + void QueueWarmupAnimation(); + void SetAnchorPosition(const XrVector3f& position); + [[nodiscard]] const XrVector3f& AnchorPosition() const { return anchorPosition_; } + [[nodiscard]] int TotalMoveCount() const { return totalMoves_; } + [[nodiscard]] std::optional ActiveMoveIndex() const; + [[nodiscard]] std::optional GetFacingFace(const XrQuaternionf& viewerOrientation) const; + [[nodiscard]] bool MapScreenGridToFaceIndices(Face face, const XrQuaternionf& viewerOrientation, + std::array& outMapping) const; + [[nodiscard]] std::optional> GetFaceDisplayColors(Face face) const; + + bool BuildMesh(std::vector& outVerts, std::vector& outIdx, XrPosef& outPose); + void Solving(); + + private: + struct CubieState { + int x{0}, y{0}, z{0}; + Color faces[6] = {Color::Unknown, Color::Unknown, Color::Unknown, + Color::Unknown, Color::Unknown, Color::Unknown}; + XrVector3f displayColors[6]{}; + }; + + // Cubie/color helpers + void EnsureInitialized(); + void InitializeFromCubeModel(); + void InitializeDefaultColors(); + void AssignFaceColors(Face face, const std::array& colors, + const std::array* displayColors); + void ApplyPreviewOverride(); + + // Face move animation helpers + void BeginNextFaceMove(); + bool IsCubieInActiveLayer(const CubieState& c) const; + float CurrentFaceAngleRad() const; + void WarnIfMissingStickerColors(const Move& upcomingMove) const; + static int SignForCW(MoveType type); + void ApplyFaceMoveToCubies(const Move& move); + void RotateCubie(CubieState& c, char axis, int signedQuarterTurns) const; + + void PushTri(std::vector& v, std::vector& i, + const XrVector3f& a, const XrVector3f& b, const XrVector3f& c, + const XrVector3f& color); + void PushBox(std::vector& v, std::vector& i, + const XrVector3f& center, float half, bool rotate, char axis, float angleRad); + void PushSticker(std::vector& v, std::vector& i, + const XrVector3f& center, float half, char face, const XrVector3f& color, + bool rotate, char axis, float angleRad); + static XrVector3f ToRgb(Color c); + XrVector3f ToDisplayColor(Color c) const; + + // Orientation helpers + void UpdateOrientation(float dt); + void StartNextOrientationOp(); + XrQuaternionf DeltaForOp(CubeTurnOp op); + struct ViewerFacingInfo { + Face front{Face::F}; + Face back{Face::B}; + Face left{Face::L}; + Face right{Face::R}; + Face up{Face::U}; + Face down{Face::D}; + }; + std::array ComputeFaceWorldNormals() const; + std::optional ComputeViewerFacingInfo(const XrQuaternionf& viewerOrientation, + const std::array& normals) const; + bool BuildDeltaBetweenFaces(Face from, Face to, const std::array& normals, XrQuaternionf& outDelta) const; + void EnsureTurnQuats(); + void SetDefaultOrientation(); + + // Face animation state + std::deque faceQueue_; + bool faceMoveActive_{false}; + Move currentFaceMove_{}; + float faceMoveTimer_{0.f}; + float faceMoveDuration_{0.6f}; + float pauseAfterMove_{1.0f}; + float pauseTimer_{0.f}; + bool pauseActive_{false}; + + std::vector cubies_; + bool initialized_{false}; + const CubeModel* cubeModel_{nullptr}; + float cubeSize_{0.3f}; + float gap_{0.003f}; + float stickerEps_{0.0008f}; + bool swapRB_{true}; + std::optional previewFace_{}; + std::array previewColors_{}; + std::array previewDisplayColors_{}; + + // Orientation state + struct OrientationCommand { + CubeTurnOp op; + XrQuaternionf delta{}; + }; + bool didWarmup_{false}; + std::deque orientationOps_; + XrQuaternionf cubeOrientation_{}; + XrQuaternionf orientationStart_{}; + XrQuaternionf orientationEnd_{}; + float orientationTimer_{0.f}; + float orientationDuration_{0.6f}; + bool orientationAnimating_{false}; + XrQuaternionf downQuat_{}; + XrQuaternionf rightQuat_{}; + XrQuaternionf leftQuat_{}; + XrQuaternionf upQuat_{}; + bool turnQuatsInit_{false}; + XrVector3f anchorPosition_{}; + int totalMoves_{0}; + int movesStarted_{0}; + std::optional currentMoveIdx_{}; +}; + +} // namespace SecureMR diff --git a/samples/rubics_cube/src/main/java/com/bytedance/pico/secure_mr_demo/rubics_cube/RubicsCubeActivity.java b/samples/rubics_cube/src/main/java/com/bytedance/pico/secure_mr_demo/rubics_cube/RubicsCubeActivity.java new file mode 100644 index 0000000..3d75fdd --- /dev/null +++ b/samples/rubics_cube/src/main/java/com/bytedance/pico/secure_mr_demo/rubics_cube/RubicsCubeActivity.java @@ -0,0 +1,32 @@ +package com.bytedance.pico.secure_mr_demo.rubics_cube; + +import android.Manifest; +import android.app.NativeActivity; +import android.content.pm.PackageManager; +import android.os.Bundle; +public class RubicsCubeActivity extends NativeActivity { + + static { + System.loadLibrary("rubics_cube"); + } + + private static final int REQ_CAMERA = 1001; + + public void requestCameraFromNative() { + if (checkSelfPermission(android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { + requestPermissions(new String[]{android.Manifest.permission.CAMERA}, REQ_CAMERA); + } else { +// nativeOnPermissionResult(REQ_CAMERA, +// new String[]{Manifest.permission.CAMERA}, +// new int[]{PackageManager.PERMISSION_GRANTED}); + } + } + + @Override + public void onRequestPermissionsResult(int rc, String[] perms, int[] grants) { + super.onRequestPermissionsResult(rc, perms, grants); +// nativeOnPermissionResult(rc, perms, grants); + } + +// private static native void nativeOnPermissionResult(int rc, String[] perms, int[] grants); +} diff --git a/samples/rubics_cube/src/main/res/mipmap-hdpi/ic_launcher.png b/samples/rubics_cube/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..ed04140 Binary files /dev/null and b/samples/rubics_cube/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/samples/rubics_cube/src/main/res/mipmap-hdpi/ic_launcher_round.png b/samples/rubics_cube/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..ed04140 Binary files /dev/null and b/samples/rubics_cube/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/samples/rubics_cube/src/main/res/mipmap-mdpi/ic_launcher.png b/samples/rubics_cube/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..d613024 Binary files /dev/null and b/samples/rubics_cube/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/samples/rubics_cube/src/main/res/mipmap-mdpi/ic_launcher_round.png b/samples/rubics_cube/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..d613024 Binary files /dev/null and b/samples/rubics_cube/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/samples/rubics_cube/src/main/res/mipmap-xhdpi/ic_launcher.png b/samples/rubics_cube/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..47431d4 Binary files /dev/null and b/samples/rubics_cube/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/samples/rubics_cube/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/samples/rubics_cube/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..47431d4 Binary files /dev/null and b/samples/rubics_cube/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/samples/rubics_cube/src/main/res/mipmap-xxhdpi/ic_launcher.png b/samples/rubics_cube/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..e04a254 Binary files /dev/null and b/samples/rubics_cube/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/samples/rubics_cube/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/samples/rubics_cube/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..e04a254 Binary files /dev/null and b/samples/rubics_cube/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/samples/rubics_cube/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/samples/rubics_cube/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..8f654af Binary files /dev/null and b/samples/rubics_cube/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/samples/rubics_cube/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/samples/rubics_cube/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..8f654af Binary files /dev/null and b/samples/rubics_cube/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/samples/whackamole/AndroidManifest.xml b/samples/whackamole/AndroidManifest.xml new file mode 100644 index 0000000..350364a --- /dev/null +++ b/samples/whackamole/AndroidManifest.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/samples/whackamole/CMakeLists.txt b/samples/whackamole/CMakeLists.txt new file mode 100644 index 0000000..22baf96 --- /dev/null +++ b/samples/whackamole/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.22) +project(whackamole) + +set(SAMPLE_SRCS + ${CMAKE_CURRENT_LIST_DIR}/cpp/whackamole_detection.cpp +) +set(SAMPLE_DIR ${CMAKE_CURRENT_LIST_DIR}/cpp) +set(USE_SECURE_MR_UTILS ON) + +include(../../base/base.cmake) diff --git a/samples/whackamole/README.md b/samples/whackamole/README.md new file mode 100644 index 0000000..72aa11c --- /dev/null +++ b/samples/whackamole/README.md @@ -0,0 +1,92 @@ +## Whack‑a‑Mole (Ducks) — JavaScript Operator Collision Demo + +This sample demonstrates how to integrate a JavaScript operator into a SecureMR pipeline to implement gameplay logic: detecting collisions between “moles” (duck glTFs) and human wrist joints, toggling visibility on hits, scheduling timed appearances/disappearances, and persisting a score — all driven by tensors exchanged between C++ and JavaScript. + +### What This Shows + +- Pose detection and landmark extraction run in SecureMR pipelines +- A JavaScript operator consumes landmark and game‑state tensors +- Per‑frame collision detection against wrist joints +- Visibility toggling and timed on/off scheduling for each duck +- Persistent score accumulation exposed to the render pipeline + +### Key Files + +- `samples/whackamole/cpp/whackamole_detection.cpp` — C++ pipelines, tensor wiring, asset loading +- `samples/whackamole/cpp/whackamole_detection.h` — globals, placeholders, pipeline members +- `assets/whackamole/jointsTransformer.js` — JavaScript operator logic (collisions, timers, score) + +### Pipelines Overview + +- Detection + Affine Update: find a person and stabilize ROI (`isPoseDetectedGlobal`, `roiAffineUpdatedGlobal`) +- Landmark: compute 4×4 transforms for bones and emit tensors to JavaScript (`bodyLandmarkGlobal`) +- Rendering: render duck instances and red boxes with per‑object visibility and poses +- Init (one‑time): compute initial human center; place ducks; seed timer deadlines + +### JavaScript Operator + +The operator is invoked from the Landmark pipeline and reads/writes the following tensors: + +Inputs + +- `positionArray` — landmark keypoints for extracting wrist positions +- `molesTransforms` — per‑duck world 4×4 transforms for rendering +- `moleCollisions` — per‑duck 4×4 transforms for collision (without stage pose) +- `realtimesincestartup` — seconds since start +- `moleOnTimings` — per‑duck “on” duration (seconds) +- `moleOffTimings` — per‑duck “off” duration (seconds) +- `moleVisibleArray` — Uint8[5], persisted visibility states +- `moleDeadlines` — Float32[5], persisted next toggle deadlines +- `score` — Int32 scalar, persisted total hits + +Outputs + +- `matrixArray` — bone matrices for the pose marker skeleton +- `moleVisibleArray` — updated per‑duck visibility +- `moleDeadlines` — updated deadlines after toggles and hits +- `score` — updated total + +Operator Logic (high level) + +- Initialize `moleVisibleArray` and `moleDeadlines` once, persist across frames +- On each frame: + - Timers: toggle ducks on/off when `realtimesincestartup >= deadline` + - Active ducks: compute distance to wrist joints; if within threshold, count a hit + - On hit: increment `score`, hide duck, and set reappear deadline using `moleOnTimings` + +### Timing and Persistence + +- C++ regenerates `moleOnTimings` in [2, 5]s and `moleOffTimings` in [1, 2]s per frame +- A 10‑second warm‑up delays initial appearance +- `moleVisibleArray`, `moleDeadlines`, and `score` are global tensors and survive across frames + +### Rendering + +- Ducks: five instances, each bound to a per‑duck pose and visibility tensor +- Red boxes: one instance per body landmark matrix for wrists and ankles to visualize the landmark outputs +- TV: a glTF that displays the current `score` using `debugRenderText` onto a texture created at init + +### Assets + +- `Duck.gltf` — mole visual +- `redbox.gltf` — landmark visual +- `pose_marker.gltf` — skeleton driven by `bodyLandmarkGlobal` +- `scoreboard_panel.gltf` — UI surface for score + +### How To Run + +1. Build the sample and deploy to your target device (PICO) +2. Start the app; wait ~10s for warm‑up (ducks remain hidden) +3. Move wrists to hit visible ducks; watch score update on the TV + +### Customization + +- Collision radius: adjust `hitThreshold` in `jointsTransformer.js` +- Duck patterns: change offsets in the init pipeline; or drive via a tensor +- Timer ranges: modify C++ generation of `moleOnTimings` and `moleOffTimings` +- Score UI: change font, position, color in `debugRenderText` + +### Notes + +- Collision uses `moleCollisions` transforms (no stage pose) to avoid scene‑space bias +- Rendering uses `molesTransforms` transforms (with stage pose) to place ducks in world space diff --git a/samples/whackamole/build.gradle.kts b/samples/whackamole/build.gradle.kts new file mode 100644 index 0000000..dda1a19 --- /dev/null +++ b/samples/whackamole/build.gradle.kts @@ -0,0 +1,61 @@ +plugins { + id("com.android.application") + id("org.jetbrains.kotlin.android") +} +android { + compileSdk = 32 + ndkVersion = "26.3.11579264" + namespace = "com.bytedance.pico.secure_mr_demo.whackamole" + defaultConfig { + minSdk = 32 + targetSdk = 32 + versionCode = 1 + versionName = "1.0" + applicationId = "com.bytedance.pico.secure_mr_demo.whackamole" + externalNativeBuild { + cmake { + arguments.add("-DANDROID_STL=c++_shared") + arguments.add("-DANDROID_USE_LEGACY_TOOLCHAIN_FILE=OFF") + } + ndk { + abiFilters.add("arm64-v8a") + } + } + } + lint { + disable.add("ExpiredTargetSdkVersion") + } + buildFeatures { + prefab = true + } + buildTypes { + getByName("debug") { + isDebuggable = true + isJniDebuggable = true + } + getByName("release") { + isDebuggable = false + isJniDebuggable = false + } + } + externalNativeBuild { + cmake { + version = "3.22.1" + path("CMakeLists.txt") + } + } + sourceSets { + getByName("main") { + manifest.srcFile("AndroidManifest.xml") + assets.srcDirs("../../assets/common", "../../assets/whackamole") + } + } + packaging { + jniLibs { + keepDebugSymbols.add("**.so") + } + } + kotlinOptions { + jvmTarget = "1.8" + } +} diff --git a/samples/whackamole/cpp/whackamole_detection.cpp b/samples/whackamole/cpp/whackamole_detection.cpp new file mode 100644 index 0000000..c13ff98 --- /dev/null +++ b/samples/whackamole/cpp/whackamole_detection.cpp @@ -0,0 +1,731 @@ +// Copyright (2025) Bytedance Ltd. and/or its affiliates +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "whackamole_detection.h" + +#include + +extern AAssetManager* g_assetManager; + +namespace SecureMR { + +static constexpr unsigned int NODE_COUNT = 13; + +static bool LoadModelData(const std::string& filePath, std::vector& modelData) { + if (g_assetManager == nullptr) { + Log::Write(Log::Level::Error, "LoadModelData: AssetManager is not set"); + return false; + } + + AAsset* asset = AAssetManager_open(g_assetManager, filePath.c_str(), AASSET_MODE_BUFFER); + if (asset == nullptr) { + Log::Write(Log::Level::Error, Fmt("LoadModelData: Error: Failed to open asset %s", filePath.c_str())); + return false; + } + + off_t assetLength = AAsset_getLength(asset); + modelData.resize(assetLength); + + AAsset_read(asset, modelData.data(), assetLength); + AAsset_close(asset); + + return true; +} + +WhackamoleDetector::WhackamoleDetector(const XrInstance& instance, const XrSession& session) + : xr_instance(instance), xr_session(session) {} + +WhackamoleDetector::~WhackamoleDetector() { + keepRunning = false; + if (pipelineInitializer && pipelineInitializer->joinable()) { + pipelineInitializer->join(); + } + for (auto& runner : pipelineRunners) { + if (runner.joinable()) runner.join(); + } +} + +void WhackamoleDetector::CreateFramework() { + Log::Write(Log::Level::Info, "CreateFramework ..."); + frameworkSession = std::make_shared(xr_instance, xr_session, 512, 512); + startTime = std::chrono::steady_clock::now(); + Log::Write(Log::Level::Info, "CreateFramework done."); +} + +void WhackamoleDetector::CreatePipelines() { + pipelineInitializer = std::make_unique([this]() { + // Note: global tensors must be created before they are referred + // in each individual pipeline + CreateGlobalTensor(); + CreateSecureMrVSTImagePipeline(); + CreateSecureMrModelInferencePipeline(); + CreateSecureMrRenderingPipeline(); + + initialized.notify_all(); + pipelineAllInitialized = true; + }); +} + +void WhackamoleDetector::UpdateHandPose(const XrVector3f* const leftHandDelta, const XrVector3f* const rightHandDelta) { + const XrVector3f* hand = leftHandDelta; + if (hand == nullptr) hand = rightHandDelta; + if (hand == nullptr) return; + + CreateAndRunSecureMrMovePipeline(hand->x, hand->y, hand->z); +} + +void WhackamoleDetector::CreateGlobalTensor() { + vstOutputLeftUint8Global = std::make_shared( + frameworkSession, + TensorAttribute{.dimensions = {512, 512}, .channels = 3, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_UINT8_PICO}); + resizedLeftFp32Global = std::make_shared( + frameworkSession, + TensorAttribute{.dimensions = {128, 128}, .channels = 3, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}); + + bodyLandmarkGlobal = std::make_shared( + frameworkSession, + TensorAttribute{.dimensions = {NODE_COUNT, 4, 4}, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}); + molesTransformsGlobal = std::make_shared( + frameworkSession, + TensorAttribute{.dimensions = {MAX_MOLES, 4, 4}, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}); + moleCollisionsGlobal = std::make_shared( + frameworkSession, + TensorAttribute{.dimensions = {MAX_MOLES, 4, 4}, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}); + molesVisibleGlobal = std::make_shared( + frameworkSession, TensorAttribute_ScalarArray{MAX_MOLES, XR_SECURE_MR_TENSOR_DATA_TYPE_INT8_PICO}); + moleDeadlinesGlobal = std::make_shared( + frameworkSession, TensorAttribute_ScalarArray{MAX_MOLES, XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}); + scoreGlobal = std::make_shared( + frameworkSession, TensorAttribute_ScalarArray{1, XR_SECURE_MR_TENSOR_DATA_TYPE_INT32_PICO}); + realtimeSinceStartupGlobal = std::make_shared( + frameworkSession, TensorAttribute_ScalarArray{1, XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}); + moleOnTimingsGlobal = std::make_shared( + frameworkSession, TensorAttribute_ScalarArray{MAX_MOLES, XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}); + moleOffTimingsGlobal = std::make_shared( + frameworkSession, TensorAttribute_ScalarArray{MAX_MOLES, XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}); + + isPoseDetectedGlobal = std::make_shared( + frameworkSession, TensorAttribute_ScalarArray{1, XR_SECURE_MR_TENSOR_DATA_TYPE_INT8_PICO}); + + roiAffineGlobal = std::make_shared( + frameworkSession, + TensorAttribute{.dimensions = {2, 3}, .channels = 1, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}); + roiAffineUpdatedGlobal = std::make_shared(*roiAffineGlobal); + + *roiAffineUpdatedGlobal = std::vector{0.5f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f}; + + roiImageGlobal = std::make_shared( + frameworkSession, + TensorAttribute{.dimensions = {256, 256}, .channels = 3, .usage = XR_SECURE_MR_TENSOR_TYPE_MAT_DYNAMIC_TEXTURE_PICO, + .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_DYNAMIC_TEXTURE_UINT8_PICO}); + blankTextureGlobal = std::make_shared( + frameworkSession, + TensorAttribute{.dimensions = {256, 256}, .channels = 4, .usage = XR_SECURE_MR_TENSOR_TYPE_MAT_DYNAMIC_TEXTURE_PICO, + .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_DYNAMIC_TEXTURE_UINT8_PICO}); + + { + std::vector initMoleTransforms(MAX_MOLES * 16, 0.0f); + for (int i = 0; i < MAX_MOLES; ++i) { + const int base = i * 16; + initMoleTransforms[base + 0] = 1.0f; + initMoleTransforms[base + 5] = 1.0f; + initMoleTransforms[base + 10] = 1.0f; + initMoleTransforms[base + 15] = 1.0f; + initMoleTransforms[base + 3] = -1.0f + static_cast(i) * 0.5f; + initMoleTransforms[base + 11] = -2.0f; + } + *molesTransformsGlobal = initMoleTransforms; + *moleCollisionsGlobal = initMoleTransforms; + std::vector initVisible(MAX_MOLES, static_cast(0)); + *molesVisibleGlobal = initVisible; + *scoreGlobal = std::vector{0}; + *realtimeSinceStartupGlobal = std::vector{0.0f}; + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_real_distribution distOn(2.0f, 5.0f); + std::uniform_real_distribution distOff(1.0f, 2.0f); + std::vector initOn(MAX_MOLES); + std::vector initOff(MAX_MOLES); + std::vector initDeadlines(MAX_MOLES); + for (int i = 0; i < MAX_MOLES; ++i) { + initOn[i] = distOn(gen); + initOff[i] = distOff(gen); + initDeadlines[i] = 10.0f + initOn[i]; + } + *moleOnTimingsGlobal = initOn; + *moleOffTimingsGlobal = initOff; + *moleDeadlinesGlobal = initDeadlines; + } + + + std::vector gltfData; + if (LoadModelData(GLTF_PATH, gltfData)) { + poseMarkerGltf = std::make_shared(frameworkSession, gltfData.data(), gltfData.size()); + const auto initPipeline = std::make_shared(frameworkSession); + const auto initPose = std::make_shared( + initPipeline, + TensorAttribute{.dimensions = {4, 4}, .channels = 1, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}, + reinterpret_cast(stagePoseData), sizeof(stagePoseData)); + const auto transitPlaceholder = PipelineTensor::PipelineGLTFPlaceholder(initPipeline); + initPipeline->execRenderCommand(std::make_shared(transitPlaceholder, initPose, true)); + initPipeline->submit({{transitPlaceholder, poseMarkerGltf}}, XR_NULL_HANDLE, nullptr); + + } else { + Log::Write(Log::Level::Error, "Failed to load glTF data from file."); + } + + std::vector scoreboardGltfData; + if (LoadModelData(GLTF_SCOREBOARD_PATH, scoreboardGltfData)) { + scoreboardGltf = std::make_shared(frameworkSession, scoreboardGltfData.data(), scoreboardGltfData.size()); + const auto initPipeline2 = std::make_shared(frameworkSession); + const auto scoreboardPose = std::make_shared( + initPipeline2, + TensorAttribute{.dimensions = {4, 4}, .channels = 1, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}); + float scoreboardPoseData[]{0.8f, 0.0f, 0.0f, -0.3f, 0.0f, 0.8f, 0.0f, 0.2f, 0.0f, 0.0f, 0.8f, -1.4f, 0.0f, 0.0f, 0.0f, 1.0f}; + scoreboardPose->setData(reinterpret_cast(scoreboardPoseData), sizeof(scoreboardPoseData)); + const auto scoreboardPlaceholder = PipelineTensor::PipelineGLTFPlaceholder(initPipeline2); + + const auto newTextureId = std::make_shared( + initPipeline2, + TensorAttribute{.dimensions = {1}, .channels = 1, .usage = XR_SECURE_MR_TENSOR_TYPE_SCALAR_PICO, + .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_UINT16_PICO}); + const auto blankTexturePh = PipelineTensor::PipelinePlaceholderLike(initPipeline2, blankTextureGlobal); + + initPipeline2->execRenderCommand(std::make_shared(scoreboardPlaceholder, scoreboardPose, true)); + initPipeline2->newTextureToGLTF(scoreboardPlaceholder, blankTexturePh, newTextureId); + + auto updateMaterialCmd = std::make_shared(); + updateMaterialCmd->gltfTensor = scoreboardPlaceholder; + updateMaterialCmd->materialIds = std::vector{0}; + updateMaterialCmd->attribute = RenderCommand_UpdateMaterial::MaterialAttribute::TEXTURE_BASE_COLOR; + updateMaterialCmd->materialValues = newTextureId; + (*initPipeline2).execRenderCommand(updateMaterialCmd); + + initPipeline2->submit({{scoreboardPlaceholder, scoreboardGltf}, {blankTexturePh, blankTextureGlobal}}, XR_NULL_HANDLE, nullptr); + } else { + Log::Write(Log::Level::Error, "Failed to load scoreboard glTF data from file."); + } + + std::vector duckGltfData; + if (LoadModelData(GLTF_DUCK_PATH, duckGltfData)) { + for (int i = 0; i < MAX_MOLES; ++i) { + duckGltfs[i] = std::make_shared(frameworkSession, duckGltfData.data(), duckGltfData.size()); + } + } else { + Log::Write(Log::Level::Error, "Failed to load duck glTF data from file."); + } + + std::vector redboxGltfData; + if (LoadModelData(GLTF_REDBOX_PATH, redboxGltfData)) { + for (int i = 0; i < MAX_JOINTS; ++i) { + redboxGltfs[i] = std::make_shared(frameworkSession, redboxGltfData.data(), redboxGltfData.size()); + } + } else { + Log::Write(Log::Level::Error, "Failed to load redbox glTF data from file."); + } + +} + +void WhackamoleDetector::RunPipelines() { + pipelineRunners.emplace_back([this]() { + { + std::unique_lock guard(initialized_mtx); + initialized.wait(guard); + } + while (keepRunning) { + RunSecureMrVSTImagePipeline(); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + }); + + pipelineRunners.emplace_back([this]() { + { + std::unique_lock guard(initialized_mtx); + initialized.wait(guard); + } + while (keepRunning) { + RunSecureMrModelInferencePipeline(); + std::this_thread::sleep_for(std::chrono::milliseconds(60)); + } + }); + + pipelineRunners.emplace_back([this]() { + { + std::unique_lock guard(initialized_mtx); + initialized.wait(guard); + } + while (keepRunning) { + RunSecureMrRenderingPipeline(); + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + } + }); +} + +void WhackamoleDetector::CreateSecureMrVSTImagePipeline() { + Log::Write(Log::Level::Info, "Secure MR CreateSecureMrVSTImagePipeline"); + + m_secureMrVSTImagePipeline = std::make_shared(frameworkSession); + + vstOutputLeftUint8Placeholder = + PipelineTensor::PipelinePlaceholderLike(m_secureMrVSTImagePipeline, vstOutputLeftUint8Global); + vstOutputLeftFp32Placeholder = + PipelineTensor::PipelinePlaceholderLike(m_secureMrVSTImagePipeline, resizedLeftFp32Global); + + static float RESHAPE_MAT_512_TO_128[]{0.25f, 0.0f, 0.0f, 0.0f, 0.25f, 0.0f}; + const auto affineMatReshape = std::make_shared( + m_secureMrVSTImagePipeline, + TensorAttribute{.dimensions = {2, 3}, .channels = 1, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}, + reinterpret_cast(RESHAPE_MAT_512_TO_128), sizeof(RESHAPE_MAT_512_TO_128)); + + const auto resizedLeftUint8 = std::make_shared( + m_secureMrVSTImagePipeline, + TensorAttribute{.dimensions = {128, 128}, .channels = 3, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_UINT8_PICO}); + + (*m_secureMrVSTImagePipeline) + .cameraAccess(nullptr, vstOutputLeftUint8Placeholder, nullptr, nullptr) + .applyAffine(affineMatReshape, vstOutputLeftUint8Placeholder, resizedLeftUint8) + .assignment(resizedLeftUint8, vstOutputLeftFp32Placeholder) + .arithmetic("({0} / 255.0)", {vstOutputLeftFp32Placeholder}, vstOutputLeftFp32Placeholder); +} + +void WhackamoleDetector::CreateSecureMrModelInferencePipeline() { + Log::Write(Log::Level::Info, "Secure MR: CreateSecureMrModelInferencePipeline"); + + m_secureMrDetectionPipeline = std::make_shared(frameworkSession); + m_secureMrLandmarkPipeline = std::make_shared(frameworkSession); + m_secureMrAffineUpdatePipeline = std::make_shared(frameworkSession); + + // Step 1: pipeline placeholders for global tensors + smallF32ImagePlaceholder = + PipelineTensor::PipelinePlaceholderLike(m_secureMrDetectionPipeline, resizedLeftFp32Global); + largeU8ImagePlaceholder = + PipelineTensor::PipelinePlaceholderLike(m_secureMrLandmarkPipeline, vstOutputLeftUint8Global); + isPoseDetectedPlaceholder = + PipelineTensor::PipelinePlaceholderLike(m_secureMrDetectionPipeline, isPoseDetectedGlobal); + bodyLandmarkPlaceholder = PipelineTensor::PipelinePlaceholderLike(m_secureMrLandmarkPipeline, bodyLandmarkGlobal); + moleCollisionsPlaceholder = PipelineTensor::PipelinePlaceholderLike(m_secureMrLandmarkPipeline, moleCollisionsGlobal); + molesVisiblePlaceholder = PipelineTensor::PipelinePlaceholderLike(m_secureMrLandmarkPipeline, molesVisibleGlobal); + moleDeadlinesPlaceholder = PipelineTensor::PipelinePlaceholderLike(m_secureMrLandmarkPipeline, moleDeadlinesGlobal); + scorePlaceholder = PipelineTensor::PipelinePlaceholderLike(m_secureMrLandmarkPipeline, scoreGlobal); + realtimeSinceStartupPlaceholder = + PipelineTensor::PipelinePlaceholderLike(m_secureMrLandmarkPipeline, realtimeSinceStartupGlobal); + moleOnTimingsPlaceholder = + PipelineTensor::PipelinePlaceholderLike(m_secureMrLandmarkPipeline, moleOnTimingsGlobal); + moleOffTimingsPlaceholder = + PipelineTensor::PipelinePlaceholderLike(m_secureMrLandmarkPipeline, moleOffTimingsGlobal); + + roiAffinePh1 = PipelineTensor::PipelinePlaceholderLike(m_secureMrDetectionPipeline, roiAffineGlobal); + roiAffinePh2 = PipelineTensor::PipelinePlaceholderLike(m_secureMrAffineUpdatePipeline, roiAffineGlobal); + roiAffinePh3 = PipelineTensor::PipelinePlaceholderLike(m_secureMrAffineUpdatePipeline, roiAffineUpdatedGlobal); + roiAffinePh4 = PipelineTensor::PipelinePlaceholderLike(m_secureMrLandmarkPipeline, roiAffineUpdatedGlobal); + + roiImagePh = PipelineTensor::PipelinePlaceholderLike(m_secureMrLandmarkPipeline, roiImageGlobal); + + // Step 2: local tensors + auto poseAnchor = std::make_shared( + m_secureMrDetectionPipeline, + TensorAttribute{.dimensions = {896, 12}, .channels = 1, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}); + auto poseScores = std::make_shared( + m_secureMrDetectionPipeline, + TensorAttribute_ScalarArray{.size = 896, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}); + auto bestPoseScore = std::make_shared( + m_secureMrDetectionPipeline, + TensorAttribute_ScalarArray{.size = 1, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}); + auto anchorMatTensor = std::make_shared( + m_secureMrDetectionPipeline, + TensorAttribute{.dimensions = {896, 4}, .channels = 1, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}); + /** + * poseAnchors[:, 4:8] -> center X, center Y, head X, head Y to compute the radius of the ROI, using the + * Vitruvian man model + */ + auto poseKeypointAll = std::make_shared( + m_secureMrDetectionPipeline, + TensorAttribute{.dimensions = {896, 4}, .channels = 1, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}); + auto bestPoseIndex = std::make_shared( + m_secureMrDetectionPipeline, + TensorAttribute{.dimensions = {1, 1}, .channels = 1, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_INT32_PICO}); + auto bestPoseIndexPlusOne = std::make_shared( + m_secureMrDetectionPipeline, + TensorAttribute{.dimensions = {1, 1}, .channels = 1, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_INT32_PICO}); + auto bestPoseSrcSlice2 = + std::make_shared(m_secureMrDetectionPipeline, TensorAttribute_SliceArray{.size = 2}); + auto bestPoseSrcSlice1 = + std::make_shared(m_secureMrDetectionPipeline, TensorAttribute_SliceArray{.size = 1}); + const auto bestKeypointFloat = std::make_shared( + m_secureMrDetectionPipeline, TensorAttribute_Point2Array{2, XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}); + const auto bestHipKeypoint = std::make_shared( + m_secureMrDetectionPipeline, + TensorAttribute{.dimensions = {1, 2}, .channels = 1, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}); + const auto bestHeadKeypoint = std::make_shared(*bestHipKeypoint); + const auto bestKeypointVec = std::make_shared(*bestHipKeypoint); + const auto bestKeypointVecPerp = std::make_shared(*bestHipKeypoint); + const auto bestKeypointVecMultiplier = std::make_shared(*bestHipKeypoint); + const auto bestLeftKeypoint = std::make_shared(*bestHipKeypoint); + const auto roiPoints = std::make_shared( + m_secureMrDetectionPipeline, + TensorAttribute_Point2Array{.size = 3, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}); + const auto affineMatReshape = std::make_shared( + m_secureMrDetectionPipeline, + TensorAttribute{.dimensions = {2, 3}, .channels = 1, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}); + + const auto roiImage = std::make_shared( + m_secureMrLandmarkPipeline, + TensorAttribute{.dimensions = {256, 256}, .channels = 3, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_UINT8_PICO}); + const auto roiImageFp32 = std::make_shared( + m_secureMrLandmarkPipeline, + TensorAttribute{.dimensions = {256, 256}, .channels = 3, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}); + const auto skeletonLandmarks = std::make_shared( + m_secureMrLandmarkPipeline, + TensorAttribute{.dimensions = {39, 3}, .channels = 1, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}); + const auto landmarkTVec = std::make_shared( + m_secureMrLandmarkPipeline, + TensorAttribute{.dimensions = {1, 3}, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}); + std::array, NODE_COUNT> landmarkTVecLast{}; + for (auto& each : landmarkTVecLast) { + static float DEFAULT_XYZ[]{0.0f, 0.0f, 0.0f}; + each = std::make_shared( + m_secureMrLandmarkPipeline, + TensorAttribute{.dimensions = {1, 3}, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}, + reinterpret_cast(DEFAULT_XYZ), sizeof(DEFAULT_XYZ)); + } + const auto landmarkMat = std::make_shared( + m_secureMrLandmarkPipeline, + TensorAttribute{.dimensions = {4, 4}, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}); + static float DEFAULT_R_VEC[]{0.0, 0.0, 0.0}; + const auto rvecTensor = std::make_shared( + m_secureMrLandmarkPipeline, + TensorAttribute{.dimensions = {1, 3}, .channels = 1, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}, + reinterpret_cast(DEFAULT_R_VEC), sizeof(DEFAULT_R_VEC)); + static float DEFAULT_XYZ_MULTIPLIER[]{1.0, -1.0, -1.0}; + const auto yReverse = std::make_shared( + m_secureMrLandmarkPipeline, + TensorAttribute{.dimensions = {1, 3}, .channels = 1, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}, + reinterpret_cast(DEFAULT_XYZ_MULTIPLIER), sizeof(DEFAULT_XYZ_MULTIPLIER)); + + // Step 2(+): Set init data to local tensors + static float RESHAPE_MAT_128_TO_512[]{4.0f, 0.0f, 0.0f, 0.0f, 4.0f, 0.0f}; + *bestPoseSrcSlice1 = std::vector{0, -1}; // init data for bestPoseSrcSlice: [0:-1] + *bestPoseSrcSlice2 = std::vector{0, -1, 0, -1}; // init data for bestPoseSrcSlice: [0:-1, 0:-1] + affineMatReshape->setData(reinterpret_cast(RESHAPE_MAT_128_TO_512), sizeof(RESHAPE_MAT_128_TO_512)); + *bestKeypointVecMultiplier = std::vector{1.0f, -1.0f}; + if (std::vector anchorData; LoadModelData(ANCHOR_MAT, anchorData)) { + anchorMatTensor->setData(reinterpret_cast(anchorData.data()), anchorData.size()); + } else { + Log::Write(Log::Level::Error, "Failed to load anchor.mat data from file."); + } + + // Step 3: Assembly! + if (std::vector modelData1, modelData2; + LoadModelData(WHACKAMOLE_DETECTION_MODEL_PATH, modelData1) && LoadModelData(WHACKAMOLE_LANDMARK_MODEL_PATH, modelData2)) { + (*m_secureMrDetectionPipeline) + .runAlgorithm(modelData1.data(), modelData1.size(), {{"image", smallF32ImagePlaceholder}}, {}, + {{"pose_anchor", poseAnchor}, {"score", poseScores}}, + {{"pose_anchor", "box_coords"}, {"score", "box_scores"}}, "pose") + .assignment((*poseAnchor)[{{0, -1}, {4, 8}}], poseKeypointAll) + .arithmetic("({0} / 128.0 + {1}) * 128.0", {poseKeypointAll, anchorMatTensor}, poseKeypointAll) + .argMax(poseScores, bestPoseIndex) + .arithmetic("({0} + 1)", {bestPoseIndex}, bestPoseIndexPlusOne) + .assignment(bestPoseIndex, (*bestPoseSrcSlice2)[0][0]) + .assignment(bestPoseIndexPlusOne, (*bestPoseSrcSlice2)[0][1]) + .assignment((*bestPoseSrcSlice2)[0], bestPoseSrcSlice1) + .assignment((*poseKeypointAll)[bestPoseSrcSlice2], bestKeypointFloat) + .applyAffinePoint(affineMatReshape, bestKeypointFloat, bestKeypointFloat) + .assignment((*poseScores)[bestPoseSrcSlice1], bestPoseScore) + .compareTo(*bestPoseScore > std::vector{0.0}, isPoseDetectedPlaceholder) + .assignment((*bestKeypointFloat)[0], bestHipKeypoint) + .assignment((*bestKeypointFloat)[1], bestHeadKeypoint) + .arithmetic("{0} - {1}", {bestHeadKeypoint, bestHipKeypoint}, bestKeypointVec) + .elementwise(Pipeline::ElementwiseOp::MULTIPLY, {bestKeypointVec, bestKeypointVecMultiplier}, bestKeypointVec) + .assignment((*bestKeypointVec)[std::vector{0, 0}], (*bestKeypointVecPerp)[std::vector{0, 1}]) + .assignment((*bestKeypointVec)[std::vector{0, 1}], (*bestKeypointVecPerp)[std::vector{0, 0}]) + .arithmetic("{0} + {1}", {bestHipKeypoint, bestKeypointVecPerp}, bestLeftKeypoint) + .assignment(bestHipKeypoint, (*roiPoints)[0]) + .assignment(bestHeadKeypoint, (*roiPoints)[1]) + .assignment(bestLeftKeypoint, (*roiPoints)[2]) + .getAffine(roiPoints, std::array{128.0f, 128.0f, 128.0f, 0.0f, 255.0f, 128.0f}, roiAffinePh1); + + m_secureMrAffineUpdatePipeline->assignment(roiAffinePh2, roiAffinePh3); + + (*m_secureMrLandmarkPipeline) + .applyAffine(roiAffinePh4, largeU8ImagePlaceholder, roiImagePh) + .assignment(roiImagePh, roiImageFp32) + .arithmetic("({0} - 127.5)/ 127.5", {roiImageFp32}, roiImageFp32) + .runAlgorithm(modelData2.data(), modelData2.size(), {{"input_1", roiImageFp32}}, {}, + {{"landmarks", skeletonLandmarks}}, {{"landmarks", "Identity_4"}}, "pose_landmark"); + + + + // Add javascript operator + const auto stagePoseTensor = std::make_shared( + m_secureMrLandmarkPipeline, + TensorAttribute{.dimensions = {4, 4}, .channels = 1, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}, + reinterpret_cast(stagePoseData), sizeof(stagePoseData)); + + + if (std::vector javascriptCode; + LoadModelData(JOINT_TRANSFORMER_JSCODE_PATH, javascriptCode)) + { + m_secureMrLandmarkPipeline->runJavascript(javascriptCode.data(), javascriptCode.size(), + {{"positionArray", skeletonLandmarks}, + {"moleCollisions", moleCollisionsPlaceholder}, + {"realtimesincestartup", realtimeSinceStartupPlaceholder}, + {"moleOnTimings", moleOnTimingsPlaceholder}, + {"moleOffTimings", moleOffTimingsPlaceholder}, + {"moleVisibleArray", molesVisiblePlaceholder}, + {"moleDeadlines", moleDeadlinesPlaceholder}, + {"score", scorePlaceholder}, + {"stagePose", stagePoseTensor}}, + {{"matrixArray", bodyLandmarkPlaceholder}, + {"moleVisibleArray", molesVisiblePlaceholder}, + {"moleDeadlines", moleDeadlinesPlaceholder}, + {"score", scorePlaceholder}} + ); + + } + + + } else { + Log::Write(Log::Level::Error, "Failed to load model data from file."); + } +} + +void WhackamoleDetector::CreateSecureMrRenderingPipeline() { + m_secureMrRenderingPipeline = std::make_shared(frameworkSession); + + // Step 1: placeholders + isPoseDetectedPlaceholder2 = + PipelineTensor::PipelinePlaceholderLike(m_secureMrRenderingPipeline, isPoseDetectedGlobal); + gltfPlaceholderTensor = PipelineTensor::PipelinePlaceholderLike(m_secureMrRenderingPipeline, poseMarkerGltf); + bodyLandmarkPlaceholder2 = PipelineTensor::PipelinePlaceholderLike(m_secureMrRenderingPipeline, bodyLandmarkGlobal); + gltfScoreboardPlaceholder = PipelineTensor::PipelinePlaceholderLike(m_secureMrRenderingPipeline, scoreboardGltf); + molesTransformsPlaceholder2 = PipelineTensor::PipelinePlaceholderLike(m_secureMrRenderingPipeline, molesTransformsGlobal); + molesVisiblePlaceholder2 = PipelineTensor::PipelinePlaceholderLike(m_secureMrRenderingPipeline, molesVisibleGlobal); + currentScorePh = PipelineTensor::PipelinePlaceholderLike(m_secureMrRenderingPipeline, scoreGlobal); + + // Step 2 : Assembly! + m_secureMrRenderingPipeline->execRenderCommand(std::make_shared( + gltfPlaceholderTensor, std::vector{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, + bodyLandmarkPlaceholder2)); + + for (int i = 0; i < MAX_MOLES; ++i) { + moleGltfPlaceholders[i] = PipelineTensor::PipelineGLTFPlaceholder(m_secureMrRenderingPipeline); + molePoseTensors[i] = std::make_shared( + m_secureMrRenderingPipeline, + TensorAttribute{.dimensions = {4, 4}, .channels = 1, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}); + moleVisibleTensors[i] = std::make_shared( + m_secureMrRenderingPipeline, + TensorAttribute_ScalarArray{.size = 1, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_INT8_PICO}); + (*m_secureMrRenderingPipeline) + .assignment((*molesVisiblePlaceholder2)[std::vector{static_cast(i)}], moleVisibleTensors[i]); + (*m_secureMrRenderingPipeline) + .assignment((*molesTransformsPlaceholder2)[{{i, i + 1}, {0, 4}, {0, 4}}], molePoseTensors[i]); + + auto renderCmd = std::make_shared(moleGltfPlaceholders[i], molePoseTensors[i], true); + renderCmd->visible = moleVisibleTensors[i]; + m_secureMrRenderingPipeline->execRenderCommand(renderCmd); + } + + + const auto stagePoseTensor = std::make_shared( + m_secureMrRenderingPipeline, + TensorAttribute{.dimensions = {4, 4}, .channels = 1, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}, + reinterpret_cast(stagePoseData), sizeof(stagePoseData)); + + static int whackerindices[4]{2,3,8,9}; + + for (int i = 0; i < MAX_JOINTS; ++i) { + redboxGltfPlaceholders[i] = PipelineTensor::PipelineGLTFPlaceholder(m_secureMrRenderingPipeline); + redboxPoseTensors[i] = std::make_shared( + m_secureMrRenderingPipeline, + TensorAttribute{.dimensions = {4, 4}, .channels = 1, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}); + + const int w = whackerindices[i]; + (*m_secureMrRenderingPipeline) + .assignment((*bodyLandmarkPlaceholder2)[{{w, w + 1}, {0, 4}, {0, 4}}], redboxPoseTensors[i]); + (*m_secureMrRenderingPipeline) + .arithmetic("{0} * {1}",{stagePoseTensor, redboxPoseTensors[i]}, redboxPoseTensors[i]); + + + + auto renderCmd = std::make_shared(redboxGltfPlaceholders[i], redboxPoseTensors[i], true); + m_secureMrRenderingPipeline->execRenderCommand(renderCmd); + } + + + (*m_secureMrRenderingPipeline) + .debugRenderText(gltfScoreboardPlaceholder, currentScorePh, 256, 256, std::tuple{0.5F, 0.5F}, 64.0F, + std::array, 2>{{{255, 255, 255, 255}, {0, 0, 0, 255}}}, + static_cast(1)); +} + +XrSecureMrPipelineRunPICO WhackamoleDetector::RunSecureMrVSTImagePipeline(const XrSecureMrPipelineRunPICO pre) { + return m_secureMrVSTImagePipeline->submit({{vstOutputLeftUint8Placeholder, vstOutputLeftUint8Global}, + {vstOutputLeftFp32Placeholder, resizedLeftFp32Global}}, + pre, nullptr); +} + +XrSecureMrPipelineRunPICO WhackamoleDetector::RunSecureMrModelInferencePipeline(const XrSecureMrPipelineRunPICO pre) { + const auto detection = m_secureMrDetectionPipeline->submit({{smallF32ImagePlaceholder, resizedLeftFp32Global}, + {isPoseDetectedPlaceholder, isPoseDetectedGlobal}, + {roiAffinePh1, roiAffineGlobal}}, + pre, nullptr); + const auto affine = m_secureMrAffineUpdatePipeline->submit( + {{roiAffinePh2, roiAffineGlobal}, {roiAffinePh3, roiAffineUpdatedGlobal}}, detection, isPoseDetectedGlobal); + const auto now = std::chrono::steady_clock::now(); + const float elapsed = std::chrono::duration(now - startTime).count(); + *realtimeSinceStartupGlobal = std::vector{elapsed}; + if (elapsed >= 10.0f && !molesInitApplied) { + CreateAndRunInitMolesPipeline(); + molesInitApplied = true; + } + { + std::vector onTimings(MAX_MOLES); + std::vector offTimings(MAX_MOLES); + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_real_distribution distOn(2.0f, 5.0f); + std::uniform_real_distribution distOff(300.0f, 312.0f); + for (int i = 0; i < MAX_MOLES; ++i) { + onTimings[i] = distOn(gen); + offTimings[i] = distOff(gen); + } + *moleOnTimingsGlobal = onTimings; + *moleOffTimingsGlobal = offTimings; + } + return m_secureMrLandmarkPipeline->submit({{largeU8ImagePlaceholder, vstOutputLeftUint8Global}, + {bodyLandmarkPlaceholder, bodyLandmarkGlobal}, + {moleCollisionsPlaceholder, moleCollisionsGlobal}, + {molesVisiblePlaceholder, molesVisibleGlobal}, + {moleDeadlinesPlaceholder, moleDeadlinesGlobal}, + {realtimeSinceStartupPlaceholder, realtimeSinceStartupGlobal}, + {moleOnTimingsPlaceholder, moleOnTimingsGlobal}, + {moleOffTimingsPlaceholder, moleOffTimingsGlobal}, + {scorePlaceholder, scoreGlobal}, + {roiAffinePh4, roiAffineUpdatedGlobal}, + {roiImagePh, roiImageGlobal}}, + affine, nullptr); +} + +void WhackamoleDetector::CreateAndRunInitMolesPipeline() { + if (m_secureMrInitMolesPipeline == nullptr) { + m_secureMrInitMolesPipeline = std::make_shared(frameworkSession); + bodyLandmarkInitPlaceholder = PipelineTensor::PipelinePlaceholderLike(m_secureMrInitMolesPipeline, bodyLandmarkGlobal); + molesTransformsInitPlaceholder = PipelineTensor::PipelinePlaceholderLike(m_secureMrInitMolesPipeline, molesTransformsGlobal); + molesCollisionsInitPlaceholder = PipelineTensor::PipelinePlaceholderLike(m_secureMrInitMolesPipeline, moleCollisionsGlobal); + } + + const auto mat11 = std::make_shared( + m_secureMrInitMolesPipeline, + TensorAttribute{.dimensions = {4, 4}, .channels = 1, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}); + const auto mat12 = std::make_shared(*mat11); + const auto centerAvgMat = std::make_shared(*mat11); + const auto worldMat = std::make_shared(*mat11); + const auto stagePoseInit = std::make_shared( + m_secureMrInitMolesPipeline, + TensorAttribute{.dimensions = {4, 4}, .channels = 1, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}, + reinterpret_cast(stagePoseData), sizeof(stagePoseData)); + + + + (*m_secureMrInitMolesPipeline) + .assignment(stagePoseInit, worldMat); + + for (int i = 0; i < MAX_MOLES; ++i) { + const auto moleMat = std::make_shared( + m_secureMrInitMolesPipeline, + TensorAttribute{.dimensions = {4, 4}, .channels = 1, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}); + + float ox = 0.0f, oy = 0.0f, oz = 0.0f; + switch (i) { + case 0: ox = -0.4f; oy = 0.5f; oz = 0.0f; break; + case 1: ox = 0.4f; oy = 0.5f; oz = 0.0f; break; + case 2: ox = -0.4f; oy = 0.0f; oz = 0.0f; break; + case 3: ox = 0.4f; oy = 0.0f; oz = 0.0f; break; + case 4: ox = 0.0f; oy = 0.9f; oz = 0.0f; break; + default: ox = 0.0f; oy = 0.0f; oz = 0.0f; break; + } + float offsetData[16]{ + 1.0f, 0.0f, 0.0f, ox, + 0.0f, 1.0f, 0.0f, oy, + 0.0f, 0.0f, 1.0f, oz, + 0.0f, 0.0f, 0.0f, 1.0f}; + const auto offsetMat = std::make_shared( + m_secureMrInitMolesPipeline, + TensorAttribute{.dimensions = {4, 4}, .channels = 1, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}, + reinterpret_cast(offsetData), sizeof(offsetData)); + const auto collisionMat = std::make_shared( + m_secureMrInitMolesPipeline, + TensorAttribute{.dimensions = {4, 4}, .channels = 1, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}); + + (*m_secureMrInitMolesPipeline) + .arithmetic("{0} * {1}", {offsetMat, worldMat}, moleMat) + .assignment(offsetMat, collisionMat) + .assignment(moleMat, (*molesTransformsInitPlaceholder)[{{i, i + 1}, {0, 4}, {0, 4}}]) + .assignment(collisionMat, (*molesCollisionsInitPlaceholder)[{{i, i + 1}, {0, 4}, {0, 4}}]); + } + + m_secureMrInitMolesPipeline->submit({{bodyLandmarkInitPlaceholder, bodyLandmarkGlobal}, + {molesTransformsInitPlaceholder, molesTransformsGlobal}, + {molesCollisionsInitPlaceholder, moleCollisionsGlobal}}, + XR_NULL_HANDLE, nullptr); +} + +XrSecureMrPipelineRunPICO WhackamoleDetector::RunSecureMrRenderingPipeline(const XrSecureMrPipelineRunPICO pre) { + return m_secureMrRenderingPipeline->submit({{gltfPlaceholderTensor, poseMarkerGltf}, + {isPoseDetectedPlaceholder2, isPoseDetectedGlobal}, + {bodyLandmarkPlaceholder2, bodyLandmarkGlobal}, + {gltfScoreboardPlaceholder, scoreboardGltf}, + {molesTransformsPlaceholder2, molesTransformsGlobal}, + {molesVisiblePlaceholder2, molesVisibleGlobal}, + {moleGltfPlaceholders[0], duckGltfs[0]}, + {moleGltfPlaceholders[1], duckGltfs[1]}, + {moleGltfPlaceholders[2], duckGltfs[2]}, + {moleGltfPlaceholders[3], duckGltfs[3]}, + {moleGltfPlaceholders[4], duckGltfs[4]}, + {redboxGltfPlaceholders[0], redboxGltfs[0]}, + {redboxGltfPlaceholders[1], redboxGltfs[1]}, + {redboxGltfPlaceholders[2], redboxGltfs[2]}, + {redboxGltfPlaceholders[3], redboxGltfs[3]}, + {currentScorePh, scoreGlobal}}, + pre, nullptr); +} + +void WhackamoleDetector::CreateAndRunSecureMrMovePipeline(const float x, const float y, const float z) { + std::ostringstream oss; + oss << "updated hand-pose delta {" << x << ',' << y << ',' << z << '}'; + Log::Write(Log::Level::Info, oss.str()); + if (!pipelineAllInitialized) return; + if (m_secureMrMovePipeline == nullptr) { + m_secureMrMovePipeline = std::make_shared(frameworkSession); + gltfPlaceholderTensor2 = PipelineTensor::PipelineGLTFPlaceholder(m_secureMrMovePipeline); + + stagePose = std::make_shared( + m_secureMrMovePipeline, + TensorAttribute{.dimensions = {4, 4}, .channels = 1, .dataType = XR_SECURE_MR_TENSOR_DATA_TYPE_FLOAT32_PICO}); + + m_secureMrMovePipeline->execRenderCommand( + std::make_shared(gltfPlaceholderTensor2, stagePose)); + } + + stagePoseData[3] += x; + stagePoseData[7] += y; + stagePoseData[11] += z; + stagePose->setData(reinterpret_cast(stagePoseData), sizeof(stagePoseData)); + m_secureMrMovePipeline->submit({{gltfPlaceholderTensor2, poseMarkerGltf}}, nullptr, nullptr); +} + +std::shared_ptr CreateSecureMrProgram(const XrInstance& instance, const XrSession& session) { + return std::make_shared(instance, session); +} +} // namespace SecureMR diff --git a/samples/whackamole/cpp/whackamole_detection.h b/samples/whackamole/cpp/whackamole_detection.h new file mode 100644 index 0000000..141f5b9 --- /dev/null +++ b/samples/whackamole/cpp/whackamole_detection.h @@ -0,0 +1,309 @@ +// Copyright (2026) Bytedance Ltd. and/or its affiliates +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once +#include "pch.h" +#include +#include +#include +#include "logger.h" +#include "common.h" +#include "securemr_base.h" +#include "securemr_utils/adapter.hpp" +#include "securemr_utils/pipeline.h" +#include "securemr_utils/tensor.h" +#include "securemr_utils/rendercommand.h" +#include "securemr_utils/session.h" +#include + +#define WHACKAMOLE_DETECTION_MODEL_PATH "detection.serialized.bin" +#define WHACKAMOLE_LANDMARK_MODEL_PATH "landmark.serialized.bin" +#define JOINT_TRANSFORMER_JSCODE_PATH "jointsTransformer.js" +#define GLTF_PATH "pose_marker.gltf" +#define GLTF_SCOREBOARD_PATH "scoreboard_panel.gltf" +#define GLTF_DUCK_PATH "Duck.gltf" +#define GLTF_REDBOX_PATH "redbox.gltf" +#define ANCHOR_MAT "anchors_1.mat" +#define MAX_MOLES 5 +#define MAX_JOINTS 4 + +namespace SecureMR { + +class WhackamoleDetector : public ISecureMR { + public: + WhackamoleDetector(const XrInstance& instance, const XrSession& session); + + ~WhackamoleDetector() override; + + void CreateFramework() override; + + void CreatePipelines() override; + + void RunPipelines() override; + + [[nodiscard]] bool LoadingFinished() const override { return pipelineAllInitialized; } + + void UpdateHandPose(const XrVector3f* leftHandDelta, const XrVector3f* rightHandDelta) override; + + protected: + /** + * Create all the global tensors, must be called before create any pipelines + */ + void CreateGlobalTensor(); + + /** + * Create the pipeline for retrieving RGB image + */ + void CreateSecureMrVSTImagePipeline(); + + /** + * Create the pipelines where the core logic of pose detection is running. There + * are 3 bundled pipelines, and they shall be run in sequence: + * + *
    + *
  1. m_secureMrDetectionPipeline, the pipeline where the detection + * algorithm is running, it intakes RGB image, determine the region which it believes + * contains a human body, and outputs a confidence score and an affine matrix from the + * RGB image to the region, in roiAffineGlobal
  2. + *
  3. m_secureMrAffineUpdatePipeline, the pipeline to update the affine + * matrix in roiAffineUpdatedGlobal using roiAffineGlobal + * only if the detection score is high; otherwise, the old values in + * roiAffineUpdatedGlobal is retained.
  4. + *
  5. m_secureMrLandmarkPipeline, the pipeline where the pose-landmark + * model is running. It applies the affine matrix to get the image patch most likely + * containing the human body, runs the pose-landmark models and compute each-bone + * transforms for skeleton animation
  6. + *
+ */ + void CreateSecureMrModelInferencePipeline(); + + /** + * Create the pipeline to update the world transform and each bone's local transform to drive the skeleton animation + */ + void CreateSecureMrRenderingPipeline(); + + /** + * Submit the pipeline for retrieving RGB images for execution + */ + XrSecureMrPipelineRunPICO RunSecureMrVSTImagePipeline(XrSecureMrPipelineRunPICO pre = XR_NULL_HANDLE); + + /** + * Submit the pipeline for pose detection logic + */ + XrSecureMrPipelineRunPICO RunSecureMrModelInferencePipeline(XrSecureMrPipelineRunPICO pre = XR_NULL_HANDLE); + + /** + * Submit the pipeline for updating renderer + */ + XrSecureMrPipelineRunPICO RunSecureMrRenderingPipeline(XrSecureMrPipelineRunPICO pre = XR_NULL_HANDLE); + + void CreateAndRunSecureMrMovePipeline(float x, float y, float z); + void CreateAndRunInitMolesPipeline(); + + XrInstance xr_instance; + XrSession xr_session; + + private: + /** + * Root framework + */ + std::shared_ptr frameworkSession; + + // Global tensors + // Recall that global tensors are used to share data + // between pipelines, and can also server as pipeline + // execution condition + + /** + * Caching the latest left-eye image --- shared between + * the VST, the inference and the 2D-to-3D pipelines + *
+ * In R8G8B8 format + */ + std::shared_ptr vstOutputLeftUint8Global; + /** + * Resized left-eye image, but converted to floating point, i.e., + * R32G32B32 format + */ + std::shared_ptr resizedLeftFp32Global; + + /** + * A flag to determine whether the pose detection model has + * made a confident detection, used to determined whether + * the render pipeline should be invoked based on the + * outcome from the inference pipeline. + */ + std::shared_ptr isPoseDetectedGlobal; + + /** + * The affine transform from the raw camera image (512, 512) + * to the ROI (region of interest) where the human body + * is detected. + */ + std::shared_ptr roiAffineGlobal; + + /** + * The affine transform can be unstable some time. In order to avoid + * jittering, we discard the affine transform and reuse the previous + * affine if the confidence for the new affine transform is low. + *
+ * This global tensor stores the affine transform from input camera + * image to ROI after the rectification described above. + **/ + std::shared_ptr roiAffineUpdatedGlobal; + + + std::shared_ptr roiImageGlobal; + + /** + * The global tensor stores skeleton animation's parameters + * for current frame. It is of dimensions: (11, 4, 4), i.e., + * 11 4x4 transform matrices each corresponding to one of + * the 11 bones in the glTF. + **/ + std::shared_ptr bodyLandmarkGlobal; + std::shared_ptr bodyLandmarkGlobal2; + std::shared_ptr molesTransformsGlobal; + std::shared_ptr moleCollisionsGlobal; + std::shared_ptr molesVisibleGlobal; + std::shared_ptr moleDeadlinesGlobal; + std::shared_ptr scoreGlobal; + std::shared_ptr realtimeSinceStartupGlobal; + std::shared_ptr moleOnTimingsGlobal; + std::shared_ptr moleOffTimingsGlobal; + /** + * The glTF to be driven by the skeleton animation. + **/ + std::shared_ptr poseMarkerGltf; + std::shared_ptr scoreboardGltf; + std::array, MAX_MOLES> duckGltfs; + std::array, MAX_JOINTS> redboxGltfs; + std::shared_ptr blankTextureGlobal; + + // Pipelines, as computation graphs able to be individually + // scheduled, consisting of operators as nodes and local tensors + // as edges. + + /** + * The VST pipeline, for camera access + */ + std::shared_ptr m_secureMrVSTImagePipeline; + /** + * The pipeline where the detection algorithm is running. It intakes RGB image, + * determine the region which it believes contains a human body, and outputs a + * confidence score and an affine matrix from the RGB image to the region, in + * roiAffineGlobal + */ + std::shared_ptr m_secureMrDetectionPipeline; + /** + * The pipeline to update the affine matrix in roiAffineUpdatedGlobal + * using roiAffineGlobal only if the detection score is high; + * otherwise, the old values in roiAffineUpdatedGlobal is retained. + */ + std::shared_ptr m_secureMrAffineUpdatePipeline; + /** + * The pipeline where the pose-landmark model is running. It applies the + * affine matrix to get the image patch most likely containing the human body, + * runs the pose-landmark models and compute each-bone transforms for skeleton + * animation + */ + std::shared_ptr m_secureMrLandmarkPipeline; + + /** + * Render pipeline, where the animation is updated timely + */ + std::shared_ptr m_secureMrRenderingPipeline; + std::shared_ptr m_secureMrMovePipeline; + std::shared_ptr m_secureMrInitMolesPipeline; + + // Placeholders for each pipeline + // Recall placeholders are pipeline's local references to + // global tensors, to avoid memory copy and competition + // on the shared data between pipelines executed in different + // threads. + + // Placeholders for the VST pipeline + + std::shared_ptr vstOutputLeftUint8Placeholder; + std::shared_ptr vstOutputLeftFp32Placeholder; + + // Placeholders for the inference pipeline + + std::shared_ptr smallF32ImagePlaceholder; + std::shared_ptr largeU8ImagePlaceholder; + std::shared_ptr isPoseDetectedPlaceholder; + std::shared_ptr bodyLandmarkPlaceholder; + std::shared_ptr molesTransformsPlaceholder; + std::shared_ptr moleCollisionsPlaceholder; + std::shared_ptr molesVisiblePlaceholder; + std::shared_ptr moleDeadlinesPlaceholder; + std::shared_ptr scorePlaceholder; + std::shared_ptr realtimeSinceStartupPlaceholder; + std::shared_ptr moleOnTimingsPlaceholder; + std::shared_ptr moleOffTimingsPlaceholder; + /** + * roiAffinePh1 and roiAffinePh2 refers to the same + * roiAffineGlobal, from m_secureMrDetectionPipeline + * and m_secureMrAffineUpdatePipeline respectively. + *
+ * roiAffinePh3 and roiAffinePh4 refers to the same + * roiAffineUpdatedGlobal, from + * m_secureMrAffineUpdatePipeline + * and m_secureMrLandmarkPipeline respectively. + */ + std::shared_ptr roiAffinePh1, roiAffinePh2, roiAffinePh3, roiAffinePh4, roiImagePh; + + // PiPlaceholders for the render pipeline + + std::shared_ptr gltfPlaceholderTensor; + std::shared_ptr gltfScoreboardPlaceholder; + std::shared_ptr isPoseDetectedPlaceholder2; + std::shared_ptr bodyLandmarkPlaceholder2; + std::shared_ptr molesTransformsPlaceholder2; + std::shared_ptr molesVisiblePlaceholder2; + std::shared_ptr currentScorePh; + std::array, MAX_MOLES> moleGltfPlaceholders; + std::array, MAX_MOLES> molePoseTensors; + std::array, MAX_MOLES> moleVisibleTensors; + + std::array, MAX_JOINTS> redboxGltfPlaceholders; + std::array, MAX_JOINTS> redboxPoseTensors; + + std::chrono::steady_clock::time_point startTime; + + // Pipeline placeholder/local tensor for the move pipeline + std::shared_ptr gltfPlaceholderTensor2; + std::shared_ptr stagePose; + float stagePoseData[16]{ + 1.0f, 0.0f, 0.0f, 0.6f, // + 0.0f, 1.0f, 0.0f, -0.2f, // + 0.0f, 0.0f, 1.0f, -2.5f, // + 0.0f, 0.0f, 0.0f, 1.0f // + }; + std::shared_ptr bodyLandmarkInitPlaceholder; + std::shared_ptr molesTransformsInitPlaceholder; + std::shared_ptr molesCollisionsInitPlaceholder; + bool molesInitApplied = false; + + // Run-time control + + std::vector pipelineRunners; + std::unique_ptr pipelineInitializer; + std::condition_variable initialized; + std::mutex initialized_mtx; + bool keepRunning = true; + bool pipelineAllInitialized = false; +}; + +} // namespace SecureMR diff --git a/scripts/compare_output.py b/scripts/compare_output.py new file mode 100644 index 0000000..cd8e7ac --- /dev/null +++ b/scripts/compare_output.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 +import argparse +import glob +import os +import struct +import sys + + +def load_expected(src: str): + if os.path.isfile(src): + data = open(src, "rb").read() + if len(data) % 4 != 0: + print(f"Expected file {src} size {len(data)} is not divisible by 4 bytes (float32).", flush=True) + count = len(data) // 4 + return list(struct.unpack("<%df" % count, data[: count * 4])) + return [float(x) for x in src.split(",") if x.strip()] + + +def main(): + parser = argparse.ArgumentParser( + description="Compare model_inspect outputs against expected float32 values or file." + ) + parser.add_argument("expected", help="Comma-separated float32 values or float32 binary file") + parser.add_argument("output_dir", help="Directory containing model_inspect_output_*.bin") + parser.add_argument("output_name", nargs="?", default="", help="Specific output file name to compare") + args = parser.parse_args() + + expected = load_expected(args.expected) + if not expected: + print("No expected values to compare; skipping.", flush=True) + return 0 + + files = sorted(glob.glob(os.path.join(args.output_dir, "model_inspect_output_*.bin"))) + if not files: + print("No output files available for comparison.", flush=True) + return 0 + + if len(files) > 1: + if not args.output_name: + print("--output-name is not given, but found multiple outputs from model:") + for f in files: + print(f" {os.path.basename(f)}") + return 0 + first = os.path.join(args.output_dir, args.output_name) + if not os.path.exists(first): + print(f"{args.output_name} not found in {args.output_dir}", flush=True) + return 1 + else: + first = files[0] + + data = open(first, "rb").read() + need = len(expected) + actual = [] + for i in range(need): + offset = i * 4 + if offset + 4 > len(data): + break + actual.append(struct.unpack_from(" {status}" + ) + fid.write(ret + "\n") + if idx < 10: + print(ret) + if idx == 10: + print(" ...") + print(f"Diff results saved in {compared_result_txt}") + + if len(actual) < need: + print(f"Warning: output had only {len(actual)} float32 values, expected {need}.", flush=True) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/logcat b/scripts/logcat new file mode 100644 index 0000000..8593904 --- /dev/null +++ b/scripts/logcat @@ -0,0 +1,220 @@ +#!/usr/bin/env python3 +""" +Android Logcat Viewer +A command-line tool for viewing and filtering Android logcat output with colored formatting. + +Features: +- Color-coded log levels (Verbose, Debug, Info, Warn, Error, Fatal) +- Package name filtering +- Real-time log monitoring +- Clean timestamp and thread information display +- Automatic process monitoring and reconnection + +Usage: + # View all logs + ./logcat + + # Filter logs by package name + ./logcat -p com.example.app + + # Common use cases: + 1. Monitor specific app logs: + ./logcat -p com.android.chrome + + 2. Watch app startup: + ./logcat -p com.example.app + (Will wait until the app starts and then begin showing logs) + +Key Features: +- Automatically clears existing logs when starting +- Waits for the specified package to start if it's not running +- Handles process termination and restart gracefully +- Color-coded output for better readability +- Clean exit with Ctrl+C + +Requirements: +- Python 3.6+ +- ADB (Android Debug Bridge) installed and in PATH +- Connected Android device or emulator +""" + +import subprocess +import re +import sys +from datetime import datetime +from enum import Enum +from typing import Optional +import argparse +import threading +import queue +import signal +import time + +class LogLevel(Enum): + VERBOSE = ('\033[37m', 'V') # 白色 + DEBUG = ('\033[36m', 'D') # 青色 + INFO = ('\033[32m', 'I') # 绿色 + WARN = ('\033[33m', 'W') # 黄色 + ERROR = ('\033[31m', 'E') # 红色 + FATAL = ('\033[35m', 'F') # 紫色 + + def __init__(self, color_code, letter): + self.color_code = color_code + self.letter = letter + +class LogEntry: + def __init__(self, timestamp: str, pid: str, tid: str, level: LogLevel, + tag: str, message: str): + self.timestamp = timestamp + self.pid = pid + self.tid = tid + self.level = level + self.tag = tag + self.message = message + +class LogcatReader: + RESET_COLOR = '\033[0m' + + def __init__(self, package_name: Optional[str] = None, regex_pattern: Optional[str] = None): + self.package_name = package_name + self.regex_pattern = regex_pattern + self.log_queue = queue.Queue() + self.should_stop = False + self._compiled_regex = re.compile(regex_pattern) if regex_pattern else None + + def start(self): + # 清除已有日志 + subprocess.run(['adb', 'logcat', '-c']) + + # 启动日志读取线程 + self.read_thread = threading.Thread(target=self._read_logs) + self.read_thread.daemon = True + self.read_thread.start() + + # 启动日志处理线程 + self.process_thread = threading.Thread(target=self._process_logs) + self.process_thread.daemon = True + self.process_thread.start() + + def stop(self): + self.should_stop = True + if self.read_thread: + self.read_thread.join() + if self.process_thread: + self.process_thread.join() + + def _get_package_pid(self): + while not self.should_stop: + try: + # 使用shell命令获取进程PID + cmd = f"adb shell pidof {self.package_name}" + result = subprocess.check_output(cmd, shell=True, text=True) + pid = result.strip() + if pid: + print(f"找到包名 {self.package_name} 的PID: {pid}") + return pid + except subprocess.CalledProcessError: + print(f"等待包名 {self.package_name} 启动...") + # 等待1秒后重试 + time.sleep(1) + return None + + def _read_logs(self): + cmd = ['adb', 'logcat', '-v', 'threadtime'] + if self.package_name: + + pid = self._get_package_pid() + if pid: + cmd.extend(['--pid', pid]) + else: + # 如果获取不到PID,就用包名过滤 + cmd.extend([f'--pid=$(pidof {self.package_name})']) + process = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + encoding='utf-8', + errors='replace' + ) + + while not self.should_stop: + line = process.stdout.readline() + if line: + self.log_queue.put(line) + + process.terminate() + + def _process_logs(self): + log_pattern = re.compile( + r'(\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\.\d{3})\s+' # timestamp + r'(\d+)\s+' # pid + r'(\d+)\s+' # tid + r'([VDIWEF])\s+' # log level + r'([^:]+)\s*:\s*' # tag + r'(.*)' # message + ) + + while not self.should_stop: + try: + line = self.log_queue.get(timeout=1) + match = log_pattern.match(line) + if match: + timestamp, pid, tid, level_char, tag, message = match.groups() + # 转换日志级别 + level = next((l for l in LogLevel if l.letter == level_char), None) + if level: + entry = LogEntry( + timestamp=timestamp, + pid=pid, + tid=tid, + level=level, + tag=tag.strip(), + message=message.strip() + ) + # 如果有正则过滤,只输出匹配的日志 + if self._compiled_regex: + if self._compiled_regex.search(line): + self._print_log(entry) + else: + self._print_log(entry) + except queue.Empty: + continue + + def _print_log(self, entry: LogEntry): + # 格式化输出 + print(f'{entry.level.color_code}' + f'{entry.timestamp} {entry.pid}/{entry.tid} ' + f'{entry.level.letter}/{entry.tag}: {entry.message}' + f'{self.RESET_COLOR}') + +def main(): + parser = argparse.ArgumentParser(description='Android Logcat Viewer') + parser.add_argument('-p', '--package', + help='Package name to filter logs') + parser.add_argument('-e', '--regex', + help='Regex pattern to filter log output') + args = parser.parse_args() + + if not args.package and not args.regex: + args.regex = "Secure MR" + + reader = LogcatReader(args.package, args.regex) + + def signal_handler(signum, frame): + print('\nStopping log reader...') + reader.stop() + sys.exit(0) + + signal.signal(signal.SIGINT, signal_handler) + + try: + reader.start() + # 保持主线程运行 + while True: + signal.pause() + except KeyboardInterrupt: + reader.stop() + +if __name__ == '__main__': + main() diff --git a/scripts/run_model_inspect.sh b/scripts/run_model_inspect.sh new file mode 100644 index 0000000..359adcd --- /dev/null +++ b/scripts/run_model_inspect.sh @@ -0,0 +1,229 @@ +#!/bin/bash +set -euo pipefail + +usage() { + cat < --json + --input Input tensor file (NHWC float32). Optional; random input is used if omitted. + --duration Run time in seconds (default: 20) + --output Expected output: comma-separated float32 values or a float32 binary file (first output tensor) + --output-name Model output file name to compared with --output + --model Serialized model .bin + --json Model spec .json +Examples: + ./scripts/run_model_inspect.sh --model assets/mnistwild/mnist.serialized.bin --json assets/mnistwild/mnist.serialized.json + ./scripts/run_model_inspect.sh --model assets/pose/detection.serialized.bin --json assets/pose/detection.serialized.json + ./scripts/run_model_inspect.sh --model assets/pose/landmark.serialized.bin --json assets/pose/landmark.serialized.json + ./scripts/run_model_inspect.sh --model assets/UFO/facedetector_fp16_qnn229.bin --json assets/UFO/facedetector_fp16_qnn229.json + ./scripts/run_model_inspect.sh --model assets/yolo_det/yolo.serialized.bin --json assets/yolo_det/yolo.serialized.json + ./scripts/run_model_inspect.sh --model assets/yolo_det/yolom.serialized.bin --json assets/yolo_det/yolom.serialized.json +EOF + exit 1 +} + +INPUT_FILE="" +RUNTIME=20 +EXPECTED_VALUES="" +MODEL_FILE="" +JSON_FILE="" +OUTPUT_FILE="" + +while [ "$#" -gt 0 ]; do + case "$1" in + --input) + INPUT_FILE="$2" + shift 2 + ;; + --duration) + RUNTIME="$2" + shift 2 + ;; + --output) + EXPECTED_VALUES="$2" + shift 2 + ;; + --output-name) + OUTPUT_FILE="$2" + shift 2 + ;; + --model) + MODEL_FILE="$2" + shift 2 + ;; + --json) + JSON_FILE="$2" + shift 2 + ;; + -h|--help) + usage + ;; + --) + shift + break + ;; + -*) + usage + ;; + *) + break + ;; + esac +done + +if [ "$#" -ne 0 ]; then + usage +fi + +if [ -z "$MODEL_FILE" ] || [ -z "$JSON_FILE" ]; then + usage +fi + +if [ ! -f "$MODEL_FILE" ]; then + echo "Error: Bin file not found: $MODEL_FILE" + exit 1 +fi + +if [ ! -f "$JSON_FILE" ]; then + echo "Error: Json file not found: $JSON_FILE" + exit 1 +fi + +if [ -n "$INPUT_FILE" ] && [ ! -f "$INPUT_FILE" ]; then + echo "Error: Input tensor file not found: $INPUT_FILE" + exit 1 +fi + +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" &> /dev/null && pwd) +REPO_ROOT=$(dirname "$SCRIPT_DIR") + +PACKAGE_NAME="com.bytedance.pico.secure_mr_demo.model_inspect" +COMPONENT="$PACKAGE_NAME/android.app.NativeActivity" +# Keep under Android setprop length limits; use app external storage. +DEVICE_TMP_DIR="/sdcard/Android/data/${PACKAGE_NAME}/files" +DEVICE_INPUT_PATH="$DEVICE_TMP_DIR/input.bin" +DEVICE_OUTPUT_DIRS=("${DEVICE_TMP_DIR}/model_inspect" "/data/local/tmp/securemr_model_inspect") +LOCAL_OUTPUT_DIR="${REPO_ROOT}/tmp_data/model_inspect_outputs_$(date +%Y%m%d_%H%M%S)" +LOGCAT_PID="" + +screen_is_on() { + local state + state="$(adb shell dumpsys power 2>/dev/null | grep -m1 -E 'Display Power|mWakefulness' || true)" + if echo "$state" | grep -q -E 'ON|Awake'; then + return 0 + fi + return 1 +} + +ensure_screen_on() { + if screen_is_on; then + return + fi + echo "Waking device screen..." + adb shell input keyevent 26 + sleep 3 +} + +turn_screen_off() { + if screen_is_on; then + adb shell input keyevent 26 >/dev/null 2>&1 || true + fi +} + +cleanup() { + if [ -n "$LOGCAT_PID" ]; then + kill "$LOGCAT_PID" 2>/dev/null || true + LOGCAT_PID="" + fi + adb shell am force-stop "$PACKAGE_NAME" >/dev/null 2>&1 || true + turn_screen_off +} +trap cleanup EXIT + +echo "Building and installing model_inspect..." +cd "$REPO_ROOT" +./gradlew :samples:model_inspect:installDebug + +echo "Cleaning device temp directory: $DEVICE_TMP_DIR" +adb shell rm -rf "$DEVICE_TMP_DIR" +adb shell mkdir -p "$DEVICE_TMP_DIR" + +echo "Cleaning output directories: ${DEVICE_OUTPUT_DIRS[*]}" +for dir in "${DEVICE_OUTPUT_DIRS[@]}"; do + adb shell rm -f "$dir"/* >/dev/null 2>&1 || true +done + +echo "--------------------------------------------------" +echo "Testing model: $(basename "$MODEL_FILE")" + +echo "Pushing model files to $DEVICE_TMP_DIR..." +adb push "$MODEL_FILE" "$DEVICE_TMP_DIR/model.serialized.bin" >/dev/null +adb push "$JSON_FILE" "$DEVICE_TMP_DIR/model.serialized.json" >/dev/null + +if [ -n "$INPUT_FILE" ]; then + echo "Pushing input tensor to $DEVICE_INPUT_PATH (NHWC float32 assumed)..." + adb push "$INPUT_FILE" "$DEVICE_INPUT_PATH" >/dev/null + adb shell setprop debug.securemr.model_inspect.input "$DEVICE_INPUT_PATH" +else + adb shell setprop debug.securemr.model_inspect.input "''" +fi + +echo "Setting property debug.securemr.model_inspect.model_dir to $DEVICE_TMP_DIR" +adb shell setprop debug.securemr.model_inspect.model_dir "$DEVICE_TMP_DIR" + +ensure_screen_on + +existing_pid="$(adb shell pidof "$PACKAGE_NAME" 2>/dev/null | tr -d '\r' || true)" +if [ -n "$existing_pid" ]; then + echo "App already running (pid $existing_pid); stopping before relaunch..." + adb shell am force-stop "$PACKAGE_NAME" + sleep 3 +fi + +echo "Starting logcat..." +export PYTHONUNBUFFERED=1 +"$REPO_ROOT/scripts/logcat" -p "$PACKAGE_NAME" -e ModelInspect 2>&1 & +LOGCAT_PID=$! + +echo "Launching app..." +adb shell am force-stop com.bytedance.pico.openmr +sleep 2 +adb shell am start -n "$COMPONENT" + +echo "Waiting for ${RUNTIME} seconds..." +sleep "$RUNTIME" + +echo "Stopping app..." +adb shell am force-stop "$PACKAGE_NAME" + +mkdir -p "$LOCAL_OUTPUT_DIR" +echo "Pulling outputs to $LOCAL_OUTPUT_DIR..." +outputs_pulled=0 +for dir in "${DEVICE_OUTPUT_DIRS[@]}"; do + mapfile -t files < <(adb shell ls "$dir" 2>/dev/null | tr -d '\r' || true) + if [ "${#files[@]}" -eq 0 ]; then + continue + fi + for f in "${files[@]}"; do + if [[ "$f" == model_inspect_output_*.bin ]]; then + adb shell cat "$dir/$f" >"$LOCAL_OUTPUT_DIR/$f" + outputs_pulled=1 + fi + done +done + +if [ "$outputs_pulled" -eq 1 ]; then + echo "Outputs saved under $LOCAL_OUTPUT_DIR" +else + echo "No output files pulled (none found on device)." +fi + +if [ -n "$EXPECTED_VALUES" ] && [ "$outputs_pulled" -eq 1 ]; then + echo "$REPO_ROOT/scripts/compare_output.py" "$EXPECTED_VALUES" "$LOCAL_OUTPUT_DIR" "$OUTPUT_FILE" + python3 "$REPO_ROOT/scripts/compare_output.py" "$EXPECTED_VALUES" "$LOCAL_OUTPUT_DIR" "$OUTPUT_FILE" +fi + +echo "Turning off screen..." +turn_screen_off + +echo "--------------------------------------------------" +echo "Test completed." diff --git a/settings.gradle b/settings.gradle index ad60106..96b0259 100644 --- a/settings.gradle +++ b/settings.gradle @@ -19,4 +19,9 @@ include 'samples:ufo' include 'samples:pose' include 'samples:ufo_origin' include 'samples:yolo_det' +include 'samples:mnistwild' +include 'samples:rubics_cube' +include 'samples:model_inspect' +include 'samples:readback' +include 'samples:whackamole'