Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/modules/openxr/extensions/openxr_frame_synthesis_extension.cpp
21155 views
1
/**************************************************************************/
2
/* openxr_frame_synthesis_extension.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_frame_synthesis_extension.h"
32
33
#include "core/config/project_settings.h"
34
#include "servers/rendering/rendering_server.h"
35
#include "servers/xr/xr_server.h"
36
37
#define GL_RGBA16F 0x881A
38
#define GL_DEPTH24_STENCIL8 0x88F0
39
40
#define VK_FORMAT_R16G16B16A16_SFLOAT 97
41
#define VK_FORMAT_D24_UNORM_S8_UINT 129
42
43
OpenXRFrameSynthesisExtension *OpenXRFrameSynthesisExtension::singleton = nullptr;
44
45
OpenXRFrameSynthesisExtension *OpenXRFrameSynthesisExtension::get_singleton() {
46
return singleton;
47
}
48
49
void OpenXRFrameSynthesisExtension::_bind_methods() {
50
ClassDB::bind_method(D_METHOD("is_available"), &OpenXRFrameSynthesisExtension::is_available);
51
52
ClassDB::bind_method(D_METHOD("is_enabled"), &OpenXRFrameSynthesisExtension::is_enabled);
53
ClassDB::bind_method(D_METHOD("set_enabled", "enable"), &OpenXRFrameSynthesisExtension::set_enabled);
54
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
55
56
ClassDB::bind_method(D_METHOD("get_relax_frame_interval"), &OpenXRFrameSynthesisExtension::get_relax_frame_interval);
57
ClassDB::bind_method(D_METHOD("set_relax_frame_interval", "relax_frame_interval"), &OpenXRFrameSynthesisExtension::set_relax_frame_interval);
58
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "relax_frame_interval"), "set_relax_frame_interval", "get_relax_frame_interval");
59
60
ClassDB::bind_method(D_METHOD("skip_next_frame"), &OpenXRFrameSynthesisExtension::skip_next_frame);
61
}
62
63
OpenXRFrameSynthesisExtension::OpenXRFrameSynthesisExtension() {
64
singleton = this;
65
}
66
67
OpenXRFrameSynthesisExtension::~OpenXRFrameSynthesisExtension() {
68
singleton = nullptr;
69
}
70
71
HashMap<String, bool *> OpenXRFrameSynthesisExtension::get_requested_extensions(XrVersion p_version) {
72
HashMap<String, bool *> request_extensions;
73
74
if (GLOBAL_GET("xr/openxr/extensions/frame_synthesis")) {
75
request_extensions[XR_EXT_FRAME_SYNTHESIS_EXTENSION_NAME] = &frame_synthesis_ext;
76
}
77
78
return request_extensions;
79
}
80
81
void OpenXRFrameSynthesisExtension::on_instance_created(const XrInstance p_instance) {
82
// Register this as a projection view extension
83
if (frame_synthesis_ext) {
84
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
85
ERR_FAIL_NULL(openxr_api);
86
openxr_api->register_projection_views_extension(this);
87
}
88
89
// Enable this if our extension was successfully enabled
90
enabled = frame_synthesis_ext;
91
render_state.enabled = frame_synthesis_ext;
92
}
93
94
void OpenXRFrameSynthesisExtension::on_instance_destroyed() {
95
// Unregister this as a projection view extension.
96
if (frame_synthesis_ext) {
97
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
98
ERR_FAIL_NULL(openxr_api);
99
openxr_api->unregister_projection_views_extension(this);
100
}
101
102
frame_synthesis_ext = false;
103
enabled = false;
104
render_state.enabled = false;
105
}
106
107
void OpenXRFrameSynthesisExtension::prepare_view_configuration(uint32_t p_view_count) {
108
if (!frame_synthesis_ext) {
109
return;
110
}
111
112
// Called during initialization, we can safely change this.
113
render_state.config_views.resize(p_view_count);
114
115
for (XrFrameSynthesisConfigViewEXT &config_view : render_state.config_views) {
116
config_view.type = XR_TYPE_FRAME_SYNTHESIS_CONFIG_VIEW_EXT;
117
config_view.next = nullptr;
118
119
// These will be set by xrEnumerateViewConfigurationViews.
120
config_view.recommendedMotionVectorImageRectWidth = 0;
121
config_view.recommendedMotionVectorImageRectHeight = 0;
122
}
123
}
124
125
void *OpenXRFrameSynthesisExtension::set_view_configuration_and_get_next_pointer(uint32_t p_view, void *p_next_pointer) {
126
if (!frame_synthesis_ext) {
127
return nullptr;
128
}
129
130
// Called during initialization, we can safely access this.
131
ERR_FAIL_UNSIGNED_INDEX_V(p_view, render_state.config_views.size(), nullptr);
132
133
XrFrameSynthesisConfigViewEXT &config_view = render_state.config_views[p_view];
134
config_view.next = p_next_pointer;
135
136
return &config_view;
137
}
138
139
void OpenXRFrameSynthesisExtension::print_view_configuration_info(uint32_t p_view) const {
140
if (!frame_synthesis_ext) {
141
return;
142
}
143
144
// Called during initialization, we can safely access this.
145
if (p_view < render_state.config_views.size()) {
146
const XrFrameSynthesisConfigViewEXT &config_view = render_state.config_views[p_view];
147
148
print_line(" - motion vector width: ", itos(config_view.recommendedMotionVectorImageRectWidth));
149
print_line(" - motion vector height: ", itos(config_view.recommendedMotionVectorImageRectHeight));
150
}
151
}
152
153
void OpenXRFrameSynthesisExtension::on_session_destroyed() {
154
if (!frame_synthesis_ext) {
155
return;
156
}
157
158
// Free our swapchains.
159
free_swapchains();
160
}
161
162
void OpenXRFrameSynthesisExtension::on_main_swapchains_created() {
163
if (!frame_synthesis_ext) {
164
return;
165
}
166
167
// It is possible that our swapchain information gets resized,
168
// and that our motion vector and depth resolution changes with this.
169
// So (re)create our swapchains here as well.
170
// Note that we do this even if motion vectors aren't enabled yet.
171
172
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
173
ERR_FAIL_NULL(openxr_api);
174
175
RenderingServer *rendering_server = RenderingServer::get_singleton();
176
ERR_FAIL_NULL(rendering_server);
177
178
// Out with the old.
179
free_swapchains();
180
181
// We only support stereo.
182
size_t view_count = render_state.config_views.size();
183
ERR_FAIL_COND(view_count != 2);
184
185
// Determine specific values for each renderer.
186
int swapchain_format = 0;
187
int depth_swapchain_format = 0;
188
String rendering_driver_name = rendering_server->get_current_rendering_driver_name();
189
if (rendering_driver_name.contains("opengl")) {
190
swapchain_format = GL_RGBA16F;
191
depth_swapchain_format = GL_DEPTH24_STENCIL8;
192
} else if (rendering_driver_name == "vulkan") {
193
String rendering_method = rendering_server->get_current_rendering_method();
194
if (rendering_method == "mobile") {
195
swapchain_format = VK_FORMAT_R16G16B16A16_SFLOAT;
196
depth_swapchain_format = VK_FORMAT_D24_UNORM_S8_UINT;
197
} else {
198
WARN_PRINT("OpenXR: Frame synthesis not supported for this rendering method!");
199
frame_synthesis_ext = false;
200
openxr_api->unregister_projection_views_extension(this);
201
return;
202
}
203
} else {
204
WARN_PRINT("OpenXR: Frame synthesis not supported for this rendering driver!");
205
frame_synthesis_ext = false;
206
openxr_api->unregister_projection_views_extension(this);
207
return;
208
}
209
210
// We assume the size for each eye is the same, it should be.
211
uint32_t width = render_state.config_views[0].recommendedMotionVectorImageRectWidth;
212
uint32_t height = render_state.config_views[0].recommendedMotionVectorImageRectHeight;
213
214
// Create swapchains for motion vectors and depth.
215
render_state.swapchains[SWAPCHAIN_MOTION_VECTOR].create(0, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT, swapchain_format, width, height, 1, view_count);
216
render_state.swapchains[SWAPCHAIN_DEPTH].create(0, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT, depth_swapchain_format, width, height, 1, view_count);
217
218
// Set up our frame synthesis info.
219
render_state.frame_synthesis_info.resize(view_count);
220
221
uint32_t index = 0;
222
for (XrFrameSynthesisInfoEXT &frame_synthesis_info : render_state.frame_synthesis_info) {
223
frame_synthesis_info.type = XR_TYPE_FRAME_SYNTHESIS_INFO_EXT;
224
frame_synthesis_info.next = nullptr;
225
frame_synthesis_info.layerFlags = 0;
226
227
// Set up motion vector.
228
frame_synthesis_info.motionVectorSubImage.swapchain = render_state.swapchains[SWAPCHAIN_MOTION_VECTOR].get_swapchain();
229
frame_synthesis_info.motionVectorSubImage.imageArrayIndex = index;
230
frame_synthesis_info.motionVectorSubImage.imageRect.offset.x = 0;
231
frame_synthesis_info.motionVectorSubImage.imageRect.offset.y = 0;
232
frame_synthesis_info.motionVectorSubImage.imageRect.extent.width = width;
233
frame_synthesis_info.motionVectorSubImage.imageRect.extent.height = height;
234
235
// Q: this should be 1.0, -1.0, 1.0. We output OpenGL NDC, frame synthesis expects Vulkan NDC, but might be a problem on runtime I'm testing.
236
frame_synthesis_info.motionVectorScale = { 1.0, 1.0, 1.0, 0.0 };
237
frame_synthesis_info.motionVectorOffset = { 0.0, 0.0, 0.0, 0.0 };
238
frame_synthesis_info.appSpaceDeltaPose = { { 0.0, 0.0, 0.0, 1.0 }, { 0.0, 0.0, 0.0 } };
239
240
// Set up depth image.
241
frame_synthesis_info.depthSubImage.swapchain = render_state.swapchains[SWAPCHAIN_DEPTH].get_swapchain();
242
frame_synthesis_info.depthSubImage.imageArrayIndex = index;
243
frame_synthesis_info.depthSubImage.imageRect.offset.x = 0;
244
frame_synthesis_info.depthSubImage.imageRect.offset.y = 0;
245
frame_synthesis_info.depthSubImage.imageRect.extent.width = width;
246
frame_synthesis_info.depthSubImage.imageRect.extent.height = height;
247
248
frame_synthesis_info.minDepth = 0.0;
249
frame_synthesis_info.maxDepth = 1.0;
250
251
// Note: reverse-Z, these are just defaults for now.
252
frame_synthesis_info.nearZ = 100.0;
253
frame_synthesis_info.farZ = 0.01;
254
255
index++;
256
}
257
}
258
259
void OpenXRFrameSynthesisExtension::on_pre_render() {
260
if (!frame_synthesis_ext) {
261
return;
262
}
263
264
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
265
ERR_FAIL_NULL(openxr_api);
266
267
size_t view_count = render_state.config_views.size();
268
if (!enabled || view_count != 2 || render_state.skip_next_frame) {
269
// Unset these just in case.
270
openxr_api->set_velocity_texture(RID());
271
openxr_api->set_velocity_depth_texture(RID());
272
273
// Remember our transform just in case we (re)start frame synthesis later on.
274
render_state.previous_transform = XRServer::get_singleton()->get_world_origin();
275
276
return;
277
}
278
279
// Acquire our swapchains.
280
for (int i = 0; i < SWAPCHAIN_MAX; i++) {
281
bool should_render = true;
282
render_state.swapchains[i].acquire(should_render);
283
}
284
285
// Set our images.
286
openxr_api->set_velocity_texture(render_state.swapchains[SWAPCHAIN_MOTION_VECTOR].get_image());
287
openxr_api->set_velocity_depth_texture(render_state.swapchains[SWAPCHAIN_DEPTH].get_image());
288
289
// Set our size.
290
uint32_t width = render_state.config_views[0].recommendedMotionVectorImageRectWidth;
291
uint32_t height = render_state.config_views[0].recommendedMotionVectorImageRectHeight;
292
openxr_api->set_velocity_target_size(Size2i(width, height));
293
294
// Get our head motion
295
Transform3D world_transform = XRServer::get_singleton()->get_world_origin();
296
Transform3D delta_transform = render_state.previous_transform.affine_inverse() * world_transform;
297
Quaternion delta_quat = delta_transform.basis.get_quaternion();
298
Vector3 delta_origin = delta_transform.origin;
299
300
// Z near/far can change per frame, so make sure we update this.
301
for (XrFrameSynthesisInfoEXT &frame_synthesis_info : render_state.frame_synthesis_info) {
302
frame_synthesis_info.layerFlags = render_state.relax_frame_interval ? XR_FRAME_SYNTHESIS_INFO_REQUEST_RELAXED_FRAME_INTERVAL_BIT_EXT : 0;
303
304
frame_synthesis_info.appSpaceDeltaPose = {
305
{ (float)delta_quat.x, (float)delta_quat.y, (float)delta_quat.z, (float)delta_quat.w },
306
{ (float)delta_origin.x, (float)delta_origin.y, (float)delta_origin.z }
307
};
308
309
// Note: reverse-Z.
310
frame_synthesis_info.nearZ = openxr_api->get_render_state_z_far();
311
frame_synthesis_info.farZ = openxr_api->get_render_state_z_near();
312
}
313
314
// Remember our transform.
315
render_state.previous_transform = world_transform;
316
}
317
318
void OpenXRFrameSynthesisExtension::on_post_draw_viewport(RID p_render_target) {
319
// Check if our extension is supported and enabled.
320
if (!frame_synthesis_ext || !enabled || render_state.config_views.size() != 2 || render_state.skip_next_frame) {
321
return;
322
}
323
324
// Release our swapchains.
325
for (int i = 0; i < SWAPCHAIN_MAX; i++) {
326
render_state.swapchains[i].release();
327
}
328
}
329
330
void *OpenXRFrameSynthesisExtension::set_projection_views_and_get_next_pointer(int p_view_index, void *p_next_pointer) {
331
// Check if our extension is supported and enabled.
332
if (!frame_synthesis_ext || !enabled || render_state.config_views.size() != 2) {
333
return nullptr;
334
}
335
336
// Did we skip this frame?
337
if (render_state.skip_next_frame) {
338
// Only unset when we've handled both eyes.
339
if (p_view_index == 1) {
340
render_state.skip_next_frame = false;
341
}
342
return nullptr;
343
}
344
345
// Check if we can run frame synthesis.
346
size_t view_count = render_state.config_views.size();
347
if (enabled && view_count == 2) {
348
render_state.frame_synthesis_info[p_view_index].next = p_next_pointer;
349
return &render_state.frame_synthesis_info[p_view_index];
350
}
351
352
return nullptr;
353
}
354
355
bool OpenXRFrameSynthesisExtension::is_available() const {
356
return frame_synthesis_ext;
357
}
358
359
bool OpenXRFrameSynthesisExtension::is_enabled() const {
360
return frame_synthesis_ext && enabled;
361
}
362
363
void OpenXRFrameSynthesisExtension::set_enabled(bool p_enabled) {
364
if (enabled == p_enabled) {
365
return;
366
}
367
ERR_FAIL_COND(!frame_synthesis_ext && p_enabled);
368
369
enabled = p_enabled;
370
371
RenderingServer *rendering_server = RenderingServer::get_singleton();
372
ERR_FAIL_NULL(rendering_server);
373
rendering_server->call_on_render_thread(callable_mp(this, &OpenXRFrameSynthesisExtension::_set_render_state_enabled_rt).bind(enabled));
374
}
375
376
bool OpenXRFrameSynthesisExtension::get_relax_frame_interval() const {
377
return relax_frame_interval;
378
}
379
380
void OpenXRFrameSynthesisExtension::set_relax_frame_interval(bool p_relax_frame_interval) {
381
if (relax_frame_interval == p_relax_frame_interval) {
382
return;
383
}
384
relax_frame_interval = p_relax_frame_interval;
385
386
RenderingServer *rendering_server = RenderingServer::get_singleton();
387
ERR_FAIL_NULL(rendering_server);
388
rendering_server->call_on_render_thread(callable_mp(this, &OpenXRFrameSynthesisExtension::_set_relax_frame_interval_rt).bind(relax_frame_interval));
389
}
390
391
void OpenXRFrameSynthesisExtension::_set_render_state_enabled_rt(bool p_enabled) {
392
render_state.enabled = p_enabled;
393
}
394
395
void OpenXRFrameSynthesisExtension::_set_relax_frame_interval_rt(bool p_relax_frame_interval) {
396
render_state.relax_frame_interval = p_relax_frame_interval;
397
}
398
399
void OpenXRFrameSynthesisExtension::free_swapchains() {
400
for (int i = 0; i < SWAPCHAIN_MAX; i++) {
401
render_state.swapchains[i].queue_free();
402
}
403
}
404
405
void OpenXRFrameSynthesisExtension::skip_next_frame() {
406
RenderingServer *rendering_server = RenderingServer::get_singleton();
407
ERR_FAIL_NULL(rendering_server);
408
rendering_server->call_on_render_thread(callable_mp(this, &OpenXRFrameSynthesisExtension::_set_skip_next_frame_rt));
409
}
410
411
void OpenXRFrameSynthesisExtension::_set_skip_next_frame_rt() {
412
render_state.skip_next_frame = true;
413
}
414
415