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