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