Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/modules/openxr/openxr_api.cpp
10277 views
1
/**************************************************************************/
2
/* openxr_api.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "openxr_api.h"
32
33
#include "openxr_interface.h"
34
#include "openxr_util.h"
35
36
#include "core/config/engine.h"
37
#include "core/config/project_settings.h"
38
#include "core/os/memory.h"
39
#include "core/version.h"
40
41
#include "openxr_platform_inc.h"
42
43
#ifdef VULKAN_ENABLED
44
#include "extensions/platform/openxr_vulkan_extension.h"
45
#endif
46
47
#ifdef METAL_ENABLED
48
#include "extensions/platform/openxr_metal_extension.h"
49
#endif
50
51
#if defined(GLES3_ENABLED) && !defined(MACOS_ENABLED)
52
#include "extensions/platform/openxr_opengl_extension.h"
53
#endif
54
55
#ifdef D3D12_ENABLED
56
#include "extensions/platform/openxr_d3d12_extension.h"
57
#endif
58
59
#include "extensions/openxr_composition_layer_depth_extension.h"
60
#include "extensions/openxr_debug_utils_extension.h"
61
#include "extensions/openxr_eye_gaze_interaction.h"
62
#include "extensions/openxr_fb_display_refresh_rate_extension.h"
63
#include "extensions/openxr_fb_foveation_extension.h"
64
#include "extensions/openxr_fb_update_swapchain_extension.h"
65
#include "extensions/openxr_hand_tracking_extension.h"
66
67
#ifndef DISABLE_DEPRECATED
68
#include "extensions/openxr_extension_wrapper_extension.h"
69
#endif // DISABLE_DEPRECATED
70
71
#ifdef ANDROID_ENABLED
72
#define OPENXR_LOADER_NAME "libopenxr_loader.so"
73
#endif
74
75
////////////////////////////////////
76
// OpenXRAPI::OpenXRSwapChainInfo
77
78
Vector<OpenXRAPI::OpenXRSwapChainInfo> OpenXRAPI::OpenXRSwapChainInfo::free_queue;
79
80
bool OpenXRAPI::OpenXRSwapChainInfo::create(XrSwapchainCreateFlags p_create_flags, XrSwapchainUsageFlags p_usage_flags, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size) {
81
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
82
ERR_FAIL_NULL_V(openxr_api, false);
83
84
XrSession xr_session = openxr_api->get_session();
85
ERR_FAIL_COND_V(xr_session == XR_NULL_HANDLE, false);
86
87
OpenXRGraphicsExtensionWrapper *xr_graphics_extension = openxr_api->get_graphics_extension();
88
ERR_FAIL_NULL_V(xr_graphics_extension, false);
89
90
// We already have a swapchain?
91
ERR_FAIL_COND_V(swapchain != XR_NULL_HANDLE, false);
92
93
XrResult result;
94
95
void *next_pointer = nullptr;
96
for (OpenXRExtensionWrapper *wrapper : openxr_api->get_registered_extension_wrappers()) {
97
void *np = wrapper->set_swapchain_create_info_and_get_next_pointer(next_pointer);
98
if (np != nullptr) {
99
next_pointer = np;
100
}
101
}
102
103
XrSwapchainCreateInfo swapchain_create_info = {
104
XR_TYPE_SWAPCHAIN_CREATE_INFO, // type
105
next_pointer, // next
106
p_create_flags, // createFlags
107
p_usage_flags, // usageFlags
108
p_swapchain_format, // format
109
p_sample_count, // sampleCount
110
p_width, // width
111
p_height, // height
112
1, // faceCount
113
p_array_size, // arraySize
114
1 // mipCount
115
};
116
117
XrSwapchain new_swapchain;
118
result = openxr_api->xrCreateSwapchain(xr_session, &swapchain_create_info, &new_swapchain);
119
if (XR_FAILED(result)) {
120
print_line("OpenXR: Failed to get swapchain [", openxr_api->get_error_string(result), "]");
121
return false;
122
}
123
124
if (!xr_graphics_extension->get_swapchain_image_data(new_swapchain, p_swapchain_format, p_width, p_height, p_sample_count, p_array_size, &swapchain_graphics_data)) {
125
openxr_api->xrDestroySwapchain(new_swapchain);
126
return false;
127
}
128
129
swapchain = new_swapchain;
130
131
return true;
132
}
133
134
void OpenXRAPI::OpenXRSwapChainInfo::queue_free() {
135
if (image_acquired) {
136
release();
137
}
138
139
if (swapchain != XR_NULL_HANDLE) {
140
free_queue.push_back(*this);
141
142
swapchain_graphics_data = nullptr;
143
swapchain = XR_NULL_HANDLE;
144
}
145
}
146
147
void OpenXRAPI::OpenXRSwapChainInfo::free_queued() {
148
for (OpenXRAPI::OpenXRSwapChainInfo &swapchain_info : free_queue) {
149
swapchain_info.free();
150
}
151
free_queue.clear();
152
}
153
154
void OpenXRAPI::OpenXRSwapChainInfo::free() {
155
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
156
ERR_FAIL_NULL(openxr_api);
157
158
if (image_acquired) {
159
release();
160
}
161
162
if (openxr_api->get_graphics_extension() && swapchain_graphics_data != nullptr) {
163
openxr_api->get_graphics_extension()->cleanup_swapchain_graphics_data(&swapchain_graphics_data);
164
}
165
166
if (swapchain != XR_NULL_HANDLE) {
167
openxr_api->xrDestroySwapchain(swapchain);
168
swapchain = XR_NULL_HANDLE;
169
}
170
}
171
172
bool OpenXRAPI::OpenXRSwapChainInfo::acquire(bool &p_should_render) {
173
ERR_FAIL_COND_V(image_acquired, true); // This was not released when it should be, error out and reuse...
174
175
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
176
ERR_FAIL_NULL_V(openxr_api, false);
177
178
XrResult result;
179
180
if (!skip_acquire_swapchain) {
181
XrSwapchainImageAcquireInfo swapchain_image_acquire_info = {
182
XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO, // type
183
nullptr // next
184
};
185
186
result = openxr_api->xrAcquireSwapchainImage(swapchain, &swapchain_image_acquire_info, &image_index);
187
if (!XR_UNQUALIFIED_SUCCESS(result)) {
188
// Make sure end_frame knows we need to submit an empty frame
189
p_should_render = false;
190
191
if (XR_FAILED(result)) {
192
// Unexpected failure, log this!
193
print_line("OpenXR: failed to acquire swapchain image [", openxr_api->get_error_string(result), "]");
194
return false;
195
} else {
196
// In this scenario we silently fail, the XR runtime is simply not ready yet to acquire the swapchain.
197
return false;
198
}
199
}
200
}
201
202
XrSwapchainImageWaitInfo swapchain_image_wait_info = {
203
XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO, // type
204
nullptr, // next
205
1000000000 // 1s timeout in nanoseconds
206
};
207
208
// Wait for a maximum of 10 seconds before calling it a critical failure...
209
for (int retry = 0; retry < 10; retry++) {
210
result = openxr_api->xrWaitSwapchainImage(swapchain, &swapchain_image_wait_info);
211
if (result != XR_TIMEOUT_EXPIRED) {
212
break;
213
}
214
WARN_PRINT("OpenXR: timed out waiting for swapchain image.");
215
}
216
217
if (!XR_UNQUALIFIED_SUCCESS(result)) {
218
// Make sure end_frame knows we need to submit an empty frame
219
p_should_render = false;
220
221
if (XR_FAILED(result)) {
222
// Unexpected failure, log this!
223
print_line("OpenXR: failed to wait for swapchain image [", openxr_api->get_error_string(result), "]");
224
return false;
225
} else {
226
WARN_PRINT("OpenXR: couldn't to wait for swapchain but not a complete error [" + openxr_api->get_error_string(result) + "]");
227
228
// Make sure to skip trying to acquire the swapchain image in the next frame
229
skip_acquire_swapchain = true;
230
return false;
231
}
232
} else {
233
skip_acquire_swapchain = false;
234
}
235
236
image_acquired = true;
237
return true;
238
}
239
240
bool OpenXRAPI::OpenXRSwapChainInfo::release() {
241
if (!image_acquired) {
242
// Already released or never acquired.
243
return true;
244
}
245
246
image_acquired = false; // Regardless if we succeed or not, consider this released.
247
248
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
249
ERR_FAIL_NULL_V(openxr_api, false);
250
251
XrSwapchainImageReleaseInfo swapchain_image_release_info = {
252
XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO, // type
253
nullptr // next
254
};
255
XrResult result = openxr_api->xrReleaseSwapchainImage(swapchain, &swapchain_image_release_info);
256
if (XR_FAILED(result)) {
257
print_line("OpenXR: failed to release swapchain image! [", openxr_api->get_error_string(result), "]");
258
return false;
259
}
260
261
return true;
262
}
263
264
RID OpenXRAPI::OpenXRSwapChainInfo::get_image() {
265
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
266
267
if (image_acquired && openxr_api && openxr_api->get_graphics_extension()) {
268
return OpenXRAPI::get_singleton()->get_graphics_extension()->get_texture(swapchain_graphics_data, image_index);
269
} else {
270
return RID();
271
}
272
}
273
274
RID OpenXRAPI::OpenXRSwapChainInfo::get_density_map() {
275
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
276
277
if (image_acquired && openxr_api && openxr_api->get_graphics_extension()) {
278
return openxr_api->get_graphics_extension()->get_density_map(swapchain_graphics_data, image_index);
279
} else {
280
return RID();
281
}
282
}
283
284
////////////////////////////////////
285
// OpenXRAPI
286
287
OpenXRAPI *OpenXRAPI::singleton = nullptr;
288
Vector<OpenXRExtensionWrapper *> OpenXRAPI::registered_extension_wrappers;
289
290
bool OpenXRAPI::openxr_is_enabled(bool p_check_run_in_editor) {
291
if (XRServer::get_xr_mode() == XRServer::XRMODE_DEFAULT) {
292
if (Engine::get_singleton()->is_editor_hint() && p_check_run_in_editor) {
293
// For now, don't start OpenXR when the editor starts up. In the future, this may change
294
// if we want to integrate more XR features into the editor experience.
295
return false;
296
} else {
297
return GLOBAL_GET("xr/openxr/enabled");
298
}
299
} else {
300
return XRServer::get_xr_mode() == XRServer::XRMODE_ON;
301
}
302
}
303
304
String OpenXRAPI::get_default_action_map_resource_name() {
305
String name = GLOBAL_GET("xr/openxr/default_action_map");
306
307
return name;
308
}
309
310
String OpenXRAPI::get_error_string(XrResult result) const {
311
if (XR_SUCCEEDED(result)) {
312
return String("Succeeded");
313
}
314
315
if (instance == XR_NULL_HANDLE) {
316
Array args = { Variant(result) };
317
return String("Error code {0}").format(args);
318
}
319
320
char resultString[XR_MAX_RESULT_STRING_SIZE];
321
xrResultToString(instance, result, resultString);
322
323
return String(resultString);
324
}
325
326
String OpenXRAPI::get_swapchain_format_name(int64_t p_swapchain_format) const {
327
// This is rendering engine dependent...
328
if (graphics_extension) {
329
return graphics_extension->get_swapchain_format_name(p_swapchain_format);
330
}
331
332
return String("Swapchain format ") + String::num_int64(int64_t(p_swapchain_format));
333
}
334
335
void OpenXRAPI::set_object_name(XrObjectType p_object_type, uint64_t p_object_handle, const String &p_object_name) {
336
OpenXRDebugUtilsExtension *debug_utils = OpenXRDebugUtilsExtension::get_singleton();
337
if (!debug_utils || !debug_utils->get_active()) {
338
// Not enabled/active? Ignore.
339
return;
340
}
341
342
debug_utils->set_object_name(p_object_type, p_object_handle, p_object_name.utf8().get_data());
343
}
344
345
void OpenXRAPI::begin_debug_label_region(const String &p_label_name) {
346
OpenXRDebugUtilsExtension *debug_utils = OpenXRDebugUtilsExtension::get_singleton();
347
if (!debug_utils || !debug_utils->get_active()) {
348
// Not enabled/active? Ignore.
349
return;
350
}
351
352
debug_utils->begin_debug_label_region(p_label_name.utf8().get_data());
353
}
354
355
void OpenXRAPI::end_debug_label_region() {
356
OpenXRDebugUtilsExtension *debug_utils = OpenXRDebugUtilsExtension::get_singleton();
357
if (!debug_utils || !debug_utils->get_active()) {
358
// Not enabled/active? Ignore.
359
return;
360
}
361
362
debug_utils->end_debug_label_region();
363
}
364
365
void OpenXRAPI::insert_debug_label(const String &p_label_name) {
366
OpenXRDebugUtilsExtension *debug_utils = OpenXRDebugUtilsExtension::get_singleton();
367
if (!debug_utils || !debug_utils->get_active()) {
368
// Not enabled/active? Ignore.
369
return;
370
}
371
372
debug_utils->insert_debug_label(p_label_name.utf8().get_data());
373
}
374
375
bool OpenXRAPI::load_layer_properties() {
376
// This queries additional layers that are available and can be initialized when we create our OpenXR instance
377
if (!layer_properties.is_empty()) {
378
// already retrieved this
379
return true;
380
}
381
382
// Note, instance is not yet setup so we can't use get_error_string to retrieve our error
383
uint32_t num_layer_properties = 0;
384
XrResult result = xrEnumerateApiLayerProperties(0, &num_layer_properties, nullptr);
385
ERR_FAIL_COND_V_MSG(XR_FAILED(result), false, "OpenXR: Failed to enumerate number of api layer properties");
386
387
layer_properties.resize(num_layer_properties);
388
for (XrApiLayerProperties &layer : layer_properties) {
389
layer.type = XR_TYPE_API_LAYER_PROPERTIES;
390
layer.next = nullptr;
391
}
392
393
result = xrEnumerateApiLayerProperties(num_layer_properties, &num_layer_properties, layer_properties.ptr());
394
ERR_FAIL_COND_V_MSG(XR_FAILED(result), false, "OpenXR: Failed to enumerate api layer properties");
395
396
for (const XrApiLayerProperties &layer : layer_properties) {
397
print_verbose(vformat("OpenXR: Found OpenXR layer %s.", layer.layerName));
398
}
399
400
return true;
401
}
402
403
bool OpenXRAPI::load_supported_extensions() {
404
// This queries supported extensions that are available and can be initialized when we create our OpenXR instance
405
406
if (!supported_extensions.is_empty()) {
407
// already retrieved this
408
return true;
409
}
410
411
// Note, instance is not yet setup so we can't use get_error_string to retrieve our error
412
uint32_t num_supported_extensions = 0;
413
XrResult result = xrEnumerateInstanceExtensionProperties(nullptr, 0, &num_supported_extensions, nullptr);
414
ERR_FAIL_COND_V_MSG(XR_FAILED(result), false, "OpenXR: Failed to enumerate number of extension properties");
415
416
supported_extensions.resize(num_supported_extensions);
417
418
// set our types
419
for (XrExtensionProperties &extension : supported_extensions) {
420
extension.type = XR_TYPE_EXTENSION_PROPERTIES;
421
extension.next = nullptr;
422
}
423
result = xrEnumerateInstanceExtensionProperties(nullptr, num_supported_extensions, &num_supported_extensions, supported_extensions.ptr());
424
ERR_FAIL_COND_V_MSG(XR_FAILED(result), false, "OpenXR: Failed to enumerate extension properties");
425
426
for (const XrExtensionProperties &extension : supported_extensions) {
427
print_verbose(vformat("OpenXR: Found OpenXR extension %s.", extension.extensionName));
428
}
429
430
return true;
431
}
432
433
bool OpenXRAPI::is_extension_supported(const String &p_extension) const {
434
for (const XrExtensionProperties &extension : supported_extensions) {
435
if (extension.extensionName == p_extension) {
436
return true;
437
}
438
}
439
440
return false;
441
}
442
443
bool OpenXRAPI::is_extension_enabled(const String &p_extension) const {
444
CharString extension = p_extension.ascii();
445
446
for (int i = 0; i < enabled_extensions.size(); i++) {
447
if (strcmp(enabled_extensions[i].ptr(), extension.ptr()) == 0) {
448
return true;
449
}
450
}
451
452
return false;
453
}
454
455
bool OpenXRAPI::is_top_level_path_supported(const String &p_toplevel_path) {
456
String required_extension = OpenXRInteractionProfileMetadata::get_singleton()->get_top_level_extension(p_toplevel_path);
457
458
// If unsupported is returned we likely have a misspelled interaction profile path in our action map. Always output that as an error.
459
ERR_FAIL_COND_V_MSG(required_extension == XR_PATH_UNSUPPORTED_NAME, false, "OpenXR: Unsupported toplevel path " + p_toplevel_path);
460
461
if (required_extension == "") {
462
// no extension needed, core top level are always "supported", they just won't be used if not really supported
463
return true;
464
}
465
466
if (!is_extension_enabled(required_extension)) {
467
// It is very likely we have top level paths for which the extension is not available so don't flood the logs with unnecessary spam.
468
print_verbose("OpenXR: Top level path " + p_toplevel_path + " requires extension " + required_extension);
469
return false;
470
}
471
472
return true;
473
}
474
475
bool OpenXRAPI::is_interaction_profile_supported(const String &p_ip_path) {
476
String required_extension = OpenXRInteractionProfileMetadata::get_singleton()->get_interaction_profile_extension(p_ip_path);
477
478
// If unsupported is returned we likely have a misspelled interaction profile path in our action map. Always output that as an error.
479
ERR_FAIL_COND_V_MSG(required_extension == XR_PATH_UNSUPPORTED_NAME, false, "OpenXR: Unsupported interaction profile " + p_ip_path);
480
481
if (required_extension == "") {
482
// no extension needed, core interaction profiles are always "supported", they just won't be used if not really supported
483
return true;
484
}
485
486
if (!is_extension_enabled(required_extension)) {
487
// It is very likely we have interaction profiles for which the extension is not available so don't flood the logs with unnecessary spam.
488
print_verbose("OpenXR: Interaction profile " + p_ip_path + " requires extension " + required_extension);
489
return false;
490
}
491
492
return true;
493
}
494
495
bool OpenXRAPI::interaction_profile_supports_io_path(const String &p_ip_path, const String &p_io_path) {
496
if (!is_interaction_profile_supported(p_ip_path)) {
497
return false;
498
}
499
500
const OpenXRInteractionProfileMetadata::IOPath *io_path = OpenXRInteractionProfileMetadata::get_singleton()->get_io_path(p_ip_path, p_io_path);
501
502
// If the io_path is not part of our metadata we've likely got a misspelled name or a bad action map, report
503
ERR_FAIL_NULL_V_MSG(io_path, false, "OpenXR: Unsupported io path " + String(p_ip_path) + String(p_io_path));
504
505
if (io_path->openxr_extension_name == "") {
506
// no extension needed, core io paths are always "supported", they just won't be used if not really supported
507
return true;
508
}
509
510
if (!is_extension_enabled(io_path->openxr_extension_name)) {
511
// It is very likely we have io paths for which the extension is not available so don't flood the logs with unnecessary spam.
512
print_verbose("OpenXR: IO path " + String(p_ip_path) + String(p_io_path) + " requires extension " + io_path->openxr_extension_name);
513
return false;
514
}
515
516
return true;
517
}
518
519
void OpenXRAPI::copy_string_to_char_buffer(const String p_string, char *p_buffer, int p_buffer_len) {
520
CharString char_string = p_string.utf8();
521
int len = char_string.length();
522
if (len < p_buffer_len - 1) {
523
// was having weird CI issues with strcpy so....
524
memcpy(p_buffer, char_string.get_data(), len);
525
p_buffer[len] = '\0';
526
} else {
527
memcpy(p_buffer, char_string.get_data(), p_buffer_len - 1);
528
p_buffer[p_buffer_len - 1] = '\0';
529
}
530
}
531
532
PackedStringArray OpenXRAPI::get_all_requested_extensions() {
533
// This returns all extensions we will request regardless of whether they are available.
534
// This is mostly used by the editor to filter features not enabled through project settings.
535
536
PackedStringArray requested_extensions;
537
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
538
const HashMap<String, bool *> &wrapper_request_extensions = wrapper->get_requested_extensions();
539
540
for (const KeyValue<String, bool *> &requested_extension : wrapper_request_extensions) {
541
if (!requested_extensions.has(requested_extension.key)) {
542
requested_extensions.push_back(requested_extension.key);
543
}
544
}
545
}
546
547
return requested_extensions;
548
}
549
550
bool OpenXRAPI::create_instance() {
551
// Create our OpenXR instance, this will query any registered extension wrappers for extensions we need to enable.
552
553
// We can request an extension multiple times if there are dependencies
554
struct RequestExtension {
555
String name;
556
bool *enabled;
557
};
558
559
// Find all extensions we wish to enable.
560
Vector<RequestExtension> requested_extensions;
561
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
562
const HashMap<String, bool *> &wrapper_request_extensions = wrapper->get_requested_extensions();
563
564
for (const KeyValue<String, bool *> &requested_extension : wrapper_request_extensions) {
565
requested_extensions.push_back({ requested_extension.key, requested_extension.value });
566
}
567
}
568
569
// Check which extensions are supported.
570
enabled_extensions.clear();
571
572
for (RequestExtension &requested_extension : requested_extensions) {
573
if (!is_extension_supported(requested_extension.name)) {
574
if (requested_extension.enabled == nullptr) {
575
// Null means this is a mandatory extension so we fail.
576
ERR_FAIL_V_MSG(false, String("OpenXR: OpenXR Runtime does not support ") + requested_extension.name + String(" extension!"));
577
} else {
578
// Set this extension as not supported.
579
*requested_extension.enabled = false;
580
}
581
} else {
582
if (requested_extension.enabled != nullptr) {
583
// Set this extension as supported.
584
*requested_extension.enabled = true;
585
}
586
587
// And record that we want to enable it (dependent extensions may be requested multiple times).
588
CharString ext_name = requested_extension.name.ascii();
589
if (!enabled_extensions.has(ext_name)) {
590
enabled_extensions.push_back(ext_name);
591
}
592
}
593
}
594
595
Vector<const char *> extension_ptrs;
596
for (int i = 0; i < enabled_extensions.size(); i++) {
597
print_verbose(String("OpenXR: Enabling extension ") + String(enabled_extensions[i].get_data()));
598
extension_ptrs.push_back(enabled_extensions[i].get_data());
599
}
600
601
// We explicitly set the version to 1.0.48 in order to workaround a bug (see #108850) in Meta's runtime.
602
// Once that is fixed, restore this to using XR_API_VERSION_1_0, which is the version associated with the
603
// OpenXR headers that we're using.
604
XrVersion openxr_version = XR_MAKE_VERSION(1, 0, 48);
605
606
// Create our OpenXR instance
607
XrApplicationInfo application_info{
608
"Godot Engine", // applicationName, if we're running a game we'll update this down below.
609
1, // applicationVersion, we don't currently have this
610
"Godot Engine", // engineName
611
GODOT_VERSION_MAJOR * 10000 + GODOT_VERSION_MINOR * 100 + GODOT_VERSION_PATCH, // engineVersion 4.0 -> 40000, 4.0.1 -> 40001, 4.1 -> 40100, etc.
612
openxr_version, // apiVersion
613
};
614
615
void *next_pointer = nullptr;
616
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
617
void *np = wrapper->set_instance_create_info_and_get_next_pointer(next_pointer);
618
if (np != nullptr) {
619
next_pointer = np;
620
}
621
}
622
623
XrInstanceCreateInfo instance_create_info = {
624
XR_TYPE_INSTANCE_CREATE_INFO, // type
625
next_pointer, // next
626
0, // createFlags
627
application_info, // applicationInfo
628
0, // enabledApiLayerCount, need to find out if we need support for this?
629
nullptr, // enabledApiLayerNames
630
uint32_t(extension_ptrs.size()), // enabledExtensionCount
631
extension_ptrs.ptr() // enabledExtensionNames
632
};
633
634
// Get our project name
635
String project_name = GLOBAL_GET("application/config/name");
636
if (!project_name.is_empty()) {
637
copy_string_to_char_buffer(project_name, instance_create_info.applicationInfo.applicationName, XR_MAX_APPLICATION_NAME_SIZE);
638
}
639
640
XrResult result = xrCreateInstance(&instance_create_info, &instance);
641
ERR_FAIL_COND_V_MSG(XR_FAILED(result), false, "Failed to create XR instance [" + get_error_string(result) + "].");
642
643
// from this point on we can use get_error_string to get more info about our errors...
644
645
XrInstanceProperties instanceProps = {
646
XR_TYPE_INSTANCE_PROPERTIES, // type;
647
nullptr, // next
648
0, // runtimeVersion, from here will be set by our get call
649
"" // runtimeName
650
};
651
652
OPENXR_API_INIT_XR_FUNC_V(xrGetInstanceProperties);
653
654
result = xrGetInstanceProperties(instance, &instanceProps);
655
if (XR_FAILED(result)) {
656
// not fatal probably
657
print_line("OpenXR: Failed to get XR instance properties [", get_error_string(result), "]");
658
659
runtime_name = "";
660
runtime_version = "";
661
} else {
662
runtime_name = instanceProps.runtimeName;
663
runtime_version = OpenXRUtil::make_xr_version_string(instanceProps.runtimeVersion);
664
print_line("OpenXR: Running on OpenXR runtime: ", runtime_name, " ", runtime_version);
665
}
666
667
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
668
wrapper->on_instance_created(instance);
669
}
670
671
return true;
672
}
673
674
bool OpenXRAPI::get_system_info() {
675
// Retrieve basic OpenXR system info based on the form factor we desire
676
677
// Retrieve the system for our form factor, fails if form factor is not available
678
XrSystemGetInfo system_get_info = {
679
XR_TYPE_SYSTEM_GET_INFO, // type;
680
nullptr, // next
681
form_factor // formFactor
682
};
683
684
XrResult result = xrGetSystem(instance, &system_get_info, &system_id);
685
if (XR_FAILED(result)) {
686
print_line("OpenXR: Failed to get system for our form factor [", get_error_string(result), "]");
687
return false;
688
}
689
690
// obtain info about our system, writing this out completely to make CI on Linux happy..
691
void *next_pointer = nullptr;
692
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
693
void *np = wrapper->set_system_properties_and_get_next_pointer(next_pointer);
694
if (np != nullptr) {
695
next_pointer = np;
696
}
697
}
698
699
XrSystemProperties system_properties = {
700
XR_TYPE_SYSTEM_PROPERTIES, // type
701
next_pointer, // next
702
0, // systemId, from here will be set by our get call
703
0, // vendorId
704
"", // systemName
705
{
706
0, // maxSwapchainImageHeight
707
0, // maxSwapchainImageWidth
708
0, // maxLayerCount
709
}, // graphicsProperties
710
{
711
false, // orientationTracking
712
false // positionTracking
713
} // trackingProperties
714
};
715
716
result = xrGetSystemProperties(instance, system_id, &system_properties);
717
if (XR_FAILED(result)) {
718
print_line("OpenXR: Failed to get System properties [", get_error_string(result), "]");
719
return false;
720
}
721
722
// remember this state, we'll use it later
723
system_name = String(system_properties.systemName);
724
vendor_id = system_properties.vendorId;
725
graphics_properties = system_properties.graphicsProperties;
726
tracking_properties = system_properties.trackingProperties;
727
728
return true;
729
}
730
731
bool OpenXRAPI::load_supported_view_configuration_types() {
732
// This queries the supported configuration types, likely there will only be one choosing between Mono (phone AR) and Stereo (HMDs)
733
734
ERR_FAIL_COND_V(instance == XR_NULL_HANDLE, false);
735
736
supported_view_configuration_types.clear();
737
738
uint32_t num_view_configuration_types = 0;
739
XrResult result = xrEnumerateViewConfigurations(instance, system_id, 0, &num_view_configuration_types, nullptr);
740
if (XR_FAILED(result)) {
741
print_line("OpenXR: Failed to get view configuration count [", get_error_string(result), "]");
742
return false;
743
}
744
745
supported_view_configuration_types.resize(num_view_configuration_types);
746
747
result = xrEnumerateViewConfigurations(instance, system_id, num_view_configuration_types, &num_view_configuration_types, supported_view_configuration_types.ptr());
748
ERR_FAIL_COND_V_MSG(XR_FAILED(result), false, "OpenXR: Failed to enumerateview configurations");
749
ERR_FAIL_COND_V_MSG(num_view_configuration_types == 0, false, "OpenXR: Failed to enumerateview configurations"); // JIC there should be at least 1!
750
751
for (const XrViewConfigurationType &view_configuration_type : supported_view_configuration_types) {
752
print_verbose(vformat("OpenXR: Found supported view configuration %s.", OpenXRUtil::get_view_configuration_name(view_configuration_type)));
753
}
754
755
// Check value we loaded at startup...
756
if (!is_view_configuration_supported(view_configuration)) {
757
print_verbose(vformat("OpenXR: %s isn't supported, defaulting to %s.", OpenXRUtil::get_view_configuration_name(view_configuration), OpenXRUtil::get_view_configuration_name(supported_view_configuration_types[0])));
758
759
view_configuration = supported_view_configuration_types[0];
760
}
761
762
return true;
763
}
764
765
bool OpenXRAPI::load_supported_environmental_blend_modes() {
766
// This queries the supported environmental blend modes.
767
768
ERR_FAIL_COND_V(instance == XR_NULL_HANDLE, false);
769
770
supported_environment_blend_modes.clear();
771
772
uint32_t num_supported_environment_blend_modes = 0;
773
XrResult result = xrEnumerateEnvironmentBlendModes(instance, system_id, view_configuration, 0, &num_supported_environment_blend_modes, nullptr);
774
if (XR_FAILED(result)) {
775
print_line("OpenXR: Failed to get supported environmental blend mode count [", get_error_string(result), "]");
776
return false;
777
}
778
779
supported_environment_blend_modes.resize(num_supported_environment_blend_modes);
780
781
result = xrEnumerateEnvironmentBlendModes(instance, system_id, view_configuration, num_supported_environment_blend_modes, &num_supported_environment_blend_modes, supported_environment_blend_modes.ptrw());
782
ERR_FAIL_COND_V_MSG(XR_FAILED(result), false, "OpenXR: Failed to enumerate environmental blend modes");
783
ERR_FAIL_COND_V_MSG(num_supported_environment_blend_modes == 0, false, "OpenXR: Failed to enumerate environmental blend modes"); // JIC there should be at least 1!
784
785
for (const XrEnvironmentBlendMode &supported_environment_blend_mode : supported_environment_blend_modes) {
786
print_verbose(vformat("OpenXR: Found environmental blend mode %s.", OpenXRUtil::get_environment_blend_mode_name(supported_environment_blend_mode)));
787
}
788
789
return true;
790
}
791
792
bool OpenXRAPI::is_view_configuration_supported(XrViewConfigurationType p_configuration_type) const {
793
return supported_view_configuration_types.has(p_configuration_type);
794
}
795
796
bool OpenXRAPI::load_supported_view_configuration_views(XrViewConfigurationType p_configuration_type) {
797
// This loads our view configuration for each view so for a stereo HMD, we'll get two entries (that are likely identical)
798
// The returned data supplies us with the recommended render target size
799
800
if (!is_view_configuration_supported(p_configuration_type)) {
801
print_line("OpenXR: View configuration ", OpenXRUtil::get_view_configuration_name(view_configuration), " is not supported.");
802
return false;
803
}
804
805
if (!view_configuration_views.is_empty()) {
806
// free previous results
807
view_configuration_views.clear();
808
}
809
810
uint32_t view_count = 0;
811
XrResult result = xrEnumerateViewConfigurationViews(instance, system_id, p_configuration_type, 0, &view_count, nullptr);
812
if (XR_FAILED(result)) {
813
print_line("OpenXR: Failed to get view configuration count [", get_error_string(result), "]");
814
return false;
815
}
816
817
view_configuration_views.resize(view_count);
818
819
for (XrViewConfigurationView &view_configuration_view : view_configuration_views) {
820
view_configuration_view.type = XR_TYPE_VIEW_CONFIGURATION_VIEW;
821
view_configuration_view.next = nullptr;
822
}
823
824
result = xrEnumerateViewConfigurationViews(instance, system_id, p_configuration_type, view_count, &view_count, view_configuration_views.ptr());
825
ERR_FAIL_COND_V_MSG(XR_FAILED(result), false, "OpenXR: Failed to enumerate view configurations");
826
827
for (const XrViewConfigurationView &view_configuration_view : view_configuration_views) {
828
print_verbose("OpenXR: Found supported view configuration view");
829
print_verbose(String(" - width: ") + itos(view_configuration_view.maxImageRectWidth));
830
print_verbose(String(" - height: ") + itos(view_configuration_view.maxImageRectHeight));
831
print_verbose(String(" - sample count: ") + itos(view_configuration_view.maxSwapchainSampleCount));
832
print_verbose(String(" - recommended render width: ") + itos(view_configuration_view.recommendedImageRectWidth));
833
print_verbose(String(" - recommended render height: ") + itos(view_configuration_view.recommendedImageRectHeight));
834
print_verbose(String(" - recommended render sample count: ") + itos(view_configuration_view.recommendedSwapchainSampleCount));
835
}
836
837
return true;
838
}
839
840
void OpenXRAPI::destroy_instance() {
841
view_configuration_views.clear();
842
supported_view_configuration_types.clear();
843
supported_environment_blend_modes.clear();
844
845
if (instance != XR_NULL_HANDLE) {
846
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
847
wrapper->on_instance_destroyed();
848
}
849
850
xrDestroyInstance(instance);
851
instance = XR_NULL_HANDLE;
852
}
853
enabled_extensions.clear();
854
855
if (graphics_extension != nullptr) {
856
unregister_extension_wrapper(graphics_extension);
857
memdelete(graphics_extension);
858
graphics_extension = nullptr;
859
}
860
}
861
862
bool OpenXRAPI::create_session() {
863
ERR_FAIL_COND_V(instance == XR_NULL_HANDLE, false);
864
ERR_FAIL_COND_V(session != XR_NULL_HANDLE, false);
865
866
void *next_pointer = nullptr;
867
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
868
void *np = wrapper->set_session_create_and_get_next_pointer(next_pointer);
869
if (np != nullptr) {
870
next_pointer = np;
871
}
872
}
873
874
XrSessionCreateInfo session_create_info = {
875
XR_TYPE_SESSION_CREATE_INFO, // type
876
next_pointer, // next
877
0, // createFlags
878
system_id // systemId
879
};
880
881
XrResult result = xrCreateSession(instance, &session_create_info, &session);
882
if (XR_FAILED(result)) {
883
print_line("OpenXR: Failed to create session [", get_error_string(result), "]");
884
return false;
885
}
886
887
set_object_name(XR_OBJECT_TYPE_SESSION, uint64_t(session), "Main Godot OpenXR Session");
888
889
begin_debug_label_region("Godot session active");
890
891
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
892
wrapper->on_session_created(session);
893
}
894
895
// Check our environment blend mode. This needs to happen after we call `on_session_created()`
896
// on the extension wrappers, so they can emulate alpha blend mode.
897
if (!set_environment_blend_mode(environment_blend_mode)) {
898
print_verbose(String("OpenXR: ") + OpenXRUtil::get_environment_blend_mode_name(environment_blend_mode) + String(" isn't supported, defaulting to ") + OpenXRUtil::get_environment_blend_mode_name(supported_environment_blend_modes[0]));
899
set_environment_blend_mode(supported_environment_blend_modes[0]);
900
}
901
902
return true;
903
}
904
905
bool OpenXRAPI::load_supported_reference_spaces() {
906
// loads the supported reference spaces for our OpenXR session
907
908
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false);
909
910
supported_reference_spaces.clear();
911
912
uint32_t num_reference_spaces = 0;
913
XrResult result = xrEnumerateReferenceSpaces(session, 0, &num_reference_spaces, nullptr);
914
if (XR_FAILED(result)) {
915
print_line("OpenXR: Failed to get reference space count [", get_error_string(result), "]");
916
return false;
917
}
918
919
supported_reference_spaces.resize(num_reference_spaces);
920
921
result = xrEnumerateReferenceSpaces(session, num_reference_spaces, &num_reference_spaces, supported_reference_spaces.ptr());
922
ERR_FAIL_COND_V_MSG(XR_FAILED(result), false, "OpenXR: Failed to enumerate reference spaces");
923
ERR_FAIL_COND_V_MSG(num_reference_spaces == 0, false, "OpenXR: Failed to enumerate reference spaces");
924
925
for (const XrReferenceSpaceType &supported_reference_space : supported_reference_spaces) {
926
print_verbose(vformat("OpenXR: Found supported reference space %s.", OpenXRUtil::get_reference_space_name(supported_reference_space)));
927
}
928
929
return true;
930
}
931
932
bool OpenXRAPI::is_reference_space_supported(XrReferenceSpaceType p_reference_space) {
933
return supported_reference_spaces.has(p_reference_space);
934
}
935
936
bool OpenXRAPI::setup_play_space() {
937
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false);
938
939
XrPosef identityPose = {
940
{ 0.0, 0.0, 0.0, 1.0 },
941
{ 0.0, 0.0, 0.0 }
942
};
943
944
XrReferenceSpaceType new_reference_space;
945
XrSpace new_play_space = XR_NULL_HANDLE;
946
bool will_emulate_local_floor = false;
947
948
if (custom_play_space != XR_NULL_HANDLE) {
949
new_play_space = custom_play_space;
950
// We use this to mark custom reference spaces.
951
new_reference_space = XR_REFERENCE_SPACE_TYPE_MAX_ENUM;
952
} else if (is_reference_space_supported(requested_reference_space)) {
953
new_reference_space = requested_reference_space;
954
} else if (requested_reference_space == XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT && is_reference_space_supported(XR_REFERENCE_SPACE_TYPE_STAGE)) {
955
print_verbose("OpenXR: LOCAL_FLOOR space isn't supported, emulating using STAGE and LOCAL spaces.");
956
957
new_reference_space = XR_REFERENCE_SPACE_TYPE_LOCAL;
958
will_emulate_local_floor = true;
959
960
if (local_floor_emulation.local_space == XR_NULL_HANDLE) {
961
XrReferenceSpaceCreateInfo create_info = {
962
XR_TYPE_REFERENCE_SPACE_CREATE_INFO, // type
963
nullptr, // next
964
XR_REFERENCE_SPACE_TYPE_LOCAL, // referenceSpaceType
965
identityPose, // poseInReferenceSpace
966
};
967
968
XrResult result = xrCreateReferenceSpace(session, &create_info, &local_floor_emulation.local_space);
969
if (XR_FAILED(result)) {
970
print_line("OpenXR: Failed to create LOCAL space in order to emulate LOCAL_FLOOR [", get_error_string(result), "]");
971
will_emulate_local_floor = false;
972
}
973
974
set_object_name(XR_OBJECT_TYPE_SPACE, uint64_t(local_floor_emulation.local_space), "Emulation local space");
975
}
976
977
if (local_floor_emulation.stage_space == XR_NULL_HANDLE) {
978
XrReferenceSpaceCreateInfo create_info = {
979
XR_TYPE_REFERENCE_SPACE_CREATE_INFO, // type
980
nullptr, // next
981
XR_REFERENCE_SPACE_TYPE_STAGE, // referenceSpaceType
982
identityPose, // poseInReferenceSpace
983
};
984
985
XrResult result = xrCreateReferenceSpace(session, &create_info, &local_floor_emulation.stage_space);
986
if (XR_FAILED(result)) {
987
print_line("OpenXR: Failed to create STAGE space in order to emulate LOCAL_FLOOR [", get_error_string(result), "]");
988
will_emulate_local_floor = false;
989
}
990
991
set_object_name(XR_OBJECT_TYPE_SPACE, uint64_t(local_floor_emulation.stage_space), "Emulation stage space");
992
}
993
994
if (!will_emulate_local_floor) {
995
if (local_floor_emulation.local_space != XR_NULL_HANDLE) {
996
xrDestroySpace(local_floor_emulation.local_space);
997
local_floor_emulation.local_space = XR_NULL_HANDLE;
998
}
999
if (local_floor_emulation.stage_space != XR_NULL_HANDLE) {
1000
xrDestroySpace(local_floor_emulation.stage_space);
1001
local_floor_emulation.stage_space = XR_NULL_HANDLE;
1002
}
1003
}
1004
} else {
1005
// Fallback on LOCAL, which all OpenXR runtimes are required to support.
1006
print_verbose(String("OpenXR: ") + OpenXRUtil::get_reference_space_name(requested_reference_space) + String(" isn't supported, defaulting to LOCAL space."));
1007
new_reference_space = XR_REFERENCE_SPACE_TYPE_LOCAL;
1008
}
1009
1010
if (new_play_space == XR_NULL_HANDLE) {
1011
void *next_pointer = nullptr;
1012
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
1013
void *np = wrapper->set_reference_space_create_info_and_get_next_pointer(
1014
new_reference_space, next_pointer);
1015
if (np != nullptr) {
1016
next_pointer = np;
1017
}
1018
}
1019
1020
XrReferenceSpaceCreateInfo play_space_create_info = {
1021
XR_TYPE_REFERENCE_SPACE_CREATE_INFO, // type
1022
next_pointer, // next
1023
new_reference_space, // referenceSpaceType
1024
identityPose, // poseInReferenceSpace
1025
};
1026
1027
XrResult result = xrCreateReferenceSpace(session, &play_space_create_info, &new_play_space);
1028
if (XR_FAILED(result)) {
1029
print_line("OpenXR: Failed to create play space [", get_error_string(result), "]");
1030
return false;
1031
}
1032
}
1033
1034
// If we've previously created a play space, clean it up first.
1035
// But if it was a custom reference space, we don't touch it - it's the job of the extension that
1036
// created it to clean it up.
1037
if (play_space != XR_NULL_HANDLE && reference_space != XR_REFERENCE_SPACE_TYPE_MAX_ENUM) {
1038
// TODO Investigate if destroying our play space here is safe,
1039
// it may still be used in the rendering thread.
1040
1041
xrDestroySpace(play_space);
1042
}
1043
play_space = new_play_space;
1044
reference_space = new_reference_space;
1045
1046
set_object_name(XR_OBJECT_TYPE_SPACE, uint64_t(play_space), "Play space");
1047
1048
local_floor_emulation.enabled = will_emulate_local_floor;
1049
local_floor_emulation.should_reset_floor_height = will_emulate_local_floor;
1050
1051
// Update render state so this play space is used rendering the upcoming frame.
1052
set_render_play_space(play_space);
1053
1054
return true;
1055
}
1056
1057
bool OpenXRAPI::setup_view_space() {
1058
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false);
1059
1060
if (!is_reference_space_supported(XR_REFERENCE_SPACE_TYPE_VIEW)) {
1061
print_line("OpenXR: reference space XR_REFERENCE_SPACE_TYPE_VIEW is not supported.");
1062
return false;
1063
}
1064
1065
XrPosef identityPose = {
1066
{ 0.0, 0.0, 0.0, 1.0 },
1067
{ 0.0, 0.0, 0.0 }
1068
};
1069
1070
void *next_pointer = nullptr;
1071
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
1072
void *np = wrapper->set_reference_space_create_info_and_get_next_pointer(XR_REFERENCE_SPACE_TYPE_VIEW, next_pointer);
1073
if (np != nullptr) {
1074
next_pointer = np;
1075
}
1076
}
1077
1078
XrReferenceSpaceCreateInfo view_space_create_info = {
1079
XR_TYPE_REFERENCE_SPACE_CREATE_INFO, // type
1080
next_pointer, // next
1081
XR_REFERENCE_SPACE_TYPE_VIEW, // referenceSpaceType
1082
identityPose // poseInReferenceSpace
1083
};
1084
1085
XrResult result = xrCreateReferenceSpace(session, &view_space_create_info, &view_space);
1086
if (XR_FAILED(result)) {
1087
print_line("OpenXR: Failed to create view space [", get_error_string(result), "]");
1088
return false;
1089
}
1090
1091
set_object_name(XR_OBJECT_TYPE_SPACE, uint64_t(view_space), "View space");
1092
1093
return true;
1094
}
1095
1096
bool OpenXRAPI::reset_emulated_floor_height() {
1097
ERR_FAIL_COND_V(!local_floor_emulation.enabled, false);
1098
ERR_FAIL_COND_V(local_floor_emulation.local_space == XR_NULL_HANDLE, false);
1099
ERR_FAIL_COND_V(local_floor_emulation.stage_space == XR_NULL_HANDLE, false);
1100
1101
XrResult result;
1102
1103
XrSpaceLocation stage_location = {
1104
XR_TYPE_SPACE_LOCATION, // type
1105
nullptr, // next
1106
0, // locationFlags
1107
{ { 0.0, 0.0, 0.0, 1.0 }, { 0.0, 0.0, 0.0 } }, // pose
1108
};
1109
1110
result = xrLocateSpace(local_floor_emulation.stage_space, local_floor_emulation.local_space, get_predicted_display_time(), &stage_location);
1111
1112
if (XR_FAILED(result)) {
1113
print_line("OpenXR: Failed to locate STAGE space in LOCAL space, in order to emulate LOCAL_FLOOR [", get_error_string(result), "]");
1114
return false;
1115
}
1116
1117
XrPosef pose = {
1118
{ 0.0, 0.0, 0.0, 1.0 },
1119
{ 0.0, stage_location.pose.position.y, 0.0 }
1120
};
1121
1122
XrReferenceSpaceCreateInfo create_info = {
1123
XR_TYPE_REFERENCE_SPACE_CREATE_INFO, // type
1124
nullptr, // next
1125
XR_REFERENCE_SPACE_TYPE_LOCAL, // referenceSpaceType
1126
pose, // poseInReferenceSpace
1127
};
1128
1129
XrSpace new_play_space;
1130
1131
void *next_pointer = nullptr;
1132
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
1133
void *np = wrapper->set_reference_space_create_info_and_get_next_pointer(
1134
create_info.referenceSpaceType, next_pointer);
1135
if (np != nullptr) {
1136
next_pointer = np;
1137
}
1138
}
1139
create_info.next = next_pointer;
1140
1141
result = xrCreateReferenceSpace(session, &create_info, &new_play_space);
1142
if (XR_FAILED(result)) {
1143
print_line("OpenXR: Failed to recreate emulated LOCAL_FLOOR play space with latest floor estimate [", get_error_string(result), "]");
1144
return false;
1145
}
1146
1147
xrDestroySpace(play_space);
1148
play_space = new_play_space;
1149
1150
// If we've made it this far, it means we can properly emulate LOCAL_FLOOR, so we'll
1151
// report that as the reference space to the outside world.
1152
reference_space = XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT;
1153
1154
// Update render state so this play space is used rendering the upcoming frame.
1155
set_render_play_space(play_space);
1156
1157
return true;
1158
}
1159
1160
bool OpenXRAPI::load_supported_swapchain_formats() {
1161
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false);
1162
1163
supported_swapchain_formats.clear();
1164
1165
uint32_t num_swapchain_formats = 0;
1166
XrResult result = xrEnumerateSwapchainFormats(session, 0, &num_swapchain_formats, nullptr);
1167
if (XR_FAILED(result)) {
1168
print_line("OpenXR: Failed to get swapchain format count [", get_error_string(result), "]");
1169
return false;
1170
}
1171
1172
supported_swapchain_formats.resize(num_swapchain_formats);
1173
1174
result = xrEnumerateSwapchainFormats(session, num_swapchain_formats, &num_swapchain_formats, supported_swapchain_formats.ptrw());
1175
ERR_FAIL_COND_V_MSG(XR_FAILED(result), false, "OpenXR: Failed to enumerate swapchain formats");
1176
1177
for (int64_t swapchain_format : supported_swapchain_formats) {
1178
print_verbose(String("OpenXR: Found supported swapchain format ") + get_swapchain_format_name(swapchain_format));
1179
}
1180
1181
return true;
1182
}
1183
1184
bool OpenXRAPI::is_swapchain_format_supported(int64_t p_swapchain_format) {
1185
return supported_swapchain_formats.has(p_swapchain_format);
1186
}
1187
1188
bool OpenXRAPI::obtain_swapchain_formats() {
1189
ERR_FAIL_NULL_V(graphics_extension, false);
1190
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false);
1191
1192
{
1193
// Build a vector with swapchain formats we want to use, from best fit to worst
1194
Vector<int64_t> usable_swapchain_formats;
1195
color_swapchain_format = 0;
1196
1197
graphics_extension->get_usable_swapchain_formats(usable_swapchain_formats);
1198
1199
// now find out which one is supported
1200
for (int i = 0; i < usable_swapchain_formats.size() && color_swapchain_format == 0; i++) {
1201
if (is_swapchain_format_supported(usable_swapchain_formats[i])) {
1202
color_swapchain_format = usable_swapchain_formats[i];
1203
}
1204
}
1205
1206
ERR_FAIL_COND_V_MSG(color_swapchain_format == 0, false, "OpenXR: No usable color swap chain format available!");
1207
1208
print_verbose(String("Using color swap chain format:") + get_swapchain_format_name(color_swapchain_format));
1209
}
1210
1211
{
1212
// Build a vector with swapchain formats we want to use, from best fit to worst
1213
Vector<int64_t> usable_swapchain_formats;
1214
depth_swapchain_format = 0;
1215
1216
graphics_extension->get_usable_depth_formats(usable_swapchain_formats);
1217
1218
// now find out which one is supported
1219
for (int i = 0; i < usable_swapchain_formats.size() && depth_swapchain_format == 0; i++) {
1220
if (is_swapchain_format_supported(usable_swapchain_formats[i])) {
1221
depth_swapchain_format = usable_swapchain_formats[i];
1222
}
1223
}
1224
1225
ERR_FAIL_COND_V_MSG(depth_swapchain_format == 0, false, "OpenXR: No usable depth swap chain format available!");
1226
1227
print_verbose(String("Using depth swap chain format:") + get_swapchain_format_name(depth_swapchain_format));
1228
}
1229
1230
return true;
1231
}
1232
1233
bool OpenXRAPI::create_main_swapchains(Size2i p_size) {
1234
ERR_NOT_ON_RENDER_THREAD_V(false);
1235
ERR_FAIL_NULL_V(graphics_extension, false);
1236
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false);
1237
1238
/*
1239
TODO: We need to improve on this, for now we're taking our old approach of creating our main swapchains and substituting
1240
those for the ones Godot normally creates.
1241
This however means we can only use swapchains for our main XR view.
1242
1243
It would have been nicer if we could override the swapchain creation in Godot with ours but we have a timing issue here.
1244
We can't create XR swapchains until after our XR session is fully instantiated, yet Godot creates its swapchain much earlier.
1245
1246
We only creates a swapchain for the main output here.
1247
Additional swapchains may be created through our composition layer extension.
1248
1249
Finally an area we need to expand upon is that Foveated rendering is only enabled for the swap chain we create,
1250
as we render 3D content into internal buffers that are copied into the swapchain, we do now have (basic) VRS support
1251
*/
1252
1253
render_state.main_swapchain_size = p_size;
1254
uint32_t sample_count = 1;
1255
1256
// We start with our color swapchain...
1257
if (color_swapchain_format != 0) {
1258
if (!render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].create(0, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT, color_swapchain_format, render_state.main_swapchain_size.width, render_state.main_swapchain_size.height, sample_count, view_configuration_views.size())) {
1259
return false;
1260
}
1261
1262
set_object_name(XR_OBJECT_TYPE_SWAPCHAIN, uint64_t(render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].get_swapchain()), "Main color swapchain");
1263
}
1264
1265
// We create our depth swapchain if:
1266
// - we've enabled submitting depth buffer
1267
// - we support our depth layer extension
1268
// - we have our spacewarp extension (not yet implemented)
1269
if (depth_swapchain_format != 0 && submit_depth_buffer && OpenXRCompositionLayerDepthExtension::get_singleton()->is_available()) {
1270
if (!render_state.main_swapchains[OPENXR_SWAPCHAIN_DEPTH].create(0, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, depth_swapchain_format, render_state.main_swapchain_size.width, render_state.main_swapchain_size.height, sample_count, view_configuration_views.size())) {
1271
return false;
1272
}
1273
1274
set_object_name(XR_OBJECT_TYPE_SWAPCHAIN, uint64_t(render_state.main_swapchains[OPENXR_SWAPCHAIN_DEPTH].get_swapchain()), "Main depth swapchain");
1275
}
1276
1277
// We create our velocity swapchain if:
1278
// - we have our spacewarp extension (not yet implemented)
1279
{
1280
// TBD
1281
}
1282
1283
for (uint32_t i = 0; i < render_state.views.size(); i++) {
1284
render_state.views[i].type = XR_TYPE_VIEW;
1285
render_state.views[i].next = nullptr;
1286
1287
render_state.projection_views[i].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;
1288
render_state.projection_views[i].next = nullptr;
1289
render_state.projection_views[i].subImage.swapchain = render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].get_swapchain();
1290
render_state.projection_views[i].subImage.imageArrayIndex = i;
1291
render_state.projection_views[i].subImage.imageRect.offset.x = 0;
1292
render_state.projection_views[i].subImage.imageRect.offset.y = 0;
1293
render_state.projection_views[i].subImage.imageRect.extent.width = render_state.main_swapchain_size.width;
1294
render_state.projection_views[i].subImage.imageRect.extent.height = render_state.main_swapchain_size.height;
1295
1296
if (render_state.submit_depth_buffer && OpenXRCompositionLayerDepthExtension::get_singleton()->is_available() && !render_state.depth_views.is_empty()) {
1297
render_state.projection_views[i].next = &render_state.depth_views[i];
1298
1299
render_state.depth_views[i].type = XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR;
1300
render_state.depth_views[i].next = nullptr;
1301
render_state.depth_views[i].subImage.swapchain = render_state.main_swapchains[OPENXR_SWAPCHAIN_DEPTH].get_swapchain();
1302
render_state.depth_views[i].subImage.imageArrayIndex = i;
1303
render_state.depth_views[i].subImage.imageRect.offset.x = 0;
1304
render_state.depth_views[i].subImage.imageRect.offset.y = 0;
1305
render_state.depth_views[i].subImage.imageRect.extent.width = render_state.main_swapchain_size.width;
1306
render_state.depth_views[i].subImage.imageRect.extent.height = render_state.main_swapchain_size.height;
1307
// OpenXR spec says that: minDepth < maxDepth.
1308
render_state.depth_views[i].minDepth = 0.0;
1309
render_state.depth_views[i].maxDepth = 1.0;
1310
// But we can reverse near and far for reverse-Z.
1311
render_state.depth_views[i].nearZ = 100.0; // Near and far Z will be set to the correct values in fill_projection_matrix
1312
render_state.depth_views[i].farZ = 0.01;
1313
}
1314
};
1315
1316
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
1317
wrapper->on_main_swapchains_created();
1318
}
1319
1320
return true;
1321
}
1322
1323
void OpenXRAPI::destroy_session() {
1324
// TODO need to figure out if we're still rendering our current frame
1325
// in a separate rendering thread and if so,
1326
// if we need to wait for completion.
1327
// We could be pulling the rug from underneath rendering...
1328
1329
if (running) {
1330
if (session != XR_NULL_HANDLE) {
1331
xrEndSession(session);
1332
}
1333
1334
running = false;
1335
render_state.running = false;
1336
}
1337
1338
render_state.views.clear();
1339
render_state.projection_views.clear();
1340
render_state.depth_views.clear();
1341
1342
free_main_swapchains();
1343
OpenXRSwapChainInfo::free_queued();
1344
1345
supported_swapchain_formats.clear();
1346
1347
// destroy our spaces
1348
if (play_space != XR_NULL_HANDLE) {
1349
xrDestroySpace(play_space);
1350
play_space = XR_NULL_HANDLE;
1351
render_state.play_space = XR_NULL_HANDLE;
1352
}
1353
if (view_space != XR_NULL_HANDLE) {
1354
xrDestroySpace(view_space);
1355
view_space = XR_NULL_HANDLE;
1356
}
1357
if (local_floor_emulation.local_space != XR_NULL_HANDLE) {
1358
xrDestroySpace(local_floor_emulation.local_space);
1359
local_floor_emulation.local_space = XR_NULL_HANDLE;
1360
}
1361
if (local_floor_emulation.stage_space != XR_NULL_HANDLE) {
1362
xrDestroySpace(local_floor_emulation.stage_space);
1363
local_floor_emulation.stage_space = XR_NULL_HANDLE;
1364
}
1365
local_floor_emulation.enabled = false;
1366
local_floor_emulation.should_reset_floor_height = false;
1367
1368
supported_reference_spaces.clear();
1369
1370
if (session != XR_NULL_HANDLE) {
1371
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
1372
wrapper->on_session_destroyed();
1373
}
1374
1375
end_debug_label_region();
1376
1377
xrDestroySession(session);
1378
session = XR_NULL_HANDLE;
1379
}
1380
}
1381
1382
bool OpenXRAPI::on_state_idle() {
1383
print_verbose("On state idle");
1384
1385
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
1386
wrapper->on_state_idle();
1387
}
1388
1389
return true;
1390
}
1391
1392
bool OpenXRAPI::on_state_ready() {
1393
print_verbose("On state ready");
1394
1395
// begin session
1396
XrSessionBeginInfo session_begin_info = {
1397
XR_TYPE_SESSION_BEGIN_INFO, // type
1398
nullptr, // next
1399
view_configuration // primaryViewConfigurationType
1400
};
1401
1402
XrResult result = xrBeginSession(session, &session_begin_info);
1403
if (XR_FAILED(result)) {
1404
print_line("OpenXR: Failed to begin session [", get_error_string(result), "]");
1405
return false;
1406
}
1407
1408
// we're running
1409
running = true;
1410
set_render_session_running(true);
1411
1412
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
1413
wrapper->on_state_ready();
1414
}
1415
1416
if (xr_interface) {
1417
xr_interface->on_state_ready();
1418
}
1419
1420
return true;
1421
}
1422
1423
bool OpenXRAPI::on_state_synchronized() {
1424
print_verbose("On state synchronized");
1425
1426
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
1427
wrapper->on_state_synchronized();
1428
}
1429
1430
if (xr_interface) {
1431
xr_interface->on_state_synchronized();
1432
}
1433
1434
return true;
1435
}
1436
1437
bool OpenXRAPI::on_state_visible() {
1438
print_verbose("On state visible");
1439
1440
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
1441
wrapper->on_state_visible();
1442
}
1443
1444
if (xr_interface) {
1445
xr_interface->on_state_visible();
1446
}
1447
1448
return true;
1449
}
1450
1451
bool OpenXRAPI::on_state_focused() {
1452
print_verbose("On state focused");
1453
1454
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
1455
wrapper->on_state_focused();
1456
}
1457
1458
if (xr_interface) {
1459
xr_interface->on_state_focused();
1460
}
1461
1462
return true;
1463
}
1464
1465
bool OpenXRAPI::on_state_stopping() {
1466
print_verbose("On state stopping");
1467
1468
if (xr_interface) {
1469
xr_interface->on_state_stopping();
1470
}
1471
1472
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
1473
wrapper->on_state_stopping();
1474
}
1475
1476
if (running) {
1477
XrResult result = xrEndSession(session);
1478
if (XR_FAILED(result)) {
1479
// we only report this..
1480
print_line("OpenXR: Failed to end session [", get_error_string(result), "]");
1481
}
1482
1483
running = false;
1484
set_render_session_running(false);
1485
}
1486
1487
return true;
1488
}
1489
1490
bool OpenXRAPI::on_state_loss_pending() {
1491
print_verbose("On state loss pending");
1492
1493
if (xr_interface) {
1494
xr_interface->on_state_loss_pending();
1495
}
1496
1497
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
1498
wrapper->on_state_loss_pending();
1499
}
1500
1501
return true;
1502
}
1503
1504
bool OpenXRAPI::on_state_exiting() {
1505
print_verbose("On state existing");
1506
1507
if (xr_interface) {
1508
xr_interface->on_state_exiting();
1509
}
1510
1511
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
1512
wrapper->on_state_exiting();
1513
}
1514
1515
return true;
1516
}
1517
1518
void OpenXRAPI::set_form_factor(XrFormFactor p_form_factor) {
1519
ERR_FAIL_COND(is_initialized());
1520
1521
form_factor = p_form_factor;
1522
}
1523
1524
uint32_t OpenXRAPI::get_view_count() {
1525
return view_configuration_views.size();
1526
}
1527
1528
void OpenXRAPI::set_view_configuration(XrViewConfigurationType p_view_configuration) {
1529
ERR_FAIL_COND(is_initialized());
1530
1531
view_configuration = p_view_configuration;
1532
}
1533
1534
bool OpenXRAPI::set_requested_reference_space(XrReferenceSpaceType p_requested_reference_space) {
1535
if (custom_play_space != XR_NULL_HANDLE) {
1536
return false;
1537
}
1538
1539
requested_reference_space = p_requested_reference_space;
1540
play_space_is_dirty = true;
1541
1542
return true;
1543
}
1544
1545
void OpenXRAPI::set_custom_play_space(XrSpace p_custom_space) {
1546
custom_play_space = p_custom_space;
1547
play_space_is_dirty = true;
1548
}
1549
1550
void OpenXRAPI::set_submit_depth_buffer(bool p_submit_depth_buffer) {
1551
ERR_FAIL_COND(is_initialized());
1552
1553
submit_depth_buffer = p_submit_depth_buffer;
1554
}
1555
1556
bool OpenXRAPI::is_initialized() {
1557
return (instance != XR_NULL_HANDLE);
1558
}
1559
1560
bool OpenXRAPI::is_running() {
1561
if (instance == XR_NULL_HANDLE) {
1562
return false;
1563
}
1564
if (session == XR_NULL_HANDLE) {
1565
return false;
1566
}
1567
1568
return running;
1569
}
1570
1571
bool OpenXRAPI::openxr_loader_init() {
1572
#ifdef ANDROID_ENABLED
1573
ERR_FAIL_COND_V_MSG(openxr_loader_library_handle != nullptr, false, "OpenXR Loader library is already loaded.");
1574
1575
{
1576
Error error_code = OS::get_singleton()->open_dynamic_library(OPENXR_LOADER_NAME, openxr_loader_library_handle);
1577
ERR_FAIL_COND_V_MSG(error_code != OK, false, "OpenXR loader not found.");
1578
}
1579
1580
{
1581
Error error_code = OS::get_singleton()->get_dynamic_library_symbol_handle(openxr_loader_library_handle, "xrGetInstanceProcAddr", (void *&)xrGetInstanceProcAddr);
1582
ERR_FAIL_COND_V_MSG(error_code != OK, false, "Symbol xrGetInstanceProcAddr not found in OpenXR Loader library.");
1583
}
1584
#endif
1585
1586
// Resolve the symbols that don't require an instance
1587
OPENXR_API_INIT_XR_FUNC_V(xrCreateInstance);
1588
OPENXR_API_INIT_XR_FUNC_V(xrEnumerateApiLayerProperties);
1589
OPENXR_API_INIT_XR_FUNC_V(xrEnumerateInstanceExtensionProperties);
1590
1591
return true;
1592
}
1593
1594
bool OpenXRAPI::resolve_instance_openxr_symbols() {
1595
ERR_FAIL_COND_V(instance == XR_NULL_HANDLE, false);
1596
1597
OPENXR_API_INIT_XR_FUNC_V(xrAcquireSwapchainImage);
1598
OPENXR_API_INIT_XR_FUNC_V(xrApplyHapticFeedback);
1599
OPENXR_API_INIT_XR_FUNC_V(xrAttachSessionActionSets);
1600
OPENXR_API_INIT_XR_FUNC_V(xrBeginFrame);
1601
OPENXR_API_INIT_XR_FUNC_V(xrBeginSession);
1602
OPENXR_API_INIT_XR_FUNC_V(xrCreateAction);
1603
OPENXR_API_INIT_XR_FUNC_V(xrCreateActionSet);
1604
OPENXR_API_INIT_XR_FUNC_V(xrCreateActionSpace);
1605
OPENXR_API_INIT_XR_FUNC_V(xrCreateReferenceSpace);
1606
OPENXR_API_INIT_XR_FUNC_V(xrCreateSession);
1607
OPENXR_API_INIT_XR_FUNC_V(xrCreateSwapchain);
1608
OPENXR_API_INIT_XR_FUNC_V(xrDestroyAction);
1609
OPENXR_API_INIT_XR_FUNC_V(xrDestroyActionSet);
1610
OPENXR_API_INIT_XR_FUNC_V(xrDestroyInstance);
1611
OPENXR_API_INIT_XR_FUNC_V(xrDestroySession);
1612
OPENXR_API_INIT_XR_FUNC_V(xrDestroySpace);
1613
OPENXR_API_INIT_XR_FUNC_V(xrDestroySwapchain);
1614
OPENXR_API_INIT_XR_FUNC_V(xrEndFrame);
1615
OPENXR_API_INIT_XR_FUNC_V(xrEndSession);
1616
OPENXR_API_INIT_XR_FUNC_V(xrEnumerateEnvironmentBlendModes);
1617
OPENXR_API_INIT_XR_FUNC_V(xrEnumerateReferenceSpaces);
1618
OPENXR_API_INIT_XR_FUNC_V(xrEnumerateSwapchainFormats);
1619
OPENXR_API_INIT_XR_FUNC_V(xrEnumerateViewConfigurations);
1620
OPENXR_API_INIT_XR_FUNC_V(xrEnumerateViewConfigurationViews);
1621
OPENXR_API_INIT_XR_FUNC_V(xrGetActionStateBoolean);
1622
OPENXR_API_INIT_XR_FUNC_V(xrGetActionStateFloat);
1623
OPENXR_API_INIT_XR_FUNC_V(xrGetActionStateVector2f);
1624
OPENXR_API_INIT_XR_FUNC_V(xrGetCurrentInteractionProfile);
1625
OPENXR_API_INIT_XR_FUNC_V(xrGetReferenceSpaceBoundsRect);
1626
OPENXR_API_INIT_XR_FUNC_V(xrGetSystem);
1627
OPENXR_API_INIT_XR_FUNC_V(xrGetSystemProperties);
1628
OPENXR_API_INIT_XR_FUNC_V(xrLocateViews);
1629
OPENXR_API_INIT_XR_FUNC_V(xrLocateSpace);
1630
OPENXR_API_INIT_XR_FUNC_V(xrPathToString);
1631
OPENXR_API_INIT_XR_FUNC_V(xrPollEvent);
1632
OPENXR_API_INIT_XR_FUNC_V(xrReleaseSwapchainImage);
1633
OPENXR_API_INIT_XR_FUNC_V(xrResultToString);
1634
OPENXR_API_INIT_XR_FUNC_V(xrStringToPath);
1635
OPENXR_API_INIT_XR_FUNC_V(xrSuggestInteractionProfileBindings);
1636
OPENXR_API_INIT_XR_FUNC_V(xrSyncActions);
1637
OPENXR_API_INIT_XR_FUNC_V(xrWaitFrame);
1638
OPENXR_API_INIT_XR_FUNC_V(xrWaitSwapchainImage);
1639
1640
return true;
1641
}
1642
1643
XrResult OpenXRAPI::try_get_instance_proc_addr(const char *p_name, PFN_xrVoidFunction *p_addr) {
1644
return xrGetInstanceProcAddr(instance, p_name, p_addr);
1645
}
1646
1647
XrResult OpenXRAPI::get_instance_proc_addr(const char *p_name, PFN_xrVoidFunction *p_addr) {
1648
XrResult result = try_get_instance_proc_addr(p_name, p_addr);
1649
1650
if (result != XR_SUCCESS) {
1651
String error_message = String("Symbol ") + p_name + " not found in OpenXR instance.";
1652
ERR_FAIL_V_MSG(result, error_message.utf8().get_data());
1653
}
1654
1655
return result;
1656
}
1657
1658
bool OpenXRAPI::initialize(const String &p_rendering_driver) {
1659
ERR_FAIL_COND_V_MSG(instance != XR_NULL_HANDLE, false, "OpenXR instance was already created");
1660
1661
if (!openxr_loader_init()) {
1662
return false;
1663
}
1664
1665
if (p_rendering_driver == "vulkan") {
1666
#ifdef VULKAN_ENABLED
1667
graphics_extension = memnew(OpenXRVulkanExtension);
1668
register_extension_wrapper(graphics_extension);
1669
#else
1670
// shouldn't be possible...
1671
ERR_FAIL_V(false);
1672
#endif
1673
} else if (p_rendering_driver == "metal") {
1674
#ifdef METAL_ENABLED
1675
graphics_extension = memnew(OpenXRMetalExtension);
1676
register_extension_wrapper(graphics_extension);
1677
#else
1678
// shouldn't be possible...
1679
ERR_FAIL_V(false);
1680
#endif
1681
} else if (p_rendering_driver == "opengl3") {
1682
#if defined(GLES3_ENABLED) && !defined(MACOS_ENABLED)
1683
graphics_extension = memnew(OpenXROpenGLExtension);
1684
register_extension_wrapper(graphics_extension);
1685
#else
1686
// shouldn't be possible...
1687
ERR_FAIL_V(false);
1688
#endif
1689
} else if (p_rendering_driver == "d3d12") {
1690
#ifdef D3D12_ENABLED
1691
graphics_extension = memnew(OpenXRD3D12Extension);
1692
register_extension_wrapper(graphics_extension);
1693
#else
1694
// shouldn't be possible...
1695
ERR_FAIL_V(false);
1696
#endif
1697
} else {
1698
ERR_FAIL_V_MSG(false, "OpenXR: Unsupported rendering device.");
1699
}
1700
1701
// Also register our rendering extensions
1702
register_extension_wrapper(memnew(OpenXRFBUpdateSwapchainExtension(p_rendering_driver)));
1703
register_extension_wrapper(memnew(OpenXRFBFoveationExtension(p_rendering_driver)));
1704
1705
// initialize
1706
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
1707
wrapper->on_before_instance_created();
1708
}
1709
1710
if (!load_layer_properties()) {
1711
destroy_instance();
1712
return false;
1713
}
1714
1715
if (!load_supported_extensions()) {
1716
destroy_instance();
1717
return false;
1718
}
1719
1720
if (!create_instance()) {
1721
destroy_instance();
1722
return false;
1723
}
1724
1725
if (!resolve_instance_openxr_symbols()) {
1726
destroy_instance();
1727
return false;
1728
}
1729
1730
if (!get_system_info()) {
1731
destroy_instance();
1732
return false;
1733
}
1734
1735
if (!load_supported_view_configuration_types()) {
1736
destroy_instance();
1737
return false;
1738
}
1739
1740
if (!load_supported_view_configuration_views(view_configuration)) {
1741
destroy_instance();
1742
return false;
1743
}
1744
1745
if (!load_supported_environmental_blend_modes()) {
1746
destroy_instance();
1747
return false;
1748
}
1749
1750
return true;
1751
}
1752
1753
bool OpenXRAPI::initialize_session() {
1754
if (!create_session()) {
1755
destroy_session();
1756
return false;
1757
}
1758
1759
if (!load_supported_reference_spaces()) {
1760
destroy_session();
1761
return false;
1762
}
1763
1764
if (!setup_view_space()) {
1765
destroy_session();
1766
return false;
1767
}
1768
1769
if (!load_supported_swapchain_formats()) {
1770
destroy_session();
1771
return false;
1772
}
1773
1774
if (!obtain_swapchain_formats()) {
1775
destroy_session();
1776
return false;
1777
}
1778
1779
allocate_view_buffers(view_configuration_views.size(), submit_depth_buffer);
1780
1781
return true;
1782
}
1783
1784
void OpenXRAPI::finish() {
1785
destroy_session();
1786
1787
destroy_instance();
1788
}
1789
1790
void OpenXRAPI::set_xr_interface(OpenXRInterface *p_xr_interface) {
1791
xr_interface = p_xr_interface;
1792
}
1793
1794
void OpenXRAPI::register_extension_wrapper(OpenXRExtensionWrapper *p_extension_wrapper) {
1795
registered_extension_wrappers.push_back(p_extension_wrapper);
1796
}
1797
1798
void OpenXRAPI::unregister_extension_wrapper(OpenXRExtensionWrapper *p_extension_wrapper) {
1799
registered_extension_wrappers.erase(p_extension_wrapper);
1800
}
1801
1802
const Vector<OpenXRExtensionWrapper *> &OpenXRAPI::get_registered_extension_wrappers() {
1803
return registered_extension_wrappers;
1804
}
1805
1806
void OpenXRAPI::register_extension_metadata() {
1807
for (OpenXRExtensionWrapper *extension_wrapper : registered_extension_wrappers) {
1808
extension_wrapper->on_register_metadata();
1809
}
1810
}
1811
1812
void OpenXRAPI::cleanup_extension_wrappers() {
1813
for (OpenXRExtensionWrapper *extension_wrapper : registered_extension_wrappers) {
1814
#ifndef DISABLE_DEPRECATED
1815
// Fix crash when the extension wrapper comes from GDExtension.
1816
OpenXRExtensionWrapperExtension *gdextension_extension_wrapper = dynamic_cast<OpenXRExtensionWrapperExtension *>(extension_wrapper);
1817
if (gdextension_extension_wrapper) {
1818
memdelete(gdextension_extension_wrapper);
1819
} else
1820
#endif
1821
{
1822
memdelete(extension_wrapper);
1823
}
1824
}
1825
registered_extension_wrappers.clear();
1826
}
1827
1828
XrHandTrackerEXT OpenXRAPI::get_hand_tracker(int p_hand_index) {
1829
ERR_FAIL_INDEX_V(p_hand_index, OpenXRHandTrackingExtension::HandTrackedHands::OPENXR_MAX_TRACKED_HANDS, XR_NULL_HANDLE);
1830
1831
OpenXRHandTrackingExtension *hand_tracking = OpenXRHandTrackingExtension::get_singleton();
1832
ERR_FAIL_NULL_V(hand_tracking, XR_NULL_HANDLE);
1833
1834
OpenXRHandTrackingExtension::HandTrackedHands hand = static_cast<OpenXRHandTrackingExtension::HandTrackedHands>(p_hand_index);
1835
return hand_tracking->get_hand_tracker(hand)->hand_tracker;
1836
}
1837
1838
Size2 OpenXRAPI::get_recommended_target_size() {
1839
RenderingServer *rendering_server = RenderingServer::get_singleton();
1840
ERR_FAIL_COND_V(view_configuration_views.is_empty(), Size2());
1841
1842
Size2 target_size;
1843
1844
if (rendering_server && rendering_server->is_on_render_thread()) {
1845
target_size.width = view_configuration_views[0].recommendedImageRectWidth * render_state.render_target_size_multiplier;
1846
target_size.height = view_configuration_views[0].recommendedImageRectHeight * render_state.render_target_size_multiplier;
1847
} else {
1848
target_size.width = view_configuration_views[0].recommendedImageRectWidth * render_target_size_multiplier;
1849
target_size.height = view_configuration_views[0].recommendedImageRectHeight * render_target_size_multiplier;
1850
}
1851
1852
return target_size;
1853
}
1854
1855
XRPose::TrackingConfidence OpenXRAPI::get_head_center(Transform3D &r_transform, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity) {
1856
XrResult result;
1857
1858
if (!running) {
1859
return XRPose::XR_TRACKING_CONFIDENCE_NONE;
1860
}
1861
1862
// Get display time
1863
XrTime display_time = get_predicted_display_time();
1864
if (display_time == 0) {
1865
return XRPose::XR_TRACKING_CONFIDENCE_NONE;
1866
}
1867
1868
XrSpaceVelocity velocity = {
1869
XR_TYPE_SPACE_VELOCITY, // type
1870
nullptr, // next
1871
0, // velocityFlags
1872
{ 0.0, 0.0, 0.0 }, // linearVelocity
1873
{ 0.0, 0.0, 0.0 } // angularVelocity
1874
};
1875
1876
XrSpaceLocation location = {
1877
XR_TYPE_SPACE_LOCATION, // type
1878
&velocity, // next
1879
0, // locationFlags
1880
{
1881
{ 0.0, 0.0, 0.0, 0.0 }, // orientation
1882
{ 0.0, 0.0, 0.0 } // position
1883
} // pose
1884
};
1885
1886
result = xrLocateSpace(view_space, play_space, display_time, &location);
1887
if (XR_FAILED(result)) {
1888
print_line("OpenXR: Failed to locate view space in play space [", get_error_string(result), "]");
1889
return XRPose::XR_TRACKING_CONFIDENCE_NONE;
1890
}
1891
1892
XRPose::TrackingConfidence confidence = transform_from_location(location, r_transform);
1893
parse_velocities(velocity, r_linear_velocity, r_angular_velocity);
1894
1895
if (head_pose_confidence != confidence) {
1896
// prevent error spam
1897
head_pose_confidence = confidence;
1898
if (head_pose_confidence == XRPose::XR_TRACKING_CONFIDENCE_NONE) {
1899
print_line("OpenXR head space location not valid (check tracking?)");
1900
} else if (head_pose_confidence == XRPose::XR_TRACKING_CONFIDENCE_LOW) {
1901
print_verbose("OpenVR Head pose now tracking with low confidence");
1902
} else {
1903
print_verbose("OpenVR Head pose now tracking with high confidence");
1904
}
1905
}
1906
1907
return confidence;
1908
}
1909
1910
bool OpenXRAPI::get_view_transform(uint32_t p_view, Transform3D &r_transform) {
1911
ERR_NOT_ON_RENDER_THREAD_V(false);
1912
1913
if (!render_state.running) {
1914
return false;
1915
}
1916
1917
// we don't have valid view info
1918
if (render_state.views.is_empty() || !render_state.view_pose_valid) {
1919
return false;
1920
}
1921
1922
// Note, the timing of this is set right before rendering, which is what we need here.
1923
r_transform = transform_from_pose(render_state.views[p_view].pose);
1924
1925
return true;
1926
}
1927
1928
bool OpenXRAPI::get_view_projection(uint32_t p_view, double p_z_near, double p_z_far, Projection &p_camera_matrix) {
1929
ERR_NOT_ON_RENDER_THREAD_V(false);
1930
ERR_FAIL_NULL_V(graphics_extension, false);
1931
1932
if (!render_state.running) {
1933
return false;
1934
}
1935
1936
// we don't have valid view info
1937
if (render_state.views.is_empty() || !render_state.view_pose_valid) {
1938
return false;
1939
}
1940
1941
// if we're using depth views, make sure we update our near and far there...
1942
if (!render_state.depth_views.is_empty()) {
1943
for (XrCompositionLayerDepthInfoKHR &depth_view : render_state.depth_views) {
1944
// As we are using reverse-Z these need to be flipped.
1945
depth_view.nearZ = p_z_far;
1946
depth_view.farZ = p_z_near;
1947
}
1948
}
1949
1950
render_state.z_near = p_z_near;
1951
render_state.z_far = p_z_far;
1952
1953
// now update our projection
1954
return graphics_extension->create_projection_fov(render_state.views[p_view].fov, p_z_near, p_z_far, p_camera_matrix);
1955
}
1956
1957
Vector2 OpenXRAPI::get_eye_focus(uint32_t p_view, float p_aspect) {
1958
ERR_FAIL_NULL_V(graphics_extension, Vector2());
1959
1960
if (!render_state.running) {
1961
return Vector2();
1962
}
1963
1964
// xrWaitFrame not run yet
1965
if (render_state.predicted_display_time == 0) {
1966
return Vector2();
1967
}
1968
1969
// we don't have valid view info
1970
if (render_state.views.is_empty() || !render_state.view_pose_valid) {
1971
return Vector2();
1972
}
1973
1974
Projection cm;
1975
if (!graphics_extension->create_projection_fov(render_state.views[p_view].fov, 0.1, 1000.0, cm)) {
1976
return Vector2();
1977
}
1978
1979
// Default focus to center...
1980
Vector3 focus = cm.xform(Vector3(0.0, 0.0, 999.9));
1981
1982
// Lets check for eye tracking...
1983
OpenXREyeGazeInteractionExtension *eye_gaze_interaction = OpenXREyeGazeInteractionExtension::get_singleton();
1984
if (eye_gaze_interaction && eye_gaze_interaction->supports_eye_gaze_interaction()) {
1985
Vector3 eye_gaze_pose;
1986
if (eye_gaze_interaction->get_eye_gaze_pose(1.0, eye_gaze_pose)) {
1987
Transform3D view_transform = transform_from_pose(render_state.views[p_view].pose);
1988
1989
eye_gaze_pose = view_transform.xform_inv(eye_gaze_pose);
1990
focus = cm.xform(eye_gaze_pose);
1991
}
1992
}
1993
1994
return Vector2(focus.x, focus.y);
1995
}
1996
1997
bool OpenXRAPI::poll_events() {
1998
ERR_FAIL_COND_V(instance == XR_NULL_HANDLE, false);
1999
2000
XrEventDataBuffer runtimeEvent;
2001
runtimeEvent.type = XR_TYPE_EVENT_DATA_BUFFER;
2002
runtimeEvent.next = nullptr;
2003
// runtimeEvent.varying = ...
2004
2005
XrResult pollResult = xrPollEvent(instance, &runtimeEvent);
2006
while (pollResult == XR_SUCCESS) {
2007
bool handled = false;
2008
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
2009
handled |= wrapper->on_event_polled(runtimeEvent);
2010
}
2011
switch (runtimeEvent.type) {
2012
case XR_TYPE_EVENT_DATA_EVENTS_LOST: {
2013
XrEventDataEventsLost *event = (XrEventDataEventsLost *)&runtimeEvent;
2014
2015
// We probably didn't poll fast enough, just output warning
2016
WARN_PRINT("OpenXR EVENT: " + itos(event->lostEventCount) + " event data lost!");
2017
} break;
2018
case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING: {
2019
XrEventDataInstanceLossPending *event = (XrEventDataInstanceLossPending *)&runtimeEvent;
2020
2021
// TODO We get this event if we're about to loose our OpenXR instance.
2022
// We should queue exiting Godot at this point.
2023
2024
print_verbose(String("OpenXR EVENT: instance loss pending at ") + itos(event->lossTime));
2025
return false;
2026
} break;
2027
case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: {
2028
XrEventDataSessionStateChanged *event = (XrEventDataSessionStateChanged *)&runtimeEvent;
2029
2030
session_state = event->state;
2031
if (session_state >= XR_SESSION_STATE_MAX_ENUM) {
2032
print_verbose(String("OpenXR EVENT: session state changed to UNKNOWN - ") + itos(session_state));
2033
} else {
2034
print_verbose(String("OpenXR EVENT: session state changed to ") + OpenXRUtil::get_session_state_name(session_state));
2035
2036
switch (session_state) {
2037
case XR_SESSION_STATE_IDLE:
2038
on_state_idle();
2039
break;
2040
case XR_SESSION_STATE_READY:
2041
on_state_ready();
2042
break;
2043
case XR_SESSION_STATE_SYNCHRONIZED:
2044
on_state_synchronized();
2045
break;
2046
case XR_SESSION_STATE_VISIBLE:
2047
on_state_visible();
2048
break;
2049
case XR_SESSION_STATE_FOCUSED:
2050
on_state_focused();
2051
break;
2052
case XR_SESSION_STATE_STOPPING:
2053
on_state_stopping();
2054
break;
2055
case XR_SESSION_STATE_LOSS_PENDING:
2056
on_state_loss_pending();
2057
break;
2058
case XR_SESSION_STATE_EXITING:
2059
on_state_exiting();
2060
break;
2061
default:
2062
break;
2063
}
2064
}
2065
} break;
2066
case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING: {
2067
XrEventDataReferenceSpaceChangePending *event = (XrEventDataReferenceSpaceChangePending *)&runtimeEvent;
2068
2069
print_verbose(String("OpenXR EVENT: reference space type ") + OpenXRUtil::get_reference_space_name(event->referenceSpaceType) + " change pending!");
2070
if (local_floor_emulation.enabled) {
2071
local_floor_emulation.should_reset_floor_height = true;
2072
}
2073
2074
if (xr_interface) {
2075
xr_interface->on_reference_space_change_pending();
2076
}
2077
} break;
2078
case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED: {
2079
print_verbose("OpenXR EVENT: interaction profile changed!");
2080
2081
XrEventDataInteractionProfileChanged *event = (XrEventDataInteractionProfileChanged *)&runtimeEvent;
2082
if (event->session == session) {
2083
// Make sure we get our interaction profile change
2084
interaction_profile_changed = true;
2085
}
2086
} break;
2087
default:
2088
if (!handled) {
2089
print_verbose(String("OpenXR Unhandled event type ") + OpenXRUtil::get_structure_type_name(runtimeEvent.type));
2090
}
2091
break;
2092
}
2093
2094
runtimeEvent.type = XR_TYPE_EVENT_DATA_BUFFER;
2095
pollResult = xrPollEvent(instance, &runtimeEvent);
2096
}
2097
2098
if (pollResult == XR_EVENT_UNAVAILABLE) {
2099
// processed all events in the queue
2100
return true;
2101
} else {
2102
ERR_FAIL_V_MSG(false, "OpenXR: Failed to poll events!");
2103
}
2104
}
2105
2106
void OpenXRAPI::_allocate_view_buffers(uint32_t p_view_count, bool p_submit_depth_buffer) {
2107
// Must be called from rendering thread!
2108
ERR_NOT_ON_RENDER_THREAD;
2109
2110
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
2111
ERR_FAIL_NULL(openxr_api);
2112
2113
openxr_api->render_state.submit_depth_buffer = p_submit_depth_buffer;
2114
2115
// Allocate buffers we'll be populating with view information.
2116
openxr_api->render_state.views.resize(p_view_count);
2117
openxr_api->render_state.projection_views.resize(p_view_count);
2118
2119
if (p_submit_depth_buffer && OpenXRCompositionLayerDepthExtension::get_singleton()->is_available()) {
2120
openxr_api->render_state.depth_views.resize(p_view_count);
2121
}
2122
}
2123
2124
void OpenXRAPI::_set_render_session_running(bool p_is_running) {
2125
// Must be called from rendering thread!
2126
ERR_NOT_ON_RENDER_THREAD;
2127
2128
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
2129
ERR_FAIL_NULL(openxr_api);
2130
openxr_api->render_state.running = p_is_running;
2131
}
2132
2133
void OpenXRAPI::_set_render_display_info(XrTime p_predicted_display_time, bool p_should_render) {
2134
// Must be called from rendering thread!
2135
ERR_NOT_ON_RENDER_THREAD;
2136
2137
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
2138
ERR_FAIL_NULL(openxr_api);
2139
openxr_api->render_state.predicted_display_time = p_predicted_display_time;
2140
openxr_api->render_state.should_render = p_should_render;
2141
}
2142
2143
void OpenXRAPI::_set_render_play_space(uint64_t p_play_space) {
2144
// Must be called from rendering thread!
2145
ERR_NOT_ON_RENDER_THREAD;
2146
2147
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
2148
ERR_FAIL_NULL(openxr_api);
2149
openxr_api->render_state.play_space = XrSpace(p_play_space);
2150
}
2151
2152
void OpenXRAPI::_set_render_state_multiplier(double p_render_target_size_multiplier) {
2153
// Must be called from rendering thread!
2154
ERR_NOT_ON_RENDER_THREAD;
2155
2156
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
2157
ERR_FAIL_NULL(openxr_api);
2158
openxr_api->render_state.render_target_size_multiplier = p_render_target_size_multiplier;
2159
}
2160
2161
void OpenXRAPI::_set_render_state_render_region(const Rect2i &p_render_region) {
2162
ERR_NOT_ON_RENDER_THREAD;
2163
2164
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
2165
ERR_FAIL_NULL(openxr_api);
2166
openxr_api->render_state.render_region = p_render_region;
2167
}
2168
2169
bool OpenXRAPI::process() {
2170
ERR_FAIL_COND_V(instance == XR_NULL_HANDLE, false);
2171
2172
if (!poll_events()) {
2173
return false;
2174
}
2175
2176
if (!running) {
2177
return false;
2178
}
2179
2180
// We call xrWaitFrame as early as possible, this will allow OpenXR to get
2181
// proper timing info between this point, and when we're ready to start rendering.
2182
// As the name suggests, OpenXR can pause the thread to minimize the time between
2183
// retrieving tracking data and using that tracking data to render.
2184
// OpenXR thus works best if rendering is performed on a separate thread.
2185
void *frame_wait_info_next_pointer = nullptr;
2186
for (OpenXRExtensionWrapper *extension : frame_info_extensions) {
2187
void *np = extension->set_frame_wait_info_and_get_next_pointer(frame_wait_info_next_pointer);
2188
if (np != nullptr) {
2189
frame_wait_info_next_pointer = np;
2190
}
2191
}
2192
2193
XrFrameWaitInfo frame_wait_info = { XR_TYPE_FRAME_WAIT_INFO, frame_wait_info_next_pointer };
2194
frame_state.predictedDisplayTime = 0;
2195
frame_state.predictedDisplayPeriod = 0;
2196
frame_state.shouldRender = false;
2197
2198
XrResult result = xrWaitFrame(session, &frame_wait_info, &frame_state);
2199
if (XR_FAILED(result)) {
2200
print_line("OpenXR: xrWaitFrame() was not successful [", get_error_string(result), "]");
2201
2202
// reset just in case
2203
frame_state.predictedDisplayTime = 0;
2204
frame_state.predictedDisplayPeriod = 0;
2205
frame_state.shouldRender = false;
2206
2207
set_render_display_info(0, false);
2208
2209
return false;
2210
}
2211
2212
if (frame_state.predictedDisplayPeriod > 500000000) {
2213
// display period more then 0.5 seconds? must be wrong data
2214
print_verbose(String("OpenXR resetting invalid display period ") + rtos(frame_state.predictedDisplayPeriod));
2215
frame_state.predictedDisplayPeriod = 0;
2216
}
2217
2218
set_render_display_info(frame_state.predictedDisplayTime, frame_state.shouldRender);
2219
2220
// This is before setup_play_space() to ensure that it happens on the frame after
2221
// the play space has been created.
2222
if (unlikely(local_floor_emulation.should_reset_floor_height && !play_space_is_dirty)) {
2223
reset_emulated_floor_height();
2224
local_floor_emulation.should_reset_floor_height = false;
2225
}
2226
2227
if (unlikely(play_space_is_dirty)) {
2228
setup_play_space();
2229
play_space_is_dirty = false;
2230
}
2231
2232
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
2233
wrapper->on_process();
2234
}
2235
2236
return true;
2237
}
2238
2239
void OpenXRAPI::free_main_swapchains() {
2240
for (int i = 0; i < OPENXR_SWAPCHAIN_MAX; i++) {
2241
render_state.main_swapchains[i].queue_free();
2242
}
2243
}
2244
2245
void OpenXRAPI::pre_render() {
2246
ERR_FAIL_COND(session == XR_NULL_HANDLE);
2247
2248
// Must be called from rendering thread!
2249
ERR_NOT_ON_RENDER_THREAD;
2250
2251
if (!render_state.running) {
2252
return;
2253
}
2254
2255
// Process any swapchains that were queued to be freed
2256
OpenXRSwapChainInfo::free_queued();
2257
2258
Size2i swapchain_size = get_recommended_target_size();
2259
if (swapchain_size != render_state.main_swapchain_size) {
2260
// Out with the old.
2261
free_main_swapchains();
2262
2263
// In with the new.
2264
create_main_swapchains(swapchain_size);
2265
}
2266
2267
void *view_locate_info_next_pointer = nullptr;
2268
for (OpenXRExtensionWrapper *extension : frame_info_extensions) {
2269
void *np = extension->set_view_locate_info_and_get_next_pointer(view_locate_info_next_pointer);
2270
if (np != nullptr) {
2271
view_locate_info_next_pointer = np;
2272
}
2273
}
2274
2275
// Get our view info for the frame we're about to render, note from the OpenXR manual:
2276
// "Repeatedly calling xrLocateViews with the same time may not necessarily return the same result. Instead the prediction gets increasingly accurate as the function is called closer to the given time for which a prediction is made"
2277
2278
// We're calling this "relatively" early, the positioning we're obtaining here will be used to do our frustum culling,
2279
// occlusion culling, etc. There is however a technique that we can investigate in the future where after our entire
2280
// Vulkan command buffer is build, but right before vkSubmitQueue is called, we call xrLocateViews one more time and
2281
// update the view and projection matrix once more with a slightly more accurate predication and then submit the
2282
// command queues.
2283
2284
// That is not possible yet but worth investigating in the future.
2285
2286
XrViewLocateInfo view_locate_info = {
2287
XR_TYPE_VIEW_LOCATE_INFO, // type
2288
view_locate_info_next_pointer, // next
2289
view_configuration, // viewConfigurationType
2290
render_state.predicted_display_time, // displayTime
2291
render_state.play_space // space
2292
};
2293
XrViewState view_state = {
2294
XR_TYPE_VIEW_STATE, // type
2295
nullptr, // next
2296
0 // viewStateFlags
2297
};
2298
uint32_t view_count_output;
2299
XrResult result = xrLocateViews(session, &view_locate_info, &view_state, render_state.views.size(), &view_count_output, render_state.views.ptr());
2300
if (XR_FAILED(result)) {
2301
print_line("OpenXR: Couldn't locate views [", get_error_string(result), "]");
2302
return;
2303
}
2304
2305
bool pose_valid = true;
2306
for (uint64_t i = 0; i < view_count_output; i++) {
2307
if ((view_state.viewStateFlags & XR_VIEW_STATE_ORIENTATION_VALID_BIT) == 0 ||
2308
(view_state.viewStateFlags & XR_VIEW_STATE_POSITION_VALID_BIT) == 0) {
2309
pose_valid = false;
2310
}
2311
}
2312
if (render_state.view_pose_valid != pose_valid) {
2313
render_state.view_pose_valid = pose_valid;
2314
if (!render_state.view_pose_valid) {
2315
print_verbose("OpenXR View pose became invalid");
2316
} else {
2317
print_verbose("OpenXR View pose became valid");
2318
}
2319
}
2320
2321
// We should get our frame no from the rendering server, but this will do.
2322
begin_debug_label_region(String("Session Frame ") + String::num_uint64(++render_state.frame));
2323
2324
// let's start our frame..
2325
XrFrameBeginInfo frame_begin_info = {
2326
XR_TYPE_FRAME_BEGIN_INFO, // type
2327
nullptr // next
2328
};
2329
result = xrBeginFrame(session, &frame_begin_info);
2330
if (XR_FAILED(result)) {
2331
print_line("OpenXR: failed to begin frame [", get_error_string(result), "]");
2332
return;
2333
}
2334
2335
// Reset this, we haven't found a viewport for output yet
2336
render_state.has_xr_viewport = false;
2337
2338
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
2339
wrapper->on_pre_render();
2340
}
2341
}
2342
2343
bool OpenXRAPI::pre_draw_viewport(RID p_render_target) {
2344
// Must be called from rendering thread!
2345
ERR_NOT_ON_RENDER_THREAD_V(false);
2346
2347
// We found an XR viewport!
2348
render_state.has_xr_viewport = true;
2349
2350
if (instance == XR_NULL_HANDLE || session == XR_NULL_HANDLE || !render_state.running || !render_state.view_pose_valid || !render_state.should_render) {
2351
return false;
2352
}
2353
2354
// Acquire our images
2355
for (int i = 0; i < OPENXR_SWAPCHAIN_MAX; i++) {
2356
if (!render_state.main_swapchains[i].is_image_acquired() && render_state.main_swapchains[i].get_swapchain() != XR_NULL_HANDLE) {
2357
if (!render_state.main_swapchains[i].acquire(render_state.should_render)) {
2358
return false;
2359
}
2360
}
2361
}
2362
2363
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
2364
wrapper->on_pre_draw_viewport(p_render_target);
2365
}
2366
2367
return true;
2368
}
2369
2370
XrSwapchain OpenXRAPI::get_color_swapchain() {
2371
ERR_NOT_ON_RENDER_THREAD_V(XR_NULL_HANDLE);
2372
2373
return render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].get_swapchain();
2374
}
2375
2376
RID OpenXRAPI::get_color_texture() {
2377
ERR_NOT_ON_RENDER_THREAD_V(RID());
2378
2379
return render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].get_image();
2380
}
2381
2382
RID OpenXRAPI::get_depth_texture() {
2383
ERR_NOT_ON_RENDER_THREAD_V(RID());
2384
2385
// Note, image will not be acquired if we didn't have a suitable swap chain format.
2386
if (render_state.submit_depth_buffer && render_state.main_swapchains[OPENXR_SWAPCHAIN_DEPTH].is_image_acquired()) {
2387
return render_state.main_swapchains[OPENXR_SWAPCHAIN_DEPTH].get_image();
2388
} else {
2389
return RID();
2390
}
2391
}
2392
2393
RID OpenXRAPI::get_density_map_texture() {
2394
ERR_NOT_ON_RENDER_THREAD_V(RID());
2395
2396
OpenXRFBFoveationExtension *fov_ext = OpenXRFBFoveationExtension::get_singleton();
2397
if (fov_ext && fov_ext->is_enabled()) {
2398
return render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].get_density_map();
2399
}
2400
2401
return RID();
2402
}
2403
2404
void OpenXRAPI::set_velocity_texture(RID p_render_target) {
2405
velocity_texture = p_render_target;
2406
}
2407
2408
RID OpenXRAPI::get_velocity_texture() {
2409
return velocity_texture;
2410
}
2411
2412
void OpenXRAPI::set_velocity_depth_texture(RID p_render_target) {
2413
velocity_depth_texture = p_render_target;
2414
}
2415
2416
RID OpenXRAPI::get_velocity_depth_texture() {
2417
return velocity_depth_texture;
2418
}
2419
2420
void OpenXRAPI::set_velocity_target_size(const Size2i &p_target_size) {
2421
velocity_target_size = p_target_size;
2422
}
2423
2424
Size2i OpenXRAPI::get_velocity_target_size() {
2425
return velocity_target_size;
2426
}
2427
2428
const XrCompositionLayerProjection *OpenXRAPI::get_projection_layer() const {
2429
ERR_NOT_ON_RENDER_THREAD_V(nullptr);
2430
2431
return &render_state.projection_layer;
2432
}
2433
2434
void OpenXRAPI::post_draw_viewport(RID p_render_target) {
2435
// Must be called from rendering thread!
2436
ERR_NOT_ON_RENDER_THREAD;
2437
2438
if (instance == XR_NULL_HANDLE || session == XR_NULL_HANDLE || !render_state.running || !render_state.view_pose_valid || !render_state.should_render) {
2439
return;
2440
}
2441
2442
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
2443
wrapper->on_post_draw_viewport(p_render_target);
2444
}
2445
}
2446
2447
void OpenXRAPI::end_frame() {
2448
XrResult result;
2449
2450
ERR_FAIL_COND(session == XR_NULL_HANDLE);
2451
2452
// Must be called from rendering thread!
2453
ERR_NOT_ON_RENDER_THREAD;
2454
2455
if (!render_state.running) {
2456
return;
2457
}
2458
2459
if (render_state.should_render && render_state.view_pose_valid) {
2460
if (!render_state.has_xr_viewport) {
2461
print_line("OpenXR: No viewport was marked with use_xr, there is no rendered output!");
2462
} else if (!render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].is_image_acquired()) {
2463
print_line("OpenXR: No swapchain could be acquired to render to!");
2464
}
2465
}
2466
2467
Rect2i new_render_region = (render_state.render_region != Rect2i()) ? render_state.render_region : Rect2i(Point2i(0, 0), render_state.main_swapchain_size);
2468
2469
for (XrCompositionLayerProjectionView &projection_view : render_state.projection_views) {
2470
projection_view.subImage.imageRect.offset.x = new_render_region.position.x;
2471
projection_view.subImage.imageRect.offset.y = new_render_region.position.y;
2472
projection_view.subImage.imageRect.extent.width = new_render_region.size.width;
2473
projection_view.subImage.imageRect.extent.height = new_render_region.size.height;
2474
}
2475
if (render_state.submit_depth_buffer && OpenXRCompositionLayerDepthExtension::get_singleton()->is_available() && !render_state.depth_views.is_empty()) {
2476
for (XrCompositionLayerDepthInfoKHR &depth_view : render_state.depth_views) {
2477
depth_view.subImage.imageRect.offset.x = new_render_region.position.x;
2478
depth_view.subImage.imageRect.offset.y = new_render_region.position.y;
2479
depth_view.subImage.imageRect.extent.width = new_render_region.size.width;
2480
depth_view.subImage.imageRect.extent.height = new_render_region.size.height;
2481
}
2482
}
2483
2484
// must have:
2485
// - should_render set to true
2486
// - a valid view pose for projection_views[eye].pose to submit layer
2487
// - an image to render
2488
if (!render_state.should_render || !render_state.view_pose_valid || !render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].is_image_acquired()) {
2489
// submit 0 layers when we shouldn't render
2490
XrFrameEndInfo frame_end_info = {
2491
XR_TYPE_FRAME_END_INFO, // type
2492
nullptr, // next
2493
render_state.predicted_display_time, // displayTime
2494
environment_blend_mode, // environmentBlendMode
2495
0, // layerCount
2496
nullptr // layers
2497
};
2498
result = xrEndFrame(session, &frame_end_info);
2499
if (XR_FAILED(result)) {
2500
print_line("OpenXR: rendering skipped and failed to end frame! [", get_error_string(result), "]");
2501
return;
2502
}
2503
2504
end_debug_label_region(); // Session frame #
2505
2506
// neither eye is rendered
2507
return;
2508
}
2509
2510
// release our swapchain image if we acquired it
2511
for (int i = 0; i < OPENXR_SWAPCHAIN_MAX; i++) {
2512
if (render_state.main_swapchains[i].is_image_acquired()) {
2513
render_state.main_swapchains[i].release();
2514
}
2515
}
2516
2517
for (uint32_t eye = 0; eye < render_state.views.size(); eye++) {
2518
render_state.projection_views[eye].fov = render_state.views[eye].fov;
2519
render_state.projection_views[eye].pose = render_state.views[eye].pose;
2520
}
2521
2522
Vector<OrderedCompositionLayer> ordered_layers_list;
2523
bool projection_layer_is_first = true;
2524
2525
// Add composition layers from providers
2526
for (OpenXRExtensionWrapper *extension : composition_layer_providers) {
2527
for (int i = 0; i < extension->get_composition_layer_count(); i++) {
2528
OrderedCompositionLayer layer = {
2529
extension->get_composition_layer(i),
2530
extension->get_composition_layer_order(i),
2531
};
2532
if (layer.composition_layer) {
2533
ordered_layers_list.push_back(layer);
2534
if (layer.sort_order == 0) {
2535
WARN_PRINT_ONCE_ED("Composition layer returned sort order 0, it may be overwritten by projection layer.");
2536
} else if (layer.sort_order < 0) {
2537
projection_layer_is_first = false;
2538
}
2539
}
2540
}
2541
}
2542
2543
XrCompositionLayerFlags layer_flags = XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT;
2544
if (!projection_layer_is_first || environment_blend_mode != XR_ENVIRONMENT_BLEND_MODE_OPAQUE) {
2545
layer_flags |= XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT;
2546
}
2547
2548
render_state.projection_layer.layerFlags = layer_flags;
2549
render_state.projection_layer.space = render_state.play_space;
2550
render_state.projection_layer.viewCount = (uint32_t)render_state.projection_views.size();
2551
render_state.projection_layer.views = render_state.projection_views.ptr();
2552
2553
if (projection_views_extensions.size() > 0) {
2554
for (uint32_t v = 0; v < render_state.projection_views.size(); v++) {
2555
void *next_pointer = nullptr;
2556
for (OpenXRExtensionWrapper *wrapper : projection_views_extensions) {
2557
void *np = wrapper->set_projection_views_and_get_next_pointer(v, next_pointer);
2558
if (np != nullptr) {
2559
next_pointer = np;
2560
}
2561
}
2562
render_state.projection_views[v].next = next_pointer;
2563
}
2564
}
2565
2566
ordered_layers_list.push_back({ (const XrCompositionLayerBaseHeader *)&render_state.projection_layer, 0 });
2567
2568
// Sort our layers.
2569
ordered_layers_list.sort_custom<OrderedCompositionLayer>();
2570
2571
// Now make a list we can pass on to OpenXR.
2572
Vector<const XrCompositionLayerBaseHeader *> layers_list;
2573
for (OrderedCompositionLayer &ordered_layer : ordered_layers_list) {
2574
layers_list.push_back(ordered_layer.composition_layer);
2575
}
2576
2577
void *frame_end_info_next_pointer = nullptr;
2578
for (OpenXRExtensionWrapper *extension : frame_info_extensions) {
2579
void *np = extension->set_frame_end_info_and_get_next_pointer(frame_end_info_next_pointer);
2580
if (np != nullptr) {
2581
frame_end_info_next_pointer = np;
2582
}
2583
}
2584
2585
XrFrameEndInfo frame_end_info = {
2586
XR_TYPE_FRAME_END_INFO, // type
2587
frame_end_info_next_pointer, // next
2588
render_state.predicted_display_time, // displayTime
2589
environment_blend_mode, // environmentBlendMode
2590
static_cast<uint32_t>(layers_list.size()), // layerCount
2591
layers_list.ptr() // layers
2592
};
2593
result = xrEndFrame(session, &frame_end_info);
2594
if (XR_FAILED(result)) {
2595
print_line("OpenXR: failed to end frame! [", get_error_string(result), "]");
2596
return;
2597
}
2598
2599
end_debug_label_region(); // Session frame #
2600
}
2601
2602
float OpenXRAPI::get_display_refresh_rate() const {
2603
OpenXRDisplayRefreshRateExtension *drrext = OpenXRDisplayRefreshRateExtension::get_singleton();
2604
if (drrext) {
2605
return drrext->get_refresh_rate();
2606
}
2607
2608
return 0.0;
2609
}
2610
2611
void OpenXRAPI::set_display_refresh_rate(float p_refresh_rate) {
2612
OpenXRDisplayRefreshRateExtension *drrext = OpenXRDisplayRefreshRateExtension::get_singleton();
2613
if (drrext != nullptr) {
2614
drrext->set_refresh_rate(p_refresh_rate);
2615
}
2616
}
2617
2618
Array OpenXRAPI::get_available_display_refresh_rates() const {
2619
OpenXRDisplayRefreshRateExtension *drrext = OpenXRDisplayRefreshRateExtension::get_singleton();
2620
if (drrext != nullptr) {
2621
return drrext->get_available_refresh_rates();
2622
}
2623
2624
return Array();
2625
}
2626
2627
double OpenXRAPI::get_render_target_size_multiplier() const {
2628
return render_target_size_multiplier;
2629
}
2630
2631
void OpenXRAPI::set_render_target_size_multiplier(double multiplier) {
2632
render_target_size_multiplier = multiplier;
2633
set_render_state_multiplier(multiplier);
2634
}
2635
2636
Rect2i OpenXRAPI::get_render_region() const {
2637
return render_region;
2638
}
2639
2640
void OpenXRAPI::set_render_region(const Rect2i &p_render_region) {
2641
render_region = p_render_region;
2642
set_render_state_render_region(p_render_region);
2643
}
2644
2645
bool OpenXRAPI::is_foveation_supported() const {
2646
OpenXRFBFoveationExtension *fov_ext = OpenXRFBFoveationExtension::get_singleton();
2647
return fov_ext != nullptr && fov_ext->is_enabled();
2648
}
2649
2650
int OpenXRAPI::get_foveation_level() const {
2651
OpenXRFBFoveationExtension *fov_ext = OpenXRFBFoveationExtension::get_singleton();
2652
if (fov_ext != nullptr && fov_ext->is_enabled()) {
2653
switch (fov_ext->get_foveation_level()) {
2654
case XR_FOVEATION_LEVEL_NONE_FB:
2655
return 0;
2656
case XR_FOVEATION_LEVEL_LOW_FB:
2657
return 1;
2658
case XR_FOVEATION_LEVEL_MEDIUM_FB:
2659
return 2;
2660
case XR_FOVEATION_LEVEL_HIGH_FB:
2661
return 3;
2662
default:
2663
return 0;
2664
}
2665
}
2666
2667
return 0;
2668
}
2669
2670
void OpenXRAPI::set_foveation_level(int p_foveation_level) {
2671
ERR_FAIL_UNSIGNED_INDEX(p_foveation_level, 4);
2672
OpenXRFBFoveationExtension *fov_ext = OpenXRFBFoveationExtension::get_singleton();
2673
if (fov_ext != nullptr && fov_ext->is_enabled()) {
2674
XrFoveationLevelFB levels[] = { XR_FOVEATION_LEVEL_NONE_FB, XR_FOVEATION_LEVEL_LOW_FB, XR_FOVEATION_LEVEL_MEDIUM_FB, XR_FOVEATION_LEVEL_HIGH_FB };
2675
fov_ext->set_foveation_level(levels[p_foveation_level]);
2676
}
2677
}
2678
2679
bool OpenXRAPI::get_foveation_dynamic() const {
2680
OpenXRFBFoveationExtension *fov_ext = OpenXRFBFoveationExtension::get_singleton();
2681
if (fov_ext != nullptr && fov_ext->is_enabled()) {
2682
return fov_ext->get_foveation_dynamic() == XR_FOVEATION_DYNAMIC_LEVEL_ENABLED_FB;
2683
}
2684
return false;
2685
}
2686
2687
void OpenXRAPI::set_foveation_dynamic(bool p_foveation_dynamic) {
2688
OpenXRFBFoveationExtension *fov_ext = OpenXRFBFoveationExtension::get_singleton();
2689
if (fov_ext != nullptr && fov_ext->is_enabled()) {
2690
fov_ext->set_foveation_dynamic(p_foveation_dynamic ? XR_FOVEATION_DYNAMIC_LEVEL_ENABLED_FB : XR_FOVEATION_DYNAMIC_DISABLED_FB);
2691
}
2692
}
2693
2694
Size2 OpenXRAPI::get_play_space_bounds() const {
2695
Size2 ret;
2696
2697
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, Size2());
2698
2699
XrExtent2Df extents;
2700
2701
XrResult result = xrGetReferenceSpaceBoundsRect(session, reference_space, &extents);
2702
if (XR_FAILED(result)) {
2703
print_line("OpenXR: failed to get play space bounds! [", get_error_string(result), "]");
2704
return ret;
2705
}
2706
2707
ret.width = extents.width;
2708
ret.height = extents.height;
2709
2710
return ret;
2711
}
2712
2713
PackedInt64Array OpenXRAPI::get_supported_swapchain_formats() {
2714
return supported_swapchain_formats;
2715
}
2716
2717
OpenXRAPI::OpenXRAPI() {
2718
// OpenXRAPI is only constructed if OpenXR is enabled.
2719
singleton = this;
2720
2721
if (Engine::get_singleton()->is_editor_hint()) {
2722
// Enabled OpenXR in the editor? Adjust our settings for the editor
2723
} else {
2724
// Load settings from project settings
2725
int form_factor_setting = GLOBAL_GET("xr/openxr/form_factor");
2726
switch (form_factor_setting) {
2727
case 0: {
2728
form_factor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY;
2729
} break;
2730
case 1: {
2731
form_factor = XR_FORM_FACTOR_HANDHELD_DISPLAY;
2732
} break;
2733
default:
2734
break;
2735
}
2736
2737
int view_configuration_setting = GLOBAL_GET("xr/openxr/view_configuration");
2738
switch (view_configuration_setting) {
2739
case 0: {
2740
view_configuration = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_MONO;
2741
} break;
2742
case 1: {
2743
view_configuration = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
2744
} break;
2745
/* we don't support quad and observer configurations (yet)
2746
case 2: {
2747
view_configuration = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_QUAD_VARJO;
2748
} break;
2749
case 3: {
2750
view_configuration = XR_VIEW_CONFIGURATION_TYPE_SECONDARY_MONO_FIRST_PERSON_OBSERVER_MSFT;
2751
} break;
2752
*/
2753
default:
2754
break;
2755
}
2756
2757
int reference_space_setting = GLOBAL_GET("xr/openxr/reference_space");
2758
switch (reference_space_setting) {
2759
case 0: {
2760
requested_reference_space = XR_REFERENCE_SPACE_TYPE_LOCAL;
2761
} break;
2762
case 1: {
2763
requested_reference_space = XR_REFERENCE_SPACE_TYPE_STAGE;
2764
} break;
2765
case 2: {
2766
requested_reference_space = XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT;
2767
} break;
2768
default:
2769
break;
2770
}
2771
2772
int environment_blend_mode_setting = GLOBAL_GET("xr/openxr/environment_blend_mode");
2773
switch (environment_blend_mode_setting) {
2774
case 0: {
2775
environment_blend_mode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
2776
} break;
2777
case 1: {
2778
environment_blend_mode = XR_ENVIRONMENT_BLEND_MODE_ADDITIVE;
2779
} break;
2780
case 2: {
2781
environment_blend_mode = XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND;
2782
} break;
2783
default:
2784
break;
2785
}
2786
2787
submit_depth_buffer = GLOBAL_GET("xr/openxr/submit_depth_buffer");
2788
}
2789
}
2790
2791
OpenXRAPI::~OpenXRAPI() {
2792
composition_layer_providers.clear();
2793
frame_info_extensions.clear();
2794
supported_extensions.clear();
2795
layer_properties.clear();
2796
2797
#ifdef ANDROID_ENABLED
2798
if (openxr_loader_library_handle) {
2799
OS::get_singleton()->close_dynamic_library(openxr_loader_library_handle);
2800
openxr_loader_library_handle = nullptr;
2801
}
2802
#endif
2803
2804
singleton = nullptr;
2805
}
2806
2807
Transform3D OpenXRAPI::transform_from_pose(const XrPosef &p_pose) {
2808
Quaternion q(p_pose.orientation.x, p_pose.orientation.y, p_pose.orientation.z, p_pose.orientation.w);
2809
Basis basis(q);
2810
Vector3 origin(p_pose.position.x, p_pose.position.y, p_pose.position.z);
2811
2812
return Transform3D(basis, origin);
2813
}
2814
2815
template <typename T>
2816
XRPose::TrackingConfidence _transform_from_location(const T &p_location, Transform3D &r_transform) {
2817
XRPose::TrackingConfidence confidence = XRPose::XR_TRACKING_CONFIDENCE_NONE;
2818
const XrPosef &pose = p_location.pose;
2819
2820
// Check orientation
2821
if (p_location.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) {
2822
Quaternion q(pose.orientation.x, pose.orientation.y, pose.orientation.z, pose.orientation.w);
2823
r_transform.basis = Basis(q);
2824
2825
if (p_location.locationFlags & XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT) {
2826
// Fully valid orientation, so either 3DOF or 6DOF tracking with high confidence so default to HIGH_TRACKING
2827
confidence = XRPose::XR_TRACKING_CONFIDENCE_HIGH;
2828
} else {
2829
// Orientation is being tracked but we're using old/predicted data, so low tracking confidence
2830
confidence = XRPose::XR_TRACKING_CONFIDENCE_LOW;
2831
}
2832
} else {
2833
r_transform.basis = Basis();
2834
}
2835
2836
// Check location
2837
if (p_location.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) {
2838
r_transform.origin = Vector3(pose.position.x, pose.position.y, pose.position.z);
2839
2840
if (!(p_location.locationFlags & XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT)) {
2841
// Location is being tracked but we're using old/predicted data, so low tracking confidence
2842
confidence = XRPose::XR_TRACKING_CONFIDENCE_LOW;
2843
} else if (confidence == XRPose::XR_TRACKING_CONFIDENCE_NONE) {
2844
// Position tracking without orientation tracking?
2845
confidence = XRPose::XR_TRACKING_CONFIDENCE_HIGH;
2846
}
2847
} else {
2848
// No tracking or 3DOF I guess..
2849
r_transform.origin = Vector3();
2850
}
2851
2852
return confidence;
2853
}
2854
2855
XRPose::TrackingConfidence OpenXRAPI::transform_from_location(const XrSpaceLocation &p_location, Transform3D &r_transform) {
2856
return _transform_from_location(p_location, r_transform);
2857
}
2858
2859
XRPose::TrackingConfidence OpenXRAPI::transform_from_location(const XrHandJointLocationEXT &p_location, Transform3D &r_transform) {
2860
return _transform_from_location(p_location, r_transform);
2861
}
2862
2863
void OpenXRAPI::parse_velocities(const XrSpaceVelocity &p_velocity, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity) {
2864
if (p_velocity.velocityFlags & XR_SPACE_VELOCITY_LINEAR_VALID_BIT) {
2865
XrVector3f linear_velocity = p_velocity.linearVelocity;
2866
r_linear_velocity = Vector3(linear_velocity.x, linear_velocity.y, linear_velocity.z);
2867
} else {
2868
r_linear_velocity = Vector3();
2869
}
2870
if (p_velocity.velocityFlags & XR_SPACE_VELOCITY_ANGULAR_VALID_BIT) {
2871
XrVector3f angular_velocity = p_velocity.angularVelocity;
2872
r_angular_velocity = Vector3(angular_velocity.x, angular_velocity.y, angular_velocity.z);
2873
} else {
2874
r_angular_velocity = Vector3();
2875
}
2876
}
2877
2878
bool OpenXRAPI::xr_result(XrResult result, const char *format, Array args) const {
2879
if (XR_SUCCEEDED(result)) {
2880
return true;
2881
}
2882
2883
char resultString[XR_MAX_RESULT_STRING_SIZE];
2884
xrResultToString(instance, result, resultString);
2885
2886
print_error(String("OpenXR ") + String(format).format(args) + String(" [") + String(resultString) + String("]"));
2887
2888
return false;
2889
}
2890
2891
XrPath OpenXRAPI::get_xr_path(const String &p_path) {
2892
ERR_FAIL_COND_V(instance == XR_NULL_HANDLE, XR_NULL_PATH);
2893
2894
if (p_path.is_empty()) {
2895
// This isn't necesairily an issue, so silently return a null path.
2896
return XR_NULL_PATH;
2897
}
2898
2899
XrPath path = XR_NULL_PATH;
2900
2901
XrResult result = xrStringToPath(instance, p_path.utf8().get_data(), &path);
2902
if (XR_FAILED(result)) {
2903
print_line("OpenXR: failed to get path for ", p_path, "! [", get_error_string(result), "]");
2904
return XR_NULL_PATH;
2905
}
2906
2907
return path;
2908
}
2909
2910
String OpenXRAPI::get_xr_path_name(const XrPath &p_path) {
2911
ERR_FAIL_COND_V(instance == XR_NULL_HANDLE, String());
2912
2913
uint32_t size = 0;
2914
char path_name[XR_MAX_PATH_LENGTH];
2915
2916
XrResult result = xrPathToString(instance, p_path, XR_MAX_PATH_LENGTH, &size, path_name);
2917
if (XR_FAILED(result)) {
2918
ERR_FAIL_V_MSG(String(), "OpenXR: failed to get name for a path! [" + get_error_string(result) + "]");
2919
}
2920
2921
return String(path_name);
2922
}
2923
2924
RID OpenXRAPI::get_tracker_rid(XrPath p_path) {
2925
for (const RID &tracker_rid : tracker_owner.get_owned_list()) {
2926
Tracker *tracker = tracker_owner.get_or_null(tracker_rid);
2927
if (tracker && tracker->toplevel_path == p_path) {
2928
return tracker_rid;
2929
}
2930
}
2931
2932
return RID();
2933
}
2934
2935
RID OpenXRAPI::find_tracker(const String &p_name) {
2936
for (const RID &tracker_rid : tracker_owner.get_owned_list()) {
2937
Tracker *tracker = tracker_owner.get_or_null(tracker_rid);
2938
if (tracker && tracker->name == p_name) {
2939
return tracker_rid;
2940
}
2941
}
2942
2943
return RID();
2944
}
2945
2946
RID OpenXRAPI::tracker_create(const String p_name) {
2947
ERR_FAIL_COND_V(instance == XR_NULL_HANDLE, RID());
2948
2949
Tracker new_tracker;
2950
new_tracker.name = p_name;
2951
new_tracker.toplevel_path = XR_NULL_PATH;
2952
new_tracker.active_profile_rid = RID();
2953
2954
new_tracker.toplevel_path = get_xr_path(p_name);
2955
ERR_FAIL_COND_V(new_tracker.toplevel_path == XR_NULL_PATH, RID());
2956
2957
return tracker_owner.make_rid(new_tracker);
2958
}
2959
2960
String OpenXRAPI::tracker_get_name(RID p_tracker) {
2961
if (p_tracker.is_null()) {
2962
return String("None");
2963
}
2964
2965
Tracker *tracker = tracker_owner.get_or_null(p_tracker);
2966
ERR_FAIL_NULL_V(tracker, String());
2967
2968
return tracker->name;
2969
}
2970
2971
void OpenXRAPI::tracker_check_profile(RID p_tracker, XrSession p_session) {
2972
if (p_session == XR_NULL_HANDLE) {
2973
p_session = session;
2974
}
2975
2976
Tracker *tracker = tracker_owner.get_or_null(p_tracker);
2977
ERR_FAIL_NULL(tracker);
2978
2979
if (tracker->toplevel_path == XR_NULL_PATH) {
2980
// no path, how was this even created?
2981
return;
2982
}
2983
2984
XrInteractionProfileState profile_state = {
2985
XR_TYPE_INTERACTION_PROFILE_STATE, // type
2986
nullptr, // next
2987
XR_NULL_PATH // interactionProfile
2988
};
2989
2990
XrResult result = xrGetCurrentInteractionProfile(p_session, tracker->toplevel_path, &profile_state);
2991
if (XR_FAILED(result)) {
2992
print_line("OpenXR: Failed to get interaction profile for", itos(tracker->toplevel_path), "[", get_error_string(result), "]");
2993
return;
2994
}
2995
2996
XrPath new_profile = profile_state.interactionProfile;
2997
XrPath was_profile = get_interaction_profile_path(tracker->active_profile_rid);
2998
if (was_profile != new_profile) {
2999
tracker->active_profile_rid = get_interaction_profile_rid(new_profile);
3000
3001
if (xr_interface) {
3002
xr_interface->tracker_profile_changed(p_tracker, tracker->active_profile_rid);
3003
}
3004
}
3005
}
3006
3007
void OpenXRAPI::tracker_free(RID p_tracker) {
3008
Tracker *tracker = tracker_owner.get_or_null(p_tracker);
3009
ERR_FAIL_NULL(tracker);
3010
3011
// there is nothing to free here
3012
3013
tracker_owner.free(p_tracker);
3014
}
3015
3016
RID OpenXRAPI::action_set_create(const String p_name, const String p_localized_name, const int p_priority) {
3017
ERR_FAIL_COND_V(instance == XR_NULL_HANDLE, RID());
3018
ActionSet action_set;
3019
3020
action_set.name = p_name;
3021
action_set.is_attached = false;
3022
3023
// create our action set...
3024
XrActionSetCreateInfo action_set_info = {
3025
XR_TYPE_ACTION_SET_CREATE_INFO, // type
3026
nullptr, // next
3027
"", // actionSetName
3028
"", // localizedActionSetName
3029
uint32_t(p_priority) // priority
3030
};
3031
3032
copy_string_to_char_buffer(p_name, action_set_info.actionSetName, XR_MAX_ACTION_SET_NAME_SIZE);
3033
copy_string_to_char_buffer(p_localized_name, action_set_info.localizedActionSetName, XR_MAX_LOCALIZED_ACTION_SET_NAME_SIZE);
3034
3035
XrResult result = xrCreateActionSet(instance, &action_set_info, &action_set.handle);
3036
if (XR_FAILED(result)) {
3037
print_line("OpenXR: failed to create action set ", p_name, "! [", get_error_string(result), "]");
3038
return RID();
3039
}
3040
3041
set_object_name(XR_OBJECT_TYPE_ACTION_SET, uint64_t(action_set.handle), p_name);
3042
3043
return action_set_owner.make_rid(action_set);
3044
}
3045
3046
RID OpenXRAPI::find_action_set(const String p_name) {
3047
for (const RID &action_set_rid : action_set_owner.get_owned_list()) {
3048
ActionSet *action_set = action_set_owner.get_or_null(action_set_rid);
3049
if (action_set && action_set->name == p_name) {
3050
return action_set_rid;
3051
}
3052
}
3053
3054
return RID();
3055
}
3056
3057
String OpenXRAPI::action_set_get_name(RID p_action_set) {
3058
if (p_action_set.is_null()) {
3059
return String("None");
3060
}
3061
3062
ActionSet *action_set = action_set_owner.get_or_null(p_action_set);
3063
ERR_FAIL_NULL_V(action_set, String());
3064
3065
return action_set->name;
3066
}
3067
3068
XrActionSet OpenXRAPI::action_set_get_handle(RID p_action_set) {
3069
if (p_action_set.is_null()) {
3070
return XR_NULL_HANDLE;
3071
}
3072
3073
ActionSet *action_set = action_set_owner.get_or_null(p_action_set);
3074
ERR_FAIL_NULL_V(action_set, XR_NULL_HANDLE);
3075
3076
return action_set->handle;
3077
}
3078
3079
bool OpenXRAPI::attach_action_sets(const Vector<RID> &p_action_sets) {
3080
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false);
3081
3082
Vector<XrActionSet> action_handles;
3083
action_handles.resize(p_action_sets.size());
3084
for (int i = 0; i < p_action_sets.size(); i++) {
3085
ActionSet *action_set = action_set_owner.get_or_null(p_action_sets[i]);
3086
ERR_FAIL_NULL_V(action_set, false);
3087
3088
if (action_set->is_attached) {
3089
return false;
3090
}
3091
3092
action_handles.set(i, action_set->handle);
3093
}
3094
3095
// So according to the docs, once we attach our action set to our session it becomes read only..
3096
// https://www.khronos.org/registry/OpenXR/specs/1.0/man/html/xrAttachSessionActionSets.html
3097
XrSessionActionSetsAttachInfo attach_info = {
3098
XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO, // type
3099
nullptr, // next
3100
(uint32_t)p_action_sets.size(), // countActionSets,
3101
action_handles.ptr() // actionSets
3102
};
3103
3104
XrResult result = xrAttachSessionActionSets(session, &attach_info);
3105
if (XR_FAILED(result)) {
3106
print_line("OpenXR: failed to attach action sets! [", get_error_string(result), "]");
3107
return false;
3108
}
3109
3110
for (int i = 0; i < p_action_sets.size(); i++) {
3111
ActionSet *action_set = action_set_owner.get_or_null(p_action_sets[i]);
3112
ERR_FAIL_NULL_V(action_set, false);
3113
action_set->is_attached = true;
3114
}
3115
3116
/* For debugging:
3117
print_verbose("Attached set " + action_set->name);
3118
List<RID> action_rids;
3119
action_owner.get_owned_list(&action_rids);
3120
for (int i = 0; i < action_rids.size(); i++) {
3121
Action * action = action_owner.get_or_null(action_rids[i]);
3122
if (action && action->action_set_rid == p_action_set) {
3123
print_verbose(" - Action " + action->name + ": " + OpenXRUtil::get_action_type_name(action->action_type));
3124
for (int j = 0; j < action->trackers.size(); j++) {
3125
Tracker * tracker = tracker_owner.get_or_null(action->trackers[j].tracker_rid);
3126
if (tracker) {
3127
print_verbose(" - " + tracker->name);
3128
}
3129
}
3130
}
3131
}
3132
*/
3133
3134
return true;
3135
}
3136
3137
void OpenXRAPI::action_set_free(RID p_action_set) {
3138
ActionSet *action_set = action_set_owner.get_or_null(p_action_set);
3139
ERR_FAIL_NULL(action_set);
3140
3141
if (action_set->handle != XR_NULL_HANDLE) {
3142
xrDestroyActionSet(action_set->handle);
3143
}
3144
3145
action_set_owner.free(p_action_set);
3146
}
3147
3148
RID OpenXRAPI::get_action_rid(XrAction p_action) {
3149
for (const RID &action_rid : action_owner.get_owned_list()) {
3150
Action *action = action_owner.get_or_null(action_rid);
3151
if (action && action->handle == p_action) {
3152
return action_rid;
3153
}
3154
}
3155
3156
return RID();
3157
}
3158
3159
RID OpenXRAPI::find_action(const String &p_name, const RID &p_action_set) {
3160
for (const RID &action_rid : action_owner.get_owned_list()) {
3161
Action *action = action_owner.get_or_null(action_rid);
3162
if (action && action->name == p_name && (p_action_set.is_null() || action->action_set_rid == p_action_set)) {
3163
return action_rid;
3164
}
3165
}
3166
3167
return RID();
3168
}
3169
3170
RID OpenXRAPI::action_create(RID p_action_set, const String p_name, const String p_localized_name, OpenXRAction::ActionType p_action_type, const Vector<RID> &p_trackers) {
3171
ERR_FAIL_COND_V(instance == XR_NULL_HANDLE, RID());
3172
3173
Action action;
3174
action.name = p_name;
3175
3176
ActionSet *action_set = action_set_owner.get_or_null(p_action_set);
3177
ERR_FAIL_NULL_V(action_set, RID());
3178
ERR_FAIL_COND_V(action_set->handle == XR_NULL_HANDLE, RID());
3179
action.action_set_rid = p_action_set;
3180
3181
switch (p_action_type) {
3182
case OpenXRAction::OPENXR_ACTION_BOOL:
3183
action.action_type = XR_ACTION_TYPE_BOOLEAN_INPUT;
3184
break;
3185
case OpenXRAction::OPENXR_ACTION_FLOAT:
3186
action.action_type = XR_ACTION_TYPE_FLOAT_INPUT;
3187
break;
3188
case OpenXRAction::OPENXR_ACTION_VECTOR2:
3189
action.action_type = XR_ACTION_TYPE_VECTOR2F_INPUT;
3190
break;
3191
case OpenXRAction::OPENXR_ACTION_POSE:
3192
action.action_type = XR_ACTION_TYPE_POSE_INPUT;
3193
break;
3194
case OpenXRAction::OPENXR_ACTION_HAPTIC:
3195
action.action_type = XR_ACTION_TYPE_VIBRATION_OUTPUT;
3196
break;
3197
default:
3198
ERR_FAIL_V(RID());
3199
break;
3200
}
3201
3202
Vector<XrPath> toplevel_paths;
3203
for (int i = 0; i < p_trackers.size(); i++) {
3204
Tracker *tracker = tracker_owner.get_or_null(p_trackers[i]);
3205
if (tracker != nullptr && tracker->toplevel_path != XR_NULL_PATH) {
3206
ActionTracker action_tracker = {
3207
p_trackers[i], // tracker
3208
XR_NULL_HANDLE, // space
3209
false // was_location_valid
3210
};
3211
action.trackers.push_back(action_tracker);
3212
3213
toplevel_paths.push_back(tracker->toplevel_path);
3214
}
3215
}
3216
3217
XrActionCreateInfo action_info = {
3218
XR_TYPE_ACTION_CREATE_INFO, // type
3219
nullptr, // next
3220
"", // actionName
3221
action.action_type, // actionType
3222
uint32_t(toplevel_paths.size()), // countSubactionPaths
3223
toplevel_paths.ptr(), // subactionPaths
3224
"" // localizedActionName
3225
};
3226
3227
copy_string_to_char_buffer(p_name, action_info.actionName, XR_MAX_ACTION_NAME_SIZE);
3228
copy_string_to_char_buffer(p_localized_name, action_info.localizedActionName, XR_MAX_LOCALIZED_ACTION_NAME_SIZE);
3229
3230
XrResult result = xrCreateAction(action_set->handle, &action_info, &action.handle);
3231
if (XR_FAILED(result)) {
3232
print_line("OpenXR: failed to create action ", p_name, "! [", get_error_string(result), "]");
3233
return RID();
3234
}
3235
3236
set_object_name(XR_OBJECT_TYPE_ACTION, uint64_t(action.handle), p_name);
3237
3238
return action_owner.make_rid(action);
3239
}
3240
3241
String OpenXRAPI::action_get_name(RID p_action) {
3242
if (p_action.is_null()) {
3243
return String("None");
3244
}
3245
3246
Action *action = action_owner.get_or_null(p_action);
3247
ERR_FAIL_NULL_V(action, String());
3248
3249
return action->name;
3250
}
3251
3252
XrAction OpenXRAPI::action_get_handle(RID p_action) {
3253
if (p_action.is_null()) {
3254
return XR_NULL_HANDLE;
3255
}
3256
3257
Action *action = action_owner.get_or_null(p_action);
3258
ERR_FAIL_NULL_V(action, XR_NULL_HANDLE);
3259
3260
return action->handle;
3261
}
3262
3263
void OpenXRAPI::action_free(RID p_action) {
3264
Action *action = action_owner.get_or_null(p_action);
3265
ERR_FAIL_NULL(action);
3266
3267
if (action->handle != XR_NULL_HANDLE) {
3268
xrDestroyAction(action->handle);
3269
}
3270
3271
action_owner.free(p_action);
3272
}
3273
3274
RID OpenXRAPI::get_interaction_profile_rid(XrPath p_path) {
3275
for (const RID &ip_rid : interaction_profile_owner.get_owned_list()) {
3276
InteractionProfile *ip = interaction_profile_owner.get_or_null(ip_rid);
3277
if (ip && ip->path == p_path) {
3278
return ip_rid;
3279
}
3280
}
3281
3282
return RID();
3283
}
3284
3285
XrPath OpenXRAPI::get_interaction_profile_path(RID p_interaction_profile) {
3286
if (p_interaction_profile.is_null()) {
3287
return XR_NULL_PATH;
3288
}
3289
3290
InteractionProfile *ip = interaction_profile_owner.get_or_null(p_interaction_profile);
3291
ERR_FAIL_NULL_V(ip, XR_NULL_PATH);
3292
3293
return ip->path;
3294
}
3295
3296
RID OpenXRAPI::interaction_profile_create(const String p_name) {
3297
if (!is_interaction_profile_supported(p_name)) {
3298
// The extension enabling this path must not be active, we will silently skip this interaction profile
3299
return RID();
3300
}
3301
3302
InteractionProfile new_interaction_profile;
3303
3304
XrResult result = xrStringToPath(instance, p_name.utf8().get_data(), &new_interaction_profile.path);
3305
if (XR_FAILED(result)) {
3306
print_line("OpenXR: failed to get path for ", p_name, "! [", get_error_string(result), "]");
3307
return RID();
3308
}
3309
3310
RID existing_ip = get_interaction_profile_rid(new_interaction_profile.path);
3311
if (existing_ip.is_valid()) {
3312
return existing_ip;
3313
}
3314
3315
new_interaction_profile.name = p_name;
3316
return interaction_profile_owner.make_rid(new_interaction_profile);
3317
}
3318
3319
String OpenXRAPI::interaction_profile_get_name(RID p_interaction_profile) {
3320
if (p_interaction_profile.is_null()) {
3321
return String("None");
3322
}
3323
3324
InteractionProfile *ip = interaction_profile_owner.get_or_null(p_interaction_profile);
3325
ERR_FAIL_NULL_V(ip, String());
3326
3327
return ip->name;
3328
}
3329
3330
void OpenXRAPI::interaction_profile_clear_bindings(RID p_interaction_profile) {
3331
InteractionProfile *ip = interaction_profile_owner.get_or_null(p_interaction_profile);
3332
ERR_FAIL_NULL(ip);
3333
3334
ip->bindings.clear();
3335
}
3336
3337
int OpenXRAPI::interaction_profile_add_binding(RID p_interaction_profile, RID p_action, const String p_path) {
3338
InteractionProfile *ip = interaction_profile_owner.get_or_null(p_interaction_profile);
3339
ERR_FAIL_NULL_V(ip, -1);
3340
3341
if (!interaction_profile_supports_io_path(ip->name, p_path)) {
3342
return -1;
3343
}
3344
3345
XrActionSuggestedBinding binding;
3346
3347
Action *action = action_owner.get_or_null(p_action);
3348
ERR_FAIL_COND_V(action == nullptr || action->handle == XR_NULL_HANDLE, -1);
3349
3350
binding.action = action->handle;
3351
3352
XrResult result = xrStringToPath(instance, p_path.utf8().get_data(), &binding.binding);
3353
if (XR_FAILED(result)) {
3354
print_line("OpenXR: failed to get path for ", p_path, "! [", get_error_string(result), "]");
3355
return -1;
3356
}
3357
3358
ip->bindings.push_back(binding);
3359
3360
return ip->bindings.size() - 1;
3361
}
3362
3363
bool OpenXRAPI::interaction_profile_add_modifier(RID p_interaction_profile, const PackedByteArray &p_modifier) {
3364
InteractionProfile *ip = interaction_profile_owner.get_or_null(p_interaction_profile);
3365
ERR_FAIL_NULL_V(ip, false);
3366
3367
if (!p_modifier.is_empty()) {
3368
// Add it to our stack.
3369
ip->modifiers.push_back(p_modifier);
3370
}
3371
3372
return true;
3373
}
3374
3375
bool OpenXRAPI::interaction_profile_suggest_bindings(RID p_interaction_profile) {
3376
ERR_FAIL_COND_V(instance == XR_NULL_HANDLE, false);
3377
3378
InteractionProfile *ip = interaction_profile_owner.get_or_null(p_interaction_profile);
3379
ERR_FAIL_NULL_V(ip, false);
3380
3381
void *next = nullptr;
3382
3383
// Note, extensions should only add binding modifiers if they are supported, else this may fail.
3384
XrBindingModificationsKHR binding_modifiers;
3385
Vector<const XrBindingModificationBaseHeaderKHR *> modifiers;
3386
if (!ip->modifiers.is_empty()) {
3387
for (const PackedByteArray &modifier : ip->modifiers) {
3388
const XrBindingModificationBaseHeaderKHR *ptr = (const XrBindingModificationBaseHeaderKHR *)modifier.ptr();
3389
modifiers.push_back(ptr);
3390
}
3391
3392
binding_modifiers.type = XR_TYPE_BINDING_MODIFICATIONS_KHR;
3393
binding_modifiers.next = next;
3394
binding_modifiers.bindingModificationCount = modifiers.size();
3395
binding_modifiers.bindingModifications = modifiers.ptr();
3396
next = &binding_modifiers;
3397
}
3398
3399
const XrInteractionProfileSuggestedBinding suggested_bindings = {
3400
XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING, // type
3401
next, // next
3402
ip->path, // interactionProfile
3403
uint32_t(ip->bindings.size()), // countSuggestedBindings
3404
ip->bindings.ptr() // suggestedBindings
3405
};
3406
3407
XrResult result = xrSuggestInteractionProfileBindings(instance, &suggested_bindings);
3408
if (result == XR_ERROR_PATH_UNSUPPORTED) {
3409
// this is fine, not all runtimes support all devices.
3410
print_verbose("OpenXR Interaction profile " + ip->name + " is not supported on this runtime");
3411
} else if (XR_FAILED(result)) {
3412
print_line("OpenXR: failed to suggest bindings for ", ip->name, "! [", get_error_string(result), "]");
3413
// reporting is enough...
3414
}
3415
3416
/* For debugging:
3417
print_verbose("Suggested bindings for " + ip->name);
3418
for (int i = 0; i < ip->bindings.size(); i++) {
3419
uint32_t strlen;
3420
char path[XR_MAX_PATH_LENGTH];
3421
3422
String action_name = action_get_name(get_action_rid(ip->bindings[i].action));
3423
3424
XrResult result = xrPathToString(instance, ip->bindings[i].binding, XR_MAX_PATH_LENGTH, &strlen, path);
3425
if (XR_FAILED(result)) {
3426
print_line("OpenXR: failed to retrieve bindings for ", action_name, "! [", get_error_string(result), "]");
3427
}
3428
print_verbose(" - " + action_name + " => " + String(path));
3429
}
3430
*/
3431
3432
return true;
3433
}
3434
3435
void OpenXRAPI::interaction_profile_free(RID p_interaction_profile) {
3436
InteractionProfile *ip = interaction_profile_owner.get_or_null(p_interaction_profile);
3437
ERR_FAIL_NULL(ip);
3438
3439
ip->bindings.clear();
3440
ip->modifiers.clear();
3441
3442
interaction_profile_owner.free(p_interaction_profile);
3443
}
3444
3445
bool OpenXRAPI::sync_action_sets(const Vector<RID> p_active_sets) {
3446
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false);
3447
3448
if (!running) {
3449
return false;
3450
}
3451
3452
Vector<XrActiveActionSet> active_sets;
3453
for (int i = 0; i < p_active_sets.size(); i++) {
3454
ActionSet *action_set = action_set_owner.get_or_null(p_active_sets[i]);
3455
if (action_set && action_set->handle != XR_NULL_HANDLE) {
3456
XrActiveActionSet aset;
3457
aset.actionSet = action_set->handle;
3458
aset.subactionPath = XR_NULL_PATH;
3459
active_sets.push_back(aset);
3460
}
3461
}
3462
3463
ERR_FAIL_COND_V(active_sets.is_empty(), false);
3464
3465
XrActionsSyncInfo sync_info = {
3466
XR_TYPE_ACTIONS_SYNC_INFO, // type
3467
nullptr, // next
3468
uint32_t(active_sets.size()), // countActiveActionSets
3469
active_sets.ptr() // activeActionSets
3470
};
3471
3472
XrResult result = xrSyncActions(session, &sync_info);
3473
if (XR_FAILED(result)) {
3474
ERR_FAIL_V_MSG(false, "OpenXR: failed to sync active action sets! [" + get_error_string(result) + "]");
3475
}
3476
3477
if (interaction_profile_changed) {
3478
// Just in case, see if we already have active trackers...
3479
for (const RID &tracker : tracker_owner.get_owned_list()) {
3480
tracker_check_profile(tracker);
3481
}
3482
3483
interaction_profile_changed = false;
3484
}
3485
3486
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
3487
wrapper->on_sync_actions();
3488
}
3489
3490
return true;
3491
}
3492
3493
bool OpenXRAPI::get_action_bool(RID p_action, RID p_tracker) {
3494
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false);
3495
Action *action = action_owner.get_or_null(p_action);
3496
ERR_FAIL_NULL_V(action, false);
3497
Tracker *tracker = tracker_owner.get_or_null(p_tracker);
3498
ERR_FAIL_NULL_V(tracker, false);
3499
3500
if (!running) {
3501
return false;
3502
}
3503
3504
ERR_FAIL_COND_V(action->action_type != XR_ACTION_TYPE_BOOLEAN_INPUT, false);
3505
3506
XrActionStateGetInfo get_info = {
3507
XR_TYPE_ACTION_STATE_GET_INFO, // type
3508
nullptr, // next
3509
action->handle, // action
3510
tracker->toplevel_path // subactionPath
3511
};
3512
3513
XrActionStateBoolean result_state;
3514
result_state.type = XR_TYPE_ACTION_STATE_BOOLEAN,
3515
result_state.next = nullptr;
3516
XrResult result = xrGetActionStateBoolean(session, &get_info, &result_state);
3517
if (XR_FAILED(result)) {
3518
print_line("OpenXR: couldn't get action boolean! [", get_error_string(result), "]");
3519
return false;
3520
}
3521
3522
return result_state.isActive && result_state.currentState;
3523
}
3524
3525
float OpenXRAPI::get_action_float(RID p_action, RID p_tracker) {
3526
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, 0.0);
3527
Action *action = action_owner.get_or_null(p_action);
3528
ERR_FAIL_NULL_V(action, 0.0);
3529
Tracker *tracker = tracker_owner.get_or_null(p_tracker);
3530
ERR_FAIL_NULL_V(tracker, 0.0);
3531
3532
if (!running) {
3533
return 0.0;
3534
}
3535
3536
ERR_FAIL_COND_V(action->action_type != XR_ACTION_TYPE_FLOAT_INPUT, 0.0);
3537
3538
XrActionStateGetInfo get_info = {
3539
XR_TYPE_ACTION_STATE_GET_INFO, // type
3540
nullptr, // next
3541
action->handle, // action
3542
tracker->toplevel_path // subactionPath
3543
};
3544
3545
XrActionStateFloat result_state;
3546
result_state.type = XR_TYPE_ACTION_STATE_FLOAT,
3547
result_state.next = nullptr;
3548
XrResult result = xrGetActionStateFloat(session, &get_info, &result_state);
3549
if (XR_FAILED(result)) {
3550
print_line("OpenXR: couldn't get action float! [", get_error_string(result), "]");
3551
return 0.0;
3552
}
3553
3554
return result_state.isActive ? result_state.currentState : 0.0;
3555
}
3556
3557
Vector2 OpenXRAPI::get_action_vector2(RID p_action, RID p_tracker) {
3558
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, Vector2());
3559
Action *action = action_owner.get_or_null(p_action);
3560
ERR_FAIL_NULL_V(action, Vector2());
3561
Tracker *tracker = tracker_owner.get_or_null(p_tracker);
3562
ERR_FAIL_NULL_V(tracker, Vector2());
3563
3564
if (!running) {
3565
return Vector2();
3566
}
3567
3568
ERR_FAIL_COND_V(action->action_type != XR_ACTION_TYPE_VECTOR2F_INPUT, Vector2());
3569
3570
XrActionStateGetInfo get_info = {
3571
XR_TYPE_ACTION_STATE_GET_INFO, // type
3572
nullptr, // next
3573
action->handle, // action
3574
tracker->toplevel_path // subactionPath
3575
};
3576
3577
XrActionStateVector2f result_state;
3578
result_state.type = XR_TYPE_ACTION_STATE_VECTOR2F,
3579
result_state.next = nullptr;
3580
XrResult result = xrGetActionStateVector2f(session, &get_info, &result_state);
3581
if (XR_FAILED(result)) {
3582
print_line("OpenXR: couldn't get action vector2! [", get_error_string(result), "]");
3583
return Vector2();
3584
}
3585
3586
return result_state.isActive ? Vector2(result_state.currentState.x, result_state.currentState.y) : Vector2();
3587
}
3588
3589
XRPose::TrackingConfidence OpenXRAPI::get_action_pose(RID p_action, RID p_tracker, Transform3D &r_transform, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity) {
3590
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, XRPose::XR_TRACKING_CONFIDENCE_NONE);
3591
Action *action = action_owner.get_or_null(p_action);
3592
ERR_FAIL_NULL_V(action, XRPose::XR_TRACKING_CONFIDENCE_NONE);
3593
Tracker *tracker = tracker_owner.get_or_null(p_tracker);
3594
ERR_FAIL_NULL_V(tracker, XRPose::XR_TRACKING_CONFIDENCE_NONE);
3595
3596
if (!running) {
3597
return XRPose::XR_TRACKING_CONFIDENCE_NONE;
3598
}
3599
3600
ERR_FAIL_COND_V(action->action_type != XR_ACTION_TYPE_POSE_INPUT, XRPose::XR_TRACKING_CONFIDENCE_NONE);
3601
3602
// print_verbose("Checking " + action->name + " => " + tracker->name + " (" + itos(tracker->toplevel_path) + ")");
3603
3604
uint64_t index = 0xFFFFFFFF;
3605
uint64_t size = uint64_t(action->trackers.size());
3606
for (uint64_t i = 0; i < size && index == 0xFFFFFFFF; i++) {
3607
if (action->trackers[i].tracker_rid == p_tracker) {
3608
index = i;
3609
}
3610
}
3611
3612
if (index == 0xFFFFFFFF) {
3613
// couldn't find it?
3614
return XRPose::XR_TRACKING_CONFIDENCE_NONE;
3615
}
3616
3617
XrTime display_time = get_predicted_display_time();
3618
if (display_time == 0) {
3619
return XRPose::XR_TRACKING_CONFIDENCE_NONE;
3620
}
3621
3622
if (action->trackers[index].space == XR_NULL_HANDLE) {
3623
// if this is a pose we need to define spaces
3624
3625
XrActionSpaceCreateInfo action_space_info = {
3626
XR_TYPE_ACTION_SPACE_CREATE_INFO, // type
3627
nullptr, // next
3628
action->handle, // action
3629
tracker->toplevel_path, // subactionPath
3630
{
3631
{ 0.0, 0.0, 0.0, 1.0 }, // orientation
3632
{ 0.0, 0.0, 0.0 } // position
3633
} // poseInActionSpace
3634
};
3635
3636
XrSpace space;
3637
XrResult result = xrCreateActionSpace(session, &action_space_info, &space);
3638
if (XR_FAILED(result)) {
3639
print_line("OpenXR: couldn't create action space! [", get_error_string(result), "]");
3640
return XRPose::XR_TRACKING_CONFIDENCE_NONE;
3641
}
3642
3643
action->trackers.ptrw()[index].space = space;
3644
}
3645
3646
XrSpaceVelocity velocity = {
3647
XR_TYPE_SPACE_VELOCITY, // type
3648
nullptr, // next
3649
0, // velocityFlags
3650
{ 0.0, 0.0, 0.0 }, // linearVelocity
3651
{ 0.0, 0.0, 0.0 } // angularVelocity
3652
};
3653
3654
XrSpaceLocation location = {
3655
XR_TYPE_SPACE_LOCATION, // type
3656
&velocity, // next
3657
0, // locationFlags
3658
{
3659
{ 0.0, 0.0, 0.0, 0.0 }, // orientation
3660
{ 0.0, 0.0, 0.0 } // position
3661
} // pose
3662
};
3663
3664
XrResult result = xrLocateSpace(action->trackers[index].space, play_space, display_time, &location);
3665
if (XR_FAILED(result)) {
3666
print_line("OpenXR: failed to locate space! [", get_error_string(result), "]");
3667
return XRPose::XR_TRACKING_CONFIDENCE_NONE;
3668
}
3669
3670
XRPose::TrackingConfidence confidence = transform_from_location(location, r_transform);
3671
parse_velocities(velocity, r_linear_velocity, r_angular_velocity);
3672
3673
return confidence;
3674
}
3675
3676
bool OpenXRAPI::trigger_haptic_pulse(RID p_action, RID p_tracker, float p_frequency, float p_amplitude, XrDuration p_duration_ns) {
3677
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false);
3678
Action *action = action_owner.get_or_null(p_action);
3679
ERR_FAIL_NULL_V(action, false);
3680
Tracker *tracker = tracker_owner.get_or_null(p_tracker);
3681
ERR_FAIL_NULL_V(tracker, false);
3682
3683
if (!running) {
3684
return false;
3685
}
3686
3687
ERR_FAIL_COND_V(action->action_type != XR_ACTION_TYPE_VIBRATION_OUTPUT, false);
3688
3689
XrHapticActionInfo action_info = {
3690
XR_TYPE_HAPTIC_ACTION_INFO, // type
3691
nullptr, // next
3692
action->handle, // action
3693
tracker->toplevel_path // subactionPath
3694
};
3695
3696
XrHapticVibration vibration = {
3697
XR_TYPE_HAPTIC_VIBRATION, // type
3698
nullptr, // next
3699
p_duration_ns, // duration
3700
p_frequency, // frequency
3701
p_amplitude, // amplitude
3702
};
3703
3704
XrResult result = xrApplyHapticFeedback(session, &action_info, (const XrHapticBaseHeader *)&vibration);
3705
if (XR_FAILED(result)) {
3706
print_line("OpenXR: failed to apply haptic feedback! [", get_error_string(result), "]");
3707
return false;
3708
}
3709
3710
return true;
3711
}
3712
3713
void OpenXRAPI::register_composition_layer_provider(OpenXRExtensionWrapper *p_extension) {
3714
composition_layer_providers.append(p_extension);
3715
}
3716
3717
void OpenXRAPI::unregister_composition_layer_provider(OpenXRExtensionWrapper *p_extension) {
3718
composition_layer_providers.erase(p_extension);
3719
}
3720
3721
void OpenXRAPI::register_projection_views_extension(OpenXRExtensionWrapper *p_extension) {
3722
projection_views_extensions.append(p_extension);
3723
}
3724
3725
void OpenXRAPI::unregister_projection_views_extension(OpenXRExtensionWrapper *p_extension) {
3726
projection_views_extensions.erase(p_extension);
3727
}
3728
3729
void OpenXRAPI::register_frame_info_extension(OpenXRExtensionWrapper *p_extension) {
3730
frame_info_extensions.append(p_extension);
3731
}
3732
3733
void OpenXRAPI::unregister_frame_info_extension(OpenXRExtensionWrapper *p_extension) {
3734
frame_info_extensions.erase(p_extension);
3735
}
3736
3737
const Vector<XrEnvironmentBlendMode> OpenXRAPI::get_supported_environment_blend_modes() {
3738
return supported_environment_blend_modes;
3739
}
3740
3741
bool OpenXRAPI::is_environment_blend_mode_supported(XrEnvironmentBlendMode p_blend_mode) const {
3742
return supported_environment_blend_modes.has(p_blend_mode);
3743
}
3744
3745
bool OpenXRAPI::set_environment_blend_mode(XrEnvironmentBlendMode p_blend_mode) {
3746
if (emulate_environment_blend_mode_alpha_blend && p_blend_mode == XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND) {
3747
requested_environment_blend_mode = XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND;
3748
environment_blend_mode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
3749
return true;
3750
}
3751
// We allow setting this when not initialized and will check if it is supported when initializing.
3752
// After OpenXR is initialized we verify we're setting a supported blend mode.
3753
else if (!is_initialized() || is_environment_blend_mode_supported(p_blend_mode)) {
3754
requested_environment_blend_mode = p_blend_mode;
3755
environment_blend_mode = p_blend_mode;
3756
return true;
3757
}
3758
return false;
3759
}
3760
3761
void OpenXRAPI::set_emulate_environment_blend_mode_alpha_blend(bool p_enabled) {
3762
emulate_environment_blend_mode_alpha_blend = p_enabled;
3763
}
3764
3765
OpenXRAPI::OpenXRAlphaBlendModeSupport OpenXRAPI::is_environment_blend_mode_alpha_blend_supported() {
3766
if (emulate_environment_blend_mode_alpha_blend) {
3767
return OPENXR_ALPHA_BLEND_MODE_SUPPORT_EMULATING;
3768
} else if (is_environment_blend_mode_supported(XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND)) {
3769
return OPENXR_ALPHA_BLEND_MODE_SUPPORT_REAL;
3770
}
3771
return OPENXR_ALPHA_BLEND_MODE_SUPPORT_NONE;
3772
}
3773
3774